さらなる問題点!

さらに、普段のActivity等では余り関係してこない、AppWidgetProvider特有の問題があります。通常のActivity等では複数起動はほとんどありえないため、同一クラスが複数存在することは、特殊な設定を行わない限りありえません。

しかし、AppWidgetProviderは複数Home画面に張り付けられる可能性があります。
このため、自分自身のウィジェットIDをきちんと得て、どのウィジェットからの呼び出しかをきちんと理解しておく必要があるのです。

これをやらない場合、悲しいことに画面上の全てのミクが同じタイミングで瞬きしたり、口パクしたりとちょっと悲しいことになります。そんな、試験管の中の綾波みたいなのは怖いだけです。

というわけで、xFuturesさまよりソースをパチらせていただきまして

public static final Uri CONTENT_URI = Uri.parse("content://[自分とこのコンテンツIDを記載]");
// AppWidget IDを設定する
Uri uri = ContentUris.withAppendedId(CONTENT_URI, id);

な感じでuriを作成し、これをIntentへとExtendとしてくっつけて値をとれるようにしました。

補足しておきますね。

Intentを送り出す仕組み受信側によって、メソッドを変更する必要があります。
たとえばActivity以外との通信の場合、startActivityではだめで、startBroadcastを使う必要があります。今回のようにAppWidgetProviderとの通信の場合は当然startBroadcastが必要です。
ここまではIntent系のページをよく読むと書いてある場所もあります。

で、予想通りと申しますか、この事実は実はPendingIntentも同じです。
PendingIntentについてもgetActivityではダメでgetBroadcastを使用する必要があります。これにより、受信側がきちっと受け取れるようになります。

この手の汎用Intentとかの処理って、もう少し整理した表とかを作ってきちんと動作を理解しないと詰まるなあと言うのが実感。
いっそ、専用Intentの確認からしておけばよかったかなーという感じです。

ここまで、上手く辿りついたみたいですが・・・

実際には細かくあちこちで躓いています。

サンプル量の問題などから汎用のactionでテストしようとandroid.intent.action.VIEWとかを定義し、動作を確認していたんですが、ACTION_APPWIDGET_ENABLED / ACTION_APPWIDGET_UPDATE / ACTION_APPWIDGET_DISABLED / ACTION_APPWIDGET_DELETEDは取れるのに、android.intent.action.VIEWがきちんと取れない。
でも、別Activityを受信者とすると特に問題なく通信できる。
なんでだーーーーーとなったわけです。

最初、intent-filterの問題と勘違いして、設定条件を色々試した揚句に、最終的に送信、受信に関するAndroid側のソースまで軽く見てみてやっと、メソッド違いじゃねと理解する始末。

あいまいな資料をつぎはぎでコード書く⇒書いてある仕様通りに動かない⇒Androidのソース追いかける⇒おかしい、メソッドあるんじゃね⇒リファレンス読み直す⇒やっぱあるじゃねーか、なんて言う、
「最初に読んどけよ」パターンありすぎです。

androidではIntentを受信するにはIntent-filter定義が必要です。

さらに、無事にIntentを発信出来たとしても、受け側がきちんと出来上がっていないと、受け取る
相手のいないラブレターを永遠に書き続ける人になってしまいます。
うちのミクがそんなことになったらちょっとさみしいので、きちんとミク自身の中に受信先を定義
しておきます。
(自分から自分へのラブレターになりますが、まあ、内部でアラームを設定しているので、目覚まし
使って自分を起こしてるみたいなものですね)

この受信用メソッドは先ほどのサマリでも触れましたが、onReceiveでOK。
この定義は主要な場合、AndroidManifest.xml内で行います。
事前にintent-filterタグを入れ、受け取りたいaction等を定義します。
(もちろん、やろうと思えば、コード上でのIntent-Filterの事前定義も可能です。)

記入サンプル


※value 値は適当ですので参考にしないように

明示的InterntをPendingIntentを経由して使う

今回のようなケースでは、受信側のクラスが指定されるので、明示的Intentで書く必要があります。
でも、それだけではなくて、生成したIntentをそのままアラームやボタンに定義はできません。
アラームやボタンにインテントを仕掛けるには、PendingIntentへ組み込んで、後で打ち出し処理が
出来るようにしてやる必要があります。
花火に例えるとIntent打ち上げ用の打ち上げ筒=PendingIntent、アラーム=発火装置と考えれば
いいでしょうか。

下記の例ではアラームにセットするためにPendingIntentを使用していますが、AppWidgetProviderの
上に定義したボタン等からIntentを飛ばす際にもこのPendigIntentを使用する必要があります。

実際の使い方は

Uri uri = Uri.parse(“http://google.com”);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
PendingIntent sender = PendingIntent.getBroadcast(ctx, 0, intent, 0);
AlarmManager am = (AlarmManager)(ctx.getSystemService(ALARM_SERVICE));
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(currentTime);
cal.set(Calendar.MINUTE, 5);
am.set(AlarmManager.RTC_WAKEUP,cal.aTimerInMillis,sender);

こんな感じです。ちょっと嘘入りかも。
※やっぱり嘘入りでした。_wakeupが付いていると、消費電力対策になりません。イベント発生時に強制的に起動してしまいます。
違っていたらミクのソース見て後で直します。

とはいっても・・・。

まあ、ここではコンストラクタで区別できる書き方で書きましたが実際には

Intent intent = new Intent();
intent.setClass(this, DataListactivity.class)
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);

なんて形で中身空で生成したIntentにClassとコンテキスト指定してやれば明示的Intentが
書けるので、コンストラクタだけでなくてきちんとソース見ないとダメですがね。