/* HostGeolocationProfile.java Copyright (c) 2017 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.deviceplugin.host.profile; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import org.deviceconnect.android.activity.PermissionUtility; import org.deviceconnect.android.deviceplugin.host.activity.GeolocationAlertDialogActivity; import org.deviceconnect.android.event.Event; import org.deviceconnect.android.event.EventError; import org.deviceconnect.android.event.EventManager; import org.deviceconnect.android.message.MessageUtils; import org.deviceconnect.android.profile.DConnectProfile; import org.deviceconnect.android.profile.GeolocationProfile; import org.deviceconnect.android.profile.api.DConnectApi; import org.deviceconnect.android.profile.api.DeleteApi; import org.deviceconnect.android.profile.api.GetApi; import org.deviceconnect.android.profile.api.PutApi; import org.deviceconnect.message.DConnectMessage; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; /** * Geolocation Profile. * @author NTT DOCOMO, INC. */ public class HostGeolocationProfile extends GeolocationProfile implements LocationListener { /** LocationManager. */ private LocationManager mLocationManager; /** ServiceID. */ private String mServiceId; /** 前回の位置情報の計測時間を保持する. */ private long mLocationLastTime; /** 前回の位置情報を保持する. */ private Bundle mLocationCache; /** * Constructor. */ public HostGeolocationProfile() { DConnectApi mGetOnGeolocationApi = new GetApi() { @Override public String getAttribute() { return ATTRIBUTE_CURRENT_POSITION; } @Override public boolean onRequest(final Intent request, final Intent response) { PermissionUtility.requestPermissions(getContext(), new Handler(Looper.getMainLooper()), new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, new PermissionUtility.PermissionRequestCallback() { @Override public void onSuccess() { long maximumAge = (long) getMaximumAge(request); if (System.currentTimeMillis() - mLocationLastTime < maximumAge) { DConnectProfile.setResult(response, DConnectMessage.RESULT_OK); response.putExtra(GeolocationProfile.PARAM_POSITION, mLocationCache); sendResponse(response); } else { getLocationManager(response); getGPS(getHighAccuracy(request), response); } } @Override public void onFail(@NonNull String deniedPermission) { MessageUtils.setIllegalDeviceStateError(response, "ACCESS_FINE_LOCATION permission not granted."); sendResponse(response); } }); return false; } }; addApi(mGetOnGeolocationApi); DConnectApi mPutOnGeolocationApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_WATCH_POSITION; } @Override public boolean onRequest(final Intent request, final Intent response) { PermissionUtility.requestPermissions(getContext(), new Handler(Looper.getMainLooper()), new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, new PermissionUtility.PermissionRequestCallback() { @Override public void onSuccess() { getLocationManager(response); String serviceId = getServiceID(request); // イベントの登録 EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { startGPS(getHighAccuracy(request), (int) getInterval(request)); mServiceId = serviceId; DConnectProfile.setResult(response, DConnectMessage.RESULT_OK); response.putExtra(DConnectMessage.EXTRA_VALUE, "Register OnWatchPosition event"); } else { MessageUtils.setUnknownError(response, "Can not register event."); } sendResponse(response); } @Override public void onFail(@NonNull String deniedPermission) { MessageUtils.setIllegalDeviceStateError(response, "ACCESS_FINE_LOCATION permission not granted."); sendResponse(response); } }); return false; } }; addApi(mPutOnGeolocationApi); DConnectApi mDeleteOnGeolocationApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_WATCH_POSITION; } @Override public boolean onRequest(final Intent request, final Intent response) { PermissionUtility.requestPermissions(getContext(), new Handler(Looper.getMainLooper()), new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, new PermissionUtility.PermissionRequestCallback() { @Override public void onSuccess() { getLocationManager(response); // イベントの解除 EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { stopGPS(); DConnectProfile.setResult(response, DConnectMessage.RESULT_OK); response.putExtra(DConnectMessage.EXTRA_VALUE, "Unregister OnWatchPosition event"); } else { MessageUtils.setUnknownError(response, "Can not unregister event."); } sendResponse(response); } @Override public void onFail(@NonNull String deniedPermission) { MessageUtils.setIllegalDeviceStateError(response, "ACCESS_FINE_LOCATION permission not granted."); sendResponse(response); } }); return false; } }; addApi(mDeleteOnGeolocationApi); } /** * 位置情報管理クラスを取得する. * @return 位置情報管理クラス */ private LocationManager getLocationManager(final Intent response) { if (mLocationManager == null) { mLocationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE); } // GPSセンサー利用可否判定. if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { Intent intent = new Intent(getContext(), GeolocationAlertDialogActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); Bundle bundle = new Bundle(); bundle.putParcelable("response", response); intent.putExtra("Intent", bundle); getContext().startActivity(intent); } return mLocationManager; } /** * 位置情報を取得する。(1回のみ). * @param accuracy 精度. * @param response レスポンス. */ private void getGPS(final boolean accuracy, final Intent response) { if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { MessageUtils.setIllegalDeviceStateError(response, "ACCESS_FINE_LOCATION permission not granted."); sendResponse(response); return; } Criteria criteria = new Criteria(); if (accuracy) { criteria.setAccuracy(Criteria.ACCURACY_FINE); } else { criteria.setAccuracy(Criteria.ACCURACY_COARSE); } mLocationManager.requestSingleUpdate(mLocationManager.getBestProvider(criteria, true), new LocationListener() { @Override public void onLocationChanged(Location location) { Bundle position = createPositionObject(location); DConnectProfile.setResult(response, DConnectMessage.RESULT_OK); response.putExtra(GeolocationProfile.PARAM_POSITION, position); sendResponse(response); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // NOP } @Override public void onProviderEnabled(String provider) { // NOP } @Override public void onProviderDisabled(String provider) { // NOP } }, Looper.getMainLooper()); } /** * 位置情報取得開始. * @param accuracy 精度. * @param interval 受信間隔. */ private void startGPS(final boolean accuracy, final int interval) { if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } Criteria criteria = new Criteria(); if (accuracy) { criteria.setAccuracy(Criteria.ACCURACY_FINE); } else { criteria.setAccuracy(Criteria.ACCURACY_COARSE); } mLocationManager.requestLocationUpdates(mLocationManager.getBestProvider(criteria, true), interval, 0, this, Looper.getMainLooper()); } /** * 位置情報取得停止. */ private void stopGPS() { if (mLocationManager != null) { if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } mLocationManager.removeUpdates(this); } } /** * イベント登録が空か確認する. * @return 空の場合はtrue、それ以外はfalse */ private boolean isEmptyEventList() { List<Event> events = EventManager.INSTANCE.getEventList(mServiceId, GeolocationProfile.PROFILE_NAME, null, GeolocationProfile.ATTRIBUTE_ON_WATCH_POSITION.toLowerCase()); return events == null || events.size() == 0; } /** * 座標情報生成. * @param location 位置情報 * @return 位置情報オブジェクト */ private Bundle createPositionObject(final Location location) { Bundle coordinates = new Bundle(); setLatitude(coordinates, location.getLatitude()); setLongitude(coordinates, location.getLongitude()); setAltitude(coordinates, location.getAltitude()); setAccuracy(coordinates, location.getAccuracy()); setHeading(coordinates, location.getBearing()); setSpeed(coordinates, location.getSpeed()); Bundle position = new Bundle(); setCoordinates(position, coordinates); setTimeStamp(position, location.getTime()); DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss.SSSZZZ", Locale.getDefault()); setTimeStampString(position, df.format(location.getTime())); mLocationCache = position; mLocationLastTime = location.getTime(); return position; } @Override public void onLocationChanged(Location location) { Bundle position = createPositionObject(location); if (isEmptyEventList()) { stopGPS(); return; } List<Event> events = EventManager.INSTANCE.getEventList(mServiceId, GeolocationProfile.PROFILE_NAME, null, GeolocationProfile.ATTRIBUTE_ON_WATCH_POSITION.toLowerCase()); for (int i = 0; i < events.size(); i++) { Event event = events.get(i); Intent intent = EventManager.createEventMessage(event); intent.putExtra(GeolocationProfile.PARAM_POSITION, position); sendEvent(intent, event.getAccessToken()); } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // NOP } @Override public void onProviderEnabled(String provider) { // NOP } @Override public void onProviderDisabled(String provider) { // NOP } }