JavascriptProva

venerdì 29 luglio 2016

DialogFragment

Ecco i Dialog.
Mi pare di aver toccato in passato questo argomento, a proposito delle finestre di dialogo con le opzioni OK e Annulla e simili... ma non tanto da aver memorizzato qualcosa di utile.

Quello che può fare al caso mio è il DialogFragment, in modo da porgere all'utente l'informazione in modo obbligatorio, senza che magari una pressione involontaria di tasti possa far passare in secondo piano l'oggetto che presenta l'informazione.

Ci provo, poi a seconda dell'uso modificherò la cosa.

Questo è il tutorial di riferimento Ho una classe che estende DialogFragment.
public class FireMissilesDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
Ecco la classe che estende DialogFragment.
Ha un metodo onCreateDialog, che ha per parametro Bundle SavedInstanceState. Come per le activities.
E' di tipo Dialog, però, non void.
E vediamola...
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_fire_missiles)
Istanzia un oggetto builder di classe AlertDialog.Builder, mettendo come parametro getActivity().
Quindi usa il metodo setMessage con una stringa per parametro.
Quindi, in sintesi, istanzia AlertDialog.Builder con parametro getActivity() e usa il metodo setMessage di questo oggetto con una stringa per parametro.

Quindi abbiamo i metodi setPositiveButton e setNegativeButton che (ecco l'anomalia che ricordavo!) vanno scritti di seguito a .setMessage, e hanno come parametro una stringa e new DialogInterface.onClickListener.
public class FireMissilesDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_fire_missiles)
               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // FIRE ZE MISSILES!
                   }
               })
               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // User cancelled the dialog
                   }
               });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}
Ma a questo punto forse si fa prima a scriverlo...

Creiamo dunque la classe che estende DialogFragment:
 public class Dialogo extends DialogFragment{
  
 }
Ora va overridata onCreateDialog
 public class Dialogo extends DialogFragment{
  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState){
   
   return null;
   
  }
 }
Ora si istanzia AlertDialog.Builder builder.
 public class Dialogo extends DialogFragment{
  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState){
   AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
   return null;
   
  }
 }
(getActivity() è presentato come parametro di tipo Context. Forse andrà bene anche getApplicationContext()... sì, va bene, l'ho provato: non so se riferirsi al contesto dell'applicazione piuttosto che dell'activity possa creare qualche problema...)

Quindi si usa .setMessage con una stringa per parametro. Usando anche .setPositiveButton che oltre a una stringa richiede new DialogInterface.OnClickListener.
   builder.setMessage("Ciao, Ciccio Bello!")
   .setPositiveButton("Ciao", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     
    
Ecco, la cosa dovrebbe essere quasi completa.
Imposto anche il negative button:
   builder.setMessage("Ciao, Ciccio Bello!")
   .setPositiveButton("Ciao", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     
    }
    
   })
   .setNegativeButton("No", new DialogInterface.OnClickListener() {
    
    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     
    }
   });
   return null;
Quindi si corregge return null con return builder.create:
 public class Dialogo extends DialogFragment{
  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState){
   AlertDialog.Builder builder=new AlertDialog.Builder(getApplicationContext());
   builder.setMessage("Ciao, Ciccio Bello!")
   .setPositiveButton("Ciao", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     
    }
    
   })
   .setNegativeButton("No", new DialogInterface.OnClickListener() {
    
    @Override
    public void onClick(DialogInterface dialog, int which) {
     // TODO Auto-generated method stub
     
    }
   });
   return builder.create();
   
  }
 }
Ed eccola completa!

Ora rompo e ricostruisco come esercizio mnemonico...

Eccola ricostruita sbirciando un poco:
 public class Dialogo extends DialogFragment{
  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState){
   AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
   builder.setMessage("Vuoi fare questa azione?")
   .setPositiveButton("SI", new DialogInterface.OnClickListener() {
    
    @Override
    public void onClick(DialogInterface dialog, int which) {
     Log.d("SCELTA","POSITIVA");
     
    }
   })
   .setNegativeButton("NO", new DialogInterface.OnClickListener() {
    
    @Override
    public void onClick(DialogInterface dialog, int which) {
     Log.d("SCELTA", "NEGATIVA");
     
    }
   });
   
   return builder.create();
   
  }
 }
Ora come la uso?

Il tutorial dice "create an instance of this class and call show". Proviamoci.
  button=(Button)findViewById(R.id.button1);
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Dialogo mDialogo=new Dialogo();
    mDialogo.show(getFragmentManager(),"boh");
    
   }
  });
Non ho ben capito cosa siano i due parametri di show, che ho compilato un po' a cavolo, ma di fatto la finestra compare! E le scelte sono congruenti:
07-29 12:29:25.111: D/SCELTA(12881): NEGATIVA
07-29 12:29:33.569: D/SCELTA(12881): POSITIVA
07-29 12:29:56.431: D/SCELTA(12881): NEGATIVA
L'unica perplessità è che il tasto negativo viene a sinistra del positivo invece che a destra come mi aspettavo dall'ordine nel codice. Sarà così...

Ora, questa finestra di dialogo non viene eliminata se si clicca sull'activity in sottofondo, ma lo viene se si clicca sui tasti indietro, home e recents.
Praticamente non c'è differenza con un'activity a tutto schermo nella quale non si può cliccare su altra activity se non la stessa. Bisognerebbe trovare il modo di renderla veramente modale, ma adesso faccio la mia applicazione come avevo preventivato, senza cercare il modo di fare finestre modali, quindi si vedrà.

giovedì 28 luglio 2016

Studio sulla finestra dei recents.

Ora vediamo le modifiche degli status quando si passi da un'activity a un'altra mediante un Intent.

Dopo aver inserito nell'altra Activity FORM tutti i markers dei cambi di status, inserisco in MAIN il codice per passare a FORM.
 @Override
 public void onStart(){
  super.onStart();
  Log.v("MAIN", "ONSTART");
  Intent intent =new Intent(this,Form.class);
  startActivity(intent);
 }
Ecco:
07-28 18:44:55.776: V/MAIN(17395): ONCREATE
07-28 18:44:55.776: V/MAIN(17395): ONSTART
07-28 18:44:55.796: V/MAIN(17395): ONRESUME
07-28 18:44:55.836: V/MAIN(17395): ONPAUSE
07-28 18:44:56.046: V/FORM(17395): ONCREATE
07-28 18:44:56.046: V/FORM(17395): ONSTART
07-28 18:44:56.046: V/FORM(17395): ONRESUME
07-28 18:44:56.146: V/MAIN(17395): ONSTOP

Dunque MAIN va prima in "paused", quindi si crea FORM e una volta completate le tre fasi della creazione di FORM, MAIN va in "stopped".

Quando poi vado al tasto Overview:
07-28 18:47:47.774: V/FORM(17395): ONPAUSE
07-28 18:47:48.395: V/FORM(17395): ONSTOP
anche FORM va in "stopped".

Quindi spazzo via le activities dalla finestra "recenti":
07-28 18:51:55.325: V/MAIN(17986): ONDESTROY
07-28 18:51:55.365: V/FORM(17986): ONDESTROY
Ora, cosa succede con l'istruzione finish()?
Ci provo: la assegno a un Button.
  button=(Button)findViewById(R.id.button1);
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    finish();
    
   }
  });
07-28 23:33:14.535: V/MAIN(10805): ONPAUSE
07-28 23:33:15.196: V/MAIN(10805): ONSTOP
07-28 23:33:15.196: V/MAIN(10805): ONDESTROY
Ciononostante, la finestra dell'app rimane sui recents... Quindi stare sui recents non implica l'esistenza della finestra.
Cliccando sulla finestra:
07-28 23:36:36.122: V/MAIN(11725): ONCREATE
07-28 23:36:36.122: V/MAIN(11725): ONSTART
07-28 23:36:36.122: V/MAIN(11725): ONRESUME
Si ricrea da capo.

Esperimenti sulle azioni dei tasti INDIETRO e OVERVIEW.

Digressione importante.
Dato che ho difficoltà con la gestione delle eventuali pressioni dei pulsanti che potrebbero mandare a pallino le mie applicazioni, credo di dover fare tutta una sessione sul modo di far entrare nel codice la programmazione degli eventi di questi pulsanti.

Costruisco un'applicazione inutile con un MainActivity e un Service bindato ad essa, ma con tutti i "marker" sugli eventi onCreate, onStart, onResume, onPause, onStop, onDestroy, per studiare le modalità di cambiamento degli "status" con la pressione dei diversi tasti del cellulare.

Questo è ciò che avviene all'inizio:
07-28 14:47:43.211: V/MAIN(3942): ONCREATE
07-28 14:47:43.211: V/MAIN(3942): ONSTART
07-28 14:47:43.221: V/MAIN(3942): ONRESUME
07-28 14:47:43.241: D/SERVIZIO(3942): ONCREATE
Main segue la progressione di creazione, start, resume. Service viene creato in quanto bindato dall'evento onStart di Main.

Ora schiaccio il tasto INDIETRO.
07-28 14:48:03.110: V/MAIN(3942): ONPAUSE
07-28 14:48:03.751: V/MAIN(3942): ONSTOP
07-28 14:48:03.751: V/MAIN(3942): ONDESTROY
07-28 14:48:03.761: D/SERVIZIO(3942): ONDESTROY
Sembra la perfetta reversibilità dell'avvio...

Riavvio Main:
07-28 14:49:09.765: V/MAIN(4195): ONCREATE
07-28 14:49:09.765: V/MAIN(4195): ONSTART
07-28 14:49:09.775: V/MAIN(4195): ONRESUME
07-28 14:49:09.795: D/SERVIZIO(4195): ONCREATE
Ora schiaccio il tasto OVERVIEW:
07-28 14:50:01.986: V/MAIN(4195): ONPAUSE
07-28 14:50:02.647: V/MAIN(4195): ONSTOP
07-28 14:50:02.657: D/SERVIZIO(4195): ONDESTROY
Bene, MAIN non viene distrutto, ma viene distrutto il SERVICE in relazione all'evento ONSTOP di MAIN.

Rischiaccio il tasto OVERVIEW:
07-28 14:51:09.682: V/MAIN(4195): ONSTART
07-28 14:51:09.682: V/MAIN(4195): ONRESUME
07-28 14:51:09.722: D/SERVIZIO(4195): ONCREATE
Esattamente reversibile al primo click.

Conclusioni per il momento:
  1. Il tasto INDIETRO distrugge completamente un'activity
  2. Il tasto OVERVIEW riporta l'activity allo stato "stoppato" ma non la distrugge completamente.


Ora provo solo con una MAIN svincolata da qualsiasi SERVICE (rimuovo il codice per il bindaggio)
07-28 14:54:26.654: V/MAIN(4401): ONCREATE
07-28 14:54:26.654: V/MAIN(4401): ONSTART
07-28 14:54:26.654: V/MAIN(4401): ONRESUME
(come di prassi, all'apertura, senza eventi del SERVICE).

Tasto INDIETRO:
07-28 14:55:24.421: V/MAIN(4401): ONPAUSE
07-28 14:55:25.261: V/MAIN(4401): ONSTOP
07-28 14:55:25.261: V/MAIN(4401): ONDESTROY
che è giusto...

Ora riavvio e poi premo il tasto OVERVIEW:
07-28 14:56:12.998: V/MAIN(4401): ONCREATE
07-28 14:56:12.998: V/MAIN(4401): ONSTART
07-28 14:56:12.998: V/MAIN(4401): ONRESUME

..........

07-28 14:56:35.350: V/MAIN(4401): ONPAUSE
07-28 14:56:35.950: V/MAIN(4401): ONSTOP
Ho le finestrelle impilate...
Ora ripremo il tasto OVERVIEW:
07-28 14:57:12.016: V/MAIN(4401): ONSTART
07-28 14:57:12.016: V/MAIN(4401): ONRESUME
Bene: comportamento perfettamente univoco.

mercoledì 27 luglio 2016

Aggiustamento dati letti dalle SharedPreferences sugli intervalli e correzione di un bug dovuto all'interdipendenza delle SeekBars.

Devo aggiustare i tempi in relazione ai dati letti nelle SharedPreferences.
Leggo le SharedPreferences direttamente in TimerService.
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
  int intervalloMin=SP.getInt("intervalloMin", 0);
  int intervalloMax=SP.getInt("intervalloMax",0);
  int delta=intervalloMax-intervalloMin;
Ho preso i due intervalli e ne calcolo la differenza.
Ecco come ho sistemato la cosa:
 public void StartTime(){
  Intent intent=new Intent(getApplicationContext(),
        Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(), 
                0,
                intent, 
                0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
  int intervalloMin=SP.getInt("intervalloMin", 0);
  int intervalloMax=SP.getInt("intervalloMax",0);
  int delta=intervalloMax-intervalloMin;
  Random rnd=new Random();
  int casuale=rnd.nextInt(delta+1);
  int tempo=intervalloMin+casuale;
  
  alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
      SystemClock.elapsedRealtime()+tempo*60*1000, 
      pendingIntent);
  if(mAlarmChangeListener!=null) mAlarmChangeListener.AlarmChange();
Il fatto di prendere la differenza più uno mi salva dal bug della differenza pari a zero, che si manifesta quando il parametro di Random.nextInt sia zero.
Comunque funziona.

Ora devo rivedere come si fa a scegliere la suoneria...

Ma prima ecco un altro erroraccio!
Ho ottenuto ugualmente un errore dovuto al fatto che la differenza fra intervalloMin e intervalloMax era negativa.
Semplicemente avevo omesso di salvare le SharedPreferencesi di ambedue le SeekBars una volta mossa una di queste: infatti esse sono interdipendenti con lo scopo di evitare il paradosso di un intervalloMin superiore a un intervalloMax.
Ecco il codice corretto:
  seekBarMin.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMin", seekBar.getProgress());
    editor.putInt("intervalloMax",seekBarMax.getProgress());
    editor.commit();

.....................

  seekBarMax.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMax", seekBar.getProgress());
    editor.putInt("intervalloMin", seekBarMin.getProgress());
    editor.commit();
E ora funziona.

Bindaggio dell'Activity Impostazioni con il TimerService per l'interruzione dell'AlarmManager quando vengono regolate le impostazioni degli intervalli

Voglio che la regolazione delle SeekBar blocchi l'AlarmManager se è in funzione.
Per far questo devo bindare l'attività TimeSettings al TimerService.

public class TimeSettings extends Activity {

 SeekBar seekBarMin, seekBarMax;
 TextView textViewMin,textViewMax;
 SharedPreferences SP;
 
 TimerService mService;
 boolean mBound;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_time_settings);
  
  SP=getApplicationContext().getSharedPreferences("settings", Context.MODE_PRIVATE);
  seekBarMin=(SeekBar)findViewById(R.id.seekBar1);
  seekBarMin.setMax(240);
  
  
  seekBarMax=(SeekBar)findViewById(R.id.seekBar2);
  seekBarMax.setMax(240);
  
  int intervalloMin=SP.getInt("intervalloMin", 0);
  int intervalloMax=SP.getInt("intervalloMax", 0);
  
  seekBarMin.setProgress(intervalloMin);
  seekBarMax.setProgress(intervalloMax);
  
  textViewMin=(TextView)findViewById(R.id.textView1);
  setTextViewFromSeekBar(intervalloMin,textViewMin);
  textViewMax=(TextView)findViewById(R.id.textView2);
  setTextViewFromSeekBar(intervalloMax,textViewMax);
  
  seekBarMin.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMin", seekBar.getProgress());
    editor.commit();
    
   }
   
   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
    mService.StopTime();
    
   }
   
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    setTextViewFromSeekBar(progress,textViewMin);
    if(progress>seekBarMax.getProgress())seekBarMax.setProgress(progress);
   }
  });
  
  
  seekBarMax.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMax", seekBar.getProgress());
    editor.commit();
    
   }
   
   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
    mService.StopTime();
    
   }
   
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    setTextViewFromSeekBar(progress,textViewMax);
    if(progress<seekBarMin.getProgress())seekBarMin.setProgress(progress);
   }
  });
 }
 
 private void setTextViewFromSeekBar(int numero,TextView textView){
  TextView t=textView;
  int intero=10*(numero/10);
  int ore=intero/60;
  int minuti=intero%60;
  
  String stringaDelleOre;
  String stringaDeiMinuti;
  String stringaEt="";
  
  if(ore==0){
   stringaDelleOre="";
  }
  else if(ore==1){
   stringaDelleOre=ore+" ORA "; 
  }
  else{
   stringaDelleOre=ore+" ORE ";     
  }    
  if(minuti==0){
   stringaDeiMinuti="";     
  }
  else if(minuti==1){
   stringaDeiMinuti=minuti+" MINUTO ";          
  }
  else{
   stringaDeiMinuti=minuti+" MINUTI ";
  }    
  if(ore!=0 && minuti!=0) stringaEt=" E ";
  if(ore==0 && minuti==0) {
   stringaDelleOre=ore+" ORE";
   stringaEt= " E ";
   stringaDeiMinuti=minuti+" MINUTI";
  }

  t.setText(stringaDelleOre+stringaEt+stringaDeiMinuti);
 }
 
 @Override
 public void onStart(){
  super.onStart();
  Intent intent=new Intent(this,TimerService.class);
  bindService(intent,mConnection,Service.BIND_AUTO_CREATE);
 }
 
 @Override
 public void onStop(){
  super.onStop();
  if(mBound==true){
   unbindService(mConnection);
   mBound=false;
  }
 }
 
 ServiceConnection mConnection=new ServiceConnection(){

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

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


}
Proviamolo...

Funziona! Riuscito al primo colpo!

Sistemazione delle SharedPreferences delle SeekBars

Bene. Ora non devo fare altro che salvare i settings quando si muovono le SeekBars.
Non sono più tanto sicuro se sia meglio memorizzare in minuti l'intervallo minimo e l'intervallo massimo, oppure l'intervallo minimo e il range.
Credo che sia meglio memorizzare i primi, per cui devo rivedere la nomenclatura e la gestione dei miei SharedPreferences...

Ecco, ho corretto.
  seekBarMin.setProgress(intervalloMin);
  seekBarMax.setProgress(intervalloMax);
  
  textViewMin=(TextView)findViewById(R.id.textView1);
  setTextViewFromSeekBar(intervalloMin,textViewMin);
  textViewMax=(TextView)findViewById(R.id.textView2);
  setTextViewFromSeekBar(intervalloMax,textViewMax);
Farò poi i conti quando si dovrà usare matematicamente la differenza fra i due intervalli.

Ora provvedo al salvataggio. Il salvataggio dovrà avvenire una volta che si lasci la pressione sulla SeekBar.
  seekBarMin.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMin", seekBar.getProgress());
    editor.commit();

............

  seekBarMax.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    SharedPreferences.Editor editor=SP.edit();
    editor.putInt("intervalloMax", seekBar.getProgress());
    editor.commit();
E vediamo se funziona...

FUNZIONA! Mantiene le impostazioni! E le TextViews sono in linea con i valori!

Aggiustamento iniziale delle TextView che mostrano i valori delle SeekBar, con creazione di una funzione specifica.

Ora devo pensare ai Settings da salvare in memoria.
Trattandosi di memoria accessibile da ogni parte del programma, si può agire in modo indipendente dalle varia activities.

Inizio a definire le SharedPreferences all'activity TimeSettings.
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_time_settings);
  
  SP=getApplicationContext().getSharedPreferences("settings", Context.MODE_PRIVATE);
Ora dovrei incrociare questi dati con le SeekBars.
Il nome generale delle impostazioni è "settings", ma in particolare i dati devono contenere il tempo in minuti dell'intervallo fra gli avvisi e il tempo in minuti della variabilità dell'intervallo.
Quindi li chiamerò "intervallo" e "range".
Detto questo, bisogna innanzitutto settare i valori delle SeekBars a questi valori: la prima va settata a "intervallo", mentre la seconda a "intervallo" + "range".
I valori di default saranno zero e zero.

Inserisco il codice necessario nell'attività TimeSettings...

Ho spostato la riga che regola il valore massimo della SeekBar per ragioni di ordine, e ho aggiunto il codice che legge intervallo e range dai settings, con valore di default zero.
  seekBarMin=(SeekBar)findViewById(R.id.seekBar1);
  seekBarMin.setMax(240);
  
  
  seekBarMax=(SeekBar)findViewById(R.id.seekBar2);
  seekBarMax.setMax(240);
  
  int intervallo=SP.getInt("intervallo", 0);
  int range=SP.getInt("range", 0);
Ecco poi quello che regola la posizione delle SeekBars, tutto insieme, con qualche riga di codice che permette di stabilire dove ci troviamo:
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_time_settings);

                SP=getApplicationContext().getSharedPreferences("settings", Context.MODE_PRIVATE);
  seekBarMin=(SeekBar)findViewById(R.id.seekBar1);
  seekBarMin.setMax(240);
  
  
  seekBarMax=(SeekBar)findViewById(R.id.seekBar2);
  seekBarMax.setMax(240);
  
  int intervallo=SP.getInt("intervallo", 0);
  int range=SP.getInt("range", 0);
  
  seekBarMin.setProgress(intervallo);
  seekBarMax.setProgress(intervallo+range);
Quindi anche il valore iniziale delle TextView va stabilito. Ma vediamo se si stabilisce da solo, ossia se il setProgress delle SeekBars equivale a una modifica dei valori di esse tale da impostare le TextViews secondo il codice scritto in precedenza...

No. Devo provvedere io a impostare le TextViews inizialmente.
Ma ecco che in pochi secondi compatto tutto il codice che compila le TextView alla variazione di SeekBar, in modo da usare la funzione agilmente anche qui, oltre che nel codice delle SeekBar:
 private void setTextViewFromSeekBar(int numero,TextView textView){
  TextView t=textView;
  int intero=10*(numero/10);
  int ore=intero/60;
  int minuti=intero%60;
  
  String stringaDelleOre;
  String stringaDeiMinuti;
  String stringaEt="";
  
  if(ore==0){
   stringaDelleOre="";
  }
  else if(ore==1){
   stringaDelleOre=ore+" ORA "; 
  }
  else{
   stringaDelleOre=ore+" ORE ";     
  }    
  if(minuti==0){
   stringaDeiMinuti="";     
  }
  else if(minuti==1){
   stringaDeiMinuti=minuti+" MINUTO ";          
  }
  else{
   stringaDeiMinuti=minuti+" MINUTI ";
  }    
  if(ore!=0 && minuti!=0) stringaEt=" E ";
  if(ore==0 && minuti==0) {
   stringaDelleOre=ore+" ORE";
   stringaEt= " E ";
   stringaDeiMinuti=minuti+" MINUTI";
  }

  t.setText(stringaDelleOre+stringaEt+stringaDeiMinuti);
 }
e il codice delle SeekBars diventa:
        seekBarMin.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    setTextViewFromSeekBar(progress,textViewMin);
    if(progress>seekBarMax.getProgress())seekBarMax.setProgress(progress);
   }
  });
  
  
  seekBarMax.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    setTextViewFromSeekBar(progress,textViewMax);
    if(progress<seekBarMin.getProgress())seekBarMin.setProgress(progress);
   }
  });
        }
e funziona...

Ecco quindi la modifica iniziale delle TextView:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_time_settings);
  
  SP=getApplicationContext().getSharedPreferences("settings", Context.MODE_PRIVATE);
  seekBarMin=(SeekBar)findViewById(R.id.seekBar1);
  seekBarMin.setMax(240);
  
  
  seekBarMax=(SeekBar)findViewById(R.id.seekBar2);
  seekBarMax.setMax(240);
  
  int intervallo=SP.getInt("intervallo", 0);
  int range=SP.getInt("range", 0);
  
  seekBarMin.setProgress(intervallo);
  seekBarMax.setProgress(intervallo+range);
  
  textViewMin=(TextView)findViewById(R.id.textView1);
  setTextViewFromSeekBar(intervallo,textViewMin);
  textViewMax=(TextView)findViewById(R.id.textView2);
  setTextViewFromSeekBar(intervallo+range,textViewMax);
che funziona egregiamente anch'essa!