JavascriptProva

sabato 23 luglio 2016

Due SeekBar con intervalli minimo e massimo di un intervallo di tempo.

Una SeekBar con valori discreti.
Ecco.
Per rendere più "praticabile" la scelta, ho deciso di inserire i valori di 5 in 5. Potrei decidere anche di immetterli di 10 in 10, e forse è meglio.
Per fare questo, divido il numero progress della SeekBar per il "salto" della numerazione (divisione intera) e poi moltiplicare il risultato per 5, ottenendo questo:
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
che funziona.
Quindi calcolo su questo ore e minuti: le ore facendo la divisione intera del numero per 60, i minuti facendo il modulo:
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
    int ore=numero/60;
    int minuti=numero%60;
E quindi riporto sulla TextBox:
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
    int ore=numero/60;
    int minuti=numero%60;
    textView.setText(ore+" "+minuti);


Stessa cosa per l'altra SeekBar in cui voglio mettere il valore massimo dell'intervallo di campionamento:
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
    int ore=numero/60;
    int minuti=numero%60;
    textView2.setText(ore+" "+minuti);
Ora metto i vincoli:
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
    int ore=numero/60;
    int minuti=numero%60;
    textView.setText(ore+" "+minuti);
    if(progress>seekBar2.getProgress()) seekBar2.setProgress(progress);


   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int numero=5*(progress/5);
    int ore=numero/60;
    int minuti=numero%60;
    textView2.setText(ore+" "+minuti);
    if(progress<seekBar1.getProgress()) seekBar1.setProgress(progress);
E funziona praticamente egregiamente.

Codice per due SeekBars sincronizzate

Intanto metto da parte questo codice per due SeekBar sincronizzate:
public class MainActivity extends Activity {

 SeekBar seekBar1, seekBar2;
 TextView textView,textView2;
 int Incremento;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  textView=(TextView)findViewById(R.id.textView1);
  textView2=(TextView)findViewById(R.id.textView2);
  
  seekBar1=(SeekBar)findViewById(R.id.seekBar1);
  seekBar2=(SeekBar)findViewById(R.id.seekBar2);
  
  seekBar1.setMax(3*3600*1000);
  seekBar2.setMax(3*3600*1000);
  
  int ore=seekBar1.getProgress()/(60*60*1000);
  int minuti=(seekBar1.getProgress()%(60*60*1000)/(60*1000));
  textView.setText(ore+" ORE : "+minuti+" MINUTI");
  ore=seekBar2.getProgress()/(60*60*1000);
  minuti=(seekBar2.getProgress()%(60*60*1000)/(60*1000));
  textView2.setText(ore+" ORE : "+minuti+" MINUTI");
  
  seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   
   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    
   }
   
   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    int ore=progress/(60*60*1000);
    int minuti=(progress%(60*60*1000)/(60*1000));
    textView.setText(ore+" ORE : "+minuti+" MINUTI");
    if(progress>seekBar2.getProgress()) seekBar2.setProgress(progress);
   }
  });
  
  seekBar2.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) {
   
    int ore=progress/(60*60*1000);
    int minuti=(progress%(60*60*1000)/(60*1000));
    textView2.setText(ore+" ORE : "+minuti+" MINUTI");
    if(progress<seekBar1.getProgress()) seekBar1.setProgress(progress);
   }
  });
 }

}

venerdì 22 luglio 2016

Custom Events e un Service bindato con Custom Events

Cerchiamo di chiarire le idee sulla creazione di custom events.

Per prima cosa, creo l'interfaccia, che contiene l'evento, al di fuori della classe che conterrà il metodo stesso.
 public void cambiaVerita(){
  mInterfaccia.evento();
 }
Poi bisogna considerare la classe.
public class Oggetto{
 Interfaccia mInterfaccia;

 
}
...che contiene una variabile del tipo dell'interfaccia.
Questa variabile conterrà pure l'evento ( o meglio il metodo) contenuto nell'interfaccia.
Quindi si scrive il Setter:
public class Oggetto{
 Interfaccia mInterfaccia;

 public void Setter(Interfaccia interfaccia){
  mInterfaccia=interfaccia;
 
 } 
}
e quindi le condizioni che attivano l'evento, che possono essere manipolate eventualmente anche dall'esterno della classe stessa:
public class Oggetto{
 Interfaccia mInterfaccia;

 public void Setter(Interfaccia interfaccia){
  mInterfaccia=interfaccia;
 
 }
 public void cambiaVerita(){
  mInterfaccia.evento();
 }
 
}
Riprovo a scrivere il tutto...

La classe:
public class Oggetto{
 private int numero;
 Interfaccia mInterfaccia;
 
 public void Setter(Interfaccia interfaccia){
  mInterfaccia=interfaccia;
 }
 
 public void setNumero(int num){
  numero+=num;
  if(numero>10)mInterfaccia.evento();
 }
 
}


 interface Interfaccia{
 public void evento();
}


L'istanziazione:
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  Oggetto oggetto=new Oggetto();
  oggetto.Setter(new Interfaccia(){

   @Override
   public void evento() {
    Log.d("NUMERO","SUPERATO");
    
   }
   
  });
  oggetto.setNumero(3);
  oggetto.setNumero(4);
  oggetto.setNumero(4);
 }

}

Ora scriviamo un Service che abbia un evento personalizzato.

Ce l'ho fatta!
public class Servizio extends Service {

 Interfaccia mInterfaccia;
 IBinder mBinder=new LocalBinder();
 
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return mBinder;
 }
 
 public class LocalBinder extends Binder {
  Servizio getService(){
   return Servizio.this;
  }
 }
 
 public void StartTime(){
  Intent intent=new Intent(this,RecordService.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()+60*1000, pendingIntent);
  mInterfaccia.AlarmStateChange();
 }
 
 public void StopTime(){
  Intent intent=new Intent(this,RecordService.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();
  mInterfaccia.AlarmStateChange();
 }
 
 public boolean AlarmOn(){
  Intent intent=new Intent(this,RecordService.class);
  boolean check=(PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_NO_CREATE)!=null);
  return check;
 }
 
 public void Setter(Interfaccia interfaccia){
  mInterfaccia=interfaccia;
 }
 


}

interface Interfaccia{
 public void AlarmStateChange();
}

mercoledì 20 luglio 2016

TimerTask e primo approccio con gli Handler.

Devo fare una digressione sul TimerTask, che ho usato recentemente ma che non ricordo come si usi.

Sicuramente conviene rivedere quello che ho già usato:
  if(ringtone!=null){
   ringtone.play();
   TimerTask timerTask=new TimerTask(){

    @Override
    public void run() {
     ringtone.stop();
     
    }
    
   };
   Timer timer=new Timer();
   timer.schedule(timerTask, 2000);
  }
Si dichiara come qualunque istanza di classe che abbia dei metodi da implementare.
  TimerTask timerTask=new TimerTask(){
   
  }
A questo punto vengono da sole le richieste di mettere il punto e virgola finale e di implementare i metodi.
E si trasforma così:
  TimerTask timerTask=new TimerTask(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    
   }
   
  };
Quindi ci mettiamo l'azione da svolgere dopo il tempo stabilito.
  TimerTask timerTask=new TimerTask(){

   @Override
   public void run() {
    Toast.makeText(getApplicationContext(), 
        "QUESTA E' L'AZIONE DA SVOLGERE DOPO IL TEMPO STABILITO", 
        Toast.LENGTH_LONG).show();
    
   }
   
  };


Fin qui non dice niente. Bisogna impostare il TEMPO dopo il quale va eseguito questo TimerTask.
E si introduce la classe Timer.
  Timer timer=new Timer();
  timer.schedule(timerTask, 5000);
quindi si usa il metodo schedule dell'istanza di Timer, il quale può avere come secondo parametro un ritardo in millisecondi come in questo caso, ma vedo adesso che può anche avere una data alla quale far eseguire il TimerTask (e credo che nel nome della classe Date sia implicita anche la possibilità di impostare un'ora anziché una data).
Proviamo:

Bene. Con il Toast ottengo un messaggio di errore:
Can't create handler inside thread that has not called Looper.prepare()
. Vado ad approfondire e trovo questo e risolvo così:
  mHandler=new Handler(Looper.getMainLooper()){
   @Override
   public void handleMessage(Message message){
    Toast.makeText(getApplicationContext(), "QUESTA E' L'AZIONE DA SVOLGERE DOPO IL TEMPO STABILITO",Toast.LENGTH_LONG).show();
   }
  };
  TimerTask timerTask=new TimerTask(){

   @Override
   public void run() {
    Message message=mHandler.obtainMessage();
    message.sendToTarget();
    
    
   }
   
  };
  Timer timer=new Timer();
  timer.schedule(timerTask, 5000);
Devo approfondire questi Handler.

Progetto Kouros: interfacce a un bottone e a due bottoni.

Ho studiato lo schema a un solo bottone:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if(mService.AlarmOn()){
     mService.StopTime();
     ((Button)v).setText("ATTIVA");
     
    }
    else{
     mService.StartTime();
     ((Button)v).setText("DISATTIVA");
     
    }
    
   }
  });
In questo modo, se Alarm è On la pressione del pulsante disattiva e vi compare la scritta ATTIVA; mentre se Alarm è Off la pressione del pulsante attiva l'alarm e vi compare la scritta DISATTIVA:

Adesso realizzo uno schema a due bottoni.
Eccolo:
  bttStart.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if(!mService.AlarmOn()){
     mService.StartTime();
     ((Button)v).setEnabled(false);
     bttStop.setEnabled(true);
    }
    
   }
  });
  
  bttStop.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if(mService.AlarmOn()){
     mService.StopTime();
     ((Button)v).setEnabled(false);
     bttStart.setEnabled(true);
    }
    
   }
  });
ponendo nell'XML questo:
        <Button
            android:id="@+id/bttStart"
            android:layout_width="120dp"
            android:layout_height="50dp"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="20dp"
            android:background="@drawable/buttonshape"
            android:text="ATTIVA" />
        
        <Button 
            android:id="@+id/bttStop"
            android:layout_width="120dp"
            android:layout_height="50dp"
            android:layout_alignParentTop="true"
            android:layout_alignParentRight="true"
            android:layout_marginRight="10dp"
            android:layout_marginTop="20dp"
            android:background="@drawable/buttonshape"
            android:enabled="false"
            android:text="DISATTIVA" /> 
...che è una stronzata!
Quando chiudo l'attività con un AlarmOn vero, poi la riapro e trovo bttStop disabilitato come se AlarmOn fosse falso, mentre bttStart è abilitato quando dovrebbe essere disabilitato.
Se io faccio disabilitare bttStart alla sua pressione e do bttStop come disabilitato all'apertura, non faccio seguire l'abilitazione del bottone al reale status di Alarm.
Ci tornerò perché mi si sono aperti altri campi di studio in correlazione a questo...

Studio grafico dell'interfaccia (progetto Kouros)

Bisognerebbe studiare anche l'interfaccia che imposta l'innesco e il disinnesco dell'Alarm.
O un solo bottone che cambia funzione a seconda che l'Alarm sia innescato o disinnescato, o due bottoni dei quali l'uno si disabilita e l'altro si abilita a seconda dello status.
Proviamo con questa seconda possibilità...

Iniziamo a studiare l'XML...

Inserirei un Layout orizzontale con i due tasti...
Vado a guardare quello che ho già fatto con TimeManager e mi ritrovo un elemento molto importante per dare un certo "formato" al Layout:
    <RelativeLayout
        android:id="@+id/relativeLayout1"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:background="@drawable/shap"
        android:orientation="horizontal" >
Visto questo, vado a rivedermi la forma shap.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >


<corners
    android:bottomLeftRadius="10dp"
    android:bottomRightRadius="10dp"
    android:topLeftRadius="10dp"
    android:topRightRadius="10dp" />

<stroke android:width="2px" 
    android:color="#000" />

</shape> 
Vista questa, cerco di riprodurre una nuova forma...

Cliccando sulla cartella drawable, e poi new -> android XML file -> shape ottengo questo:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    

</shape>
 
da riempire adesso con ciò che più mi aggrada.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="oval">
    
<stroke android:width="5dp"
    android:color="#5f5" />
</shape>
 
E vediamo cosa ne esce impostando questa shape come background di un RelativeLayout...

Perfetto!
E' bene che mi faccia un po' di esperienza con le forme.

Ora, però, meglio metterci il classico rettangolo arrotondato, non prima però di aver provato a metterci dentro l'ovale un Button.

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.kouros.MainActivity" >



    <RelativeLayout
        android:id="@+id/pulsantiera"
        android:layout_width="match_parent"
        android:layout_height="100dp"
     android:layout_alignParentTop="true"
     android:layout_alignParentLeft="true"
     android:layout_marginTop="50dp"
     android:layout_marginLeft="10dp"
     android:background="@drawable/forma" >
        
        <Button
            android:id="@+id/bttStart"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:text="Start" />
        
        
    </RelativeLayout>


</RelativeLayout> 


Ecco:




Questa veste grafica potrebbe essere buona per una soluzione a un solo pulsante dell'innesco e della cancellazione dell'Alarm.
Se invece di "oval" metto "rectangle" ottengo un rettangolo.
In questo caso posso agire sugli angoli inserendo il tag corners.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="rectangle">
    
    <corners 
        android:topLeftRadius="15dp" 
        android:topRightRadius="15dp"
        android:bottomLeftRadius="15dp"
        android:bottomRightRadius="15dp"/>
    
 <stroke android:width="5dp"
     android:color="#5f5" />
</shape> 
Proviamo...

Progetto Kouros: creazione dell'AlarmManager nel servizio TimerService.

Innesco e cancellazione dell'AlarmManager.
Questo è il codice che nel mio progetto TimeManager gestisce la cancellazione dell'AlarmManager nel Service:
  bttStop.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mService.StopTime();
    if(mService.AlarmOn()){
     textView.setTextColor(Color.GREEN);
     textView.setText("ATTIVO");
    }else{
     textView.setTextColor(Color.RED);
     textView.setText("INATTIVO");
    }
    
   }
  });
Fa riferimento a due funzioni del Service: StopTime e AlarmOn.
Vediamo...

StopTime:
 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();
 }
Se ricordo bene, ricrea esattamente le stesse classi di StartTime:
 public void StartTime(){
  Log.e("CONTESTO START",getApplicationContext()+"");
  SharedPreferences SP=getApplicationContext().getSharedPreferences("tempi", Context.MODE_PRIVATE);

  Intent intent=new Intent(getApplicationContext(),Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
  int base=(int) (SystemClock.elapsedRealtime()+(SP.getInt("ore",0)*60+SP.getInt("minuti", 0))*60*1000);
  int range=(SP.getInt("rndOre",0)*60+SP.getInt("rndMinuti", 0))*60*1000;
  Random rand=new Random();
  int tempo=base-range+rand.nextInt(range*2+1);
  alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, tempo, pendingIntent);
 }


Confronto:
  Intent intent=new Intent(getApplicationContext(),Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);


  Intent intent=new Intent(getApplicationContext(),Form.class);
  PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent,0);
  AlarmManager alarmManager=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Poi, mentre l'uno usa il metodo set, l'altro usa il metodo cancel.

Invece AlarmOn verifica se l'AlarmManager sia attivo...

Ecco, ho ricreato la triade:
 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);
  alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+10*1000, pendingIntent);
 }
 
 public void StopTime(){
  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=null;
 }
 
 public boolean AlarmOn(){
  Intent intent=new Intent(getApplicationContext(),Form.class);
  boolean isAlarmOn=(PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_NO_CREATE)!=null);
  return isAlarmOn;
  
 }
Dunque, chiamando dalla MainActivity, il processo può essere attivato o deattivato, e si può avere il feedback se sia attivo o non attivo.

Lo schema è sempre quello. Potrei impostare anche i tempi, sempre a partire dalla MainActivity.
Dovrei studiare un sistema migliore per l'impostazione dei tempi...

Comunque per il momento lasciamolo così.

Adesso imposto una semplice suoneria alla scadenza del tempo.