Visualizzazione post con etichetta Kotlin. Mostra tutti i post
Visualizzazione post con etichetta Kotlin. Mostra tutti i post

mercoledì 26 agosto 2020

Scansioni codici QR in Kotlin Android

Nel 2013 avevo fatto qualche esperimento con la lettura dei codici a barre tramite la libreria ZXing.

Ad oggi le funzioni di scansioni dei codici sono incluse nelle librerie di Google Play ma le trovo complicate mentre la libreria ZXing (https://github.com/zxing/zxing) e' in maintenance mode. Ho trovato una alternativa a questo link    https://github.com/yuriy-budiyev/code-scanner

build.gradle(app)

implementation 'com.budiyev.android:code-scanner:2.1.0'

Layout
=====================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.budiyev.android.codescanner.CodeScannerView
android:id="@+id/scanner_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:autoFocusButtonColor="@android:color/white"
app:autoFocusButtonVisible="true"
app:flashButtonColor="@android:color/white"
app:flashButtonVisible="true"
app:frameColor="@android:color/white"
app:frameCornersSize="50dp"
app:frameCornersRadius="0dp"
app:frameAspectRatioWidth="1"
app:frameAspectRatioHeight="1"
app:frameSize="0.75"
app:frameThickness="2dp"
app:maskColor="#77000000"/>
</FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
=====================================================
codice

=====================================================

    val scannerView = findViewById<CodeScannerView>(R.id.scanner_view)

codeScanner = CodeScanner(this, scannerView)

// Parameters (default values)
codeScanner.camera = CodeScanner.CAMERA_BACK // or CAMERA_FRONT or specific camera id
codeScanner.formats = CodeScanner.ALL_FORMATS // list of type BarcodeFormat,
// ex. listOf(BarcodeFormat.QR_CODE)
codeScanner.autoFocusMode = AutoFocusMode.SAFE // or CONTINUOUS
codeScanner.scanMode = ScanMode.SINGLE // or CONTINUOUS or PREVIEW
codeScanner.isAutoFocusEnabled = true // Whether to enable auto focus or not
codeScanner.isFlashEnabled = false // Whether to enable flash or not

// Callbacks
codeScanner.decodeCallback = DecodeCallback {
runOnUiThread {
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
}
}
codeScanner.errorCallback = ErrorCallback { // or ErrorCallback.SUPPRESS
runOnUiThread {
Toast.makeText(this, "Camera initialization error: ${it.message}",
Toast.LENGTH_LONG).show()
}
}

scannerView.setOnClickListener {
codeScanner.startPreview()
}
}

override fun onResume() {
super.onResume()
codeScanner.startPreview()
}

override fun onPause() {
codeScanner.releaseResources()
super.onPause()
}

lunedì 24 agosto 2020

Decrittare PGP in Kotlin per Android


Un comodo sistema per decrittare messaggi PGP con Kotlin su Android e' quello di usare la libreria https://github.com/Tlaster/KotlinPGP che in pratica e' un wrapper per BountyCastle 

in build.gradle(app)  si aggiunge
 
implementation 'moe.tlaster:kotlinpgp:1.0.20'

il codice e' sostanzialmente autoesplicativo per quanto e' semplice

=================================================

package com.luca.innocenti.pgp_k2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import moe.tlaster.kotlinpgp.KotlinPGP

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var segreta = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"\n" +
"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\n" +
"1XGfLXE+Y3U1WlFWF19aWRB054bdz5DDgBjIbw==\n" +
"=7ffe\n" +
"-----END PGP PRIVATE KEY BLOCK-----"

var encryptedData:String = "-----BEGIN PGP MESSAGE-----\n" +
"Version: OpenPGP.js v4.10.4\n" +
"Comment: https://openpgpjs.org\n" +
"\n" +
"wcBMA8AU2yvA99UdAQf+MylbvwaDMuqBneUH8kfMhq5CxCiUwORkZ4sE7/e+\n" +
"CIb+XTc5wYCuTIIqsv8JfcCjLs6fSwjlvEcvjSAn08geiIrquiBbgnkBWPR7\n" +
"iv7CMt6LPzcKfuHt18ZZarEQEf4mwH/DxU+ohYqXpRIuAVbGbcR1n1qWPkto\n" +
"hWDQQLXFZ/fAYKdZekA+SdvISrzpEbS+yy5y0K035oD0aqee6Up93jARvHTk\n" +
"R7kWuBCFoMk23PDTZM+Hv9DNumOXIOm/avZfbVr4I0O0LYlCaq1QOkmV1sfq\n" +
"P2lLD2cGgYp0c6VeLCb9/bbZx/UXPBiakXS3dH09jDE7zUXvk9mrPFz3YlA5\n" +
"M9I/AViN02M0pF83CYzDqCkJ4GkzjAv50bXWzn4ZS0nNR1dl9rY/Ve6KSGiV\n" +
"kTRtpLQ17TI1awzTg4wyifMkrYNW\n" +
"=xPR8\n" +
"-----END PGP MESSAGE-----"

val decrypt = KotlinPGP.decrypt(segreta, "password", encryptedData)
Log.d("test", decrypt.result)

}
}

giovedì 2 aprile 2020

JSON con GSON e Kotlin

Per leggere un file Json con Kotlin si puo' utilizzare la libreria GSON aggiungendo il gradle.build la seguente linea
implementation 'com.google.code.gson:gson:2.8.6'
facciamo per esempio che il file JSON ha il seguente tracciato

    val json = "{'cicli':26,'soglia': 1.5}"
si deve creare una classe che ricostruisce il tracciato record e crea dei metodi per leggere le proprieta'
class imposta(
var cicli:Int = 0,
var soglia:Float = 0.0f) {
fun Get_cicli():Int {
return cicli
}

fun Get_soglia():Float {
return soglia
}
override fun toString(): String {
return "${this.cicli}, ${this.soglia}"
}
}

dopo si cio' si puo' chiamare la funzione .fromJson per poter popolare le variabili
override fun onCreate(savedInstanceState: Bundle?) {
...........

val json = "{'cicli':26,'soglia': 1.5}"


var impostazioni = Gson().fromJson(json, imposta::class.java)
var prova = impostazioni.Get_cicli()
Log.d(
"imposta", impostazioni.toString())
Log.d(
"imposta", prova.toString())



domenica 22 marzo 2020

Database su Android con Room e Kotlin

Il nuovo approccio per avere un database persistente su Android e' quello di utilizzare Room

Per integrare la libreria nel progetto Android si deve modificare il file build.gradle (app) aggiungendo il plugin kapt in testa

apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'apply plugin: 'kotlin-kapt'

ed aggiungendo le seguenti dipendenze (questo sono le dipendenze per Kotlin, nel caso di uso di Java la terza riga, quella che inizia con kapt, deve essere sostituita con
annotationProcessor "androidx.room:room-compiler:$room_version"
==============================
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"kapt "android.arch.persistence.room:compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Roomimplementation "androidx.room:room-ktx:$room_version"
// Test helperstestImplementation "androidx.room:room-testing:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"

La gestione delle chiamate Rooms avviene per SQL

In sintesi viene definito il tracciato record del database. Da notare che non sono esplicitamente previsti campi DateTime; per questo motivo uso lo Unix Time come Long
Il nome dell'unica tabella e' nome_tabella

Le query sono incluse nella sezione DAO. Ogni query riporta un int che dice quante righe sono state interessate dalla query stessa

Nella sezione Database viene collegata la tabella al Db
Attenzione: se si cambia il tracciato record deve essere modificato il numero di versione del Db
Il nome del file e' inserito in DatabaseBuilder

Le query vengono gestite all'interno di coroutine

Per copiare il database dal telefono al PC si puo' usare

adb -d shell "run-as com.luca.innocenti.roomdb cat /data/data/com.luca.innocenti.roomdb/databases/sensori.db" > /home/luca/room.db

dove com.luca.innocenti.roomdb e' il nome del package mentre sensori.db e' il nome del file
database sul telefono
Altrimenti da Android Studio si va su View/Tool Windows/Device File Explorer

Per leggere il file su desktop si puo' utilizzare DB Browser for SQLite

package com.luca.innocenti.roomdb

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.room.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    @Entity(tableName = "nome_tabella")
    data class TabellaEntity(
            @PrimaryKey(autoGenerate = true)
            var id: Int,            @ColumnInfo(name = "tempo") var tempo: Long = 0,            @ColumnInfo(name = "pitch") var pitch: Float = 0.0f,            @ColumnInfo(name = "roll") var roll: Float = 0.0f,            @ColumnInfo(name = "azimuth") var azimuth: Float = 0.0f,            @ColumnInfo(name = "nota") var nota: String

    )

    @Dao    interface DbDao {
        @Query("SELECT * FROM nome_tabella")
        fun getAll(): List<TabellaEntity>

        @Query("SELECT * FROM nome_tabella WHERE nota LIKE :nota")
        fun findByNota(nota: String): List<TabellaEntity>

        @Insert        fun insertAll(vararg todo: TabellaEntity)

        @Delete        fun delete(todo: TabellaEntity)

        @Query("DELETE FROM nome_tabella WHERE id = :id")
        fun getSingleRecordDelete(id: Int): Int

        @Query("UPDATE nome_tabella SET nota=:nota WHERE id = :indice")
        fun getSingleRecordUpdate(nota: String, indice: Int): Int


        @Update        fun updateDb(vararg todos: TabellaEntity)
    }

    @Database(entities = [TabellaEntity::class], version = 1)
    abstract class AppDatabase : RoomDatabase() {
        abstract fun DbDao(): DbDao
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        val db = Room.databaseBuilder(
                applicationContext,                AppDatabase::class.java, "sensori.db"        ).build()

        GlobalScope.launch {            db.DbDao().insertAll(TabellaEntity(0,12,13.4f,7.7f,0.3f,"Nota"))
            db.DbDao().insertAll(TabellaEntity(0,13,13.4f,7.7f,0.3f,"Nota2"))

            var data = db.DbDao().getAll()

            data?.forEach {                Log.d("test", it.toString())
            }
            var ricerca = db.DbDao().findByNota("Nota")
            ricerca?.forEach{                Log.d("ricerca", ricerca.toString())
            }
            db.DbDao().getSingleRecordDelete(6)

            data = db.DbDao().getAll()
            data?.forEach {                Log.d("dopo", it.toString())
            }
            db.DbDao().getSingleRecordUpdate("modifica",6)

            data = db.DbDao().getAll()

            data?.forEach {                Log.d("dopo", it.toString())
            }
            //db.clearAllTables()            db.close()
        }    }
}

mercoledì 2 agosto 2017

Kotlin su Android Studio 2

Come aggiungere il supporto a Kotlin in Android Studio 2

Per prima cosa da File/Preferences si cercano i plugin e si installa JetBrains Plugin. Fatto cio' appare nella colonna di destra il plugin di Kotlin da poter installare


Fatto cio', per convertire un progetto Android/Java in Android/Kotlin, si crea un normale progetto, successivamente con la combinazione cmd+shift+A (in Mac) si cerca Convert Java File to Kotlin

L'IDE a questa punto richiede di risincronizzare il progetto



ed il codice Java viene convertito in Kotlin. Al monte di compilare viene anche chiesto di aggiornare gradle
il codice di un semplice esempio che cambia la stringa di una textview e' il seguente

package innocenti.luca.com.kot_test

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val testo: TextView = findViewById(R.id.textView) as TextView;
        testo.setText("Luca");

    }
}


Geologi

  E so anche espatriare senza praticamente toccare strada asfaltata