JavascriptProva

domenica 6 novembre 2016

Feedback vocale e conferma

Adesso inserisco un feedback.
Quando parlo, potrei non essere stato capito bene. E' bene che ci sia una risposta che mi permetta di verificare se il mio comando è stato ben capito.
Organizzo una risposta vocale...

Inizializzo il tts:
    TextToSpeech tts;

.....

        tts=new TextToSpeech(getApplicationContext(),new TextToSpeech.OnInitListener(){

            @Override
            public void onInit(int status) {
                if(status==TextToSpeech.SUCCESS){
                    tts.setLanguage(Locale.ITALIAN);
                }
            }
        });
Ora, quando ho parlato, arrivo a OnActivityResult.
E' qui che devo inserire il feedback.
Devo inserire un button "Annulla", però...

O meglio un button "Conferma".
Ecco tutto ciò che ho realizzato:


MainActivity:
public class MainActivity extends AppCompatActivity {

    Button bttConferma;
    TextToSpeech tts;
    Helper helper;
    Button bottone;
    ArrayList lista;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bttConferma=(Button)findViewById(R.id.bttConferma);
        bttConferma.setEnabled(false);
        bttConferma.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                helper.save(lista.get(0));
                bttConferma.setEnabled(false);
                Intent i=new Intent(getApplicationContext(),Lista.class);
                startActivity(i);
            }
        });

        tts=new TextToSpeech(getApplicationContext(),new TextToSpeech.OnInitListener(){

            @Override
            public void onInit(int status) {
                if(status==TextToSpeech.SUCCESS){
                    tts.setLanguage(Locale.ITALIAN);
                }
            }
        });

        helper=new Helper(this);

        bottone=(Button)findViewById(R.id.button);
        bottone.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                bttConferma.setEnabled(false);
                cancellaTimer();
                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){
            lista=data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            tts.speak(lista.get(0),TextToSpeech.QUEUE_FLUSH,null);
            bttConferma.setEnabled(true);

        }else{
            Intent i=new Intent(getApplicationContext(),Lista.class);
            startActivity(i);
        }



    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater=this.getMenuInflater();
        inflater.inflate(R.menu.miomenu,menu);
        return true;
    }



    @Override
    public boolean onOptionsItemSelected(MenuItem menuItem){
        switch(menuItem.getItemId()){
            case R.id.stopTimer:
                cancellaTimer();
                break;
        }
        return true;
    }

    private void cancellaTimer(){
        Intent intent=new Intent(getApplicationContext(),
                Lista.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),
                0,
                intent,
                0);
        AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
        if(pendingIntent!=null)pendingIntent.cancel();
    }

    private void avviaTimer(int secondi){
        //stabilisce il ciclo
        Intent intent=new Intent(getApplicationContext(),Lista.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
        AlarmManager alarmManager=(AlarmManager) getApplicationContext().getSystemService(ALARM_SERVICE);
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+Const.secondi*1000,pendingIntent);

    }

}


Lista:
public class Lista extends AppCompatActivity {

    Button bttChiudi;
    Helper helper;
    MyCursorAdapter myCursorAdapter;
    ListView listView;

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

        bttChiudi=(Button)findViewById(R.id.bttChiudi);
        bttChiudi.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                finish();
            }
        });
        helper=new Helper(this);
        Cursor crs=helper.query();
        myCursorAdapter=new MyCursorAdapter(this,crs);
        listView=(ListView)findViewById(R.id.listView);
        listView.setAdapter(myCursorAdapter);





    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        Cursor crs=helper.query();
        if(crs.getCount()!=0) {

            Intent intent = new Intent(Lista.this, Lista.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
            AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(ALARM_SERVICE);
            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + Const.secondi * 1000, pendingIntent);
        }

    }

    class MyCursorAdapter extends CursorAdapter {

        Context context;
        public MyCursorAdapter(Context context, Cursor c) {
            super(context, c);
            this.context=context;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
            return layoutInflater.inflate(R.layout.row,null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView textView=(TextView)view.findViewById(R.id.textView);
            final TextView textView2=(TextView)view.findViewById(R.id.textView2);
            Button button=(Button)view.findViewById(R.id.button2);
            textView.setText(cursor.getString(cursor.getColumnIndexOrThrow("voce")));
            textView2.setText(""+cursor.getInt(cursor.getColumnIndexOrThrow("_id")));
            button.setOnClickListener(new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    helper.cancella(Integer.parseInt(textView2.getText().toString()));
                    myCursorAdapter.getCursor().requery();
                }
            });
        }
    }



}

HTML: Ripasso del layout a due colonne.

Partiamo subito sul sodo: mi serve un layout a due colonne.

Assodato che stabilire la larghezza della colonna laterale in termini percentuali sia sconveniente per la possibile comparsa di scrollbars orizzontali, questa va impostata in termini fissi.
Tutorial che sto seguendo

Ecco, sono riuscito a ottenere qualche risultatino decente, ossia una colonna di navigazione a sinistra: la regola fondamentale è usare il float per la barra navigation, e impostarne la larghezza (fissa), per poi impostare la proprietà margin-left del DIV "contenuto" allo stesso valore della larghezza della navigation.
<html>
<head>
<style>
#container:{
 width:300px;
 
}

#content{
 margin-left:200px;
 background-color:#AAFFAA; 
}

#navigation{
 float:left;
 width:200px;
 background-color:#FFCCAA;
}
 
</style>
</head>
<body>
<body>
    <div id="container">
        <div id="header"></div>
        <div id="navigation">
         Lunedi<br>
         Martedi<br>
         Mercoledi<br>
         Giovedi<br>
         Venerdi<br>
         Sabato<br>
        </div>
        <div id="content">
         Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,
        </div>
        <div id="footer"></div>
    </div>
</body>
</body>
</html>

sabato 5 novembre 2016

Ripasso del delete di un record dal database, requery per refreshare la listView costruita con il CursorAdapter

Ripasso la cancellazione di un record di database.
Ho ripreso il codice di J-Communicator:
 public void cancella(String etichetta, String categoria){
  SQLiteDatabase db=this.getWritableDatabase();
  db.delete("tabella", "etichetta = '"+etichetta+"' and categoria='"+categoria+"'", null);
 }
Ecco: dopo la "rituale" apertura del database, si usa il metodo delete che prende come parametri la tabella e i criteri del record da cancellare, quindi il null che probabilmente è riferito a un array di strings.
Vediamo la documentazione...

int delete (String table, 
                String whereClause, 
                String[] whereArgs)
Sì, è la whereClause, ossia le clausole per il record da cancellare.


Ora provo ad aggiungere a bindView l'aggiunta di un listener al button.
Ho terminato tutto il lavoro su bindView, e mi devo concentrare un po' su di essa.
        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView textView=(TextView)view.findViewById(R.id.textView);
            final TextView textView2=(TextView)view.findViewById(R.id.textView2);
            Button button=(Button)view.findViewById(R.id.button2);
            textView.setText(cursor.getString(cursor.getColumnIndexOrThrow("voce")));
            textView2.setText(""+cursor.getInt(cursor.getColumnIndexOrThrow("_id")));
            button.setOnClickListener(new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    helper.cancella(Integer.parseInt(textView2.getText().toString()));
                    myCursorAdapter.getCursor().requery();
                }
            });
        }
L'aggiunta di un listener va per il meglio. Ho creato anche il codice che cancella un record in relazione all'id.
Ho avuto un po' di difficoltà per il refresh della listView, ma con il requery() ho risolto.

Ripasso del CursorAdapter per collegare una ListView a una tabella di database.

Il CursorAdapter avrebbe un metodo newView che non sarebbe altro che l'inflatazione del nuovo Layout, come ArrayAdapter.
La differenza potrebbe stare nel fatto che mentre nell'ArrayAdapter il metodo getView fa tutto lui, nel CursorAdapter la view viene inflatata da newView e poi in essa vengono messi i dati tramite bindView.
Vediamo un po' i parametri dei due metodi.

Sì, credo di essere in grado di provare...

Iniziamo estendendo CursorAdapter.
    class MyCursorAdapter extends CursorAdapter {

        Context context;
        public MyCursorAdapter(Context context, Cursor c) {
            super(context, c);
            this.context=context;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
            return layoutInflater.inflate(R.layout.row,null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView textView=(TextView)view.findViewById(R.id.textView);
            Button button=(Button)view.findViewById(R.id.button2);
            textView.setText(cursor.getString(cursor.getColumnIndexOrThrow("voce")));
        }
    }
E l'ho sperimentato: ecco tutto il codice:
public class Lista extends AppCompatActivity {

    Helper helper;
    MyCursorAdapter myCursorAdapter;
    ListView listView;

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

        helper=new Helper(this);
        Cursor crs=helper.query();
        myCursorAdapter=new MyCursorAdapter(this,crs);
        listView=(ListView)findViewById(R.id.listView);
        listView.setAdapter(myCursorAdapter);





    }

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

        Intent intent=new Intent(Lista.this,Lista.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
        AlarmManager alarmManager=(AlarmManager) getApplicationContext().getSystemService(ALARM_SERVICE);
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+5*1000,pendingIntent);

    }

    class MyCursorAdapter extends CursorAdapter {

        Context context;
        public MyCursorAdapter(Context context, Cursor c) {
            super(context, c);
            this.context=context;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
            return layoutInflater.inflate(R.layout.row,null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView textView=(TextView)view.findViewById(R.id.textView);
            Button button=(Button)view.findViewById(R.id.button2);
            textView.setText(cursor.getString(cursor.getColumnIndexOrThrow("voce")));
        }
    }



}
Funziona perfettamente!

Aggiunta di eventi nel CustomAdapter

Ho modificato il CustomAdapter in modo da aggiungere un listener al button in ogni layout inflatato nella ListView.
    class CustomAdapter extends ArrayAdapter{

        Context context;
        int resource;
        ArrayList arrayList;
        public CustomAdapter(Context context, int resource, ArrayList objects) {
            super(context, resource, objects);
            this.context=context;
            this.resource=resource;
            this.arrayList=(ArrayList)objects;
        }

        @Override
        public View getView(int position,View convertView,ViewGroup group){
            LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
            convertView=layoutInflater.inflate(this.resource,null);
            final TextView textView=(TextView)convertView.findViewById(R.id.textView);
            Button button=(Button)convertView.findViewById(R.id.button2);
            textView.setText(arrayList.get(position).testoText);
            button.setText(arrayList.get(position).testoButton);
            button.setOnClickListener(new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    textView.setText("OKAY");
                }
            });
            return convertView;
        }
    }
Funziona.

Ripasso di CustomAdapter che estende ArrayAdapter

ArrayAdapter ha diversi tipi di costruttori. Mi sono rinfrescate le idee su cose viste, peraltro, anche abbastanza recentemente, ma che scordo con estrema facilità.
Ecco qui l'elenco delle firme dei costruttori:
Context, int
Context, int, int
Context, int, T[]
Context, int, int, T[]
Context, int, List
Context, int, int, List
In particolare, quello che ho usato prima è un costruttore a quattro parametri, precisamente l'ultimo in quanto ho usato una classe che eredita da List.
A quattro parametri posso usare anche il terzultimo, che invece di una classe di tipo List usa un semplice Array.
Poi si può usare a tre parametri, senza selezionare la view del layout.
La creazione di un CustomView estende ArrayAdapter e ne usa il costruttore a tre parametri context, layout e array, ossia il penultimo, Context, int, List
Provo a estendermi ArrayAdapter per crearmi un adapter personalizzato. Mi creo un tipo con due membri:
    class Tipo{
        public String testoText;
        public String testoButton;
    }
Di questo tipo creerò diverse istanze, che collocherò in un'arrayList.

Ora provo a estendere ArrayAdapter:
    class CustomAdapter extends ArrayAdapter{

        Context context;
        int resource;
        ArrayList<Tipo> arrayList;
        public CustomAdapter(Context context, int resource, ArrayList<Tipo> objects) {
            super(context, resource, objects);
            this.context=context;
            this.resource=resource;
            this.arrayList=(ArrayList)objects;
        }

        @Override
        public View getView(int position,View convertView,ViewGroup group){
            LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
            convertView=layoutInflater.inflate(this.resource,null);
            TextView textView=(TextView)convertView.findViewById(R.id.textView);
            Button button=(Button)convertView.findViewById(R.id.button2);
            textView.setText(arrayList.get(position).testoText);
            button.setText(arrayList.get(position).testoButton);
            return convertView;
        }
    } 
Creo diverse istanze di Tipo, le metto in un arrayList e do l'arrayList in pasto a una istanza di CustomAdapter.
        Tipo tipo=new Tipo();
        tipo.testoButton="Bottone";
        tipo.testoText="Testo";
        
        Tipo tipo1=new Tipo();
        tipo1.testoButton="Bottone secondo";
        tipo1.testoText="Testo secondo";
        
        Tipo tipo2=new Tipo();
        tipo2.testoButton="Altro bottone";
        tipo2.testoText="Altro testo";

        arrayList=new ArrayList<Tipo>();
        arrayList.add(tipo);
        arrayList.add(tipo1);
        arrayList.add(tipo2);

        customAdapter=new CustomAdapter(this,R.layout.row,arrayList);

        listView=(ListView) findViewById(R.id.listView);
        listView.setAdapter(customAdapter); 
Proviamo...



Sì, eccolo.

Nuovo ripasso dell'ArrayAdapter

Devo ripassare ancora ArrayAdapter.
Strano, ma non mi entra assolutamente in testa.

Risposta parziale:
Un ArrayAdapter usa un array di stringhe per porlo in una ListView.
Mi fabbrico un layout da inflatare con una TextView e un button.
Posso giocare a porre la stringa sia come testo della TextView sia come testo del button.
Vediamo se mi riesce...

A parte il context, l'adapter deve prendere come parametri il layout e la view specifica.
E da ultimo l'array da cui prendere gli elementi per metterli nella view specifica.
Ho realizzato un codice usando come view specifica sia la TextView sia il Button.
Con il primo codice:
public class Lista extends AppCompatActivity {

    ArrayAdapter arrayAdapter;
    ArrayList arrayList;
    ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista);

        arrayList=new ArrayList();
        arrayList.add("lunedi");
        arrayList.add("martedi");
        arrayList.add("mercoledi");

        arrayAdapter=new ArrayAdapter(this,R.layout.row,R.id.textView,arrayList);
        listView=(ListView) findViewById(R.id.listView);

        listView.setAdapter(arrayAdapter);
    }

...
Ho ottenuto questo:



Con questo codice, invece, che prende come view specifica il Button...
public class Lista extends AppCompatActivity {

    ArrayAdapter arrayAdapter;
    ArrayList arrayList;
    ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista);

        arrayList=new ArrayList();
        arrayList.add("lunedi");
        arrayList.add("martedi");
        arrayList.add("mercoledi");

        arrayAdapter=new ArrayAdapter(this,R.layout.row,R.id.button2,arrayList);
        listView=(ListView) findViewById(R.id.listView);

        listView.setAdapter(arrayAdapter);
    }

.....
ottengo questo: