T314 AppWidget 位置情報更新
手順
AppWidgetでサービスをstartService()
サービスは別プロセス(Manifestで指定 process:another)
サービスのonStartCommendでロケーションマネージャを起動
requestUpdate()を実装
ロケーションマネージャのonLocationChange()が発生したとき(位置変更時)にはサービスへインテントを送信(引数つき)する
送信されたインテントはサービスのonStartCommand()で受信できる
受信したデータはRemoteViewsを使ってウィジェトの画面を変更し、更新する
ウィジェットの削除時(onDisabled)で、新しくインテントを送信してstopService()を実行
サービスのonDestroy()でロケーションマネージャのremoveUpdates()を実行して停止
実行結果
1)起動時にはLocationManagerが開始しているので、位置情報を更新するとウィジェットの情報も表示される
2)もう一度、変更したら更新された。(この時に、requestCodeを変更しておくこと!)
3)停止すると、情報は更新されない(位置情報を受け取らない)oK
4)再度、開始すると、停止時に変更した位置が受け取られる
5)widgerのonDisableでstopServiceしているので削除時にはロケーションサービスは停止。
また、エラーも出ない。
Point
widgetのonDisableでサービスを停止するとき新しくintent生成すること
@Override public void onDisabled(Context context) { super.onDisabled(context); Log.v(TAG,"onDisabled"); /* * サービスの停止処理を実装 */ Intent intent = new Intent(context , MService.class); context.stopService(intent); }
サービスにintent.putExtraで値を送信するときに
requestCodeを更新すること!
//XXX 第2引数のrequestコードを更新すること!(更新しないと、値が変わらない) PendingIntent pending = PendingIntent.getService(context, requestCode++, intent, 0);//getService
MWidget.java
package com.efolab.t314; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; import android.util.Log; public class MWidget extends AppWidgetProvider { private final String TAG ="MWidget:"; Intent intent; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.v(TAG,"onUpdate"); super.onUpdate(context, appWidgetManager, appWidgetIds); intent = new Intent(context , MService.class); intent.setAction("INIT"); //サービスクラスで初期処理を実行させるフラグをセット context.startService(intent); } @Override public void onDeleted(Context context, int[] appWidgetIds) { // TODO Auto-generated method stub super.onDeleted(context, appWidgetIds); Log.v(TAG,"onDeleted"); } @Override public void onDisabled(Context context) { super.onDisabled(context); Log.v(TAG,"onDisabled"); /* * サービスの停止処理を実装 */ Intent intent = new Intent(context , MService.class); context.stopService(intent); } }
MService.java
package com.efolab.t314; import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.util.Log; import android.widget.RemoteViews; public class MService extends Service { private final static String TAG ="MService:"; public static final String ACTION_LOCATION_START = TAG+0, ACTION_LOCATION_STOP = TAG+1, ACTION_LOCATION_UPDATE = TAG+2; private RemoteViews remoteviews; private Context context; //LocationManager MLocationManager mLocationManager; @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub Log.v(TAG,"onBind"); return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { //初期にだけ、INIT処理を実行する if(intent.getAction().equals("INIT")){ init(); /* * LocationManager start */ mLocationManager = new MLocationManager(context); mLocationManager.start(); } Log.v(TAG,"onStartCommand intent.getAction="+intent.getAction()); /* * イベント受信処理をここに記述する */ if(intent.getAction().equals(ACTION_LOCATION_START)){ mLocationManager.start(); remoteviews.setTextViewText(R.id.textView2, "開始しました"); } if(intent.getAction().equals(ACTION_LOCATION_STOP)){ mLocationManager.stop(); remoteviews.setTextViewText(R.id.textView2, "停止しました"); } if(intent.getAction().equals(ACTION_LOCATION_UPDATE)){ int x = intent.getIntExtra("intX", 0); int y = intent.getIntExtra("intY", 0); remoteviews.setTextViewText(R.id.textView3, "位置更新 ,X:"+x+"Y:"+y); } //ウィジェット画面の更新処理 : 必須 AppWidgetManager manager = AppWidgetManager.getInstance(context); ComponentName widget = new ComponentName(context, MWidget.class); manager.updateAppWidget(widget, remoteviews); return super.onStartCommand(intent, flags, startId); } /* * 初期化処理 * 一度だけ実行すればいい処理を記述しておく。 */ private void init(){ this.context = getApplicationContext(); remoteviews = new RemoteViews(getPackageName(),R.layout.mwidget); /* * 暗黙的インテント * manifest.xmlへのfilter/action追記必要 */ Intent intentBtn1 = new Intent(context , this.getClass()); //明示的にこのクラスへインテントする intentBtn1.setAction(ACTION_LOCATION_START); PendingIntent pendingBtn1 = PendingIntent.getService(context, 0, intentBtn1, 0);//getService remoteviews.setOnClickPendingIntent(R.id.button1, pendingBtn1); /* * 明示的インテント * マニフェストへの記述不要 */ Intent intentBtn2 = new Intent(context , this.getClass()); //明示的インテント intentBtn2.setAction(ACTION_LOCATION_STOP); PendingIntent pendingBtn2 = PendingIntent.getService(context, 0, intentBtn2, 0); remoteviews.setOnClickPendingIntent(R.id.button2, pendingBtn2); } @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); Log.v(TAG,"onCreate"); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.v(TAG,"onDestroy"); /* * LocationManagerを停止 */ mLocationManager.stop(); } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub Log.v(TAG,"onUnbind"); return super.onUnbind(intent); } public static boolean isServiceRunning(Context context , Class<?> mClass){ ActivityManager activityManager = (ActivityManager)context.getSystemService(Activity.ACTIVITY_SERVICE); //getRunningServices List<ActivityManager.RunningServiceInfo> serviceInfos = activityManager.getRunningServices(Integer.MAX_VALUE); for(int i=0;i<serviceInfos.size();i++){ Log.v("isRunning",serviceInfos.get(i).service.getClassName()); if(serviceInfos.get(i).service.getClassName().equals(mClass.getName())){ return true; } } return false; } }
MLocationManager.java
package com.efolab.t314; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.content.Intent; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.widget.Toast; /** * <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> * <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> *<e8><bf><bd>記 * @author kamogashiratsuyoshi * */ public class MLocationManager implements LocationListener{ LocationManager lm; Context context; private int requestCode = 0; public MLocationManager(Context context){ this.context = context; lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); } public void start(){ lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10, 10, this); } public void stop(){ lm.removeUpdates(this); } @Override public void onLocationChanged(Location location) { int intX = (int)(location.getLatitude() * 1e6); int intY = (int)(location.getLongitude() * 1e6); Toast.makeText(context, "onLocationChanged:lat="+intX+",lng="+intY, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(context , MService.class); //明示的にMSerivceクラスへインテントする //引数 intent.putExtra("intX", intX); intent.putExtra("intY", intY); intent.setAction(MService.ACTION_LOCATION_UPDATE); //XXX 第2引数のrequestコードを更新すること!(更新しないと、値が変わらない) PendingIntent pending = PendingIntent.getService(context, requestCode++, intent, 0);//getService try { pending.send(); } catch (CanceledException e) { e.printStackTrace(); } } @Override public void onProviderDisabled(String arg0) { // TODO Auto-generated method stub } @Override public void onProviderEnabled(String arg0) { // TODO Auto-generated method stub } @Override public void onStatusChanged(String arg0, int arg1, Bundle arg2) { // TODO Auto-generated method stub } }
Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.efolab.t314" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name="com.efolab.t314.MWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/mwidget_info"/> </receiver> <service android:name="com.efolab.t314.MService" android:process=":another"></service> </application> </manifest>
/style/mwidget.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="T314" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止" /> </LinearLayout> </LinearLayout>