venerdì 29 dicembre 2017

Compilare ARToolKit su Android Studio 3

Le istruzioni per installare ARToolKit su Android Studio sono piuttosto vecchie e di fatto non funzionano nella configurazione attuale (sono state scritte nel 2016)

Per fare funzionare gli esempi, dopo avere compilato la libreria nell'NDK (come da istruzioni), si devono modificare i file di progetto come segue (vedi linee evidenziate in giallo)

---------------------------
buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath 'com.android.tools.build:gradle-experimental:0.11.1'

        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}
---------------------------


gradle.wrapper.properties
---------------------------
#Fri Dec 29 14:39:59 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
---------------------------


build.gradle (Module ArSimple)
---------------------------

apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23        buildToolsVersion = "23.0.2"
        defaultConfig.with {
            applicationId = "org.artoolkit.ar.samples.ARSimple"            minSdkVersion.apiLevel = 15            targetSdkVersion.apiLevel = 22            versionCode = 1            //Integer type incremented by 1 for every release, major or minor, to Google store            versionName = "1.0" //Real fully qualified major and minor release description
            buildConfigFields.with {
                //Defines fields in the generated Java BuildConfig class, in this case, for                create() {           //default config, that can be accessed by Java code                    type = "int"     //e.g. "if (1 == BuildConfig.VALUE) { /*do something*/}".                    name = "VALUE"                    //See: [app or lib]/build/generated/source/buildConfig/[package path]/                    value = "1"      //     BuildConfig.java                }
            }
        }
    }

    android.buildTypes {
        release {
            minifyEnabled = false            proguardFiles.add(file('proguard-rules.pro'))        }
    }

    android.productFlavors {
    }
    android.sources {
        main {
            jni {
                source {
                    srcDirs = ['src/main/nop']
                }
            }
        }
        main {
            jniLibs {
                source {
                    srcDirs = ['src/main/libs']
                }
            }
        }
    }
    
}

dependencies {
    //compile 'com.android.support:support-v4:23.0.1'    //compile 'com.android.support:appcompat-v7:23.0.1' //Only required when the target device API level is greater than    compile project(':aRBaseLib')
}     

------------------------------------------------------

martedì 28 novembre 2017

Maratona Firenze 2017

Il solito stupido giochino di ogni anno...quante volte sono finito su foto pubblicate da siti di giornali??
Quest'anno sono particolarmente riconoscibile per la pelata

In secondo piano




In ginocchio tenendo il nastro di partenza



Subito dopo la curva entrando in Piazza del Duomo

lunedì 20 novembre 2017

HAX emulator error per Android Studio su Mac High Sierra

Oggi ho ripreso dopo un po' di tempo il Mac per sviluppare su Android, in particolar modo dopo l'aggiornamento ad HIgh Sierra



La novita' e' che l'emulatore Android non ne voleva sapere di funzionare uscendo con Exit Code 0 dopo aver posto le rimostranze per non aver trovato l'accelerazione HAX

Dopo averne provate diverse la soluzione e' stata File->Invalidate Cache->Invalidate and Restart




giovedì 16 novembre 2017

Nexus 5x / RTKLib / Geo++ / DGPS

Stessa prova vista in questo post ma con un Nexus 5X.
L'applicazione Geo++ non e' quella presente sul market ma si tratta di una beta ricevuta dallo sviluppatore perche' Nexus 5x ha delle particolarita' (in realta' non e' molto chiaro se sia Nexus 5X oppure l'implementazione delle API perche' su Android Oreo stock la beta Geo++ non funziona mentre si ottengono risultati con Oreo beta)


Il punto topografico di riferimento e' sempre il solito come il post processing

Il punto calcolato risulta essere
43.833026632 11.307094997 201.0573

in precedenza i dati erano
dalla scheda del punto topografico
43.83304783 11.30706278 203.3

I dati post processati con RTKLib ed acquisiti con Nexus 9
43.833068271 11.307051667 184.74

da segnalare su questo argomento la seguente pubblicazione scientifica che riporta dei risultati decisamente interessanti

Precise GNSS Positioning Using Smart Devices
Eugenio Realini, Stefano Caldera Lisa Pertusini,Daniele Sampietro
Geomatics Research & Development s.r.l. (GReD), via Cavour 2, c/o ComoNExT, 22074 Lomazzo (Como), Italy

mercoledì 15 novembre 2017

Creare report PDF in Android

Ogni tanto (raramente) puo' essere necessario creare un report in una applicazione Android (anche se questa operazione e' piu' tipicamente indirizzata ad applicazione Desktop)

La soluzione piu' semplice e' appoggiarsi ad una libreria per la generazione di PDF come iTextPdf

per configurare le dipendenze si deve includere in build.gradle

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'    })
    compile 'com.android.support:appcompat-v7:26.+'    compile 'com.android.support.constraint:constraint-layout:1.0.2'    compile 'com.android.support:design:26.+'
    //dipendenza PDF    compile 'com.itextpdf:itextg:5.5.9'
    testCompile 'junit:junit:4.12'}

Fatto cio' si puo' procedere come in seguito. L'unico aspetto di interesse e' che quando si lancia l'intent per la visualizzazione del Pdf, a causa della gestione dei permessi, si deve gestire l'errore  android.os.FileUriExposedException

---------------------------------------------------------------------------------------

package com.example.lucainnocenti.photonotation;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ReportActivity extends AppCompatActivity {

    private Button pulsante;
    private FileOutputStream output;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_report);

        pulsante = (Button) findViewById(R.id.buttonpdf);
        pulsante.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                try {
                    try {
                        crea_pdf();
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (DocumentException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void crea_pdf() throws IOException, DocumentException {
        File pdfFolder = new File(Environment.getExternalStorageDirectory().getPath(), "Photonotation");
        if (!pdfFolder.exists()) {
            pdfFolder.mkdir();
            Log.i("Photonotation", "Pdf Directory created");
        }

        //Create time stamp        Date date = new Date() ;
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(date);

        String percorso = pdfFolder.getAbsolutePath() + "/" + timeStamp +".pdf";
        Log.i("Photonotation", percorso);

        File myFile = new File(percorso);

        output = new FileOutputStream(percorso);
        Document document = new Document(PageSize.A4);
        PdfWriter.getInstance(document, output);
        document.open();
        document.add(new Paragraph("Luca"));
        String path = "/storage/emulated/0/Photonotation/20171031_140238.jpg";
        Image image = Image.getInstance(path);
        document.add(image);

        document.close();

        // visualizza il pdf prodotto        
        // le prime righe servono ad evitare android.os.FileUriExposedException        
        if(Build.VERSION.SDK_INT>=24){
            try{
                Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
                m.invoke(null);
            }catch(Exception e){
                e.printStackTrace();
            }
        }


        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(myFile), "application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        startActivity(intent);


    }
}

Nexus 9 / RTKLIB/ Geo++ / DPGS

Dopo la prova del precedente post ho provato ad usare l'applicazione Geo++ su Nexus 9 con RTKLIB in postprocessing per vedere se era possibile ottenere una correzione DGPS



Il punto trigonometrico di riferimento e' sempre il solito visto qui . La novita' e' che l'antenna GPS della Provincia di Firenze che usavo come antenna base per la correzione DGPS non fornisce piu' dati probabilmente a seguito della dismissione della Provincia stessa. Un sostituto e' stato trovato nell'antenna base dell'IGM posta in via Baracca a Firenze i cui dati possono essere scaricati a questo link

http://www.epncb.oma.be/_networkdata/siteinfo4onestation.php?station=IGMI00ITA

e piu' in dettaglio qui (la sigla della stazione e' igmi. i dati sono ordinati per cartelle del giorno dell'anno progressivo e dell'ora di acquisizione. I dati del giorno 311 per esempio ora T avranno un nome file del tipo igmi311t.17d.Z)

ftp://igs.bkg.bund.de/EUREF/nrt/

I dati relativi dalla scheda del punto topografico indicano una posizione di riferimento
43.83304783 11.30706278 203.3
I dati post processati con RTKLib vengono indicate le seguenti coordinate
43.833068271 11.307051667 184.74

La cosa che mi disturba e' che RTKLib non fornisce l'errore di posizionamento

I dati post processati dalla ditta che fornisce Geo ++ forniscono un punto (nessun errore di posizionamento fornito)
43,833042 11,307012 171.7541

Gli stessi dati processati con Leica GeoOffice di Leica portano una posizione completamente differente, in particolare e' completamente sballato il dato di quota
11° 22' 52".68555 -43° 49' 59".11283 1023.8346

considerando che le differenze nella posizione si trovano nella quinta posizione decimale dopo la virgola non sembra proprio male (lasciando perdere la quota che pero' non e' di mio interesse)

mercoledì 8 novembre 2017

Precise Point Positioning con Nexus 9 ed Android 7.1.1

Con l'introduzione delle API di misura Raw Gnss avevo gia' provato qualche tempo fa la applicazione di test fornita da Google, ovvero GnssLogger, ma senza un programma per gestire il file di log l'esperimento non era andato molto in la'

Adesso ho trovato su questo link uno script in Python che converte il file di log in un formato Rinex che puo' essere dato in pasto ad RTKLib per il post processing dei dati 

Attenzione: lo script funziona su Python 3.x e non su Python 2.x

Per la prova ho usato un tablet Nexus 9 (senza supporto cellulare) che come riportato da questo link e'
uno dei pochi dispositivi certificati per funzionare con le API Raw Gnss (anzi e' uno dei pochi con la piena funzionalita')

Si usa quindi GnssLogger (vedi link per l'apk) per l'acquisizione dei dati, lo script android_to_rinex.py per la conversione in Rinex ed RTKPost in modalita' PPP per il post processing


Il risultato finale non e' niente male perche' l'errore si riduce a circa 1 m sui tre assi ed i dati riescono a convergere in meno di un minuto

GNSS Android 8

Tempo fa avevo provato l'applicazione demo delle nuovi API per GNSS di Android. Adesso e' disponibile una applicazione per verificare i dati salvati nel file dati (link). La applicazione Linux e' scaricabile da qui

https://github.com/google/gps-measurement-tools/releases/download/2.4.0.0/GnssAnalysisLinuxV2.4.0.0.zip

Per installare la applicazione si usa un web installer che permette di installare anche il runtime di Matlab senza la necessita' di avere una licenza

La applicazione e' installata di default in

/usr/local/Desktop/GnssAnalysisFiles/application

per lanciare l'applicazione si usa

./run_GnssAnalysisApp.sh ../../../MATLAB/MATLAB_Runtime/v901/

A questo punto si importa il file derivante da Android. In questo caso il file deriva da un Nexus 9 e quindi sono disponibili anche i dati GLONASS



 
Per usare Analyze and Plot si deve essere collegati ad Internet perche' devono essere scaricati in automatico i file delle effemeridi dei satelliti.

Al termine si puo' cliccare sull'icona del robottino verde per ottenere un report in formato HTML

Dalla lettura del file si vedono i test riusciti e quelli falliti. Per esempio


 

The GPS reference threshold is set to 38 dBHz. Well built phones, 
in open sky, have GPS satellites stronger than this threshold. 
GLONASS threshold is 2.5dB lower, since GLO signal is 2.5dB weaker, 
(see GPS and GLONASS ICDs).

For your log file the C/No results are:
GPS, mean of strongest 4 median C/No = 35.6 dBHz
Pass/fail reference threshold = 38.0 dBHz
Errors. Signals -2.4 dB compared to reference
FAIL BECAUSE OF WEAK SIGNALS
GLO, mean of strongest 4 median C/No = 36.0 dBHz
Pass/fail reference threshold = 35.5 dBHz
PASS








Come output si hanno anche i file .mat, .nmea e .derived 

martedì 7 novembre 2017

Motore acceso??

Il problema e' questo : e' possibile determinare se una macchina ferma ha il motore acceso o meno usando un telefono Android??

Si tratta di un problema per una app che dovevo fare ma che poi non ha mai visto la luce e la risposta e' si'....usando la FFT sui dati dell'accelerometro

Il primo grafico e' relativo ad una misura di 30 secondi a macchina ferma e spenta, il secondo sempre su una acquisizione di 30 secondi a macchina ferma ed accesa al minimo del motore



E' abbastanza evidente una frequenza di risonanza della macchina su tutti e tre gli assi intorno ai 40 Hz (il motore girava intorno ai 900 rpm ma quello che si registra e' la vibrazione di tutta la struttura)

venerdì 20 ottobre 2017

Foto ed imageview su Android

Aggiornamento: facendo delle prove con Nexus 5x e Nexus 9 ho scoperto che l'immagine risulta ruotata di 90° nonostante non ci siano modifiche nel codice
A questo link viene descritto il medesimo problema

Un sistema per una app che scatta una foto e mette il risultato in una imageview ed effettua il salvataggio (gestione dei permessi di scrittura non gestita..vedi post precedenti)



-----------------------------------------------
package com.example.lucainnocenti.photonotation;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;


public class MainActivity extends AppCompatActivity {
    private static final int CAMERA_REQUEST = 1888;
    private ImageView foto;
    private Bitmap ff;


    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        foto = (ImageView) findViewById(R.id.imageView);
        Button scatto = (Button) findViewById(R.id.button);


        scatto.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(camera, CAMERA_REQUEST);
            }
        });


    }
    protected void onActivityResult(int requestCode,int resultCode, Intent data)
    {
        if (requestCode== CAMERA_REQUEST && resultCode == Activity.RESULT_OK){
            ff = (Bitmap) data.getExtras().get("data");
            foto.setImageBitmap(ff);
            foto.setImageURI(data.getData());
            Bitmap bm=((BitmapDrawable)foto.getDrawable()).getBitmap();
            saveImageFile(bm);
        }

    }

    public String saveImageFile(Bitmap bitmap) {
        FileOutputStream out = null;
        String filename = getFilename();
        try {
            out = new FileOutputStream(filename);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.d("luca22","Errore");
        }
        return filename;
    }

    private String getFilename() {
        File file = new File(Environment.getExternalStorageDirectory()
                .getPath(), "TestFolder");
        if (!file.exists()) {
            file.mkdirs();
        }
        String uriSting = (file.getAbsolutePath() + "/"                + System.currentTimeMillis() + ".jpg");
        Log.d("luca22",uriSting);
        return uriSting;
    }
}

Azimuth Pitch e Roll su Android N

Gestione dei sensori con RotationMatrix su Android



--------------------------------------------------------------
package com.example.lucainnocenti.sensori;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity implements SensorEventListener{

    private SensorManager mSensorManager;
    private final float[] mAccelerometerReading = new float[3];
    private final float[] mMagnetometerReading = new float[3];

    private final float[] mRotationMatrix = new float[9];
    private final float[] mOrientationAngles = new float[3];
    private Sensor mAccelerometer;
    private Sensor mMagnetometer;

    private double az;
    private double pi;
    private double ro;


    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);


    }

    @Override    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    @Override    protected void onResume() {
        super.onResume();

        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);

    }

    @Override    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
    }

    @Override    public void onSensorChanged(SensorEvent event) {

        if (event.sensor == mAccelerometer) {
            System.arraycopy(event.values, 0, mAccelerometerReading, 0, mAccelerometerReading.length);
        }
        else if (event.sensor == mMagnetometer) {
            System.arraycopy(event.values, 0, mMagnetometerReading, 0, mMagnetometerReading.length);
        }
        updateOrientationAngles();
    }


    public void updateOrientationAngles() {
        mSensorManager.getRotationMatrix(mRotationMatrix, null, mAccelerometerReading, mMagnetometerReading);
        mSensorManager.getOrientation(mRotationMatrix, mOrientationAngles);
        az = Math.toDegrees(mOrientationAngles[0]);
        pi = Math.toDegrees(mOrientationAngles[1]);
        ro = Math.toDegrees(mOrientationAngles[2]);
        Log.d("orienta", "Az:"+String.valueOf(az)+" Pi:"+String.valueOf(pi)+" Ro:"+String.valueOf(ro));

    }
}

GPS su Android N

Un esempio di uso del GPS su Android N con getione dei permessi. Evidenziate in giallo le parti di codice di gestione dei permessi


---------------------------------------------------
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.lucainnocenti.gpstest">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
---------------------------------------------------



---------------------------------------------------
package com.example.lucainnocenti.gpstest;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements LocationListener{

    LocationManager locationManager;
    String provider;
    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        provider = locationManager.getBestProvider(new Criteria(), false);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            checkLocationPermission();
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        if (location != null) {

            Log.i("Location Info", "Location achieved!");

        } else {

            Log.i("Location Info", "No location :(");

        }
    }

    @Override    protected void onResume() {
        super.onResume();

        if (checkLocationPermission()) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                locationManager.requestLocationUpdates(provider, 400, 1, this);
            }
        }

    }

    @Override    protected void onPause() {
        super.onPause();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            checkLocationPermission();
            return;
        }
        locationManager.removeUpdates(this);

    }

    @Override    public void onLocationChanged(Location location) {

        Double lat = location.getLatitude();
        Double lng = location.getLongitude();

        Log.i("Location info: Lat", lat.toString());
        Log.i("Location info: Lng", lng.toString());

    }

    @Override    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override    public void onProviderEnabled(String provider) {

    }

    @Override    public void onProviderDisabled(String provider) {

    }

    public void getLocation(View view) {

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        onLocationChanged(location);


    }

    public boolean checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {


                new AlertDialog.Builder(this)
                        .setTitle("Permesso GPS")
                        .setMessage("Permesso GPS")
                        .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                            @Override                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();


            } else {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
            }
            return false;
        } else {
            return true;
        }
    }

    @Override    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        locationManager.requestLocationUpdates(provider, 400, 1, this);
                    }

                } else {

                    // permission denied,

                }
                return;
            }

        }
    }
}

Scrittura JSON su Android N

Un esempio semplice per scrivere un file JSON e gestire in permessi di scrittura su Android 7 e successivi
Nel codice le parti in azzurro sono quelle relative alla gestione dei permessi mentre quelle in giallo sono per la gestione della creazione del JSON


---------------------------------------
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.lucainnocenti.jsontester">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />



    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

---------------------------------------


---------------------------------------
package com.example.lucainnocenti.jsontester;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.JsonWriter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;


public class MainActivity extends AppCompatActivity {
    private int requestCode;
    private int grantResults[];
    private FileOutputStream out;
    private JsonWriter writer;
    private File card;
    private File file;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
        onRequestPermissionsResult(requestCode, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, grantResults);
        Button pulsante =(Button) findViewById(R.id.button);
        pulsante.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                card = Environment.getExternalStorageDirectory();
                file = new File (card.getAbsolutePath(),"dati.json");
                try {
                    out = new FileOutputStream(file);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                try {
                    writer = new JsonWriter(new OutputStreamWriter(out,"UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                try {
                    writer.beginObject();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    writer.name("nome").value("Luca");
                    writer.name("cognome").value("Innocenti");
                } catch (IOException e) {
                    e.printStackTrace();
                }

                try {
                    writer.endObject();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                ;


            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1: {

                // If request is cancelled, the result arrays are empty.                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    Log.d("permission", "granted");
                } else {


                    Toast.makeText(MainActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();

                    onDestroy();
                }
                return;
            }
        }
    }
}

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...