2014年4月21日 星期一

IMP control APK development

1. APK UI 解說

   1.1 開啟畫面  
開起畫面如上圖貼, 並伴隨音樂  .中間雲端圖示是"借用" IMP 網站的.
帳號功能尚未加入, 輸入密碼後才能進入操作畫面

  1.2 操作畫面 2-1
一進入操作畫面先執行狀態檢查.....


 1.2 操作畫面 2-2
檢查完畢若雙端網路皆開啟 (手機&IMP)且IMP 與server 連線正常,狀態列顯示"狀態已更新", 並自動調整開關位置反應實際狀況, 下方黑色顯示區 1,1,1,1,1,1 為IMP 輸出準位 (1=H, 0=L).


1.3 錯誤畫面
檢查完畢若IMP無正常連線 pop up a "無線網卡無回應" message


2.程式碼架構


3.1 MainActivity.java


相關變數宣告及定義
============================================
public class MainActivity extends Activity {
/** Called when the activity is first created. */
 TextView etResponse;  // 顯示密碼登入狀況
 private Button bt1;   // "進入" 按鍵
 String UID="jason";   // 密碼
 private int alertId;  
 private SoundPool soundPool;
 Handler aHandler;



聲音播放  
==================================================
    soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 5);
    alertId = soundPool.load(this, R.raw.start_up, 1); 

    aHandler = new Handler();
    aHandler.post(runnable); 

 final Runnable runnable = new Runnable() {
        public void run() {
          try{
          Thread.sleep(500);  
         }
    catch(InterruptedException e){
           e.printStackTrace();
          }
      soundPool.play(alertId, 1.0F, 1.0F, 0, 0, 1.0F); 
 }
          };


第一行, 將soundPool產生實體,第一個參數為soundPool可以支持的聲音數量,這決定了Android為其開設多大的緩衝區,第二個參數為聲音類型,在這裡標識為系統聲音,除此之外還有AudioManager.STREAM_RING以及AudioManager.STREAM_MUSIC等,系統會根據不同的聲音為其標誌不同的優先順序和緩衝區,最後參數為聲音品質,品質越高,聲音效果越好,但耗費更多的系統資源。

第二行,系統為soundPool載入聲音,第一個參數為上下文參數,第二個參數為聲音的id,一般我們將聲音資訊保存在resraw資料夾下。第三個參數為聲音的優先順序,當多個聲音衝突而無法同時播放時,系統會優先播放優先順序高的。

為了解決  SoundPool 音效直接放在onCreate裡,卻放不出來  (原來問題出在於播放音效時,音效檔尚未加載完成所以在DDMS上會出現sample not ready的訊息,沒有完成載入音效檔導致不能播放。). 使用timer物件runnable方法來給程式時間. 這裡延遲了 500 msec.


這裡利用 Intent 來執行 Activity 的切換 (跳至 LEDActivity, 沒有傳任何值)
==============================================================
public void onClick(View v) {
 
   String content = CodeEditText.getText().toString(); //gets you the contents of edit text
 
         if (content.equals(UID))  {  
        etResponse.setBackgroundColor(0xFF00DD00);
        etResponse.setText(content+"...ok");   
            Intent intent = new Intent();
            intent.setClass(MainActivity.this, LEDActivity.class);
            startActivity(intent);
       }          
         else{
         etResponse.setBackgroundColor(0xFFFF5809);
         etResponse.setText("密碼錯誤");   
                      }
          }


4. LEDActivity

相關變數宣告及定義
============================================
public class LEDActivity<var> extends Activity {

    TextView etResponse;     
    TextView tvIsConnected;  // 狀態列訊息
    private Button bt1;      // 狀態確認鍵
    private Switch sw1,sw2,sw3,sw4,sw5,sw6;  // 開關鍵
    String disconnect= "手機網路不存在";    // 手機網路未開錯誤訊息
    String checkIMP="請檢查無線網卡";   //未能連接 IMP 錯誤訊息
    String one="1";
    String UID="jason";           // 網址加入 UID 與 IMP squirrel agent code 設定一致 
        
    private Button t_bt1_on,t_bt2_on,t_bt3_on,t_bt4_on,t_bt5_on,t_bt6_on;    // 定開按鍵
    private Button t_bt1_off,t_bt2_off,t_bt3_off,t_bt4_off,t_bt5_off,t_bt6_off;  // 定關按鍵
    int timer=10000;    // 定該關時間 10sec
    int setting=0;
    private MyCount mc;  //計時器宣告
    String LED="";
    int vibration=0;    // vibrator 震動持續時間
    private int alert_button, alert_error; // 聲音檔 ID 定義
    private SoundPool soundPool;


計時器代碼
============================================
class MyCount extends CountDownTimer {   
public MyCount(long millisInFuture, long countDownInterval) {   
super(millisInFuture, countDownInterval);   
}   
@Override   
public void onFinish() 
{   
}   
@Override   
public void onTick(long millisUntilFinished) {                    
          tvIsConnected.setText(LED+"設定時間:"+timer/1000+" /剩餘時間:"+millisUntilFinished/1000);
             }
    }   


OnCreate代碼
============================================
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.led_main);

        // get reference to the views
        etResponse = (TextView) findViewById(R.id.etResponse);
        tvIsConnected = (TextView) findViewById(R.id.tvIsConnected);
       
        soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 5);
        alert_button = soundPool.load(this, R.raw.button, 1); 
        alert_error = soundPool.load(this, R.raw.error, 1);
        
        // add for timer 0411
        RegisterAlarmBroadcast();
        
        tvIsConnected.setText("狀態檢查中...");
        new StatusAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&status");
          
        bt1 = (Button) findViewById(R.id.button1);
        bt1.setOnClickListener(new Button.OnClickListener() {
              // check if bt1 pressed
        public void onClick(View v) {
         
        vibrate(vibration);
         
        soundPool.play(alert_button, 1.0F, 1.0F, 0, 0, 1.0F);
         
        // check if network is connected
                if(isConnected())
                        {  
                   tvIsConnected.setText("狀態檢查中...");
                         new StatusAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?                                         uid="+UID+"&status");
                          }                                       
                  // not connected               
                 else{
                   // tvIsConnected.setBackgroundColor(0xFFFF5809);
                        // tvIsConnected.setText(disconnect); 
                soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
                         Builder dialog = new AlertDialog.Builder(LEDActivity.this)
                         .setIcon(android.R.drawable.btn_star_big_on)
                         .setTitle("錯誤")  
                         .setMessage(disconnect)
                         .setNegativeButton("close",null);
                         dialog.show();             
                      }
              }
     });
        
        sw1 = (Switch) findViewById(R.id.switch1); 
        sw1.setOnCheckedChangeListener(new OnCheckedChangeListener() {   
        public void onCheckedChanged(CompoundButton buttonView,
        boolean isChecked) {
            //   tvIsConnected.setBackgroundColor(0xFFFFFFFF);
        soundPool.play(alert_button, 1.0F, 1.0F, 0, 0, 1.0F);
           vibrate(vibration);
        if(isConnected()){ 
             if (isChecked)                
               // sw1 on
                             new HttpAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&dev1=1");                                                             
                   // sw1 off                
                       else          
                         new HttpAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&dev1=0");                          
                           }  
                 
                else {
                 // not connected
                     // tvIsConnected.setBackgroundColor(0xFFFF5809);
                    // tvIsConnected.setText(disconnect);
                soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
                    Builder  dialog = new AlertDialog.Builder(LEDActivity.this)
                    .setIcon(android.R.drawable.btn_star_big_on)
                    .setTitle("錯誤")  
                    .setMessage(disconnect)
                    .setNegativeButton("close", null);
                    dialog.show(); 
                     }      
           }
              }); 

進入操作畫面立即執行 StatusAsyncTask() 狀態檢查, 之後偵測 button and switch, 若是在有網路情況下則執行  HttpAsyncTask() 否則執行錯誤對話框 AlertDialog 顯示錯誤.


定時開關代碼
============================================
 t_bt1_on = (Button) findViewById(R.id.button_t1);
        t_bt1_on.setOnClickListener(new Button.OnClickListener() {
       
               public void onClick(View v) {
         
        alarmManager.set( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timer , pendingIntent );
        setting=11;
        mc=new MyCount(timer,1000);
        mc.start();
        LED="LED1";
                      }
              });
              
        t_bt1_off = (Button) findViewById(R.id.button_t1off);
        t_bt1_off.setOnClickListener(new Button.OnClickListener() {      
       
        public void onClick(View v) 
           {
        alarmManager.set( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timer , pendingIntent );
        setting=10;
        mc=new MyCount(timer,1000);
        mc.start();
        LED="LED1";
        }
              });        

t_bt1_on/off 為定是開關鍵, 按下時執行計數 MyCount , 計數完成 傳出 setting 常數供開關改變狀態


HTTP send and get 代碼
============================================

public static String GET(String url){
        InputStream inputStream = null;
        String result = "";
        try {
             
              HttpClient httpclient = new DefaultHttpClient();   
              HttpResponse httpResponse = httpclient.execute(new HttpGet(url));
              inputStream = httpResponse.getEntity().getContent();
              if(inputStream != null)
                  result = convertInputStreamToString(inputStream);
              else
                  result = "Did not work!";
             } catch (Exception e) {
                   Log.d("InputStream", e.getLocalizedMessage());
                 }
              return result;
          }
    
    private static String convertInputStreamToString(InputStream inputStream) throws IOException{
        BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
        String line = "";
        String result = "";
        while((line = bufferedReader.readLine()) != null)
            result += line;
           inputStream.close();
          return result;
         }

    public boolean isConnected(){
        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()) 
                return true;
            else
                return false;   
            }

public class HttpAsyncTask extends AsyncTask<String, Void, String> {
              
        @Override 
        protected String doInBackground(String... urls) {
         
          GET(urls[0]); 
                 return GET(urls[0]);
               }
          
        // onPostExecute displays the results of the AsyncTask.
       @Override
        protected void onPostExecute(String result) {
         
            if(result.substring(0, 1).equals("I")) {
           
                   try {
 Thread.sleep(2000);
                catch (InterruptedException e)
                               {
e.printStackTrace();
}
       
              soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
           Builder  dialog = new AlertDialog.Builder(LEDActivity.this)
           .setIcon(android.R.drawable.btn_star_big_on)
                    .setTitle("錯誤")  
                    .setMessage("無線網卡無回應")
           .setNegativeButton("close", null);
                     dialog.show();
                tvIsConnected.setText(checkIMP);
               }
            
            else {
              tvIsConnected.setText("");
            Toast.makeText(getBaseContext(), result, Toast.LENGTH_LONG).show(); 
                 }
              }
           }

此處參考 http://hmkcode.com/android-internet-connection-using-http-get-httpclient/  文中所寫代碼, 唯其中 GET(urls[0]) 同時執行兩次 是為了克服 IMP agent 於回傳至 HTTP 時並非為IMP 及時狀態, 而是會有一次的慢差.


HTTP send and get 代碼
============================================
    private void RegisterAlarmBroadcast()
             {
             mReceiver = new BroadcastReceiver()
              {
                     @Override
             public void onReceive(Context context, Intent intent)
              {
                Toast.makeText(context, "設定完成!", Toast.LENGTH_LONG).show();
                tvIsConnected.setText("");
                switch(setting){
                    case 11: 
                       sw1.setChecked(true);
                       break;
                    case 10:    
                       sw1.setChecked(false);
                       break;
                    case 21: 
                       sw2.setChecked(true);
                       break;
                    case 20:    
                       sw2.setChecked(false);
                       break;                    
                    case 31: 
                       sw3.setChecked(true);
                       break;
                    case 30:    
                       sw3.setChecked(false);
                       break;
                    case 41: 
                       sw4.setChecked(true);
                       break;
                    case 40:    
                       sw4.setChecked(false);
                       break;    
                    case 51: 
                       sw5.setChecked(true);
                       break;
                    case 50:    
                       sw5.setChecked(false);
                       break;
                    case 61: 
                       sw6.setChecked(true);
                       break;
                    case 60:    
                       sw6.setChecked(false);
                       break;    
                        }            
                    }
                };

             // register the alarm broadcast here
              registerReceiver(mReceiver, new IntentFilter("com.techblogon.alarmexample") );
              pendingIntent = PendingIntent.getBroadcast( this, 0, new Intent("com.techblogon.alarmexample"),0          );
              alarmManager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
        }

接收計數器結束後 回傳的 setting 值 供開關狀態更新
參考 http://techblogon.com/simple-android-alarmmanager-example-to-schedule-an-event/



Vibrator 代碼
============================================

 public void vibrate(int duration)
    {
       Vibrator myVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
       myVibrator.vibrate(duration);    
    }
    
標準寫法, 不多囉嗦了.




沒有留言:

張貼留言