lunedì 2 gennaio 2017

Integrazione Google Maps e Firebase per mappatura collaborativa

Firebase puo' essere utilizzato come base dati per salvare anche dati semplici di tipo cartografico.
Il vantaggio di questo approccio e' che ogni modifica effettuata da un client viene immediatamente visualizzato sugli altri client senza necessita' di aggiornare la pagina (utile quindi nel caso si voglia fare per esempio il tracking di un oggetto che si muove)
Un esempio live puo' essere provato a questo link


La struttura dati e' molto semplice: un alberi in cui ad ogni entrata sono presenti una longitudine ed una latitudine. Cliccando sulla mappa si aggiunge un nuovo nodo alla mappa
Esempio del sistema in uso




Attenzione : Firebase non conosce il tipo float per cui ci sono un po' di magheggi nel convertire i dati float da e per tipi stringhe
------------------------
<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      html, body { height: 100%; margin: 0; padding: 0; }
      #map { height: 100%; }
    </style>
   
    <script src="https://www.gstatic.com/firebasejs/3.6.4/firebase.js"></script>
    <script type="text/javascript">  

    var latitudine = 0.0;
    var longitudine = 0.0;
    var conta = 0;
    // Initialize Firebase
  var config = {
    apiKey: "AIzaSyAchx8oVeLUgYnGujAEfwOxxxxxxxxxxxxxxxx",
    authDomain: "notifica-23xxxxxxfirebaseapp.com",
    databaseURL: "https://notifica-23xxxxxfirebaseio.com",
    storageBucket: "notifica-2xxxxxx.appspot.com",
    messagingSenderId: "392723xxxxxxx"
  };
  firebase.initializeApp(config);
</script>
<script type="text/javascript">

/*
questi sono solo esempi di come si possano inserire dati in Firebase in Javascript
le righe sono commentate e quindi non in uso/
firebase.database().ref('punti/').set({
    lat: 43,
    lng : 11
  });*/

/*

firebase.database().ref('punti/').push({
    lat: 43.0,
    lng : 12.0
  });

firebase.database().ref('punti/').push({

    lat: 42,
    lng: 11
  });
*/

  </script>


  </head>
  <body>
    <div id="map"></div>

   

  <script>

  function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {
        lat: 43,
        lng: 11
    },
    zoom: 6,
    styles: [{
        featureType: 'poi',
        stylers: [{
                visibility: 'off'
            }] // Turn off points of interest.
    }, {
        featureType: 'transit.station',
        stylers: [{
                visibility: 'off'
            }] // Turn off bus stations, train stations, etc.
    }],
    disableDoubleClickZoom: false
   });

  //questa e' la funzione che all'evento click sulla mappa aggiunge il corrispondente
  // punto nel database    map.addListener('click', function(e) {
    firebase.database().ref('punti/').push({
    lat: JSON.stringify(e.latLng.lat()),
    lng : JSON.stringify(e.latLng.lng())
   });
  var marker = new google.maps.Marker({
    position: {
      lat: e.latLng.lat(),
      lng: e.latLng.lng()
    },
    map: map
    });
  });
 //fine della funzione di inserimento
 // crea un evento sull'aggiornamento dei punti
//sicuramente esiste un sistema piu' furbo di fare la stessa cosa
  const dbRefPunti = firebase.database().ref().child('punti');
  dbRefPunti.on('child_added',function(snap,prevChildKey)
    {
      var la1;
      var lo1;
      snap.forEach(function (snapshot) {

           var obj = snapshot.val();

           var nome = snapshot.key;
           if (snapshot.key == "lat")
              {
                latitudine = JSON.stringify(obj);
                la1 = obj;
                console.log("latitudine : "+latitudine);
              }
           if (snapshot.key == "lng")
                {
                longitudine = JSON.stringify(obj);
                lo1 = obj;
                console.log("longitudine : "+longitudine);
              }
            conta++;
            console.log("Conta : "+conta);

            if (conta%2 == 0) 

              {
              console.log("Stampa : " + latitudine+":"+longitudine);
              console.log(isNaN(la1));


               //var latLng = new google.maps.LatLng(parseFloat(latitudine),parseFloat(longitudine));

               var latLng = new google.maps.LatLng(la1,lo1);

               var marker = new google.maps.Marker({position: latLng,map: map});



              }

           });
       }

    );

  // fine evento

  //fine initMaps

  google.maps.event.addDomListener(window, "load", initMap);

  };


  </script>
  <script async defer
    src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDkeozFISVmCAB3ta1rEXvMNDDzSxxxxxxxx&callback=initMap">
    </script>
   
  </body>
</html>
------------------------

venerdì 30 dicembre 2016

Firebase Storage

A differenza del serivizio Hosting, con il quale potrebbe essere confuso per alcuni usi comuni, lo Storage di Firebase impiega un sistema nativo di permessi per cui e' possibile rendere i contenuti privati e pubblici con un filtro sulla base dell'utente.
Inoltre se in Hosting l'aggiornamento avviene con una metodologia simile ai commit di GitHub, ovvero a livello di intero progetto, in Storage si gestiscono i singoli file

Nel profilo gratuito sono inclusi 5Gb di spazio disco per lo Storage (1 Gb per Hosting)



Per recuperare i file si ha a disposizione un link del tipo

https://firebasestorage.googleapis.com/v0/b/notifica-23425.appspot.com/o/images%2Fa1.jpg?alt=media&token=4f930e8d-cxxxxxxxxxxxxxxxxxxxxxx


Esempio di accesso non autenticato
oppure e' possibile scaricare il medesimo file utilizzando le URI di Google Cloud (indirizzo del formato gs://)

i permessi di accesso vengono gestiti dalla Console di Firebase sul tab Regole (come in Realtime Databases)



Per interagire con Storage, oltre alla Console, si possono usare client Javascript, Android ed IOS (operazioni di Upload, Download e Delete)




mercoledì 28 dicembre 2016

Adb over Wifi

In caso si debba sviluppare una applicazione che usa l'ingresso USB, tipo Midi via USB-OTG, puo' essere utile impiegare Adb via Wifi



Per prima cosa si collega il telefono alla USB e si seleziona Terminale in Android Studio
Il telefono puo' essere connesso ad un access point o funzionare da HotSpot

Si digita quindi

adb tcpip 5555
adb connect IP_Telefono 

si puo' scollegare il cavo USB e procedere allo sviluppo


martedì 27 dicembre 2016

Firebase Database con Android

In questo post sono indicati i passi per interfacciare Android con i database di Firebase.
Il tutto e' stato ripreso dal tutorial ufficiale



Come per il caso delle notifiche si deve inserire nelle SDK tools Google Play Services e Repository



e si deve aggiungere il file google.json nella directory /app

build.gradle (Project)
--------------------
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'        
        classpath 'com.google.gms:google-services:3.0.0'
        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
--------------------

build.gradle (app)
--------------------
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25    buildToolsVersion "23.0.1"    
    defaultConfig {
    applicationId "com.luca_innocenti.dbfirebase"        
    minSdkVersion 19        
    targetSdkVersion 25        
    versionCode 1        
    versionName "1.0"        
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }
    buildTypes {
        release {
            minifyEnabled false            
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

dependencies {
    compile  'com.google.firebase:firebase-core:10.0.0'    
    compile  'com.google.firebase:firebase-database:10.0.0'
    compile  'com.google.firebase:firebase-auth:10.0.0'

    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:25.1.0'    
   compile 'com.android.support:design:25.1.0'    testCompile 'junit:junit:4.12'}

apply plugin: 'com.google.gms.google-services'
--------------------

Si parte creando una basic activity che abbia il nome corrispondente con quella selezionata nel DB




Nella activity sono implementata una lettura ed un update di un campo del DB

per effettuare l'update si devono modificare i permessi del DB. Dato che era una prova ci sono permessi di lettura e scrittura senza limitazioni (Firebase avvisa in modo vistoso che e' una scelta da non fare)



Activity
------------------------------
package com.luca_innocenti.dbfirebase;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity {

    TextView mConditionTextView;
    Button mButton;
    Button mLogin;
    Button mInsert;

    DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
    DatabaseReference mConditionRef = mRootRef.child("condition");

    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private static final String TAG = "EmailPassword";



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

        mConditionTextView = (TextView) findViewById(R.id.textView2);
        mButton = (Button) findViewById(R.id.button2);
        mInsert = (Button) findViewById(R.id.button5);
        mLogin = (Button) findViewById(R.id.button4);

        // [START initialize_auth]        mAuth = FirebaseAuth.getInstance();
        // [END initialize_auth]
        // [START auth_state_listener]        mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // User is signed out                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
                // [START_EXCLUDE]                // [END_EXCLUDE]            }
        };
        // [END auth_state_listener]
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }


    @Override    protected void onStart()
    {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);

//qui viene aggiunto il listener, ogni volta che viene modificato il campo di interesse
// il valore modificato viene letto e mostrato nella textview 
        mConditionRef.addValueEventListener(new ValueEventListener() {
            @Override            public void onDataChange(DataSnapshot dataSnapshot) {
                String text = dataSnapshot.getValue(String.class);
                mConditionTextView.setText(text);
            }

            @Override            public void onCancelled(DatabaseError databaseError) {

            }
        });
// nel caso che si prema un
        mButton.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
              mConditionRef.setValue("Update");
            }

        });

        mInsert.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                mConditionRef.setValue("Update");
            }

        });

        mLogin.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                signIn("lucainnoc@gmail.com","chiara");
            }

        });


    }

    @Override    public void onStop() {
        super.onStop();
        if (mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }

    private void signIn(String email, String password) {
        Log.d(TAG, "signIn:" + email);


        // [START sign_in_with_email]        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithEmail:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds                        // the auth state listener will be notified and logic to handle the                        // signed in user can be handled in the listener.                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:failed", task.getException());
                            Toast.makeText(getApplicationContext(), "Fallito",Toast.LENGTH_SHORT).show();
                        }

                        // [START_EXCLUDE]                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:successo");
                            Toast.makeText(getApplicationContext(), "Riuscito",Toast.LENGTH_SHORT).show();

                        }
                        // [END_EXCLUDE]                    }
                });
        // [END sign_in_with_email]    }


    @Override    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();

        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
------------------------------

E adesso la prova modificando i dati sulla consolle di Firebase ed osservando le modifiche sull'emulatore Android (caricato con una immagine con Play Services)






Autenticazione Firebase con Android

Esempio di autenticazione basata su email/password su Firebase usando un client Android (ripreso dal GitHub ufficiale)
Per prima cosa dalla Consolle di Firebase si deve abilitare l'autenticazione mail/password



e si aggiunge un utente




build.gradle (progetto)
--------------
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'        classpath 'com.google.gms:google-services:3.0.0'
        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
--------------

build.gradle (app)
--------------
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25    buildToolsVersion "23.0.1"    defaultConfig {
        applicationId "com.luca_innocenti.dbfirebase"        minSdkVersion 19        targetSdkVersion 25        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }
    buildTypes {
        release {
            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

dependencies {
    compile  'com.google.firebase:firebase-core:10.0.0'
    compile  'com.google.firebase:firebase-auth:10.0.0'

    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:25.1.0'    compile 'com.android.support:design:25.1.0'    testCompile 'junit:junit:4.12'}
apply plugin: 'com.google.gms.google-services'
--------------


--------------
package com.luca_innocenti.dbfirebase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import org.w3c.dom.Text;
import java.util.HashMap;import java.util.Map;
public class MainActivity extends AppCompatActivity {

    TextView mConditionTextView;
    Button mButton;
    Button mLogin;
    Button mInsert;

    DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
    DatabaseReference mConditionRef = mRootRef.child("condition");
    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private static final String TAG = "EmailPassword";


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

        mConditionTextView = (TextView) findViewById(R.id.textView2);
        mButton = (Button) findViewById(R.id.button2);
        mInsert = (Button) findViewById(R.id.button5);
        mLogin = (Button) findViewById(R.id.button4);

        // [START initialize_auth]       
       //  Controlla se all'avvio l'utente e' gia' autenticato
                         mAuth = FirebaseAuth.getInstance();
        // [END initialize_auth]
        // [START auth_state_listener]        
                mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                    FirebaseUser user = firebaseAuth.getCurrentUser();
                            if (user != null) {
                                  // User is signed in                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                                  } else {
                                 // User is signed out                    Log.d(TAG, "onAuthStateChanged:signed_out");
                                 }
                      // [START_EXCLUDE]                // [END_EXCLUDE]            }
               };
        // [END auth_state_listener]


        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }


    @Override    protected void onStart()
    {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);


        mConditionRef.addValueEventListener(new ValueEventListener() {
            @Override            public void onDataChange(DataSnapshot dataSnapshot) {
                String text = dataSnapshot.getValue(String.class);
                mConditionTextView.setText(text);
            }

            @Override            public void onCancelled(DatabaseError databaseError) {

            }
        });
        mButton.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
              mConditionRef.setValue("Update");
            }

        });

        mInsert.setOnClickListener(new View.OnClickListener(){
            private DatabaseReference usersRef;

            public void onClick(View view) {
                usersRef = mRootRef.child("condition");

                Map<String,Object> nick = new HashMap<String, Object>();
                nick.put("nome","Luca");
                usersRef.setValue(nick);
                Log.d(TAG,"inserimento");

            }

        });
         

       //se si clicca il pulsante viene richiamata la procedura di autenticazione
      // per semplicita' username e password sono statici
        mLogin.setOnClickListener(new View.OnClickListener(){
            @Override            public void onClick(View view) {
                signIn("lucainnoc@gmail.com","xxxxxxxx");
            }

        });


    }

    @Override    public void onStop() {
        super.onStop();
        if (mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }

//Funzione di autenticazione 
    private void signIn(String email, String password) {
        Log.d(TAG, "signIn:" + email);


        // [START sign_in_with_email]        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithEmail:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds                        
                       // the auth state listener will be notified and logic to handle the                      
                      // signed in user can be handled in the listener.                       
                       if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:failed", task.getException());
                            Toast.makeText(getApplicationContext(), "Fallito",Toast.LENGTH_SHORT).show();
                        }

                        // [START_EXCLUDE]                        if (!task.isSuccessful()) {
                            Log.d(TAG, "signInWithEmail:successo");
                            Toast.makeText(getApplicationContext(), "Riuscito",Toast.LENGTH_SHORT).show();

                        }
                        // [END_EXCLUDE]                    }
                });
        // [END sign_in_with_email]    }


    @Override    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will        
// automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();

        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
-------------

Debian dual monitor su Virtual Box

Come usare Debian usando VirtualBox in modalita' dual monitor

Per prima cosa si deve creare una macchina virtuale con due monitor




Dopo di cio' si installano le Guest Additions secondo la classica trafile

apt-get install build-essential
apt-get install linux-headers-$(uname -r)
VBoxLinuxAdditions.run 
reboot

A questo punto con la macchina virtuale attiva si deve abilitare il secondo schermo virtuale dal menu Visualizza




ed alla fine si manda tutto a pieno schermo


Aruco Tag e filtri Kalman

Usando gli Aruco tag in ambiente esterno con illuminazione solare a diverse ore e in diversi periodi dell'anno ho trovato un errore nell...