読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

ライブ壁紙でステータスバーの通知を受け取る

Android

今回のエンジニアブログを担当させて頂きます、佐藤です。

以前ご紹介させて頂きましたライブ壁紙というものが個人的に気に入ったので、またまたライブ壁紙について書いていきたいと思います。

今回はライブ壁紙に他のアプリ通知を取得させてみました!

Androidの画面上部にあるステータスバーにアプリやメール等の通知が表示されます。
これらの「通知された」という情報は、AccessibilityServiceクラスで取得が可能です。

AccessibilityServiceクラスはユーザーの補助機能アプリにも使用されています。

onAccessibilityEventというアクションがあったときに入る関数で通知情報の取得をします。
今回は壁紙の機能として組み込んでみることができましたが、勿論AccessibilityServiceクラスを単体で使用することも可能です。
パッケージ名の保存だけでなく、通知メッセージの一部も取得できます!
◆ GetStatusBar.java

public class GetStatusBar extends AccessibilityService {  
    @Override  
    protected void onServiceConnected() {  
        // 詳細情報の設定  
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();  
        info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;  
        info.feedbackType = AccessibilityEvent.TYPES_ALL_MASK;  
        info.notificationTimeout = 100;  
        info.flags = AccessibilityServiceInfo.DEFAULT;  
        setServiceInfo(info);  
    }  
  
    @Override  
    // アクションがあるとここに入る  
    public void onAccessibilityEvent(AccessibilityEvent event) {  
        int eventType = event.getEventType();  
  
        // DataMapクラスにデータを保存する  
        DataMap datamap = new DataMap(this,"AppData");  
        switch (eventType) {  
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:  
                HashMap<String,String> hs = new HashMap<String, String>();  
  
                // 通知があった  
                hs.put("isDataset", "ok");  
  
                // 通知があったパッケージ名を保存  
                hs.put(event.getPackageName().toString(), "Notification");  
                datamap.WriteKeyValues(hs);  
                break;  
        }  
    }  
  
    @Override  
    public void onInterrupt() {  
    }  
}  

ライブ壁紙本体は以前ブログで書いたソースを少し改造しています。
通知を取得すると、画像が変わりパッケージ名が表示されます。画像をタップするともとに戻るようになっています。
◆LiveWallService.java

public class LiveWallService extends WallpaperService {  
  
    private final Handler mHandler = new Handler();  
  
    @Override  
    public Engine onCreateEngine() {  
        // 描画用の自作Engineクラス  
        return new LiveEngine();  
    }  
  
    // 描画を行うEngineクラス  
    public class LiveEngine extends Engine {  
  
        // 画像  
        private Bitmap image;  
  
        //---移動関連-------------------  
  
        //x  
        private int x = 0;  
        //y  
        private int y = 0;  
  
        //幅  
        private int width;  
        //高さ  
        private int height;  
  
        // 画像のパス  
        private int[] imgPath = {R.drawable.droidkun,R.drawable.droidkun2};  
  
        // 描画用のRunnable  
        private final Runnable drawRunnable = new Runnable() {  
            public void run() {  
                drawFrame();  
            }  
        };  
  
        //表示状態  
        private boolean visible;  
  
        // インストール済みのパッケージ名(キー)  
        private String[] installPackages;  
        // 取得したデータを保持するクラス  
        private DataMap datamap;  
        private Context context;  
  
        private String[] isDataset = {"isDataset"};  
        //====== コンストラクタ ===========//  
        public LiveEngine() {  
            context = getApplicationContext();  
            WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);  
            Display disp = wm.getDefaultDisplay();  
            image = BitmapFactory.decodeResource(getResources(), imgPath[0]);  
  
            //画像位置  
            x = disp.getWidth() / 2 - image.getWidth() / 2;  
            y = disp.getHeight() / 2 - image.getHeight() / 2;  
  
            datamap = new DataMap(context,"AppData");  
  
            // キーの設定  
            getApps();  
        }  
  
        //====== 生成時に呼び出される ===========//  
        @Override  
        public void onCreate(SurfaceHolder surfaceHolder) {  
            super.onCreate(surfaceHolder);  
        }  
  
        //====== 破棄時に呼び出される ===========//  
        @Override  
        public void onDestroy() {  
            super.onDestroy();  
            mHandler.removeCallbacks(drawRunnable);  
        }  
  
        //====== 表示状態変更時に呼び出される ===========//  
        @Override  
        public void onVisibilityChanged(boolean visible) {  
            this.visible = visible;  
            if (visible) {  
                drawFrame();  
            } else {  
                mHandler.removeCallbacks(drawRunnable);  
            }  
        }  
  
        //////////////サーフェイス ////////////////////  
        //======生成時 ===========//  
        @Override  
        public void onSurfaceCreated(SurfaceHolder surfaceHolder) {  
            super.onSurfaceCreated(surfaceHolder);  
        }  
  
        //======変更時 ===========//  
        @Override  
        public void onSurfaceChanged(SurfaceHolder holder,int format, int width , int height) {  
            super.onSurfaceChanged(holder, format, width, height);  
            this.width = width;  
            this.height = height;  
            drawFrame();  
        }  
  
        //======破棄時 ===========//  
        @Override  
        public void onSurfaceDestroyed(SurfaceHolder holder) {  
            super.onSurfaceCreated(holder);  
            visible = false;  
            mHandler .removeCallbacks(drawRunnable);  
        }  
        //////////////////////////////////////////////////  
  
        //======オフセット変更時 ===========//  
        @Override  
        public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) {  
            drawFrame();  
        }  
  
        //======描画 ===========//  
        private void drawFrame() {  
            final SurfaceHolder holder = getSurfaceHolder();  
  
            Canvas canvas = null;  
            try {  
                // キャンバスをロック  
                canvas = holder.lockCanvas();  
                /************************  
                 * 描画処理  
                 ************************/  
                if (canvas != null) {  
                    canvas.drawColor(Color.WHITE); // 背景色  
  
                    // 取得したデータを受け取る  
                    HashMap<String, String> isData = datamap.ReadKeyValues(isDataset);  
  
                    // 通知があるかどうか  
                    if (isData.get("isDataset").equals("ok")) {  
                        // 画像を変える  
                        image = BitmapFactory.decodeResource(getResources(), imgPath[1]);  
  
                        // ペイント設定  
                        Paint paint = new Paint();  
                        paint.setColor(Color.BLACK);  
                        paint.setTextSize(30);  
                        // パッケージ名表示  
                        canvas.drawText(findApps() + " から通知が来ています", 30, y, paint);  
                    }  
                    // 画像の表示  
                    canvas.drawBitmap(image, x, y, null);  
                }  
            } finally {  
                // Canvas アンロック  
                if (canvas != null) {  
                    holder.unlockCanvasAndPost(canvas);  
                }  
            }  
            // 次の描画をセット  
            mHandler.removeCallbacks(drawRunnable);  
            if (visible) {  
                mHandler.postDelayed(drawRunnable, 60);  
            }  
        }  
  
        //======タッチイベント ===========//  
        @Override  
        public void onTouchEvent(MotionEvent event) {  
            // 画像をタッチすると元に戻る  
            switch(event.getAction()) {  
                case MotionEvent.ACTION_UP:  
  
                    if ((event.getX() > x) && (event.getX() < x + image.getWidth()) &&  
                        (event.getY() > y) && (event.getY() < y + image.getHeight())) {  
                        HashMap<String,String> hashMap = new HashMap<String,String>();  
                        hashMap.put("isDataset", "");  
                        datamap.WriteKeyValues(hashMap);  
                        image = BitmapFactory.decodeResource(getResources(), imgPath[0]);  
                    }  
            }  
        }  
    }  

イベントで取得した通知をこちらで紹介したHashMapを使って受け取っています

Android端末にインストールされているアプリのパッケージ名をキーにするため、PackegeManagerクラスを使用してパッケージ名を取得しました。

    // インストールされているアプリを取得し、キーに設定をする  
    public void getApps() {  
        PackageManager pm = getPackageManager();  
        List<ApplicationInfo> list = pm.getInstalledApplications(0);  
        String[] apps = new String [list.size() + 1] ;  
        int i = 0;  
        for (ApplicationInfo ai : list) {  
            apps[i++] = ai.packageName;  
        }  
        installPackages = apps;  
    }  
  
    // 通知があったアプリがどのアプリか検索する  
    public String findApps() {  
        PackageManager pm = getPackageManager();  
        HashMap<String, String> apps = datamap.ReadKeyValues(installPackages);  
        for (int i = 0;i < installPackages.length;i++) {  
            if (apps.get(installPackages[i]).equals("Notification")) {  
                return installPackages[i];  
            }  
        }  
        return "";  
    }  
}  

今回、ユーザー補助サービスのクラスを使用するということでAndroidManifest.xmlに

<service android:name="GetStatusBar" android:enabled="true" android:exported="false">  
    <intent-filter>  
        <action android:name="android.accessibilityservice.AccessibilityService" />  
    </intent-filter>  
</service>  

と、サービスを登録する必要があります。
特に、Android 4.1以降の場合は、

<service android:name="GetStatusBar" android:enabled="true" android:exported="false"  
         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">  

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"を追加しないと使用することができませんでした。

使用には、[設定]->[ユーザー補助]でサービスをONにしないと動かないので要注意です!

今回、ライブ壁紙に機能を持たせることができましたが、
パッケージ名だけ表示してはわかりにくかったり全体的に機能も見た目も乏しいので
次回もっと作り込んで行こうと思います!!