/** HostNotificationProfile.java Copyright (c) 2014 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.deviceplugin.host.profile; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.List; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.deviceconnect.android.deviceplugin.host.HostDeviceService; import org.deviceconnect.android.deviceplugin.host.R; 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.NotificationProfile; import org.deviceconnect.android.profile.api.DConnectApi; import org.deviceconnect.android.profile.api.DeleteApi; import org.deviceconnect.android.profile.api.PostApi; import org.deviceconnect.android.profile.api.PutApi; import org.deviceconnect.message.DConnectMessage; import org.deviceconnect.message.intent.message.IntentDConnectMessage; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.drawable.Icon; import android.os.Build; import android.support.v4.app.NotificationCompat; import android.widget.Toast; /** * ホストデバイスプラグイン, Notification プロファイル. * * @author NTT DOCOMO, INC. */ public class HostNotificationProfile extends NotificationProfile { /** Random Seed. */ private static final int RANDOM_SEED = 1000000; /** * Notificationテータスレシーバー. */ private NotificationStatusReceiver mNotificationStatusReceiver; /** * Notification Flag. */ private static final String ACTON_NOTIFICATION = "org.deviceconnect.android.intent.action.notifiy"; /** ランダムシード. */ private final Random mRandom = new Random(); private final DConnectApi mPostNotifyApi = new PostApi() { @Override public String getAttribute() { return ATTRIBUTE_NOTIFY; } @Override public boolean onRequest(final Intent request, final Intent response) { if (mNotificationStatusReceiver == null) { mNotificationStatusReceiver = new NotificationStatusReceiver(); getContext().getApplicationContext().registerReceiver(mNotificationStatusReceiver, new IntentFilter(ACTON_NOTIFICATION)); } String serviceId = getServiceID(request); NotificationType type = getType(request); String body = getBody(request); int iconType = 0; String title = ""; if (type == NotificationType.PHONE) { iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.notification_00 : R.drawable.notification_00_post_lollipop; title = "PHONE"; } else if (type == NotificationType.MAIL) { iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.notification_01 : R.drawable.notification_01_post_lollipop; title = "MAIL"; } else if (type == NotificationType.SMS) { iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.notification_02 : R.drawable.notification_02_post_lollipop; title = "SMS"; } else if (type == NotificationType.EVENT) { iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.notification_03 : R.drawable.notification_03_post_lollipop; title = "EVENT"; } else { MessageUtils.setInvalidRequestParameterError(response, "type is invalid."); return true; } String encodeBody = ""; try { if (body != null) { encodeBody = URLDecoder.decode(body, "UTF-8"); } } catch (UnsupportedEncodingException e) { MessageUtils.setInvalidRequestParameterError(response, "body is invalid."); return true; } int notifyId = mRandom.nextInt(RANDOM_SEED); if (Build.MODEL.endsWith("M100")) { Toast.makeText(getContext(), encodeBody, Toast.LENGTH_SHORT).show(); response.putExtra(NotificationProfile.PARAM_NOTIFICATION_ID, notifyId); setResult(response, IntentDConnectMessage.RESULT_OK); } else { // Build intent for notification content Intent notifyIntent = new Intent(ACTON_NOTIFICATION); notifyIntent.putExtra("notificationId", notifyId); notifyIntent.putExtra("serviceId", serviceId); PendingIntent mPendingIntent = PendingIntent.getBroadcast(getContext(), notifyId, notifyIntent, Intent.FLAG_ACTIVITY_NEW_TASK); Notification notification; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getContext()) .setSmallIcon(iconType) .setContentTitle("" + title) .setContentText(encodeBody) .setContentIntent(mPendingIntent); notification = notificationBuilder.build(); } else { Notification.Builder notificationBuilder = new Notification.Builder(getContext()) .setSmallIcon(Icon.createWithResource(getContext(), iconType)) .setContentTitle("" + title) .setContentText(encodeBody) .setContentIntent(mPendingIntent); notification = notificationBuilder.build(); } // Get an instance of the NotificationManager service NotificationManager mNotification = (NotificationManager) getContext() .getSystemService(Context.NOTIFICATION_SERVICE); // Build the notification and issues it with notification // manager. mNotification.notify(notifyId, notification); response.putExtra(NotificationProfile.PARAM_NOTIFICATION_ID, notifyId); setResult(response, IntentDConnectMessage.RESULT_OK); } List<Event> events = EventManager.INSTANCE.getEventList( serviceId, HostNotificationProfile.PROFILE_NAME, null, HostNotificationProfile.ATTRIBUTE_ON_SHOW); HostDeviceService service = (HostDeviceService) getContext(); synchronized (events) { for (Event event : events) { Intent intent = EventManager.createEventMessage(event); setNotificationId(intent, String.valueOf(notifyId)); service.sendEvent(intent, event.getAccessToken()); } } return true; } }; private final DConnectApi mDeleteNotifyApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_NOTIFY; } @Override public boolean onRequest(final Intent request, final Intent response) { String serviceId = getServiceID(request); String notificationId = getNotificationId(request); int notifyId = 0; try { notifyId = Integer.parseInt(notificationId); } catch (NumberFormatException e) { MessageUtils.setInvalidRequestParameterError(response, "notificationId is invalid."); return true; } NotificationManager mNotificationManager = (NotificationManager) getContext().getSystemService( Context.NOTIFICATION_SERVICE); mNotificationManager.cancel(notifyId); setResult(response, IntentDConnectMessage.RESULT_OK); List<Event> events = EventManager.INSTANCE.getEventList( serviceId, HostNotificationProfile.PROFILE_NAME, null, HostNotificationProfile.ATTRIBUTE_ON_CLOSE); HostDeviceService service = (HostDeviceService) getContext(); synchronized (events) { for (Event event : events) { Intent intent = EventManager.createEventMessage(event); intent.putExtra(HostNotificationProfile.PARAM_NOTIFICATION_ID, notificationId); service.sendEvent(intent, event.getAccessToken()); } } return true; } }; private final DConnectApi mPutOnClickApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_CLICK; } @Override public boolean onRequest(final Intent request, final Intent response) { mNotificationStatusReceiver = new NotificationStatusReceiver(); IntentFilter intentFilter = new IntentFilter(ACTON_NOTIFICATION); getContext().registerReceiver(mNotificationStatusReceiver, intentFilter); // イベントの登録 EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; private final DConnectApi mPutOnCloseApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_CLOSE; } @Override public boolean onRequest(final Intent request, final Intent response) { // イベントの登録 EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; private final DConnectApi mPutOnShowApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_SHOW; } @Override public boolean onRequest(final Intent request, final Intent response) { // イベントの登録 EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; private final DConnectApi mDeleteOnClickApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_CLICK; } @Override public boolean onRequest(final Intent request, final Intent response) { // イベントの解除 EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { switch (error) { case FAILED: MessageUtils.setUnknownError(response, "Do not unregister event."); break; case INVALID_PARAMETER: MessageUtils.setInvalidRequestParameterError(response); break; case NOT_FOUND: MessageUtils.setUnknownError(response, "Event not found."); break; default: MessageUtils.setUnknownError(response); break; } setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; private final DConnectApi mDeleteOnCloseApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_CLOSE; } @Override public boolean onRequest(final Intent request, final Intent response) { // イベントの解除 EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { switch (error) { case FAILED: MessageUtils.setUnknownError(response, "Do not unregister event."); break; case INVALID_PARAMETER: MessageUtils.setInvalidRequestParameterError(response); break; case NOT_FOUND: MessageUtils.setUnknownError(response, "Event not found."); break; default: MessageUtils.setUnknownError(response); break; } setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; private final DConnectApi mDeleteOnShowApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_SHOW; } @Override public boolean onRequest(final Intent request, final Intent response) { // イベントの解除 EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else { switch (error) { case FAILED: MessageUtils.setUnknownError(response, "Do not unregister event."); break; case INVALID_PARAMETER: MessageUtils.setInvalidRequestParameterError(response); break; case NOT_FOUND: MessageUtils.setUnknownError(response, "Event not found."); break; default: MessageUtils.setUnknownError(response); break; } setResult(response, DConnectMessage.RESULT_ERROR); } return true; } }; public HostNotificationProfile() { addApi(mPostNotifyApi); addApi(mDeleteNotifyApi); addApi(mPutOnClickApi); addApi(mPutOnCloseApi); addApi(mPutOnShowApi); addApi(mDeleteOnClickApi); addApi(mDeleteOnCloseApi); addApi(mDeleteOnShowApi); } /** * ノーティフィケーション. */ private class NotificationStatusReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent intent) { // クリティカルセクション(バッテリーステータスの状態更新etc)にmutexロックを掛ける。 synchronized (this) { int notificationId = intent.getIntExtra("notificationId", -1); String mServiceId = intent.getStringExtra("serviceId"); List<Event> events = EventManager.INSTANCE.getEventList( mServiceId, HostNotificationProfile.PROFILE_NAME, null, HostNotificationProfile.ATTRIBUTE_ON_CLICK); for (int i = 0; i < events.size(); i++) { Event event = events.get(i); Intent evtIntent = EventManager.createEventMessage(event); evtIntent.putExtra(HostNotificationProfile.PARAM_NOTIFICATION_ID, "" + notificationId); sendEvent(evtIntent, event.getAccessToken()); } // 状態更新の為のmutexロックを外して、待っていた処理に通知する。 notifyAll(); } } } }