Category: UI

ライブ壁紙設定画面のPreferenceScreen階層が黒画面になる

PreferenceActivity#addPreferencesFromResourceからxmlで設定項目を作成した際に、<PreferenceScreen>を入れ子にすると子画面に移動した時に画面が真っ黒になる原因と対処。

▼原因
Android 2.xのバグと、ライブ壁紙向けの設定画面テーマの組み合わせが原因。(2.1で確認)

・Android 2.x系列のバグ: xmlリソースに指定した入れ子のPreferenceScreen子画面の背景が必ず黒(デフォルト)になる問題
http://stackoverflow.com/questions/2615528/preferenceactivity-and-theme-not-applying
http://code.google.com/p/android/issues/detail?id=4611#c12

・ライブ壁紙設定用PreferenceActivity向けテーマ: 半透明の白背景+黒文字

<!-- SDKのライブ壁紙サンプルWall内AndroidManifest.xmlから抜粋 -->
<activity android:label="@string/cube2_settings"
    android:name=".cube2.CubeWallpaper2Settings"
    android:theme="@android:style/Theme.Light.WallpaperSettings"
    android:exported="true">
</activity>

▼対処
・AndroidManifest.xmlの該当する<activity>にandroid:themeを指定しない(=デフォルトの黒背景テーマ)もしくは黒背景のテーマを指定する
・画面毎に個別のPreferenceActivityを作成して親画面から呼ぶようにする ()

コード管理の容易さから前者を推奨。

Android 2.1/3.2 ステータスバー通知の取得

NotificatioinManagerからは現在の通知を取得できないが、AccessibilityServiceを使用すれば他のアプリケーションから通知されるNotifycationをイベントとして取得できる。

▼ステータスバー通知 Notification / NotificationManager の特徴
・NotificationMaganerにNotificationを追加するとステータスバーに通知される
・NotificationにはViewを指定できる
・NofiticationManagerでは追加/上書/削除のみを行える
他アプリの通知を取得できないように制限されている。

▼ユーザ補助サービス AccessibilityServiceの特徴
・他アプリでの操作の一部や通知内容の一部を取得できる
・テキスト入力内容まで取得できてしまうため安易に利用できない
・通常のアプリケーションとは別に設定→ユーザ補助からサービスを許可/実行する必要がある

▼AccessibilityServiceのサンプルコード
・必要最低限のサンプルコード (理解には最良のサンプル)
本家和訳サイト
・Android 3.2で実装したところ、Toastも Notificationとして受け取って自己ループに陥ったので修正

▼AccessibilityServiceからの受け渡し
・SharedPreferencesを介してStringで受け渡す例 (抜粋)
サービスとの通信が不要で、ユーザ補助のON/OFF切り替えを認識せずに受信可能。

public class StrAccessibilityService extends AccessibilityService {

	public static final String SHARED_ACCESS_PREFS="str_access";
	public static final String ACCESS_PREFS_KEY="access_key";

	private SharedPreferences mPrefAccess;
	public static final boolean DEBUG=true;	// 通知のToast表示フラグ

	@Override
	public void onCreate() {
		super.onCreate();
	}

	@Override
	public void onServiceConnected() {
		mPrefAccess = getSharedPreferences(SHARED_ACCESS_PREFS, MODE_PRIVATE);

		AccessibilityServiceInfo info = new AccessibilityServiceInfo();
		info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
		info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
		info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS;
		info.flags = AccessibilityServiceInfo.DEFAULT;
		setServiceInfo(info);
	}

	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		int eventType = event.getEventType();
		switch (eventType) {
		case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED :
			if(DEBUG) Log.d("StrAccessibilityService", "Access NOTIFIFY:"+event.getText());
			putPreferences(event);
			break;
		}
	}

	private void putPreferences(AccessibilityEvent event){// Preferenceを変更
		SharedPreferences.Editor editor = mPrefAccess.edit();
		editor.putString(ACCESS_PREFS_KEY, ""+event.getText());
		editor.commit();
	}

}

取得側クラスは SharedPreferences.OnSharedPreferenceChangeListener を implement し、 onSharedPreferenceChanged で取得する。

	public static final String SHARED_ACCESS_PREFS="str_access";
	public static final String ACCESS_PREFS_KEY="access_key";
	private SharedPreferences mPrefAccess;

	@Override
	public void onCreate() {
		super.onCreate();
		mPrefAccess = getSharedPreferences(SHARED_ACCESS_PREFS, MODE_PRIVATE);
		mPrefAccess.registerOnSharedPreferenceChangeListener(this);
	}

	@Override	// StrAccessibilityService#putPreferencesによりコールされる
	public void onSharedPreferenceChanged(SharedPreferences prefs,String key) {
		String access_text = prefs.getString(ACCESS_PREFS_KEY,null);
		// (ここから処理)
	}