JavascriptProva

martedì 5 luglio 2016

Salvataggio codice di SharedPreferences

Ora imposto una suoneria che venga scelta tramite un pulsante e venga suonata tramite un altro pulsante.
Quindi ci faccio entrare in mezzo le Shared Preferences per memorizzarla...

Mi salvo qui il codice che avevo scritto l'altra volta, allo scopo di evitare impazzimenti qualora lo dovessi ricercare in giro:
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  SharedPreferences sharedPreferences=this.getPreferences(Context.MODE_PRIVATE);
  
  //SCRITTURA
  SharedPreferences.Editor editor=sharedPreferences.edit();
  editor.putString("Preferenza1", "Bella");
  editor.putString("Preferenza", "Buona");
  editor.commit();
  
  //LETTURA
  String defaultValue="Ciccia";
  String risultato=sharedPreferences.getString("Preferenza", defaultValue);
  Log.d("RISULTATO",risultato);
  
  
 }


}

lunedì 4 luglio 2016

Suoneria specifica per un'applicazione.

Analizziamo...

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
Quell'addPreferencesFromResource, seguito da un file xml, mi giunge completamente nuovo.
Credo che faccia riferimento a SharedPreferences, e credo di aver cominciato a capire come si fa a conservare la suoneria personalizzata.
Andiamo avanti...

Uri defaultRingtoneUri = RingtoneManager.getDefaultUri(ringTonePref.getRingtoneType());
Viene definita una variabile di tipo Uri.
Viene rilevato un Uri di default.
Ho visto una similitudine con un codice che ho imparato oggi...
Uri uri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
Non so bene dove siano state prese quelle variabili... ringTonePref e il suo metodo getRingtoneType().
Comunque questa mi sembra una riga di codice che abbia lo scopo di rilevare l'Uri della suoneria di default.
E questo Uri viene convertito in stringa:
String defaultRingtone = defaultRingtoneUri.toString();
Ora:
String selectedRingtone = ringTonePref.getPreferenceManager().getSharedPreferences().getString(NOTIFICATION_RINGTONE,defaultRingtone);
Sempre dal fantomatico ringTonePref viene presa una stringa che dovrebbe essere la suoneria selezionata.
Ora si opera un confronto:
         if (defaultRingtone.equals(selectedRingtone)) {
                ringtoneUri = defaultRingtoneUri;
            } else {
                ringtoneUri = (TextUtils.isEmpty(selectedRingtone)) ? null : Uri.parse(selectedRingtone);
            }
Se la suoneria di default è uguale alla suoneria selezionata, allora la variabile ringtoneUri viene eguagliata all'Uri di default; se invece è diversa, allora se la suoneria selezionata è vuota non suona niente (non è stata selezionata nessuna suoneria per l'applicazione specifica), altrimenti ringtoneUri viene eguagliata all'Uri corrispondente alla stringa selezionata.

Posso usare quindi SharedPreferences per impostare una suoneria personalizzata, ma prima devo impratichirmi nell'uso di SharedPreferences, appunto.

Suoneria

Dopo le divagazioni, vediamo come si può selezionare una suoneria.

Ecco: ci sono riuscito:

Inizialmente, è come per la selezione delle immagini, con la differenza che non viene fornito nessun Uri string, ma si imposta solo l'Action dell'Intent come ACTION_RINGTONE_PICKER.
  Intent intent=new Intent();
  intent.setAction(RingtoneManager.ACTION_RINGTONE_PICKER);
  startActivityForResult(intent,0);
Me la riscrivo:
  Intent intent=new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
  startActivityForResult(intent,0);
una scrittura alternativa in cui l'action è posta fra i parametri dell'Intent.

La differenza più significativa sta nella onActivityResult().

 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
  RingtoneManager.setActualDefaultRingtoneUri(getApplicationContext(), RingtoneManager.TYPE_RINGTONE, uri);
  
 }
Riscrivo:
iniziamo con questo:
Uri ringtoneUri=data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
Log.d("URI",""+ringtoneUri);
07-04 15:35:29.935: D/URI(32324): content://media/internal/audio/media/35
Ecco, con questa riga ottengo l'Uri della suoneria selezionata.

Riscrivo e vado avanti.
 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  Uri ringtoneUri=data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
  RingtoneManager.setActualDefaultRingtoneUri(getApplicationContext(), RingtoneManager.TYPE_RINGTONE, ringtoneUri);
 }


In sintesi, prima deriviamo l'Uri della suoneria selezionata, quindi stabiliamo la suoneria di default.

Ora la dobbiamo suonare con il pulsante...

  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
    ringtone =RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
    if(ringtone!=null){
     ringtone.play();
     
    }
   }
  });
Prima ricaviamo l'Uri della suoneria di default, quindi deriviamo la suoneria dall'Uri e quindi la si suona...

Riscrivo...
    Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
    Ringtone ringTone=RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
    if(ringTone !=null)ringTone.play();
Bene.

Ora dobbiamo studiare il modo di fermare la suoneria!

AlarmManager, suonerie e ripasso di ACTION_PICK

Adesso devo fare esercizio sull'altra modalità di AlarmManager.

Ecco, ho questo:
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  Intent intent=new Intent(this,Seconda.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(this, 0, intent,0);
  AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
  
 }


}


Che farne?
Intanto devo introdurre la classe Calendar.
Il codice è questo:
Calendar calendar=Calendar.getInstance();
Ma un momento: affrontiamo le suonerie, che per i miei programmi in cantiere sono molto utili.

Ecco: Si parte da un fantomatico RingtoneManager.
Si usa un Uri, che viene identificato da RingtoneManager.getDefault(RingtoneManager.ALARM_TYPE).
Proviamo:
Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
Questo è un Uri.
Poi dall'Uri si passa al ringtone vero e proprio, identificato in base all'Uri.
Ringtone ringTone=RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
E infine, se non è nullo, si suona.
  Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
  Ringtone ringTone=RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
  if(ringTone!=null)ringTone.play();
Riscrivo...

  Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
  Ringtone ringTone=RingtoneManager.getRingtone(getApplicationContext(),ringtoneUri);
  if(ringTone!=null)ringTone.play();

Come si sceglie la suoneria?

Immagino sia come scegliere le immagini...

Ripassiamo un po' il meccanismo di come si sceglievano le immagini. Posiziono un ImageView nell'attività... Quindi lo identifico con una variabile in Java.
  imageView=(ImageView)findViewById(R.id.imageView1);
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent,0);
  
   
 }
 
 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   Log.d("DATA",""+data.getData());
  }
 }
Non ho usato l'ImageView... E non ho ricordato alla perfezione i passaggi.
Dunque ora riscrivo di sana pianta il tutto aggiungendo anche l'immagine alla ImageView.
  imageView=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  startActivityForResult(intent,0);
  
 }
 
 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   imageView.setImageURI(data.getData());
  }
 }
Vediamo se funziona...

Mi appare un chooser che non c'entra nulla...

Dimenticavo questo:
  imageView=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent,0);
  
 }
 
 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   imageView.setImageURI(data.getData());
  }
 }
Vediamo, ora...

Strano.
setImageUri non funziona... Non ricordo se funzionasse prima...
E comunque è descritto che può dare problemi. Dunque devo memorizzare bene la conversione dell'uri a path.


Dobbiamo considerare context.getContentResolver() come un cursor di database, ossia un insieme di campi.
Poniamo la query fornendo l'Uri come primo parametro, quindi identificata la riga dobbiamo identificare la colonna.
La colonna è identificata da Mediastore.Images.Columns.DATA.
Identificata quesca colonna, prendiamo la stringa corrispondente.
Bene.
Lo scrivo...
   Cursor crs=getApplicationContext().getContentResolver().query(data.getData(), null, null, null, null);
   crs.moveToFirst();
   int indice=crs.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
   String path=crs.getString(indice);
   Bitmap bitmap=BitmapFactory.decodeFile(path);
   imageView.setImageBitmap(bitmap);
Vediamo...

E non funziona neanche questo! E certo! Non ho messo il permesso READ_EXTERNAL_STORAGE nel Manifest!
Credevo che non fosse più necessario, ma questo codice non è compilato per la versione più moderna di Android, quindi bisogna metterlo, e comunque devo pensare anche che potrebbe essere usato in versioni più vecchie.

Lo metto:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
E funziona tutto, sia la Bitmap sia l'ImageUri!

domenica 3 luglio 2016

AlarmManager in un Service bindato a due Activities.

Il problema è la variazione del Context, che potrebbe rendere non valida la procedura di stop dell'AlarmManager qualora il context della procedura di stop sia diverso da quello con cui è stata fatta la procedura di start.
L'esperimento che faccio adesso dovrebbe essere risolutivo.

Far passare molto tempo fra una comparsa e l'altra dell'attività Form.
In questo tempo, in cui è stata innescata una procedura start, manipolare il cellulare in vario modo.
Fare successivamente una procedura di stop, a una considerevole distanza dalla procedura di Start, verificando sempre il Context.

Vado... ci provo.
Tempo impostato a 2 minuti.
Avvio lo Start...

Faccio un po' di manovre e poi avvio lo Stop. Vediamo se appare la Form...

Ecco, lo stop ha funzionato, ma non avendo rilevato il codice del Context inizialmente non so se sia lo stesso codice nello Start e nello Stop.
Ripeto, annotando i codici dei Context.

07-03 17:22:59.307: E/CONTESTO START(2593): android.app.Application@423c10a8


07-03 17:23:55.632: E/CONTESTO STOP(3212): android.app.Application@423c8d88
Bene, i codici dei Context sono differenti, eppure lo stop ha funzionato lo stesso: siamo ben oltre i 2 minuti e l'attività Form non si è vista.


Ancora:
07-03 17:36:54.071: E/CONTESTO START(4041): android.app.Application@423c5c80
07-03 17:37:32.098: E/CONTESTO STOP(4183): android.app.Application@423c10a0
che ha funzionato.
Ora intervento per la cancellazione a quasi 2 minuti, col cronometro...
07-03 18:31:28.456: E/CONTESTO START(10875): android.app.Application@423c3ee0


07-03 18:32:51.447: E/CONTESTO STOP(11471): android.app.Application@423c1f88
Credo che il funzionamento sia abbastanza sicuro...



Ora dobbiamo far apparire lo status.
Creo una funzione CheckStatus nel Service...

Tutto fatto! Sto sperimentando, ora.
Ecco il codice del Service:
public class Timer extends Service {

 @Override
 public void onCreate(){
  Log.d("SERVICE","ONCREATE");
 }
 
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.d("SERVICE","ONDESTROY");
 }
 
 IBinder mBinder=new LocalBinder();
 
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return mBinder;
 }
 
 public class LocalBinder extends Binder{
  Timer getService(){
   return Timer.this;
  }
 }
 
 public void StartTime(){
  Log.e("CONTESTO START",getApplicationContext()+"");
  Intent intent=new Intent(getApplicationContext(),Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
  alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+120*1000, pendingIntent);
 }
 
 public void StopTime(){
  Log.e("CONTESTO STOP",getApplicationContext()+"");
  Intent intent=new Intent(getApplicationContext(),Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
  alarmManager.cancel(pendingIntent);
  if(pendingIntent!=null)pendingIntent.cancel();
 }
 
 public boolean AlarmOn(){
  Intent intent=new Intent(getApplicationContext(),Form.class);
  boolean alarmUp=(PendingIntent.getActivity(getApplicationContext(),0,intent,PendingIntent.FLAG_NO_CREATE)!=null);
  return alarmUp;
 }


}
...e speriamo che funzioni!

sabato 2 luglio 2016

Esercizio di binding di un Service

Vediamo adesso come si opera l'unbinding all'evento onStop.
Molto più semplice:
    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
Praticamente l'unica cosa fondamentale è la riga:
unbindService(mConnection);
Ovviamente, questo unbinding deve essere subordinato alla variabile di controllo mBound.
Anche sulla interfaccia mConnection abbiamo:
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
un semplice riportare a falso la variabile di controllo.

Però quel parametro non lo capisco: è qualcosa che deve essere meglio compreso.
Non credo che abbia molta importanza... semmai lo vedo dopo.

Ora mi esercito ancora a riscrivere tutti i codici.

Questo è il codice per il binding che ho scritto nel Service:
public class MioServizio extends Service{

 IBinder mBinding=new LocalBinder();
 
 
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return mBinding;
 }
 
 public class LocalBinder extends Binder{
  MioServizio getService(){
   return MioServizio.this;
   
  }
 }

}


Adesso nell'attività che vi si binda:
public class MainActivity extends Activity {

 MioServizio servizio;
 boolean mBound;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
 }
 
 @Override
 public void onStart(){
  super.onStart();
  Intent intent=new Intent(this,MioServizio.class);
  bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
 }
 
 @Override
 public void onStop(){
  super.onStop();
  if(mBound){
   unbindService(mConnection);
   mBound=false;
  }
 }
 
 ServiceConnection mConnection=new ServiceConnection(){

  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   
   LocalBinder mService=(LocalBinder)service;
   servizio=mService.getService();
   mBound=true;
  }

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


}
Ecco. E ho verificato che funziona.
Ho posto dei markers nel Service per rendermi conto di come si comporta un service "bindato" rispetto a un'Activity che lo binda:
public class MioServizio extends Service{

 IBinder mBinding=new LocalBinder();
 
 @Override
 public void onCreate(){
  Log.v("SERVICE","ONCREATE");
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("SERVICE","ONSTARTCOMMAND");
  return Service.START_NOT_STICKY;
  
 }
 
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.v("SERVICE","ONDESTROY");
 }
 
 @Override
 public IBinder onBind(Intent intent) {
  Log.v("SERVICE","BINDING");
  return mBinding;
 }
 
 public class LocalBinder extends Binder{
  MioServizio getService(){
   return MioServizio.this;
   
  }
 }

}
L'Activity viene creata, quindi viene ridotta a "icona", e il Service segue le sue sorti.
07-02 10:57:24.939: V/SERVICE(11241): ONCREATE
07-02 10:57:24.939: V/SERVICE(11241): BINDING
07-02 10:57:25.029: I/Timeline(11241): Timeline: Activity_idle id: android.os.BinderProxy@41e991d8 time:1127969
07-02 10:57:31.796: E/OpenGLRenderer(11241): SFEffectCache:clear(), mSize = 0
07-02 10:57:31.806: V/SERVICE(11241): ONDESTROY

Niente a carico del metodo onStartCommand, che invece viene chiamato in causa quando il Service sia evocato tramite il comando startService.

Ottimo! Dunque da qui in poi, sarà opportuno che, quando si usa un Service, esso sia bindato piuttosto che startato, a meno che non si presuma che esegua qualcosa per i cavoli suoi senza che sia necessaria alcun'altra interazione fra esso e il resto dell'applicazione.

venerdì 1 luglio 2016

Studio del binding dei Services

Questo è il codice del binding nel Service:
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
Che ci faccio?

Nel tutorial c'è questo codice:
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
Parto da un nucleo che mi sembra fondamentale:
 public class LocalBinder extends Binder{
  Servizio getService(){
   return Servizio.this;
  }
 }
Una classe derivata del tipo Binder, che ha un metodo chiamato getService(), il quale restituisce questo stesso Service.

Immagino che di questa classe bisogni creare un'istanza.
Che sarebbe questa:
private IBinder mBinder=new LocalBinder();
e che viene restituita dal metodo onBind:
 @Override
 public IBinder onBind(Intent intent) {
  return mBinder;
 }

Facciamo una sintesi:

Viene dichiarata una classe LocalBinder di tipo Binder, che restituisce il contesto di questo Service.
Di questa classe viene creata un'istanza di tipo IBinder.
Questa istanza viene restituita dal metodo IBinder onBind, che è quello da includere necessariamente nel Service.

Abbiamo visto IBinder e Binder. Che cosa sono? In che rapporto sono fra di loro?
Binder sarebbe una classe derivata di IBinder...
Ora vediamo come si comporta l'attività che si collega al service.
Ha una variabile di tipo Servizio:
Servizio mService;
Ora, la connessione si realizza tramite un classico Intent:
        Intent intent = new Intent(this, Servizio.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
e qui è stata posta nell'evento onStart dell'Attività.
La connessione è però verificata mediante l'evento OnServiceConnected.
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }
Che accidenti accade qui? Vengono portati come parametri className e service.
Localbinder dove lo abbiamo preso?
Ecco: è una classe pubblica dichiarata nel service, quella che estendeva Binder.
Ecco: come parametro viene fornito service che è di tipo IBinder, e questo sarebbe l'istanza della classe LocalBinder.
binder.getService() sarebbe quindi il service.
Quindi viene dichiarato true mBound, che significa che l'attività e il service sono collegati.

Un bel guazzabuglio!

Provo, molto timidamente, a scrivere il codice...


La prima variabile dichiarata era una variabile di tipo Servizio.
Servizio mService;
In questa variabile andrà posto il service.

Quindi in onStart si realizza il collegamento, con un semplice Intent:
 @Override
 public void onStart(){
  Intent intent=new Intent(this,Servizio.class);
  bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
  super.onStart();
 }
Ecco: mConnection è una classe che implementa l'interfaccia ServiceConnection:
 private ServiceConnection mConnection=new ServiceConnection(){

  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   
   
  }

  @Override
  public void onServiceDisconnected(ComponentName name) {
   // TODO Auto-generated method stub
   
  }
  
 };
Questo si scrive in modo quasi automatico.
Quindi riempiamo il corpo di onServiceConnected:
Prima si definisce l'istanza di LocalBinder estendente Binder, presentata come parametro; quindi si estrae da questa il Service.
 ServiceConnection mConnection=new ServiceConnection(){

  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   LocalBinder mBinder=(LocalBinder)service;
   mService=mBinder.getService();
   
   mService.Scrivi();
  }

  @Override
  public void onServiceDisconnected(ComponentName name) {
   // TODO Auto-generated method stub
   
  }
  
  
 };