タップしたらActivityが起動するwidgetを作成しました

全てのソースコードは下記より

https://github.com/noboru-i/ReadItNow/tree/tap-action-widget

最終的に、下記のように比較的すっきりしました。
プログラミング途中では、思ったように動かず、それを解決するためにもっとコード量が多かったのですが、
アンインストール→インストール→widgetの配置
を毎回するようにすると、うまいこと動きました。
変更し、上書きインストールなどを行うと、AppWidgetProvider#onUpdateが呼び出されたりして、
想定とは異なった動きになっていたようです。

実装

まず、AndroidManifest.xmlwidgetを定義します。

<receiver
    android:name=".appwidget.CountWidget"
    android:exported="false" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <!-- This specifies the widget provider info -->
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/widgetinfo" />
</receiver>
  • android.appwidget.action.APPWIDGET_UPDATE

ウィジェットの更新を取得するために必ず必要となります。

  • widgetinfo

xmlフォルダに定義します。

widgetinfo.xml

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_layout"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:resizeMode="vertical|horizontal"
    android:updatePeriodMillis="1800000"
    android:configure="hm.orz.chaos114.android.readitnow.ui.SettingActivity" />

widgetのレイアウトを指定します。

https://sites.google.com/a/techdoctranslator.com/jp/android/practices/ui_guidelines/widget_design
より、1セルは40dpのようなので、40dpを指定しています。

には、widget配置時に起動するActivityを指定しています。パッケージ名を含め、全てを記載します。

widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/widget_margin"
    android:background="@drawable/widget_background"
    android:orientation="vertical"
     ></LinearLayout>

https://sites.google.com/a/techdoctranslator.com/jp/android/practices/ui_guidelines/widget_design
より、8dpぐらいマージンを取ったほうがよさそうなので、"@dimen/widget_margin"を指定しています。
values/dimens.xml に定義しました。

CountWidget.java

package hm.orz.chaos114.android.readitnow.appwidget;

import hm.orz.chaos114.android.readitnow.R;
import hm.orz.chaos114.android.readitnow.ui.MainActivity;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;

public class CountWidget extends AppWidgetProvider {
	private static final String TAG = CountWidget.class.getSimpleName();

	public static final String EXTRA_APP_WIDGET_ID = "appWidgetId";

	@Override
	public void onEnabled(Context context) {
		Log.d(TAG, "#onEnabled");
	}

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		Log.d(TAG, "#onUpdate");
		Log.d(TAG, "appWidgetIds.length = " + appWidgetIds.length);
		Log.d(TAG, "appWidgetIds[0] = " + appWidgetIds[0]);

		for (int appWidgetId : appWidgetIds) {
			RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
					R.layout.widget_layout);
			Intent intent = new Intent(context, MainActivity.class);
			intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, 0);
			remoteViews.setOnClickPendingIntent(R.id.text_view, pendingIntent);
			appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
		}
	}

	@Override
	public void onDeleted(Context context, int[] appWidgetIds) {
		Log.d(TAG, "#onDeleted");
	}

	@Override
	public void onDisabled(Context context) {
		Log.d(TAG, "#onDisabled");
	}
}
  • #onUpdate

複数個のwidgetを配置し、再インストールした際など、
引数のappWidgetIdsには複数件入ってくるようなので、ループで処理しています。
PendingIntent を作成し、RemoteViews#setOnClickPendingIntent に渡します。
第2引数については、
http://y-anz-m.blogspot.jp/2011/07/androidappwidget-pendingintent-putextra.html
を参考にidを指定しました。

SettingActivity.java

package hm.orz.chaos114.android.readitnow.ui;

import hm.orz.chaos114.android.readitnow.R;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.util.Log;
import android.view.View;

public class SettingActivity extends PreferenceActivity {
	private static final String TAG = SettingActivity.class.getSimpleName();

	private int mAppWidgetId;

	@SuppressWarnings("deprecation")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.layout.query_preference);

		// Find the widget id from the intent.
		Intent intent = getIntent();
		Bundle extras = intent.getExtras();
		if (extras != null) {
			mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
					AppWidgetManager.INVALID_APPWIDGET_ID);
		}

		// If they gave us an intent without the widget id, just bail.
		if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
			finish();
		}

		ButtonPreference preference = (ButtonPreference) findPreference("complete_button");
		preference.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				finishConfigure();
			}
		});
	}

	private void finishConfigure() {
		Log.d(TAG, "#finishConfigure");

		// Make sure we pass back the original appWidgetId
		Intent resultValue = new Intent();
		resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
		setResult(RESULT_OK, resultValue);
		finish();
	}
}
  • #onCreate

起動時のIntentより、APPWIDGET_IDを取得しています。
複数のwidgetを配置した場合に、識別するためのキーとなります。

  • #finishConfigure

SettingActivityに配置したボタンを押下した際に呼ばれるよう、onCreate で指定しました。
http://y-anz-m.blogspot.jp/2011/06/androidappwidget.html
を参考に、widgetのidを入れ、RESULT_OKを指定しました。