たぼさんの部屋

いちょぼとのんびり

T314 AppWidget 位置情報更新

手順

AppWidgetでサービスをstartService()
サービスは別プロセス(Manifestで指定 process:another)
サービスのonStartCommendでロケーションマネージャを起動
requestUpdate()を実装
ロケーションマネージャのonLocationChange()が発生したとき(位置変更時)にはサービスへインテントを送信(引数つき)する
送信されたインテントはサービスのonStartCommand()で受信できる
受信したデータはRemoteViewsを使ってウィジェトの画面を変更し、更新する
ウィジェットの削除時(onDisabled)で、新しくインテントを送信してstopService()を実行
サービスのonDestroy()でロケーションマネージャのremoveUpdates()を実行して停止

実行結果

1)起動時にはLocationManagerが開始しているので、位置情報を更新するとウィジェットの情報も表示される
f:id:donsuka_kk:20121218022333p:plain
2)もう一度、変更したら更新された。(この時に、requestCodeを変更しておくこと!)
f:id:donsuka_kk:20121218022425p:plain
3)停止すると、情報は更新されない(位置情報を受け取らない)oK
4)再度、開始すると、停止時に変更した位置が受け取られる
f:id:donsuka_kk:20121218022526p:plain
5)widgerのonDisableでstopServiceしているので削除時にはロケーションサービスは停止。
また、エラーも出ない。
f:id:donsuka_kk:20121218022616p:plain

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>

xml/mwidget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/mwidget"
    android:minHeight="72dp"
    android:minWidth="294dp"
    android:updatePeriodMillis="0" >

</appwidget-provider>