JavascriptProva

lunedì 31 ottobre 2016

Nuova applicazione

Ripasso ancora il riconoscimento vocale.
Ecco, ho ributtato giù un codice di riconoscimento vocale in quattro e quattr'otto, con l'aiuto dei miei appunti di "Gioco a programmare":
public class MainActivity extends AppCompatActivity {

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

        Intent intent=new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ITALIAN);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"Parla ora");
        startActivityForResult(intent,0);

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode==RESULT_OK){
            ArrayList lista=data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            Toast.makeText(getApplicationContext(),lista.get(0),Toast.LENGTH_LONG).show();
        }

    }
}
Ora devo subordinare questa funzione a un pulsante...

Evoluzioni nell'applicazione

Ho subordinato la possibilità di spostare i folderLayout allo stato di settingMode:
        onImageTouchListener = new View.OnTouchListener(){

            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {


                X=(int)motionEvent.getRawX();
                Y=(int)motionEvent.getRawY();
                switch(motionEvent.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        if(settingMode) {
                            scrollView.requestDisallowInterceptTouchEvent(true);
                            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                            deltaX = X - params.leftMargin;
                            deltaY = Y - params.topMargin;
                        }
                        break;

                    case MotionEvent.ACTION_MOVE:
                        if(settingMode) {
                            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                            if (X - deltaX > 0 && X - deltaX + params.width < scrollView.getWidth())
                                params.leftMargin = X - deltaX;
                            if (Y - deltaY > 0 )
                                params.topMargin = Y - deltaY;

                            System.out.println("move " + params.leftMargin);
                            System.out.println("move " + params.topMargin);
                            view.setLayoutParams(params);
                        }
                        break;

                }


                return true;
            }
        }; 
E il comportamento è adeguato.

Ora (ho fatto un intermezzo per realizzare il collegamento di questa App al database Firebase) devo gestire il comportamento al tocco in caso di settingMode=false.

Devo ricostruire il tts.
Ma prima voglio provare l'introduzione delle textViews nascoste contenenti il TIPO e la CATEGORIA del folderLayout.
In precedenza avevo castato il layout a viewgroup, e vediamo se si può fare lo stesso... Ecco, sì, ci sono riuscito:
                    case MotionEvent.ACTION_DOWN:
                        if(settingMode) {
                            scrollView.requestDisallowInterceptTouchEvent(true);
                            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                            deltaX = X - params.leftMargin;
                            deltaY = Y - params.topMargin;
                        }
                        else{
                            String tipo=(String)((TextView)((ViewGroup)view).getChildAt(2)).getText();
                            String categoria=(String)((TextView)((ViewGroup)view).getChildAt(3)).getText();
                        }
                        break;
Provando con System.out.println ottengo la stampa a video di tipo e categoria.
Ora devo sistemare il tts.

Ho fatto tanta strada senza documentarla.
tts risistemato, sistemate le coordinate delle immagini, sistemato il cambio di schermata...

giovedì 27 ottobre 2016

Considerazioni.

L'immagine sarebbe salvata come stringa.
Ecco JImage:
public class JImage {
    public String etichetta;
    public String categoria;
    public String immagine;
    public String tipo;
    public int coordX;
    public int coordY;
}


Per memorizzare il database:
STRING ETICHETTA
STRING CATEGORIA
STRING IMMAGINE
STRING TIPO


INT COORDX
INT COORDY


Riguardo il codice in cui il programma legge il database e carica le immagini nel controllo ImagView.
Per fare questo però la String deve essere ritradotta in immagine, e il codice non lo ricordo...
Vediamo...

Ecco, l'avevo già creata per la funzione di salvataggio da ImagePicker, insieme al suo reverse.
    public static String BitmapToString(Bitmap bmp, int quality){
        ByteArrayOutputStream stream=new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, quality, stream);
        byte[] b=stream.toByteArray();
        String s= Base64.encodeToString(b,Base64.DEFAULT);
        return s;
    }

 public static Bitmap StringToBitmap(String stringImage){
        byte[] b=Base64.decode(stringImage,Base64.DEFAULT);
        Bitmap bmp=BitmapFactory.decodeByteArray(b,0,b.length);
        return bmp;
    }
Per convertire Bitmap a String bisogna trasformare una bitmap in un array di bytes.
Per riconvertire String a Bitmap bisogna trasformare la String in un array di bytes.

La trasformazione passa sempre per un array di bytes.

Il campo IMMAGINE nel database è il terzo, quindi considerando che c'è una chiave primaria devo estrarre il numero 3.
Modifico la chiamata della folderLayout aggiungendovi come terzo parametro la stringa IMMAGINE presa dal database:
FolderLayout folderLayout=new FolderLayout(this,isFolder,crs.getString(3));
e ora devo andare a modificarne di conseguenza il costruttore:
public FolderLayout(Context context,boolean isFolder,String stringImage) {
...
Nel corpo di folderLayout ripristino l'immagine attraverso la funzione che ho già creato:
    private boolean isFolder;
    public FolderLayout(Context context,boolean isFolder,String stringImage) {
        super(context);
        this.context=context;
        this.isFolder=isFolder;
        setWillNotDraw(false);

        if(isFolder) {
            this.setBackground(getResources().getDrawable(R.drawable.cartella));
        }else{
            this.setBackground(getResources().getDrawable(R.drawable.document));
        }

        Bitmap b=Funzioni.StringToBitmap(stringImage);
        ImageView immagine=new ImageView(context);
        immagine.setImageBitmap(b);
        this.addView(immagine);
    }

Ecco il risultato finale, dopo una serie di aggiustamenti estetici:
Ho rimaneggiato completamente il codice di folderLayout:
public class FolderLayout extends RelativeLayout {
    Context context;




    private boolean isFolder;
    public FolderLayout(Context context,boolean isFolder,String stringImage,String etichetta) {
        super(context);
        this.context=context;
        this.isFolder=isFolder;
        setWillNotDraw(false);

        if(isFolder) {
            this.setBackground(getResources().getDrawable(R.drawable.cartella));
        }else{
            this.setBackground(getResources().getDrawable(R.drawable.document));
        }


        Bitmap b=Funzioni.StringToBitmap(stringImage);
        ImageView immagine=new ImageView(context);
        immagine.setImageBitmap(b);
        this.addView(immagine);
        RelativeLayout.LayoutParams p=(RelativeLayout.LayoutParams) immagine.getLayoutParams();
        p.topMargin=30;
        p.height=180;
        p.width=180;
        p.addRule(RelativeLayout.CENTER_HORIZONTAL);

        TextView txtEtichetta=new TextView(context);
        txtEtichetta.setText(etichetta);
        txtEtichetta.setTextSize(15);
        txtEtichetta.setSingleLine(false);
        txtEtichetta.setAllCaps(true);
        txtEtichetta.setTypeface(null, Typeface.BOLD);
        this.addView(txtEtichetta);
        System.out.println(etichetta);
        RelativeLayout.LayoutParams params =(RelativeLayout.LayoutParams)txtEtichetta.getLayoutParams();
        params.topMargin=210;
        params.leftMargin=10;
        params.addRule(RelativeLayout.CENTER_HORIZONTAL);
        params.width=250;
        params.height=150;

    }
}
ed ecco la funzione che carica il database nella finestra, allo stato attuale:
    private void leggiDaDatabase(String categoria){

        Cursor crs=helper.query(categoria);
        crs.moveToFirst();
        do{
            boolean isFolder=false;
            if(crs.getString(4).equals("categoria")) isFolder=true;
            FolderLayout folderLayout=new FolderLayout(this,false,crs.getString(3),crs.getString(1));
            mainLayout.addView(folderLayout);
            RelativeLayout.LayoutParams flParams=(RelativeLayout.LayoutParams)folderLayout.getLayoutParams();
            flParams.leftMargin=crs.getInt(5);
            flParams.topMargin=crs.getInt(6);
            flParams.width=(int)(250);
            flParams.height=(int)(300);
            folderLayout.setOnTouchListener(onImageTouchListener);


        }while(crs.moveToNext());

    }

Quest'ultima funzione è la prima che deve partire.
O, meglio, ho ricostruito la sequenza.
Non appena si avvia il programma, con l'evento OnStart, parte questo codice:
    @Override
    protected void onStart(){
        super.onStart();
        mLucchettiChangeListener.onLucchettiChange(leftLock,rightLock);
        refreshScreen();
    }
Viene innescato l'evento onLucchettiChange... (che vedrò poi);
viene chiamata la funzione refreshScreen().
    //Ricarica la schermata.
    private void refreshScreen(){

        try {
            leggiDaDatabase(currentCat);
        }catch(Exception e){}ae
        txtBarraCategoria.setText(currentCat);
    }
Questo refreshScreen innesca leggiDaDatabase avendo per parametro currentCat, che sarebbe la categoria corrente.
In più, intitola la barra categoria con il nome di questa categoria corrente.

Importante: currentCat è impostata a "iniziale":
String currentCat="iniziale";
Ed ecco leggiDaDatabase.
    private void leggiDaDatabase(String categoria){

        Cursor crs=helper.query(categoria);
        crs.moveToFirst();
        do{
            boolean isFolder=false;
            if(crs.getString(4).equals("categoria")) isFolder=true;
            FolderLayout folderLayout=new FolderLayout(this,false,crs.getString(3),crs.getString(1));
            mainLayout.addView(folderLayout);
            RelativeLayout.LayoutParams flParams=(RelativeLayout.LayoutParams)folderLayout.getLayoutParams();
            flParams.leftMargin=crs.getInt(5);
            flParams.topMargin=crs.getInt(6);
            flParams.width=(int)(250);
            flParams.height=(int)(300);
            folderLayout.setOnTouchListener(onImageTouchListener);


        }while(crs.moveToNext());

    }
Questo scorre tutto il database selezionando le sole immagini che abbiano il campo categoria impostato al valore di currentCat.
Quindi inizialmente seleziona soltanto le immagini che appartengono alla schermata iniziale.

Per ogni record crea un folderLayout contenente un'immagine e un'etichetta.
E questo è il codice che ho rimaneggiato finora.

mercoledì 26 ottobre 2016

Codici per cancellare un record e per aggiungerlo o modificarlo, basati sulla chiave primaria.

Bene.
Finora mi sono occupato del cancellare di sana pianta tutto il database Firebase e riscriverlo da cellulare, e viceversa.

Ora mi dovrei occupare del modificare singoli record, perché se si modifica un singolo record cancellare e riscrivere tutto il database mi sembra un po' come quello che si compra la macchina nuova perché i posacenere di quella vecchia sono ormai pieni.

Innanzitutto devo cercare sul Firebase il mio record mediante una chiave, e userò la chiave primaria _id.
La ricerca andrà fatta mediante le query di firebase.
Se la ricerca non è vana, ossia se la query dà dei risultati, posso cancellare o modificare il mio singolo record mediante l'apposito codice.
Se ho da aggiungere un record, invece, nel caso la query mi dia ricerca vana devo usare il push().

Posso unire le due cose dicendo "Se c'è modificalo, se non c'è aggiungilo".
Bene: primo compito. Individuare un record e cancellarlo.

Per individuare un record devo creare una query.
Mettiamo che voglio individuare il record n.2
Query query=reference.orderByChild("_id").equalTo(2);
Ora pongo la condizione che esista. Vediamo il numero di children che ha la query:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        System.out.println(dataSnapshot.getChildrenCount());
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Ci sono riuscito.
Funziona egregiamente.

Ora creo la funzione "cancella" per cancellare selettivamente questo record. Ecco:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        dataSnapshot.getRef().removeValue();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Vediamo...

No, non funziona. Il dataSnapshot.getRef() così facendo è ancora relativo alla radice del database.
Provvedo:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            child.getRef().removeValue();
                        }

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
E così funziona.

Ora devo provvedere al codice che aggiunge un record al database nel caso questo sia assente.
        bttAggiungiRecord.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("_id").equalTo(2);

                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if(dataSnapshot.getChildrenCount()==0){
                            Cursor crs=helper.query();
                            crs.moveToFirst();
                            do{
                                if(crs.getInt(0)==2){
                                    PersonaOut p=new PersonaOut(crs.getInt(0),
                                            crs.getString(1),crs.getString(2),
                                            crs.getInt(3),crs.getString(4));
                                    reference.push().setValue(p);
                                }
                            }while(crs.moveToNext());

                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
Benissimo!
Ora voglio fare che invece se è presente lo modifica.
Ecco, sì:
        bttAggiungiRecord.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                Cursor crs=helper.query();
                crs.moveToFirst();
                do{
                    if(crs.getInt(0)==2){
                        p=new PersonaOut(crs.getInt(0),
                                crs.getString(1),crs.getString(2),
                                crs.getInt(3),crs.getString(4));
                    }
                }while(crs.moveToNext());

                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if(dataSnapshot.getChildrenCount()==0){
                            System.out.println(dataSnapshot.getChildrenCount());
                            reference.push().setValue(p);
                        }
                        if(dataSnapshot.getChildrenCount()==1){
                            System.out.println(dataSnapshot.getChildrenCount());
                            for(DataSnapshot child : dataSnapshot.getChildren()){
                                child.getRef().setValue(p);
                            }
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });

martedì 25 ottobre 2016

Esercizi con Firebase e Query

Cerchiamo di mettere a fuoco i problemi di questo database Firebase.
Perché devo ottenere uno snapshot per posizionarmi su un record?
Perché devo posizionarmi su un record?

Perché devo agire su di essi uno per uno. Per dire "cancella un record" devo specificare il record stesso.
Proviamo a cancellare un solo record

reference= FirebaseDatabase.getInstance().getReference();
...
reference.child("-KUw45Hi6pkgGcRzwa7h").removeValue();
e vediamo se funziona.

Certo che funziona! Ma il problema fondamentale è uno solo: io non conosco i nomi dei records, in quanto vengono assegnati dal sistema, e non posso conoscerli in anticipo (oltre al fatto che sono difficili da ricordare).
Dunque devo ricavarli.

Ammettiamo che io debba fare la stessa operazione senza conoscere il nome del record. Come faccio?
L'unica è ottenere uno snapshot del database stesso.
Ci provo, a ottenere un dataSnapshot. Ecco, così ottengo un dataSnapshot che figura fra i parametri.
Un dataSnapshot ha la proprietà getKey() che dovrebbe restituirmi il nome del nodo.
Ma se io faccio così su un dataSnapshot ottenuto dalla radice del database...
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        System.out.println(dataSnapshot.getKey());
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
...ottengo null.
Però posso ottenere anche i dataSnapshot degli elementi "discendenti" della radice, ossia di ogni figlio di essa, vale a dire di ogni nodo, perché il dataSnapshot ha la proprietà getChildren(), che mi ottiene i dataSnapshot anche degli elementi figli.
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Con questa operazione ottengo i datasnapshot di tutti i figli.
Vediamo se mi dà i nomi "illeggibili".
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEfCy2-QPeLkNFS
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEnBfijEAoDzfkW
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEoAzah1_AhLlxC
Perfetto!
Dunque per cancellare il primo record, facendo così, dobbiamo ottenere il nome del nodo in questo modo.
Però esso viene ottenuto insieme a tutti gli altri... Come fare per isolare quello che ci interessa?

Provo a fare così:
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<String> lista=new ArrayList<String>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child.getKey());
                            
                        }
                        reference.child(lista.get(0)).removeValue();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
ossia metto i risultati in un'ArrayList e individuo il primo immesso come quello di indice zero.
Vediamo se funziona...

Sì! Funziona!
Ma posso anche usare direttamente il riferimento a un nodo figlio?
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<DatabaseReference> lista=new ArrayList<DatabaseReference>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child.getRef());

                        }
                        lista.get(0).removeValue();
                    }
Sì, anche così funziona.
Ma posso usare anche un dataSnapshot di cui prendere il riferimento poi:
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<DataSnapshot> lista=new ArrayList<DataSnapshot>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child);

                        }
                        lista.get(0).getRef().removeValue();
                    }
Quindi, alla fine il removeValue() si applica a un riferimento.
Questo riferimento si può ottenere ottenendo il DataSnapshot da cui ottenere il riferimento mediante getRef(), subito o successivamente, o ottenendo, sempre dal DataSnapshot il Value da cui poi ottenere il riferimento mediante il metodo .child() del riferimento della radice.


Ora, anziché andare per numero di ordine, volendo eliminare il primo (o anche il secondo eccetera) dei records, voglio eliminare solo quello che presenti determinate caratteristiche.
Devo costruirmi una query.

Provo...
Query query=reference.orderByChild("cognome").equalTo("Rossi");
Sono un po' perplesso perché il child di reference è quello identificato dal nome irripetibile... ma forse il "child" di questo caso si riferisce anche ai "nipoti"...
Comunque questa perplessità servirà a farmi ricordare meglio la cosa.
Ora praticamente a una query io posso applicare un ValueEventListener?

Sembrerebbe di sì, e questo significa che ne posso ricavare un dataSnapshot e quindi un insieme di Children...
Vediamo:
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Query query=reference.orderByChild("cognome").equalTo("Rossi");
                        query.addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                System.out.println(dataSnapshot.getChildrenCount());
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {

                            }
                        });
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });





            }



        });
Ecco: partendo dalla query ho ottenuto un dataSnapshot contenente tutti quei record che hanno un campo "cognome" uguale a "Rossi".
Ottengo, come mi aspettavo un numero di children pari a 1 in quanto solo un record è quello in cui "cognome" è uguale a "Rossi".

Bene, quindi dato che con la query posso selezionare, adesso voglio eliminare i records (ce n'è uno solo) in cui "cognome" equivale a "Minchia".
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Query query=reference.orderByChild("cognome").equalTo("Minchia");
                        query.getRef().removeValue();

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
E invece così facendo ho eliminato tutti i records!
Perché?
Forse perché devo prima ottenere un dataSnapshot.
Ma è proprio indispensabile che ottenga la query nell'amito del metodo addListenerForSingleValueEvent?
No. Query agisce direttamente su reference, e mi seleziona i figli in relazione al criterio proposto.
Riscrivo:
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("cognome").equalTo("Minchia");
                query.getRef().removeValue();

            }



        });
Provo...

E ottengo lo stesso risultato.
Query è una selezione di un riferimento, quindi va soggetta agli stessi criteri di un riferimento.

        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("cognome").equalTo("Minchia");
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            child.getRef().removeValue();
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });

            }



        });


Perfetto!!! Adesso funziona!

Refreshing di tutto il database Firebase

Ripartiamo con i database.
Ho un SQLiteDatabase. Voglio portarlo su Firebase.
        Cursor crs=helper.query();r
        crs.moveToFirst();
        do{
            PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                    crs.getInt(3),crs.getString(4));
            reference.push().setValue(personaOut);
        }while(crs.moveToNext());
Vediamo se funziona...

Sì, funziona perfettamente.


Ora la situazione è più complessa: vorrei modificare i dati nel database SQLite e trasferire queste modifiche al database online.
Come fare?

Intanto, mi creo un metodo in Helper che consenta di modificare il campo "professione".
    public void update(String cognome,String professione){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("PROFESSIONE",professione);
        db.update("TABELLA",values, "PROFESSIONE=?",new String[]{cognome});
    }
E verifico che funzioni.

Funziona! Per ora ho solo i risultati in locale.
Trasferendoli su Firebase con questo
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Cursor crs=helper.query();
                crs.moveToFirst();
                do{
                    PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                            crs.getInt(3),crs.getString(4));
                    reference.push().setValue(personaOut);
                }while(crs.moveToNext());
            }
        });
ottengo l'aggiunta dei record a quelli già presenti sul database, con le opportune modifiche.


Ho penato molto per cancellare il database remoto e riscriverlo.
Ho trovato infine la soluzione usando addListenerForSingleValueEvent.
Il problema è che se mettevo un semplice addValueEventListener al reference, ossia al riferimento alla radice del database, una volta settato il listener, la successiva riscrittura di dati, essendo comunque un evento, triggerava il listener, e dato che il codice del listener era quello di cancellare i dati, un attimo dopo questi venivano cancellati.
Usando un listener valido per una sola operazione non ci sono più problemi.
Ora riscrivo il codice per la cancellazione e il refreshing di tutto il database.
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                reference.addListenerForSingleValueEvent(new ValueEventListener() {

                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot figlio : dataSnapshot.getChildren()){
                            DatabaseReference ref= dataSnapshot.getRef();
                            ref.removeValue();

                        }
                        Cursor crs=helper.query();
                        crs.moveToFirst();
                        do{
                            PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                                    crs.getInt(3),crs.getString(4));
                            reference.push().setValue(personaOut);
                        }while(crs.moveToNext());

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });


            }



        });
Ecco, il refreshing funziona.

domenica 23 ottobre 2016

Updating di Firebase.

Bene, ho trovato qualcosa sull'updataggio di Firebase.
E' meglio che non ci mettiamo sbarramenti e andiamo fluidi come l'acqua.
Seguo un codice di StackOverflow...

Intanto inizio dal riferimento al database.
reference= FirebaseDatabase.getInstance().getReference();
Ora faccio riferimento a un record (child del nodo principale), usando il nome del nodo, quello "impronunciabile" attribuito dal sistema:
        reference= FirebaseDatabase.getInstance().getReference();

        DatabaseReference ref= reference.child("-KUae5VW3jfCoWP2yQmc");
        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
E funziona:
10-23 13:10:57.145 19033-19033/? I/System.out: {cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}
Ora, se io cerco nello specifico il nodo che si chiama Rossi, anziché identificarlo per mezzo del codice impronunciabile del nodo?
Usare una query.
Come si usano le query su Firebase?

Ecco, io voglio cercare Rossi, non Hxydkjlsdknsokdgrunth eccetera: come devo fare?
Ci provo...

Ottengo la reference al database:
reference= FirebaseDatabase.getInstance().getReference();
Ora, nella situazione di prima io selezionavo il nodo con il nome impronunciabile facendo così:
DatabaseReference ref= reference.child("-KUae5VW3jfCoWP2yQmc");
mentre ora provo questo:
        reference= FirebaseDatabase.getInstance().getReference();
        reference.orderByChild("cognome").equalTo("Rossi").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:02:42.041 26561-26561/? I/System.out: {-KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}}
Perfetto! Ora provo a eliminare la condizione equalTo:
         reference.orderByChild("cognome").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:13:41.641 28531-28531/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
Ecco: ottengo tutti.

Ma se faccio OrderByChild("nome"):
        reference.orderByChild("nome").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:20:05.071 29020-29020/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
L'ordine è sempre lo stesso.

Ora voglio selezionare quello che si chiama Luigi di nome:
        reference.orderByChild("nome").equalTo("Luigi").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:27:48.931 31604-31604/? I/System.out: {-KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
Perfetto!

Adesso voglio trovare quel simpatico galantuomo che fa di professione lo squartatore:
        reference.orderByChild("professione").equalTo("Squartatore").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:29:49.581 32582-32582/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}}


Perfetto!
Ora faccio cambiare mestiere a un altro galantuomo per vedere se con la query "Squartatore" me li identifica tutti e due. Ecco, Mario Rossi non fa più lo stupratore ma fa lo squartatore anche lui.
Vediamo...

Non c'è stato bisogno di far correre il programma ancora: dato che il cambiamento del database mi attiva il listener ho ottenuto direttamente sulla finestra LogCat questo:
10-23 18:32:10.361 32582-32582/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Squartatore, nome=Mario, eta=45}}
Perfetto!
Ora vediamo: esiste un "tipo" Query che identifichi questi gruppi selezionati per mezzo di query?
        Query query= reference.orderByChild("professione").equalTo("Squartatore");
        
        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
Sì, esiste. Posso immagazzinare alcune queries in modo da avere la selezione già preconfezionata, forse...

Ora Mario Rossi ha messo la testa a posto e ha cambiato vita, e non fa più lo stupratore o lo squartatore, ma adesso fa il chierichetto presso la parrocchia.
Come farlo sapere anche a Firebase?

Vediamo...

Vado sul nodo:
        reference= FirebaseDatabase.getInstance().getReference();
        DatabaseReference ref=reference.child("-KUae5VW3jfCoWP2yQmc");
Quindi creo una HashMap, e ci metto "professione" e "Chierichetto".
Quindi applico al nodo in cui mi trovo il metodo updateChildren().
        Map<String,Object> mappa=new HashMap<String,Object>();
        mappa.put("professione","Chierichetto");
        ref.updateChildren(mappa);
E funziona!



Ma ho identificato il galantuomo in questione con il nome impronunciabile del nodo.
Ora lo identifico in modo più umano.
Faccio una query con il cognome.
Anche Luigi Verdi ha cambiato vita, e adesso non fa più il serial killer ma il sagrestano.
Identifichiamolo con il nome.

Ho avuto molta difficoltà a individuare il nome del nodo dallo snapshot. Ecco:
        Query query= reference.orderByChild("cognome").equalTo("Verdi");

        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                for(DataSnapshot child : dataSnapshot.getChildren()){
                    DatabaseReference r=reference.child(child.getKey());
                    Map<String,Object> mappa=new HashMap<String, Object>();
                    mappa.put("professione","sagrestano");
                    r.updateChildren(mappa);
                }

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
E finalmente Verdi fa il sagrestano!



Anche l'altro galantuomo, Carlo Bianchi, rispettabile squartatore, ha deciso di mettere la testa a posto, e si fa addirittura frate! Dunque cambia anche nome: prenderà il nome di Fra' Mamozzio.
Vediamo se riusciamo a farlo sapere a Firebase...
        Query query= reference.orderByChild("cognome").equalTo("Bianchi");

        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for(DataSnapshot record : dataSnapshot.getChildren()){
                    DatabaseReference r = reference.child(record.getKey());
                    Map<String,Object> mappa = new HashMap<String, Object>();
                    mappa.put("professione","frate");
                    mappa.put("nome","Fra Mamozzio");
                    r.updateChildren(mappa);
                    

                }


            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
Questa volta abbiamo corretto due campi. Vediamo se funziona...



Perfetto!!!