2011年5月29日日曜日

Androidでスクリーンロック解除しなくても操作可能な画面をつくる&呼び出す


音楽アプリなどで音楽再生中にスクリーンロックがかかった場合、
再度音楽を操作するのにいちいちスクリーンロックを解除するのはかなりの手間である。

Androidマーケットで常に上位のPowerAMPはこのように操作可能になっている。
このようなアプリではユーザビリティを考慮する上でも必要な機能となる。

そこで今回はスクリーンロック解除しなくても操作可能な画面&呼び出しを
ミニマム構成で作成してみた。
サンプルプロジェクトのダウンロードリンクは記事の最後。

構成は以下の通り。
  1. 画面の電源ONの通知を受け取るレシーバーの登録/登録解除を行うサービス
    ScreenStateService
  2. スクリーンロック解除画面より手前に表示させる画面
    ScreenLockEnabledActivity
  3. "2"の画面を表示させるか否かを設定する画面
    SetActivity
まず、"画面の電源ONの通知を受け取るレシーバーの登録/登録解除を行うサービス"を作成する。
画面の電源が入るとACTION_SCREEN_ONが通知される。
ACTION_SCREEN_ONを受け取るには明示的にregisterReceiverする必要がある。
ScreenStateService.java
  1. package jp.u1aryz.products.screenlockenable;  
  2.   
  3. import android.app.Service;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.IntentFilter;  
  8. import android.os.IBinder;  
  9.   
  10. public class ScreenStateService extends Service {  
  11.   
  12.     private BroadcastReceiver mScreenOnListener = new BroadcastReceiver() {  
  13.   
  14.         @Override  
  15.         public void onReceive(Context context, Intent intent) {  
  16.             String action = intent.getAction();  
  17.   
  18.             // 画面の電源が入ったらActivityを起動  
  19.             if (action.equals(Intent.ACTION_SCREEN_ON)) {  
  20.                 Intent i = new Intent(context, ScreenLockEnabledActivity.class);  
  21.                 i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
  22.                 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  23.                 context.startActivity(i);  
  24.             }  
  25.         }  
  26.     };  
  27.   
  28.     @Override  
  29.     public void onStart(Intent intent, int startId) {  
  30.         super.onStart(intent, startId);  
  31.         // ACTION_SCREEN_ONを受け取るBroadcastReceiverを登録  
  32.         IntentFilter filter = new IntentFilter();  
  33.         filter.addAction(Intent.ACTION_SCREEN_ON);  
  34.         registerReceiver(mScreenOnListener, filter);  
  35.     }  
  36.   
  37.     @Override  
  38.     public void onDestroy() {  
  39.         // BroadcastReceiverを登録解除  
  40.         unregisterReceiver(mScreenOnListener);  
  41.   
  42.         super.onDestroy();  
  43.     }  
  44.   
  45.     @Override  
  46.     public IBinder onBind(Intent intent) {  
  47.         return null;  
  48.     }  
  49. }  
サービスが起動されたらレシーバーの登録、サービスが停止されたらレシーバーの登録解除を行う。
かなり簡略化してシンプルな作りにしている。

そして今回のポイントとなる"Lock解除画面より手前に表示させる画面"を作成する。
ScreenLockEnabledActivity.java
  1. package jp.u1aryz.products.screenlockenable;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.View;  
  6. import android.view.Window;  
  7. import android.view.WindowManager;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10.   
  11. public class ScreenLockEnabledActivity extends Activity {  
  12.   
  13.     @Override  
  14.     protected void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.lock);  
  17.   
  18.         // Lock解除画面より手前に表示させる  
  19.         final Window win = getWindow();  
  20.         win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
  21.                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
  22.   
  23.         Button btnRelease = (Button) findViewById(R.id.btn_release);  
  24.         btnRelease.setOnClickListener(new OnClickListener() {  
  25.   
  26.             @Override  
  27.             public void onClick(View v) {  
  28.                 // Activityを終了することでLock解除画面に移る  
  29.                 finish();  
  30.             }  
  31.         });  
  32.     }  
  33. }  
こちらもかなりミニマムであるが、このActivityに各々操作できるウィジェット(View)等を
配置するといいと思う。

続いて"上記の画面を表示させるか否かを設定する画面"を作成する。
通常はPreferenceActivityなどで実装すると良い。
SetActivity.java
  1. package jp.u1aryz.products.screenlockenable;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.content.SharedPreferences;  
  6. import android.os.Bundle;  
  7. import android.widget.CheckBox;  
  8. import android.widget.CompoundButton;  
  9. import android.widget.CompoundButton.OnCheckedChangeListener;  
  10.   
  11. public class SetActivity extends Activity {  
  12.   
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.   
  18.         // チェックボックスの値を保存するために使用  
  19.         final SharedPreferences pref = getSharedPreferences("pref", MODE_PRIVATE);  
  20.         final Intent intent = new Intent(SetActivity.this, ScreenStateService.class);  
  21.   
  22.         CheckBox chbEnable = (CheckBox) findViewById(R.id.chb_enable);  
  23.         chbEnable.setChecked(pref.getBoolean("is_lockEnable"false));  
  24.         chbEnable.setOnCheckedChangeListener(new OnCheckedChangeListener() {  
  25.   
  26.             @Override  
  27.             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
  28.                 // チェックされたらサービスを起動  
  29.                 if (isChecked) {  
  30.                     pref.edit().putBoolean("is_lockEnable", isChecked).commit();  
  31.                     startService(intent);  
  32.                 // チェックが外されたらサービスを停止  
  33.                 } else {  
  34.                     pref.edit().putBoolean("is_lockEnable", isChecked).commit();  
  35.                     stopService(intent);  
  36.                 }  
  37.             }  
  38.         });  
  39.   
  40.     }  
  41. }  
端末の再起動のパターンやその他もろもろ対応しきれてないけどあしからず〜

ダウンロードはこちらから