JavascriptProva

venerdì 12 ottobre 2018

Aggiunta di fragments da xml.

Nuovo esercizio sui fragment.
Creare due fragments in xml.

Cliccando direttamente su New -> Fragment ottengo sia il file xml sia quello java.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#AAFFFF"
    tools:context=".primo">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:id="@+id/testo1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />


</FrameLayout> 
(ci ho aggiunto uno sfondo celeste per distinguerlo)
public class primo extends Fragment {


    public primo() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_primo, container, false);
    }

}


Adesso ottengo l'altro frammento:



Ecco, ho creato un frammento lasciando il nome di default, BlankFragment.
Vediamone il file xml e il file java.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFAAAA"
    tools:context=".BlankFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

</FrameLayout> 
Ci ho aggiunto uno sfondo rosso.

Ora devo riportare questi fragment sulla MainActivity: ho creato un altro video:



Ed ecco:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:id="@+id/container"
    tools:context=".MainActivity">


    <fragment
        android:id="@+id/fragment"
        android:name="com.example.antonello.studiofragment.primo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/fragment2"
        android:name="com.example.antonello.studiofragment.BlankFragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />
</LinearLayout> 
I due fragment hanno l'altezza impostata a wrap_content, per cui appaiono della stessa altezza delle textview compresevi dentro:






Ora vediamo il file java.
public class primo extends Fragment {


    public primo() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_primo, container, false);
    }

}
Ecco, praticamente viene inflatato il codice xml del fragment all'interno del ViewGroup chiamato container.
Tutto qui.

martedì 2 ottobre 2018

YouTubePlayerView

E' il momento di fare e disfare Mandala su YouTubePlayerView.
public class MainActivity extends YouTubeBaseActivity {
    //DICHIARAZIONI DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
    YouTubePlayerView player;
    YouTubePlayer.OnInitializedListener onInitializedListener;
    Button button;
    String videoId = "4DTXAGGPFrw";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //INIZIALIZZAZIONI IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
        player=(YouTubePlayerView)findViewById(R.id.youtubePlayer);
        button=(Button)findViewById(R.id.button);

        onInitializedListener = new OnInitializedListener() {
            @Override
            public void onInitializationSuccess(Provider provider, YouTubePlayer youTubePlayer, boolean b) {
                youTubePlayer.loadVideo(videoId);
            }

            @Override
            public void onInitializationFailure(Provider provider, YouTubeInitializationResult youTubeInitializationResult) {

            }
        };

        //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                player.initialize(PlayerConfig.API_KEY,onInitializedListener);
            }
        });
    }
}
Questo codice l'ho scritto quasi di getto, e ha funzionato egregiamente...

sabato 29 settembre 2018

Esercizio sul fragment: creazione e comunicazione con l'activity principale.

Ora voglio tenermi in esercizio con i Fragment.
Non riesco ancora a usare questo blog nella maniera che mi ero preposto, ossia come un diario per esercizi.
Programmiamo un esercizio al giorno sui Fragments, per 5 giorni. Potrebbe essere sufficiente.
Ecco, adesso rompo il mio esercizio e lo ricostruisco da capo.
Aggiunta di due fragment con messa a video in un Toast da parte della finestra principale del testo di una TextBox contenuta in uno di questi due fragments.

Ho cliccato col destro sul nome del package nella finestra "Progetto", e quindi ho cliccato New.
Nel menu ho cliccato Fragment -> Fragment (Blank), quindi ho eliminato le spunte, nella finestra, su include fragment factory methods e su include interface callback?.
, ho scelto un nuovo nome al posto di BlankFragment e Android Studio mi crea il fragment, ossia un file xml e un file java.

Sono stato messo un po' in crisi dal fatto che mi è stato messo tutto in un FrameLayout.
Nell'esercizio che ho fatto ieri non mi ero accorto di questo fatto, confuso da diversi tutorial.
Bene: all'interno del FrameLayout ci mettiamo un layout a nostro piacimento, Linear, Relative o altro.

Questo è l'XML di un fragment:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    tools:context=".fragment_origine">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="10"
        tools:context=".fragment_origine">

        <!-- TODO: Update blank fragment layout -->

        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/hello_blank_fragment" />

        <Button
            android:id="@+id/button4"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="Button" />

    </LinearLayout>

</FrameLayout> 
Per l'altro fragment faccio la stessa cosa, senza alcuna view particolare, ma solo con uno sfondo azzurrino:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragment_altro">

    <LinearLayout
        android:background="#AACCFF"
        android:layout_width="match_parent"
        android:layout_height="match_parent">


    </LinearLayout>



</FrameLayout> 

Ora che ho preparato i fragments, li dispongo all'interno del mainLayout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:id="@+id/container"
    tools:context=".MainActivity">
    
    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/fragment1"
        android:name="com.example.antonello.studiofragment.fragment_origine"/>
    
    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/fragment2"
        android:name="com.example.antonello.studiofragment.fragment_altro"/>
        



</LinearLayout> 
Funziona!


Ora devo recepire l'azione sul pulsante del primo fragment.
public class fragment_origine extends Fragment {
    TextView testo;
    Button button;
    public fragment_origine() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_origine, container, false);
        testo=view.findViewById(R.id.textview);
        button=view.findViewById(R.id.button4);
        
        return view;
    }
.....
Dichiaro e inizializzo le variabili di tipo TextView e Button.
Avevo dimenticato di specificare l'id per la TextView, e rimedio subito:
        <TextView
            android:id="@+id/textview"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/hello_blank_fragment" />
Poi ho creato un'interfaccia:
    public interface OnTextReadListener{
        public void onTextRead(String messaggio);
    }
e ho dichiarato una variabile del tipo dell'interfaccia:
public class fragment_origine extends Fragment {
    OnTextReadListener mListener;
    TextView testo;
    Button button;
    public fragment_origine() {
        // Required empty public constructor
    }
.....
Ora devo fare in modo che all'evento onAttach(Context context) venga eguagliata alla variabile del tipo dell'interfaccia, mListener, la main activity alla quale il fragment si è "attaccato":
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mListener=(OnTextReadListener)context;
    }
Quindi vado a MainActivity e dichiaro che implementa l'interfaccia di cui sopra:
public class MainActivity extends AppCompatActivity implements fragment_origine.OnTextReadListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
Quindi scrivo, sempre in MainActivity, il codice del metodo da implementare:
    @Override
    public void onTextRead(String messaggio) {
        Toast.makeText(this,messaggio,Toast.LENGTH_LONG).show();
    }
Avviando il programma, non ottengo errori.
Ma finora ho solo creato una classe che implementa il metodo dell'interfaccia dichiarata nel fragment, non ho ancora subordinato l'esecuzione di questo metodo da parte del Button.
Provvedo:
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                mListener.onTextRead(testo.getText().toString());
            }
        });
E adesso funziona!

domenica 26 agosto 2018

Esercizi sulle suonerie.

Ho bisogno di esercitarmi sulle suonerie.

Prima la sezione sulla scelta della suoneria dalle SharedPreferences o di default:
        bttSuona.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                Uri ringtoneUri=null;
                Ringtone ringtone=null;
                String stringSuoneria=sp.getString("suoneria","");
                if(TextUtils.isEmpty(stringSuoneria)){
                    ringtoneUri=RingtoneManager.getActualDefaultRingtoneUri(
                            getApplicationContext(),
                            RingtoneManager.TYPE_NOTIFICATION);
                }else{
                    ringtoneUri=Uri.parse(stringSuoneria);
                }
                ringtone=RingtoneManager.getRingtone(
                        getApplicationContext(),
                        ringtoneUri);
                ringtone.play();


            }
        });
Funziona! Cancellando le SharedPreferences, viene invocata la suoneria di default.

Ora la parte che serve per settare la suoneria.
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {

                Intent intent=new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
                intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,RingtoneManager.TYPE_NOTIFICATION);
                startActivityForResult(intent,0);

            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Uri uri=data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
        editor.putString("suoneria",uri.toString());
        editor.apply();
    }
Vediamo se funziona...

Funziona!
Distruggo e riscrivo senza sbirciare:
        bttSuona.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {

                Uri ringtoneUri;
                Ringtone ringtone;
                String strSuoneria=sp.getString("suoneria","");
                if(TextUtils.isEmpty(strSuoneria)){
                    ringtoneUri=RingtoneManager.getActualDefaultRingtoneUri(
                            getApplicationContext(),
                            RingtoneManager.TYPE_NOTIFICATION);

                }else{
                    ringtoneUri=Uri.parse(strSuoneria);
                }
                ringtone=RingtoneManager.getRingtone(getApplicationContext(),
                        ringtoneUri);
                ringtone.play();
            }
        });
Ora riscrivo la seconda parte per la scelta della suoneria.
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                Intent intent=new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
                intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,RingtoneManager.TYPE_NOTIFICATION);
                startActivityForResult(intent,0);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Uri uri=data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
        editor.putString("suoneria", uri.toString());
        editor.apply();

    }
}
Credo di aver memorizzato abbastanza...

giovedì 16 agosto 2018

Esercitazione sulle misure dello schermo

Innanzitutto devo definire una variabile di tipo Point in cui mettere i valori di larghezza e altezza del display.
Devo definire anche una variabile di tipo WindowManager.
        wm=(WindowManager)getSystemService(WINDOW_SERVICE);
        szWindow=new Point();
Ora creo un oggetto di tipo Display estraendolo mediante la funzione getDefaultDisplay di WindowManager:
        Display display= wm.getDefaultDisplay();
Quindi di questo display estraggo le dimensioni nella variabile Point:
        display.getSize(szWindow);
Bene.
Cancello tutto e poi vado avanti.
        szWindow=new Point();
        wm=(WindowManager)getSystemService(WINDOW_SERVICE);
        Display display=wm.getDefaultDisplay();
        display.getSize(szWindow);
        System.out.println(szWindow.x + " " + szWindow.y);
Provo a visualizzare in LogCat i valori della larghezza e altezza del display:
08-16 10:46:39.880 5341-5341/com.example.antonello.chatheadstudio I/System.out: 720 1184

Andando a vedere nelle caratteristiche dell'emulatore, ottengo dimensioni di 720 x 1280.
Lo scarto sarà dovuto alle barre, poi lo vedrò meglio.
Ora provo il codice su dispositivo reale.
Bene: ecco le misure:
08-16 13:05:21.371 27953-27953/com.example.antonello.chatheadstudio I/System.out: 720 1280

Da dove venga quella differenza fra emulatore e dispositivo fisico, non saprei.
Comunque funziona!

Esercitazione sui permessi a runtime senza riavviare l'App.

Codice che imposta i permessi a runtime senza necessità di riavviare l'applicazione.
Ho cancellato tutto e ora me lo ricostruisco.
public class MainActivity extends AppCompatActivity {

    Handler handler=new Handler();


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

        Runnable runnable =new Runnable() {
            @Override
            public void run() {
                if(Build.VERSION.SDK_INT >= 23 && Settings.canDrawOverlays(getApplicationContext())){
                    Intent i=new Intent(getApplicationContext(),MainActivity.class);
                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(i);
                }else{
                    handler.postDelayed(this,1000);
                }
            }
        };
        if(Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(getApplicationContext())){
            Intent intent=new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:"+getPackageName()));
            startActivity(intent);
            handler.postDelayed(runnable,1000);
        }else{
            continua();
        }

    }

    private void continua(){
        System.out.println("STO CONTINUANDO");
    }

}
Funziona! L'ho scritto bene, a quanto pare!

sabato 11 agosto 2018

Headers delle mail in PHP (From, Reply-To)

Vediamo un po' gli header delle mail...

Semplicemente l'header è "From..." eccetera.
E' praticamente una stringa.
Proviamo.

$email="antonello.iaccarino@alice.it";
$titolo="Titolo";
$testo="La vispa teresa avea tra l'erbetta a volo sorpreso gentil farfalletta";
$headers="From: trigonjac@gmail.com";


mail($email,$titolo,$testo,$headers);
Basta scrivere in una sola stringa "From: ... seguito dall'indirizzo o da una variabile che lo rappresenti."

Per l'aggiunta del Reply-To, voglio provare a scrivere questo nella stessa linea, vediamo che succede.
$headers="From: trigonjac@gmail.com Reply-To: cicciofatticcio@minkia.com";
Vediamo che succede...

Non ho ricevuto nessuna mail.br Invece disponendo su linee diverse in questo modo:
$headers="From: trigonjac@gmail.com\r\n";
$headers.="Reply-To: cicciofatticcio@minkia.com";
ottengo una mail avente per mittente trigonjac ma quando vado a rispondere rispondo a cicciofatticcio.

Da quanto ho capito il From: serve solo per notificare chi sia il mittente, mentre il Reply-To sta a indicare il destinatario della risposta.

Immagini in overlay: i permessi supplementari dall'API 23 in su.

Il macello dei permessi supplementari nelle API da 23 in su me lo sono dimenticato quasi completamente.
Ma è solo un ripasso, e inoltre ho anche i miei appunti.
Vediamo...

Devo fare un overlay e mi servono i permessi.

Intanto diciamo che il problema si pone solo per un'API superiore o uguale a 23, se contemporaneamente il permesso per gli overlay può essere negato. In tutti gli altri casi vale la classica procedura.

        if(Build.VERSION.SDK_INT >=23 && !Settings.canDrawOverlays(getApplicationContext())){
            
        }
Ecco, questa è l'eventualità in cui si deve porre una risposta diversa: API da 23 in su e assenza (!) dei permessi System.canDrawOverlays(Context).
In questo caso, che dobbiamo fare?

Il codice che viene dopo non mi risulta facilmente comprensibile, come non mi ci era mai risultato nemmeno prima.
Lo vedrò in seguito.
Intanto andiamo avanti.
        if (Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(getApplicationContext())) {

            Intent i=new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:"+getPackageName()));
            startActivityForResult(i,0);
        }

        else{
            Intent intent=new Intent(getApplicationContext(),MyService.class);
            startService(intent);
        }
Praticamente fa la distinzione del caso in cui sia un'API superiore o uguale a 23 con i permessi canDrawOverlay negati e gli altri casi.
C'è poi onActivityResult, che riscrivo:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode==RESULT_OK){
            if(Build.VERSION.SDK_INT >= 23 && Settings.canDrawOverlays(getApplicationContext())){
                Intent intent=new Intent(getApplicationContext(),MyService.class);
                startService(intent);

            }
        }
    }
Dovrebbe funzionare... Speriamo.

E infatti funziona!.

Dovrei farmi diversi Mandala ancora, e comprendere bene il codice.

venerdì 10 agosto 2018

Numeri casuali

Ho un intervallo di tempo e voglio estrarre a sorte un valore casuale in un range che va dal valore dell'intervallo meno una percentuale al valore dell'intervallo più la stessa percentuale.

min = intervallo - intervallo * percentuale
max = intervallo + intervallo * percentuale
Andiamo...

Ecco il codice, cui ho aggiunto due mie funzioni, una che converte il tempo espresso in ore, minuti e secondi in millisecondi, l'altra che dà una lettura sotto forma di stringa in ore, minuti e secondi di un tempo espresso in millisecondi.
        long intervallo = intervalInMillis(1,0,0);
        float percentuale=0.5f;
        long variazione = (long)((float)intervallo * percentuale);
        long min = intervallo - variazione;
        long max = intervallo + variazione;

        long random = (long)(Math.random() * (max - min + 1) + min);

        System.out.println("minimo "+strTime(min));
        System.out.println("massimo "+strTime(max));
        System.out.println("valore casuale "+strTime(random));

    }

    public long intervalInMillis(long ore, long minuti, long secondi){
        return ore*3600*1000+minuti*60*1000+secondi*1000;
    }

    public String strTime(long tempo) {
        long minuti = tempo / 60000;
        long ore = minuti / 60;
        minuti = minuti % 60;
        return "ore: " + ore + "; minuti: " + minuti;
    }
Ed ecco il risultato (ho scelto una percentuale del 50% in meno o in più su un intervallo di un'ora:
08-10 21:33:05.953 6860-6860/com.antonello.labagosto18 I/System.out: minimo ore: 0; minuti: 30
08-10 21:33:05.954 6860-6860/com.antonello.labagosto18 I/System.out: massimo ore: 1; minuti: 30
                                                                     valore casuale ore: 1; minuti: 29
08-10 21:34:09.683 6936-6936/com.antonello.labagosto18 I/System.out: minimo ore: 0; minuti: 30
                                                                     massimo ore: 1; minuti: 30
                                                                     valore casuale ore: 1; minuti: 13
08-10 21:34:57.812 6981-6981/com.antonello.labagosto18 I/System.out: minimo ore: 0; minuti: 30
                                                                     massimo ore: 1; minuti: 30
                                                                     valore casuale ore: 0; minuti: 39
08-10 21:35:52.024 7026-7026/? I/System.out: minimo ore: 0; minuti: 30
                                             massimo ore: 1; minuti: 30
                                             valore casuale ore: 1; minuti: 4

AlarmManager

AlarmManager.
Mi riscrivo da capo il codice...

public class MainActivity extends AppCompatActivity {
    AlarmManager alarmManager;
    PendingIntent pendingIntent;
    Calendar calendar;
    Intent intent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        calendar=Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY,20);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);


        intent=new Intent(getApplicationContext(),MyService.class);
        pendingIntent=PendingIntent.getService(getApplicationContext(),0,
                intent,0);
        alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),
                pendingIntent);


    }
}
Funziona alla perfezione!
Ho ripassato anche la modalità ELAPSED_REALTIME:
public class MainActivity extends AppCompatActivity {
    AlarmManager alarmManager;
    PendingIntent pendingIntent;
    Calendar calendar;
    Intent intent;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button=(Button)findViewById(R.id.button);



        intent=new Intent(getApplicationContext(),MyService.class);
        pendingIntent=PendingIntent.getService(getApplicationContext(),0,
                intent,0);
        alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);


        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime()+20*1000,
                        pendingIntent);
            }
        });

    }
}
E da quanto ho sperimentato, è indipendente dal fatto che durante l'intervallo il dispositivo venga spento o no.

domenica 27 maggio 2018

Esercizi con l'Ajax.

Esercizio di corretta scrittura di una XMLHttpRequest.
<!DOCTYPE html>
<html>
    <script>
        function funzione(){
            var xhttp=new XMLHttpRequest();
            xhttp.onreadystatechange=function(){
                if(this.readyState==4 && this.status == 200){
                    document.getElementById("testo").innerHTML=this.responseText;
                }
            };
            xhttp.open("GET","testo.txt",true);
            xhttp.send();
        }
        
        
    </script>
    <body>
        <div id="testo"></div>
        <input type="button" value="cambia testo" onClick="funzione()">
    </body>
</html> 
Eccolo funzionante! Però avevo commesso un errore nel punto evidenziato in rosso: invece che onreadystatechange avevo scritto onreadystatuschange.

martedì 8 maggio 2018

Istruzioni preparate per il database MySqli in php.

Che cosa sono questi "comandi preparati" per il database in SQL?
Se io ho inserito i nomi dei sette nani con questa sintassi
$sql="INSERT INTO SetteNani(nome) VALUES('Pisolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Brontolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Cucciolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Eolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Dotto');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Gongolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Mammolo')";

if($conn->multi_query($sql)===TRUE){
    echo "Tabella compilata";
}else{
    echo "Compilazione non effettuata";
}
posso farlo anche con un solo comando in cui cambio i parametri. Questo è quanto sono riuscito finora a capire.
Ecco, si fa proprio con la sintassi $conn->prepare. E poi con bind_param.

Proviamo un po'...

<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername, $username, $password,$database);

if($conn->connect_error){
    die("Connessione non riuscita ".$conn->connect_error);
}
echo "Connessione riuscita
"; $stmt=$conn->prepare("INSERT INTO SetteNani(nome) VALUES(?)"); $stmt->bind_param("s",$nome); $nome="Brontolo"; $stmt->execute(); $nome="Pisolo"; $stmt->execute(); $conn->close(); ?>
Sì, ha funzionato!
Ci aggiungo gli altri:
$stmt=$conn->prepare("INSERT INTO SetteNani(nome) VALUES(?)");
$stmt->bind_param("s",$nomedelnano);

$nomedelnano="Cucciolo";
$stmt->execute();

$nomedelnano="Eolo";
$stmt->execute();

$nomedelnano="Dotto";
$stmt->execute();

$nomedelnano="Mammolo";
$stmt->execute();

$nomedelnano="Gongolo";
$stmt->execute();
Vediamo...

Perfetto!!!

lunedì 7 maggio 2018

Esercizio con la compilazione di una tabella di database MySqli

Ora mi esercito a ottenere l'ultimo ID.
Con questa scusa mi ricostruisco tutto il codice...

<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername, $username, $password,$database);

if($conn->connect_error){
    die("Connessione non riuscita ".$conn->connect_error);
}
echo "Connessione riuscita";


$sql="INSERT INTO Tabella(nome,cognome) VALUES('Ciccio','Formaggio')";

$conn->query($sql);
$last_id = $conn->insert_id;
echo $last_id

?>
E ottengo infatti:
Connessione riuscita9

L'inserimento multiplo l'ho capito, credo, abbastanza bene. Basta sostituire al metodo $conn->query il metodo $conn->multi_query.

Non era così semplice: bisogna porre molta attenzione a questa struttura dei dati da immettere (la tabella creata si chiamava SetteNani.
$sql="INSERT INTO SetteNani(nome) VALUES('Pisolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Brontolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Gongolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Mammolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Eolo');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Dotto');";
$sql.="INSERT INTO SetteNani(nome) VALUES('Cucciolo')";
laddove è necessario il punto dopo il nome della variabile dal secondo record in poi e un punto e virgola accessorio tranne che nell'ultimo record da inserire. Inoltre il nome della tabella è case sensitive, e scrivere Settenani invece che SetteNani è stato un'altra fonte di errore.
Ecco il codice completo per la creazione di una tabella e la sua compilazione: lo riscrivo ancora una volta:
<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername, $username, $password,$database);

if($conn->connect_error){
    die("Connessione non riuscita ".$conn->connect_error);
}
echo "Connessione riuscita
"; $sql="CREATE TABLE SetteNani( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, nome VARCHAR(30) )"; if($conn->query($sql)===TRUE){ echo "Tabella creata"; }else{ echo "Tabella non creata"; } $sql="INSERT INTO SetteNani(nome) VALUES('Pisolo');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Brontolo');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Cucciolo');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Eolo');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Dotto');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Gongolo');"; $sql.="INSERT INTO SetteNani(nome) VALUES('Mammolo')"; if($conn->multi_query($sql)===TRUE){ echo "Tabella compilata"; }else{ echo "Compilazione non effettuata"; } $conn->close(); ?>
E vediamo...

Connessione riuscita
Tabella creataTabella compilata
Ciò che appare sul documento è incoraggiante.
E infatti poi vado a vedere la tabella di database e vedo che i sette nani sono lì tutti al loro posto!

Esercizio sulla connessione e creazione di tabelle in MySqli con php

Ora mi devo esercitare a scrivere il codice in php per la connessione al database del server.

<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

// Create connection
$conn = new mysqli($servername, $username, $password, $database);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";

$sql="CREATE TABLE MyGuests (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
)";

if ($conn->query($sql) === TRUE) {
    echo "Table MyGuests created successfully";
} else {
    echo "Error creating table: " . $conn->error;
}

$conn->close();
?>
Fatto il salvataggio, cancello tutto e ricomincio.

Riscrivo soltanto fino alla creazione della connessione, al momento in modo molto guidato e sbirciando abbondantemente:
<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername,$username,$password,$database);

?>
Non ho ottenuto messaggi di errore, quindi la connessione si è sicuramente creata bene.
Per essere sicuro devo inserire il codice che verifica la connessione.
Il codice va messo prima di "Connessione creata con successo".
La proprietà connect_error della connessione viene saggiata, ossia se esiste $conn->connect_error, allora la connessione "muore" (die?) e si mostra a video "Connessione non riuscita più l'errore $conn->connect_error.
$conn=new mysqli($servername,$username,$password,$database);

if($conn->connect_error){
    die("Connessione fallita " . $conn->connect_error);
}
echo "Connessione riuscita";
?>
Riscrivo il tutto senza guida, se ci riesco.
<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername,$username,$password,$database);

if($conn->connect_error){
    die("Connessione fallita ".$conn->connect_error);
}
echo "Connessione riuscita";
?>
Vediamo se funziona...

Sembra di sì: "Connessione riuscita". Non ho modo di provare se funzioni la connessione fallita perché non so come inficiarla di proposito...

Ora vado a scrivere il codice per la creazione di una tabella. Qui si tratta di impostare la variabile $sql con il linguaggio SQL.
Vediamo se ricordo...

<?php

$servername = "localhost";
$username = "XXXX";
$password = "XXXX";
$database = "id3147737_questodatabase";

$conn=new mysqli($servername,$username,$password,$database);

if($conn->connect_error){
    die("Connessione fallita ".$conn->connect_error);
}
echo "Connessione riuscita";

$sql="CREATE TABLE Tabellina(
    id INT AUTO_INCREMENT PRIMARY KEY,
    nome VARCHAR(30),
    cognome VARCHAR(30)
)";

if($conn->query($sql)===TRUE){
    echo "Tabella creata";
}else{
    echo "Tabella non creata";
}

$conn->close();
?>
Funziona.

lunedì 30 aprile 2018

Esercizi con le variabili php

Bene. Ripartiamo dalle variabili.

Scrivere una variabile con un valore numerico, una con un valore stringa e una booleana e farle apparire sulla pagina.

<?php
$numerico=23;
$stringa="vaffanculo";
$booleana=true;
echo $numerico;
echo $stringa;
echo $booleana;
?>
23vaffanculo1
Bene. Il true viene reso con 1. E scommetto che il false viene reso con 0.
6lt;?php
$numerico=23;
$stringa="vaffanculo";
$booleana=false;
echo $numerico;
echo $stringa;
echo $booleana;
?>
23vaffanculo

C'è qualcosa che non va...

Vediamo di approfondire...

Sembra che la mia sintassi sia corretta...

Vedremo poi nelle istruzioni condizionali.

Le variabili numeriche in realtà si distinguono in integer e float.
<?php
$integer=23;
$float=2.4;
echo $integer;
echo "<br>";
echo $float;
?>
23
2.4

Perfetto.
Dunque finora abbiamo visto le variabili di tipo:
  1. stringa
  2. integer
  3. float
  4. boolean>
Ne esistono altri tipi.

...che sono i più interessanti!
array, ad esempio...

Ma vediamo la funzione var_dump.
Nel tutorial questa funzione è applicata agli arrays, ma adesso la applico alle variabili già viste...

<?php
$integer=23;
$float=2.4;
$stringa="questa è una stringa";
$booleana=false;
var_dump($integer);
echo "<br>";
var_dump($float);
echo "<br>";
var_dump($stringa);
echo "<br>";
var_dump($booleana);
?>
int(23) 
float(2.4) 
string(21) "questa è una stringa" 
bool(false)
Ecco: viene espresso il tipo di variabile e il valore tra parentesi, tranne che per la stringa, per la quale viene messo tra parentesi il numero di caratteri e il valore viene scritto a seguire.

Passiamo agli array.
<?php
$setteNani=array("gongolo","mammolo","pisolo","cucciolo","dotto","eolo","brontolo");
var_dump($setteNani);
?> 
array(7) { [0]=> string(7) "gongolo" [1]=> string(7) "mammolo" [2]=> string(6) "pisolo" [3]=> string(8) "cucciolo" [4]=> string(5) "dotto" [5]=> string(4) "eolo" [6]=> string(8) "brontolo" }

Bene, sì: viene messo tra parentesi il numero di elementi dell'array, quindi gli indici tra parentesi quadre e, separata dal segno =>, ogni variabile che fa parte dell'array.

Per il momento mi fermo qua, perché poi ci sono anche in php le classi.

domenica 22 aprile 2018

Creazione di una nuova tabella in database Access con VB.NET

Ed ecco il codice per la creazione di una nuova tabella del database (Sempre Access 2003).
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0;" &
            "data source=C:\users\antonello\documents\tavoloDB.mdb"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If
        Dim cmd As New OleDb.OleDbCommand("CREATE TABLE tblCavolo(id int,testo varchar)", con)
        cmd.ExecuteNonQuery()
        con.Close()

    End Sub
End Class
...che ha funzionato.

sabato 21 aprile 2018

INSERT e UPDATE database Access mediante VB.NET

Vediamo se riesco a decifrare il codice per inserire un record nella tabella del database.

L'ho scritto.
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0;" &
            "data source=C:\Users\Antonello\Documents\tavoloDB.mdb"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If
        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella (nome,cognome) VALUES('BINGO','BONGO')"
        cmd.ExecuteNonQuery()

        con.Close()


    End Sub
End Class


Ora distruggo il mandala e lo ricostruisco una prima volta.

Ecco la mia ricostruzione:
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0;" &
        "data source=C:\Users\Antonello\Documents\tavoloDB.mdb"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If
        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella(nome,cognome) VALUES('PINCO', 'PALLINO')"
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub
End Class
In rosso, la riga che ho dimenticato, ottenendo il risultato "connessione non inizializzata".
Ripeto il Mandala:
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0;" &
            "data source=C:\users\antonello\documents\tavoloDB.mdb"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella(nome,cognome) VALUES('TIZIO','CAIO')"
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub
End Class
Questa volta me la sono cavata solo con un errore di sintassi SQL nel CommandText (VALUE al posto di VALUES).


Ora provo a cambiare il comando SQL per vedere se posso updatare piuttosto che inserire un nuovo dato.
Ecco:
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0;" &
            "data source=C:\users\antonello\documents\tavoloDB.mdb"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "UPDATE Tabella2 SET nome = 'ABDULLAH'," &
        "cognome ='AKBAR' WHERE id=2;"
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub
End Class
Ho dovuto creare una nuova tabella con il contatore chiamato id perché con l'underscore _id ottenevo un messaggio di errore nella clausola WHERE. Dunque in Access l'underscore non è ammesso, diversamente da quanto accade in Java Android dove è consigliabile.
Questo codice ha funzionato!
Ora su questa tabella di database:
id nome cognome
1 MARIO ROSSI
2 ABDULLAH AKBAR
3 GIUSEPPE VERDI
4 CICCIO PAGNOTTA
5 JOE FETECCHIA
faccio un esercizio: aggiungere PIPPO CAROGNA e sostituire il nome arabo con un nome greco.
Vai!

Ho provato:
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella2(nome, cognome) VALUES('PIPPO','CAROGNA')"
        cmd.ExecuteNonQuery()
        cmd.CommandText = "UPDATE Tabella SET nome='MIKIS',cognome='PARALIRIGORIS'" &
            "WHERE id=2"
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub
ma ottengo errore. Il primo comando me lo esegue, quindi probabilmente il problema sta nel secondo comando. Forse sbaglio qualcosa nell'esecuzione in serie di più comandi.

Ecco, forse ho capito: devo dichiarare come New anche il secondo comando.
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella2(nome, cognome) VALUES('PIPPO','CAROGNA')"
        cmd.ExecuteNonQuery()
        Dim cmd2 As New OleDb.OleDbCommand
        cmd2.Connection = con
        cmd2.CommandText = "UPDATE Tabella2 SET nome='MIKIS',cognome='PARALIRIGORIS'" &
            " WHERE id=2"
        cmd2.ExecuteNonQuery()
        con.Close()
    End Sub
Ho trovato questa soluzione. Non capisco perché non posso usare la variabile cmd per i due comandi...

Comunque la tabella è cambiata:
id nome cognome
1 MARIO ROSSI
2 MIKIS PARALIRIGORIS
3 GIUSEPPE VERDI
4 CICCIO PAGNOTTA
5 JOE FETECCHIA
12 PIPPO CAROGNA
Rimetto un altro nome al numero 2, cancello l'ultimo record (il fatto che riporta l'id = 12 testimonia i vari tentativi con ripetute cancellazioni del record che era stato aggiunto), e riprovo.

Provo così:
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella2(nome, cognome) VALUES('PIPPO','CAROGNA')"
        cmd.ExecuteNonQuery()
        cmd = New OleDb.OleDbCommand
        cmd.CommandText = "UPDATE Tabella2 SET nome='GIULIO',cognome='CESARE'" &
            " WHERE id=2"
        cmd.ExecuteNonQuery()
        con.Close()
...e ottengo l'errore La proprietà connection non è stata inizializzata.

Allora la inizializzo.
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "INSERT INTO Tabella2(nome, cognome) VALUES('PIPPO','CAROGNA')"
        cmd.ExecuteNonQuery()
        cmd = New OleDb.OleDbCommand
        cmd.Connection = con
        cmd.CommandText = "UPDATE Tabella2 SET nome='GIULIO',cognome='CESARE'" &
            " WHERE id=2"
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub
Non ho avuto messaggi di errore.
Ora vediamo la tabella:
id nome cognome
1 MARIO ROSSI
2 GIULIO CESARE
3 GIUSEPPE VERDI
4 CICCIO PAGNOTTA
5 JOE FETECCHIA
13 PIPPO CAROGNA
14 PIPPO CAROGNA
Perfetto! La duplicazione di PIPPO CAROGNA è dovuta al fatto che ho dimenticato di eliminare il record aggiunto quando l'esecuzione è stata interrotta per il primo errore.
Ho dimenticato una clausola WHERE nell'UPDATE, e mi ha updatato tutti i records con un solo valore. Cancello manualmente e ricostruisco la tabella a mano (gli ID sono tutti sbagliati).
Ecco la nuova tabella:
id nome cognome
16 MARIO ROSSI
17 GIULIO CESARE
18 GIUSEPPE VERDI
19 CICCIO PAGNOTTA
20 JOE FETECCHIA
21 PIPPO CAROGNA
Ora vediamo con questo codice:
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim cmd As New OleDb.OleDbCommand("INSERT INTO Tabella2(nome,cognome) VALUES('ANTONIO','CICCETTI')", con)
        Dim cmd2 As New OleDb.OleDbCommand("UPDATE Tabella2 SET nome='YOUSSEF',cognome='KHANZIR'" &
                                           " WHERE id=17", con)
        cmd.ExecuteNonQuery()
        cmd2.ExecuteNonQuery()
        con.Close()
    End Sub
Vediamo...

id nome cognome
16 MARIO ROSSI
17 YOUSSEF KHANZIR
18 GIUSEPPE VERDI
19 CICCIO PAGNOTTA
20 JOE FETECCHIA
21 PIPPO CAROGNA
23 ANTONIO CICCETTI
Ho dovuto cambiare da 2 a 17 per via del casino che ho combinato con gli ID cancellando e riscrivendo manualmente.

venerdì 20 aprile 2018

VB.NET: connessione a un database e importazione di una tabella

Un po' di esercizio con i database in vb.net.
Salvo il mio codice che si è dimostrato in grado di riportare il contenuto dei record di una tabella di database:
Imports System.Data.OleDb
Public Class Form1

    Dim con As OleDbConnection
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        con = New OleDb.OleDbConnection
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0; data source = c:\tavoloDB.mdb"

        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If

        Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Tabella", con)
        Dim dt As New DataTable
        da.Fill(dt)

        Debug.Print(dt.Rows(1)("nome").ToString)
        'Me.dgv.DataSource = dt

        con.Close()
    End Sub
End Class
Distruggo tutto.

Ora riscrivo il codice da zero: innanzitutto la stringa di connessione...

Ecco, prima devo dichiarare la nuova connessione, e quindi attribuirle la stringa.
Ma per prima cosa devo importare System.Data.dbOle.
Imports System.Data.OleDb
Public Class Form1
    Dim con As New OleDb.OleDbConnection
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        con.ConnectionString = "Provider=microsoft.jet.oleDB.4.0; data source=C:\tavoloDB.mdb"

    End Sub
End Class
Ora devo decidere quando aprire la connessione.
Facciamo al click del pulsante...

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If
    End Sub
Ecco la formula "rituale" per aprire la connessione al database se non è già aperta.

Ora come identifico la tabella?
Tengo presente che devo creare un oggetto tabella che deve essere riempito da un adapter.
Comincio con l'adapter.
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If Not con.State = ConnectionState.Open Then
            con.Open()
        End If
        Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Tabella", con)

    End Sub
Quindi devo creare l'oggetto tabella, il cui tipo è DataTable, e riempirla con l'adapter da.
        Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Tabella", con)
        Dim dt As New DataTable
        da.Fill(dt)
Proviamo a scrivere il campo "nome" del primo record della tabella...
        Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Tabella", con)
        Dim dt As New DataTable
        da.Fill(dt)
        Debug.Print(dt.Rows(0)("nome").ToString)
Vediamo se funziona...

CICCIO

Funziona!

Rompo e ricostruisco il Mandala...

Bene il secondo tentativo.
Ora lo replico in video.

venerdì 6 aprile 2018

Aggiornamento di una TextView ogni secondo con i Thread

Per aggiornare ogni secondo una TextView allo scopo di costruire un cronometro:



Ora rompo il Mandala e lo ricostruisco...

mercoledì 4 aprile 2018

Mandala di broadcast receiver registrato dinamicamente e attivato da un AlarmManager sulla stessa activity

Mandala.
Costruire un AlarmManager di tipo RTC_WAKEUP che aziona la suoneria mediante broadcast, con broadcastreceiver registrato dinamicamente.

Salvataggio:
public class MainActivity extends AppCompatActivity {

    BroadcastReceiver receiver;
    IntentFilter filter;
    Ringtone ringtone;

    AlarmManager alarmManager;
    Intent intent;
    PendingIntent pendingIntent;

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

        stopButton=(Button)findViewById(R.id.button);
        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ringtone.stop();
            }
        });
        receiver=new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {

                Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(getApplicationContext(),
                        RingtoneManager.TYPE_ALARM);
                ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
                ringtone.play();
            }
        };

        filter=new IntentFilter();
        filter.addAction("suona");
        registerReceiver(receiver,filter);


        intent=new Intent();
        intent.setAction("suona");

        pendingIntent=PendingIntent.getBroadcast(getApplicationContext(),
                0,
                intent,
                0);
        alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);

        calendar=Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY,9);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);


        alarmManager.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendingIntent);

    }


}

Parto da questo scheletro:
public class MainActivity extends AppCompatActivity {


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




martedì 3 aprile 2018

Mandala completo del service binding

Ora faccio il primo Mandala completo: distruggo sia la main che il Service e li ricostruisco.

Scheletro della main:
public class MainActivity extends AppCompatActivity {

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

    }

}


Scheletro del Service:
public class Servizio extends Service {

    public Servizio() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}



Binding services. Mandala della activity principale.

Prima di passare ai Mandala dell'activity principale, mi studio il codice di connessione e disconnessione del service.
In OnStart:
    @Override
    protected void onStart() {
        super.onStart();
        Intent intent=new Intent(this,Servizio.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);


    }
La parte dell'Intent è come sempre.
La parte successiva implica il dover indicare l'intent, ma anche la serviceConnection e la costante BIND_AUTO_CREATE, che non mi è molto chiara, ma per il momento soprassediamo.


    @Override
    protected void onStop() {
        super.onStop();
        if(mBound){
            unbindService(serviceConnection);
            mBound=false;
        }
    }
Viene chiuso il service usando solo la serviceConnection e viene dichiarata false la variabile booleana mBound, che ho dimenticato di aggiungere nel metodo OnServiceConnected di ServiceConnection, mentre invece nel codice che ho consultato l'avevo messa in modo improprio.
Provo a fare un mandala di tutto, non prima di aver salvato tutta la main.
public class MainActivity extends AppCompatActivity {

    ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Servizio.LocalBinder myIBinder=(Servizio.LocalBinder)iBinder;
            Servizio myService=myIBinder.getService();
            myService.metodoPerVerifica();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };


    boolean mBound=false;

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

    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent=new Intent(this,Servizio.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);


    }

    @Override
    protected void onStop() {
        super.onStop();
        if(mBound){
            unbindService(serviceConnection);
            mBound=false;
        }
    }


}

Parto da questo scheletro:
public class MainActivity extends AppCompatActivity {



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

    }
}


Ecco il Mandala ricostruito:
public class MainActivity extends AppCompatActivity {

    boolean mBound=false;

    ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Servizio.LocalBinder myIBinder=(Servizio.LocalBinder)iBinder;
            Servizio myService=myIBinder.getService();
            mBound=true;
            myService.metodoPerVerifica();

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };


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

    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent=new Intent(getApplicationContext(),Servizio.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(mBound){
            unbindService(serviceConnection);
            mBound=false;
        }
    }
}
Ora, se funziona, dovrebbe scrivermi in LogCat la parola "VERIFICA" secondo il metodo che ho creato nel Service.
Vediamo...

04-04 06:42:37.962 2148-2148/? I/System.out: VERIFICA
Perfetto!

Mandala del service.

Adesso faccio un mandala con il service. Lo distruggo completamente e lo ricostruisco.

Parto dallo scheletro:
public class Servizio extends Service {

  
    public Servizio() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


}
Ho inserito anche un metodo per la verifica, da eseguire da parte della main, che già mi risulta nella main, buon segno.
public class Servizio extends Service {

    IBinder mBinder = new LocalBinder();
    public Servizio() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public class LocalBinder extends Binder{
        Servizio getService(){
            return Servizio.this;
        }
    }

    public void metodoPerVerifica(){
        System.out.println("VERIFICA");
    }


}
Provo ad eseguire e verifichiamo.
04-04 06:12:40.044 471-471/? I/System.out: VERIFICA
Perfetto!

Ripasso del binding di un service

Non ricordo come si binda un service.
E' necessario fare un po' di mandala in proposito.

Per prima cosa si costruisce una classe di tipo LocalBinder che estende Binder.
Questa classe restituisce il Service stesso.
    class LocalBinder extends Binder {
        Servizio getService(){
            return Servizio.this;
        }
    }
Da questa classe LocalBinder si può creare un oggetto come interfaccia IBinder, in quanto, dato che LocalBinder estende Binder e IBinder è implementatata da Binder, la procedura è del tutto lecita.
IBinder mBinder=new LocalBinder();
Questo va poi "sparato" nel metodo IBinder onBind(Intent intent)
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
Sulla principale, invece, la cosa non me la ricordo affatto...

Si comincia con il creare nella sezione "dichiarazioni", un oggetto di classe ServiceConnection.
public class MainActivity extends AppCompatActivity {


    ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Servizio.LocalBinder myIBinder=(Servizio.LocalBinder)iBinder;
            Servizio servizio=myIBinder.getService();
            servizio.verbo();
            mBound=true;

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBound=false;
        }
    };
    boolean mBound=false;

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

    }
Quindi (l'ho messo nel metodo onStart dell'activity non ricordo perché), si binda il service, che viene invece "sbindato" nel metodo onStop:
    @Override
    protected void onStart() {
        super.onStart();
        Intent intent=new Intent(this,Servizio.class);
        bindService(intent,connection,BIND_AUTO_CREATE);


    }

    @Override
    protected void onStop() {
        super.onStop();
        if(mBound){
            unbindService(connection);
            mBound=false;
        }
    }

Sarebbe il caso di analizzare il codice della creazione dell'oggetto di tipo ServiceConnection...

Distruggo selettivamente la creazione dell'oggetto per ricostruirla.

Questo è ciò che mi viene in automatico:
    ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
Nel metodo onServiceConnected vengono forniti due parametri, ComponentName componentName e IBinder iBinder. Questi dovrebbero essere fondamentali...

IBinder iBinder dovrebbe essere quello che mi viene fornito dal metodo
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
del Service.
che è di classe LocalBinder. Quindi devo creare un oggetto nell'activity principale che sia dello stesso tipo LocalBinder, onde renderlo uguale all'oggetto liberatomi nei parametri, che è di tipo LocalBinder, e che quindi va castato. Provo ad eseguire a memoria:
    ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Servizio.LocalBinder myIBinder=(Servizio.LocalBinder)iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
Ricordando che la classe LocalBinder, cui appartiene il mio oggetto "da questa parte" MyIBinder e l'oggetto "da quella parte" IBinder ha un metodo di tipo Servizio che restituisce il Service che sta da "quella parte", posso ottenere da questo metodo il Service.
Il metodo era di tipo Servizio e si chiamava getService(), e restituiva Servizio.this.
    class LocalBinder extends Binder {
        Servizio getService(){
            return Servizio.this;
        }
    }
Fatto questo posso ottenere il Service da questa parte.

lunedì 2 aprile 2018

Realizzare una sveglia con AlarmManager RTC

Obiettivo: realizzare una sveglia con il pulsante per accenderla e quello per spegnerla.

Cerchiamo di studiare la sveglia.
Innanzitutto abbiamo un pulsante per l'attivazione e per la disattivazione.
Per il momento creo un pulsante per l'attivazione e uno diverso per la disattivazione.

Il codice è questo:
public class MainActivity extends AppCompatActivity {

    AlarmManager alarmManager;
    Intent intent;
    PendingIntent pendingIntent;
    Button button;
    Button button2;
    Calendar calendar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button=(Button)findViewById(R.id.button);
        button2=(Button)findViewById(R.id.button2);

        intent=new Intent(getApplicationContext(),MyService.class);
        pendingIntent=PendingIntent.getService(getApplicationContext(),0,intent,0);
        alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);

        calendar=Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY,23);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);

        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                alarmManager.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendingIntent);
            }
        });

        button2.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                if(alarmManager!=null){
                    alarmManager.cancel(pendingIntent);
                    if(pendingIntent!=null)pendingIntent.cancel();
                }
            }
        });
e funziona per una volta, non ripetibile.
La normale sveglia funziona per una volta, ma si presenta con un pulsante che consente l'opzione di rinviare o di spegnere del tutto la sveglia. Per spegnerla bisogna zittire la suoneria e disattivare alarmManager, due cose che penso di poter fare.
Per rinviare, si dovrebbe programmare a breve scadenza con un AlarmManager di tipo ELAPSED il riattacco della suoneria.

lunedì 26 marzo 2018

Colonna di labels spostabile con due parametri aggiuntivi.

Obiettivo: spostare la griglia di caselle a piacimento Per fare questo aggiungo due parametri.
Presto fatto!
Posso spostare tutta la colonna a mio piacimento mediante i parametri sinistra e alto.
    Private Sub creaColonna(mese As Integer, anno As Integer,
                            labelWidth As Integer, labelHeight As Integer,
                            horSpace As Integer, vertSpace As Integer,
                            sinistra As Integer, alto As Integer,
                            isCalendar As Boolean)

        Dim giorniDelMese As Integer = DaysInMonth(anno, mese)
        Dim perColumn = giorniDelMese
        Dim giorno As New Date(anno, mese, 1)
        For n = 0 To giorniDelMese - 1
            mLabel = New Label
            If isCalendar Then mLabel.Text = giorno.ToString("dddd" + " " + "dd/MMMM/yyyy").ToUpper
            giorno = giorno.AddDays(1)
            mLabel.Width = labelWidth
            mLabel.Height = labelHeight
            mLabel.BorderStyle = BorderStyle.FixedSingle
            mLabel.AutoSize = False
            mLabel.Left = sinistra + (mLabel.Width + horSpace) * (n \ perColumn)
            mLabel.Top = alto + (mLabel.Height + vertSpace) * (n Mod perColumn)
            Controls.Add(mLabel)
        Next
    End Sub
Ed ecco:



Perfetto! Ho raggiunto la massima flessibilità!

Funzione flessibile per la creazione di labels in colonne.

Obiettivo: per disporre varie colonne ognuna con un tipo diverso di label creare una funzione flessibile con parametri.

Ecco la funzione flessibile:
    Private Sub creaColonna(mese As Integer, anno As Integer,
                            labelWidth As Integer, labelHeight As Integer,
                            horSpace As Integer, vertSpace As Integer,
                            isCalendar As Boolean)

        Dim giorniDelMese As Integer = DaysInMonth(anno, mese)
        Dim perColumn = giorniDelMese
        Dim giorno As New Date(anno, mese, 1)
        For n = 0 To giorniDelMese - 1
            mLabel = New Label
            If isCalendar Then mLabel.Text = giorno.ToString("dddd" + " " + "dd/MMMM/yyyy").ToUpper
            giorno = giorno.AddDays(1)
            mLabel.Width = labelWidth
            mLabel.Height = labelHeight
            mLabel.BorderStyle = BorderStyle.FixedSingle
            mLabel.AutoSize = False
            mLabel.Left = (mLabel.Width + horSpace) * (n \ perColumn)
            mLabel.Top = (mLabel.Height + vertSpace) * (n Mod perColumn)
            Controls.Add(mLabel)
        Next
    End Sub
Parametri:
  1. mese
  2. anno
  3. larghezza della label
  4. altezza della label
  5. spaziatura orizzontale
  6. spaziatura verticale
  7. variabile booleana che stabilisce se la colonna sia un calendario, con scritte le date, o no.
Se impostata quest'ultima a True, ottengo:



mentre se è impostata a False ottengo:



Funziona, anche se è ancora perfettibile...

Sviluppo di un calendario su form in VB.NET

Ecco. Ricominciamo a creare un semplice calendario...

Usare delle Labels.
Non ricordo come si creano le date in VB.

Obiettivo: creare date con somme e sottrazioni di giorni fra un mese e l'altro.
Per prima cosa ho trovato il modo di sommare le date.
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim d As New Date(2018, 3, 2)
        Debug.Print(d)
        d = d.AddDays(10)
        Debug.Print(d)
    End Sub
End Class
02/03/2018
12/03/2018
Vado su calcoli più complessi: attraverso la fine dell'anno...
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim d As New Date(2018, 12, 20)
        Debug.Print(d)
        d = d.AddDays(12)
        Debug.Print(d)
    End Sub
End Class
20/12/2018
01/01/2019
Perfetto.

Anni bisestili:
2016 (anno bisestile): sommo 9 giorni alla data del 20 febbraio:
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim d As New Date(2016, 2, 20)
        Debug.Print(d)
        d = d.AddDays(9)
        Debug.Print(d)
    End Sub
End Class
20/02/2016
29/02/2016


2017 (anno non bisestile): sommo 9 giorni alla data del 20 febbraio:
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim d As New Date(2017, 2, 20)
        Debug.Print(d)
        d = d.AddDays(9)
        Debug.Print(d)
    End Sub
End Class
20/02/2017
01/03/2017
Perfetto!


Obiettivo n.2: creare una disposizione delle labels in un form secondo l'algoritmo che io chiamo "divisione intera - modulo"
Public Class Form1
    Dim mLabel As Label
    Const vertSpace = 10
    Const horSpace = 30
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        For n = 0 To 29
            mLabel = New Label
            mLabel.Width = 100
            mLabel.Height = 50
            mLabel.BorderStyle = BorderStyle.FixedSingle
            mLabel.AutoSize = False
            mLabel.Left = (mLabel.Width + horSpace) * (n \ 5)
            mLabel.Top = (mLabel.Height + vertSpace) * (n Mod 5)
            Controls.Add(mLabel)
        Next


    End Sub
End Class




Bene.



Obiettivo n.3: inserire nelle labels i giorni di un mese.

Vediamo come si fa a ottenere il numero di giorni di un mese.

Ecco, ho ottenuto tutto il necessario:
Imports System.DateTime
Public Class Form1
    Dim mLabel As Label
    Const vertSpace = 10
    Const horSpace = 30
    Const perColumn = 10
    Const anno = 2016
    Const mese = 2

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim giorno As New Date(anno, mese, 1)
        For n = 0 To DaysInMonth(anno, mese) - 1
            mLabel = New Label
            mLabel.Text = giorno
            giorno = giorno.AddDays(1)
            mLabel.Width = 100
            mLabel.Height = 50
            mLabel.BorderStyle = BorderStyle.FixedSingle
            mLabel.AutoSize = False
            mLabel.Left = (mLabel.Width + horSpace) * (n \ perColumn)
            mLabel.Top = (mLabel.Height + vertSpace) * (n Mod perColumn)
            Controls.Add(mLabel)
        Next
    End Sub
End Class





Obiettivo n.4: formattare le date

Per prima cosa ho trovato il modo di scrivere per esteso il giorno della settimana.
Ma poi ho avuto l'esigenza di mettere a capo la data per isolare il nome del giorno della settimana nella riga superiore, e ho trovato Environment.newLine. Inoltre il toUpper per fare tutte maiuscole.
Ultima modifica: il mese scritto in lettere anziché in numero:
        For n = 0 To DaysInMonth(anno, mese) - 1
            mLabel = New Label
            mLabel.Text = giorno.ToString("dddd" + Environment.NewLine + "dd/MMMM/yyyy").ToUpper
            giorno = giorno.AddDays(1)
            mLabel.Width = 120
            mLabel.Height = 50
            mLabel.BorderStyle = BorderStyle.FixedSingle
            mLabel.AutoSize = False
            mLabel.Left = (mLabel.Width + horSpace) * (n \ perColumn)
            mLabel.Top = (mLabel.Height + vertSpace) * (n Mod perColumn)
            Controls.Add(mLabel)
        Next
Ed è tutto:



Per il momento mi sembra soddisfacente!

giovedì 22 marzo 2018

Salvataggio di AutoCompleteTextView con database

Ho risolto un problema che mi è risultato piuttosto difficile, ossia usare una casella autocompletante con una tabella di database.
Ne salvo il codice:

activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="4"
    tools:context="c.antonello.tavolodatabase.MainActivity">


    <AutoCompleteTextView
        android:id="@+id/autoCompleteTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"/>


</LinearLayout> 


MainActivity:
public class MainActivity extends AppCompatActivity {

    Helper helper;

    AutoCompleteTextView completeTextView;
    SimpleCursorAdapter mAdapter;

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

        helper=new Helper(this);
        completeTextView=(AutoCompleteTextView)findViewById(R.id.autoCompleteTextView);

        mAdapter= new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_1,
                null,
                new String[]{"nome"},
                new int[]{android.R.id.text1},
                0
        );

        mAdapter.setCursorToStringConverter(new SimpleCursorAdapter.CursorToStringConverter() {
            @Override
            public CharSequence convertToString(Cursor cursor) {
                int index=cursor.getColumnIndex("nome");
                return cursor.getString(index);
            }
        });

        mAdapter.setFilterQueryProvider(new FilterQueryProvider() {
            @Override
            public Cursor runQuery(CharSequence charSequence) {
                System.out.println(charSequence);
                return helper.querySelect(charSequence.toString());
            }
        });

        completeTextView.setAdapter(mAdapter);

        completeTextView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View view, int i, KeyEvent keyEvent) {
                if(keyEvent.getAction()==KeyEvent.ACTION_DOWN){
                    switch(i){
                        case KeyEvent.KEYCODE_ENTER:
                            System.out.println(completeTextView.getText());
                            return true;
                        default:
                            break;
                    }
                }
                return false;
            }
        });
    }
}


helper:
public class Helper extends SQLiteOpenHelper {


    public Helper(Context context) {
        super(context, "mioDatabase.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table tabella(_id integer primary key,nome text)");

    }

    public void save(String stringa){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("nome",stringa);
        db.insert("tabella",null,values);

    }

    public Cursor query(){
        SQLiteDatabase db=this.getReadableDatabase();
        return db.rawQuery("select * from tabella",null);
    }

    public Cursor querySelect(String stringa){
        SQLiteDatabase db=this.getReadableDatabase();
        Cursor c=db.rawQuery("select * from tabella where nome like '%"+stringa+"%'",null);
        return c;

    }

    public void modifica(int id,String stringa){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("nome",stringa);
        db.update("tabella",values,"_id="+id,null);
    }

    public void cancella(int id){
        SQLiteDatabase db=this.getWritableDatabase();
        db.delete("tabella","_id="+id,null);
    }



    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

sabato 17 marzo 2018

CursorAdapter con aggiunta di una riga extra alla fine.

Ora dobbiamo riscrivere il codice per un CursorAdapter, aggiungendo elementi alla fine.
Eccolo:
public class MainActivity extends AppCompatActivity {

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

        listView=(ListView)findViewById(R.id.listView);
        /*helper=new Helper(this);
        helper.save("Brontolo");
        helper.save("Cucciolo");
        helper.save("Pisolo");
        helper.save("Gongolo");
        helper.save("Mammolo");
        helper.save("Dotto");
        helper.save("Eolo");*/


        MyCursorAdapter adapter=new MyCursorAdapter(this,helper.query());

        listView.setAdapter(adapter);
        LayoutInflater inflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
        View v=inflater.inflate(R.layout.row,null);
        EditText editText=(EditText) v.findViewById(R.id.editText);
        editText.setText("EXTRA");
        listView.addFooterView(v);


    }

    class MyCursorAdapter extends CursorAdapter{

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

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

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            EditText editText=(EditText) view.findViewById(R.id.editText);
            editText.setText(cursor.getString(cursor.getColumnIndex("nome")));
        }
    }
}


Riscrittura di CursorAdapter personalizzato.

La riscrittura di un CursorAdapter mi risulta facile: questo codice (contiene anche frammenti per la sperimentazione di ArrayAdapter vari già fatta prima) funziona benissimo.
public class MainActivity extends AppCompatActivity {


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

        listView=(ListView)findViewById(R.id.listView);
        helper=new Helper(this);
        /*helper.save("Brontolo");
        helper.save("Cucciolo");
        helper.save("Pisolo");
        helper.save("Gongolo");
        helper.save("Mammolo");
        helper.save("Dotto");
        helper.save("Eolo");*/

        arrayList=new ArrayList();
        arrayList.add("Alfa");
        arrayList.add("Beta");
        arrayList.add("Gamma");
        arrayList.add("Delta");
        arrayList.add("Epsilon");
        arrayList.add("Zeta");
        arrayList.add("Eta");

        String[] matrice=new String[]{"uno","due","tre","quattro","cinque","sei","sette"};

        MyCursorAdapter adapter=new MyCursorAdapter(this,helper.query());
        listView.setAdapter(adapter);

    }

    class MyCursorAdapter extends CursorAdapter{

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

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

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            TextView textView=view.findViewById(R.id.textView);
            textView.setText(cursor.getString(cursor.getColumnIndex("nome")));
        }
    }


}

Esercizi di riscrittura di ArrayAdapter nativo e personalizzato

Il problema è aggiungere una voce "vuota" a una ListView, da compilare.
Come faccio?
Inizio col creare una ListView.
La cosa mi servirà anche per esercitarmi con la ListView e con gli adapters.

Per prima cosa vediamo un adapter personalizzato. Iniziamo con un ArrayAdapter non personalizzato.
public class MainActivity extends AppCompatActivity {


    ArrayList arrayList;
    Helper helper;
    ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView=(ListView)findViewById(R.id.listView);
        helper=new Helper(this);
        /*helper.save("Brontolo");
        helper.save("Cucciolo");
        helper.save("Pisolo");
        helper.save("Gongolo");
        helper.save("Mammolo");
        helper.save("Dotto");
        helper.save("Eolo");*/

        arrayList=new ArrayList();
        arrayList.add("Alfa");
        arrayList.add("Beta");
        arrayList.add("Gamma");
        arrayList.add("Delta");
        arrayList.add("Epsilon");
        arrayList.add("Zeta");
        arrayList.add("Eta");

        ArrayAdapter arrayAdapter=new ArrayAdapter(this,R.layout.row,
                R.id.textView,arrayList);
        
        listView.setAdapter(arrayAdapter);
    }
}
Bene.

Ho fatto un excursus sugli adapters, ho trovato la soluzione alla mia esigenza specifica, e adesso devo esercitarmi un po'.
Scrittura di un arrayadapter.
public class MainActivity extends AppCompatActivity {


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

        listView=(ListView)findViewById(R.id.listView);
        helper=new Helper(this);
        /*helper.save("Brontolo");
        helper.save("Cucciolo");
        helper.save("Pisolo");
        helper.save("Gongolo");
        helper.save("Mammolo");
        helper.save("Dotto");
        helper.save("Eolo");*/

        arrayList=new ArrayList();
        arrayList.add("Alfa");
        arrayList.add("Beta");
        arrayList.add("Gamma");
        arrayList.add("Delta");
        arrayList.add("Epsilon");
        arrayList.add("Zeta");
        arrayList.add("Eta");

        String[] matrice=new String[]{"uno","due","tre","quattro","cinque","sei","sette"};

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

        listView.setAdapter(arrayAdapter);
    }
}
Ecco l'uso dell'ArrayAdapter preconfezionato.
Funziona.

Adesso cerchiamo di scrivere il codice dell'ArrayAdapter personalizzato che dovrebbe evitare di creare una nuova view quando questa esiste già.

Ecco con l'adapter personalizzato che estende ArrayAdapter: praticamente svolge la stessa funzione, credo...

        CustomAdapter adapter=new CustomAdapter(this,R.layout.row,arrayList);
        listView.setAdapter(adapter);
    }

    class CustomAdapter extends ArrayAdapter{

        public CustomAdapter(@NonNull Context context, int resource,ArrayList arrayList) {
            super(context, resource,arrayList);
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
            convertView=inflater.inflate(R.layout.row,null);
            TextView textView=convertView.findViewById(R.id.textView);
            textView.setText(getItem(position));
            return convertView;
        }
    }

Ora uso quell'artifizio per fare in modo che, se la view non è di nuova creazione, non venga nuovamente inflatata ma si usi già per quella che è.
        CustomAdapter adapter=new CustomAdapter(this,R.layout.row,arrayList);
        listView.setAdapter(adapter);

    }

    class CustomAdapter extends ArrayAdapter {
        public CustomAdapter(Context context, int resource,ArrayList lista) {
            super(context, resource,lista);
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder=null;
            if(convertView==null){
                LayoutInflater inflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
                convertView=inflater.inflate(R.layout.row,null);
                viewHolder=new ViewHolder();
                viewHolder.txtNome=(TextView)convertView.findViewById(R.id.textView);
                convertView.setTag(viewHolder);
            }else{
                viewHolder=(ViewHolder)convertView.getTag();
            }
            viewHolder.txtNome.setText(getItem(position));
            return convertView;
        }
    }

    class ViewHolder {
        public TextView txtNome;
    }
}
E funziona.

mercoledì 14 marzo 2018

Mandala sui fragments

Ecco il Mandala distrutto e ricostruito sulla creazione di fragments:

Fragments

Una domanda a bruciapelo: ricordo ancora come si creano i Fragments?
Certamente no.
E' ora di mandalizzarne un po'...

Ho ricostruito nella memoria il modo di tracciare due fragments.
Distruggo il mandala e ricostruisco tutto da capo.
Si parte dal layout della MainActivity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="c.antonello.tavolofragment.MainActivity"


    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:orientation="horizontal"
    android:weightSum="4" >

    <fragment
        android:name="c.antonello.tavolofragment.Frammento"
        android:id="@+id/frammento"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:name="c.antonello.tavolofragment.Frammento2"
        android:id="@+id/frammento2"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" />


</LinearLayout> 
mentre sul file Java della MainActivity non si fa niente in assoluto.

Quindi si creano i Fragments come due nuove classi che estendono Fragment:
public class Frammento extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.frammento,container,false);
    }
}
public class Frammento2 extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.frammento2,container,false);
    }
}
e si creano i relativi layout in xml:

frammento.xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffff88">

</LinearLayout> 


frammento2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#555555">

</LinearLayout> 
che vengono inflatati dal metodo onCreateView contenuto nel codice Java del fragment.
Ad ognuno di essi ho attribuito un colore in modo da distinguerli nell'activity risultante.
Funziona.

Adesso sono pronto per la distruzione del Mandala con ricostruzione in diretta...

martedì 13 marzo 2018

Mandala delle funzioni limitanti il trascinamento di un overlay

Ecco, ho distrutto le routines che limitano gli spostamenti dell'immagine e le ho ricostruite.

OnConfigurationChanged (Overlay)

Ed ora questa è la routine che si occupa della gestione delle rotazioni del cellulare:
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
            resetPosition();
            if(wParams.y>(szDisplay.y-statusBarHeight()-wParams.height)){
                wParams.y=szDisplay.y-statusBarHeight()-wParams.height;
                windowManager.updateViewLayout(wImage,wParams);
            }
        }
    }


Io raggrupperei queste in una serie di "routines di servizio", compresa quella che misura lo schermo e quella che misura la statusBar.
Questa ha la funzione di riportare alle "giuste misure", senza "false presenze" sul display di immagini aventi coordinate che ne superano ampiamente le dimensioni, in relazione alle rotazioni del dispositivo, e non ai movimenti di trascinamento come invece fa resetPosition().

La parte con il LANDSCAPE l'ho aggiustata, adesso devo vedere come gestire la parte che da LANDSCAPE porta a PORTRAIT.
Nel passaggio dal LANDSCAPE a PORTRAIT, la y può rimanere oltre i limiti del display, mentre la x va aggiustata.
La sintassi di resetPosition può andare per la x, perché ne riduce il valore a 0 quando l'immagine cada a meno di metà display mentre ne porta il valore alla larghezza del display quando l'immagine cada oltre metà.
Non vedo perché non vada bene così:
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
            resetPosition();
            if(wParams.y>(szDisplay.y-statusBarHeight()-wParams.height)){
                wParams.y=szDisplay.y-statusBarHeight()-wParams.height;
                windowManager.updateViewLayout(wImage,wParams);
            }
        }
        if(newConfig.orientation== Configuration.ORIENTATION_PORTRAIT){
            resetPosition();
        }
    }
Resta il "neo" della posizione y dell'immagine che non va a fondo... per il momento lo lascio così.

resetPosition dell'immagine nel display (Overlay)

Ricominciamo col mandala dello spostamento di un'immagine in overlay.
Questo è quanto ho ricostruito agevolmente finora:

Servizio:
public class OverlayService extends Service {

    int startX, startY;
    float eventX,eventY;
    Immagine wImage,tImage;

    public OverlayService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        wImage=new Immagine(this);
        tImage=new Immagine(this);
        wImage.setImageResource(R.drawable.post_it);
        tImage.setImageResource(R.drawable.monnezza);

        final WindowManager windowManager=(WindowManager) getSystemService(WINDOW_SERVICE);
        final WindowManager.LayoutParams wParams=new WindowManager.LayoutParams(
                200,
                200,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        wParams.gravity= Gravity.TOP | Gravity.START;
        wParams.x=0;
        wParams.y=100;

        WindowManager.LayoutParams tParams=new WindowManager.LayoutParams(
                200,
                200,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        tParams.gravity=Gravity.TOP | Gravity.START;
        tParams.x=0;
        tParams.y=100;

        windowManager.addView(tImage,tParams);
        windowManager.addView(wImage,wParams);

        wImage.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                view.performClick();

                switch(motionEvent.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        startX=wParams.x;
                        startY=wParams.y;
                        eventX=motionEvent.getRawX();
                        eventY=motionEvent.getRawY();
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        wParams.x=startX+(int)(motionEvent.getRawX()-eventX);
                        wParams.y=startY+(int)(motionEvent.getRawY()-eventY);
                        windowManager.updateViewLayout(wImage,wParams);
                        return true;

                }



                return true;
            }
        });




    }

    class Immagine extends android.support.v7.widget.AppCompatImageView{

        public Immagine(Context context) {
            super(context);
        }

        @Override
        public boolean performClick() {
            super.performClick();
            return true;
        }
    }
}


Ora nel corso dell'evento MOVE deve esserci qualcosa che risistemi i confini dell'immagine.
Io farei qualcosa di semplice in questo modo: creare una funzione che mi sospinga sempre in una determinata posizione l'immagine.
Per far questo devo derivare le dimensioni del display:
    private void misuraSchermo(){
        windowManager.getDefaultDisplay().getSize(szDisplay);

    }

    private void resetPosition(){
        misuraSchermo();
        if(wParams.x<=szDisplay.x/2){
            wParams.x=0;
        }else{
            wParams.x=szDisplay.x-wParams.width;
        }
        windowManager.updateViewLayout(wImage,wParams);

    }
Con queste due "routines di servizio" misuro lo schermo e poi all'evento UP riporto l'immagine a sinistra o a destra a seconda che si trovi più verso sinistra o destra.
Questa è la prima difficoltà che ho avuto nel ricordare il codice, ma predisporrò degli altri Mandala.
Ma ho commesso un errore nella ricostruzione di resetPosition.
Non ho considerato che potrei spingere l'immagine anche nella dimensione verticale, oltre che in quella orizzontale, in modo che essa si trovi illusoriamente al limite inferiore della finestra ma in realtà si trovi molto più in basso, resistendo poi al "prelievo" successivo con il tocco.
Ecco la funzione completa, dopo aver creato anche una funzione che contempla la misurazione della statusBar:
    private void resetPosition(){
        misuraSchermo();
        if(wParams.x<=(szDisplay.x/2-wParams.width/2)){
            wParams.x=0;
        }else{
            wParams.x=szDisplay.x-wParams.width;
        }
        if(wParams.y>szDisplay.y-statusBarHeight()-wParams.height){
            wParams.y=szDisplay.y-statusBarHeight()-wParams.height;
        }
        windowManager.updateViewLayout(wImage,wParams);

    }
Questa, così come quella successiva che si occupa della gestione delle rotazioni del cellulare, conviene che me le faccia subito, in modo da togliermi il pensiero.