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,一般我們將聲音資訊保存在res的raw資料夾下。第三個參數為聲音的優先順序,當多個聲音衝突而無法同時播放時,系統會優先播放優先順序高的。
為了解決 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);
}
標準寫法, 不多囉嗦了.