com.magnet.mmx.client.MMXWakeupIntentService.java Source code

Java tutorial

Introduction

Here is the source code for com.magnet.mmx.client.MMXWakeupIntentService.java

Source

/*   Copyright (c) 2015 Magnet Systems, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.magnet.mmx.client;

import com.magnet.mmx.client.common.Log;
import com.magnet.mmx.protocol.Constants;
import com.magnet.mmx.protocol.Constants.PingPongCommand;
import com.magnet.mmx.protocol.GCMPayload;
import com.magnet.mmx.protocol.MMXTypeMapper;
import com.magnet.mmx.protocol.PushMessage;
import com.magnet.mmx.util.UnknownTypeException;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;

import org.json.JSONObject;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

/**
 * The service that handles ALL wakeups for the MMXClient (GCM and timer-based)
 */
public final class MMXWakeupIntentService extends IntentService {
    private static final String TAG = MMXWakeupIntentService.class.getSimpleName();
    private static int sNoteId = 10;

    public static final String HEADER_WAKEUP = Constants.MMX + ":" + Constants.MMX_ACTION_CODE_WAKEUP + ":"
            + Constants.PingPongCommand.retrieve.name();

    public static final String HEADER_PING = Constants.MMX + ":" + Constants.MMX_ACTION_CODE_WAKEUP + ":"
            + Constants.PingPongCommand.ping.name();

    public static final String HEADER_PUSH = Constants.MMX + ":" + Constants.MMX_ACTION_CODE_PUSH + ":"
            + PingPongCommand.notify.name();

    public static final String HEADER_PUBSUB = Constants.MMX + ":" + Constants.MMX_ACTION_CODE_WAKEUP + ":"
            + PingPongCommand.pubsub.name();

    public MMXWakeupIntentService() {
        super("MMXGcmIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //verify the message is a wakeup message
        String action = intent.getAction();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onHandleIntent(): action=" + action);
        }
        if ("com.google.android.c2dm.intent.RECEIVE".equals(action)) {
            GooglePlayServicesWrapper playServicesWrapper = GooglePlayServicesWrapper.getInstance(this);
            int messageType = playServicesWrapper.getGcmMessageType(intent);
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onHandleIntent(): Received message type: " + messageType);
            }
            switch (messageType) {
            case GooglePlayServicesWrapper.GCM_MESSAGE_TYPE_SEND_ERROR:
            case GooglePlayServicesWrapper.GCM_MESSAGE_TYPE_DELETED:
            case GooglePlayServicesWrapper.GCM_MESSAGE_TYPE_SEND_EVENT:
                Log.w(TAG, "onHandleIntent(): Message type is not handled: " + messageType);
                break;
            case GooglePlayServicesWrapper.GCM_MESSAGE_TYPE_MESSAGE:
                //parse the GCM message
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "onHandleIntent(): Handling GCM message.  extras: " + intent.getExtras());
                }
                Bundle extras = intent.getExtras();
                String msg = extras.getString("msg");

                boolean isMmxHandle = false;
                if (msg != null) {
                    try {
                        PushMessage pushMessage = PushMessage.decode(msg, MMXTypeMapper.getInstance());
                        isMmxHandle = handleMMXInternalPush(pushMessage);
                        Log.d(TAG, "isMmxHandle=" + isMmxHandle + ", pushMsg=" + pushMessage);
                    } catch (UnknownTypeException ex) {
                        //This is not an internal MMX message type
                        Log.i(TAG, "onHandleIntent() forwarding intent to application");
                    } catch (Throwable e) {
                        Log.e(TAG, "onHandleIntent() generic exception caught while parsing GCM payload.", e);
                    }
                }
                if (!isMmxHandle) {
                    MMXClient.handleWakeup(this, intent);
                }
                break;
            }
            MMXGcmBroadcastReceiver.completeWakefulIntent(intent);
        } else {
            if (Intent.ACTION_BOOT_COMPLETED.equals(action) || MMXClient.ACTION_WAKEUP.equals(action)) {
                MMXClient.handleWakeup(this, intent);
            } else {
                //log and do nothing
                Log.w(TAG, "onHandleIntent(): Unsupported action: " + action);
            }
            MMXWakeupReceiver.completeWakefulIntent(intent);
        }
    }

    private void invokeNotificationForPush(GCMPayload payload) {
        // Launch the activity with action=MAIN, category=DEFAULT.  Make sure that
        // it has DEFAULT category declared in AndroidManifest.xml intent-filter.
        PendingIntent intent = PendingIntent.getActivity(this.getApplicationContext(), 0,
                new Intent(Intent.ACTION_MAIN).setPackage(this.getPackageName())
                        .addCategory(Intent.CATEGORY_DEFAULT) // it is redundant
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                        .addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED),
                PendingIntent.FLAG_UPDATE_CURRENT);
        // If title is not specified, use the app name (compatible with iOS push notification)
        String title = (payload.getTitle() == null) ? this.getApplicationInfo().name : payload.getTitle();
        Notification.Builder noteBuilder = new Notification.Builder(this).setContentIntent(intent)
                .setAutoCancel(true).setWhen(System.currentTimeMillis()).setContentTitle(title)
                .setContentText(payload.getBody());
        if (payload.getSound() != null) {
            // TODO: cannot handle custom sound yet; use notification ring tone.
            noteBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
        }
        if (payload.getIcon() != null) {
            noteBuilder.setSmallIcon(
                    this.getResources().getIdentifier(payload.getIcon(), "drawable", this.getPackageName()));
        } else {
            noteBuilder.setSmallIcon(this.getApplicationInfo().icon);
        }
        if (payload.getBadge() != null) {
            noteBuilder.setNumber(payload.getBadge());
        }
        NotificationManager noteMgr = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        noteMgr.notify(sNoteId++, noteBuilder.build());
    }

    private void invokeWakeupHandlerForPush(GCMPayload payload) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onHandleIntent(): Firing push intent");
        }
        Intent pushIntent = buildPushIntent(payload);
        MMXClient.handleWakeup(this, pushIntent);
    }

    static Intent buildPushIntent(GCMPayload payload) {
        // create an Intent with Push data formatted as Strings in intent extras
        Intent pushIntent = new Intent(MMXClient.ACTION_PUSH_RECEIVED);
        if (!TextUtils.isEmpty(payload.getTitle())) {
            pushIntent.putExtra(MMXClient.EXTRA_PUSH_TITLE, payload.getTitle());
        }
        if (!TextUtils.isEmpty(payload.getBody())) {
            pushIntent.putExtra(MMXClient.EXTRA_PUSH_BODY, payload.getBody());
        }
        if (!TextUtils.isEmpty(payload.getSound())) {
            pushIntent.putExtra(MMXClient.EXTRA_PUSH_SOUND, payload.getSound());
        }
        if (!TextUtils.isEmpty(payload.getIcon())) {
            pushIntent.putExtra(MMXClient.EXTRA_PUSH_ICON, payload.getIcon());
        }
        if (payload.getBadge() != null) {
            pushIntent.putExtra(MMXClient.EXTRA_PUSH_BADGE, payload.getBadge());

        }
        // extract notification ID from _mmx
        Map<String, ? super Object> mmx = payload.getMmx();

        if (mmx != null) {
            if (!TextUtils.isEmpty((String) mmx.get(Constants.PAYLOAD_ID_KEY))) {
                pushIntent.putExtra(MMXClient.EXTRA_PUSH_ID, (String) mmx.get(Constants.PAYLOAD_ID_KEY));
            }
            // get custom dictionary but pass it as a String in json
            Map<String, ? super Object> customEntries = (Map<String, ? super Object>) mmx
                    .get(Constants.PAYLOAD_CUSTOM_KEY);
            if (customEntries != null) {
                JSONObject jsonObject = new JSONObject(customEntries);
                pushIntent.putExtra(MMXClient.EXTRA_PUSH_CUSTOM_JSON, jsonObject.toString());
            }
        }
        return pushIntent;
    }

    private void invokeCallback(String urlString) {
        //POST to the callback url
        HttpURLConnection conn = null;
        try {
            URL url = new URL(urlString);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            int responseCode = conn.getResponseCode();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "handleMMXInternalPush(): received response from callback url: " + responseCode);
            }
        } catch (MalformedURLException e) {
            Log.e(TAG, "handleMMXInternalPush(): unable to parse ping callback url: " + urlString);
        } catch (IOException e) {
            Log.e(TAG, "handleMMXInternalPush(): unable to connect to callback url: " + urlString);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return;
    }

    private boolean handleMMXInternalPush(PushMessage pushMessage) {
        PushMessage.Action pushType = pushMessage.getAction();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "handleMMXInternalPush(): received action: " + pushType);
        }
        Object payload = pushMessage.getPayload();
        if (payload == null || !(payload instanceof GCMPayload)) {
            return false;
        }
        GCMPayload gcmPayload = (GCMPayload) payload;
        boolean handled = false;
        String typeName = pushMessage.getType();
        String urlString = (String) gcmPayload.getMmx().get(Constants.PAYLOAD_CALLBACK_URL_KEY);
        if (PushMessage.Action.WAKEUP == pushType) { // "mmx:w:*"
            if (Constants.PingPongCommand.retrieve.name().equalsIgnoreCase(typeName)) { // "mmx:w:retrieve"
                MMXClient.handleWakeup(this, new Intent(MMXClient.ACTION_RETRIEVE_MESSAGES));
                // TODO: should it do the URL callback if specified?  Currently not.
                return true;
            } else if (Constants.PingPongCommand.ping.name().equalsIgnoreCase(typeName)) { // "mmx:w:ping"
                handled = true;
            }
        } else if (PushMessage.Action.PUSH == pushType) { // "mmx:p:*"
            // TODO: console should use "mmx:p:notify" instead of "mmx:p".
            // TODO: the following code causes MMXPushEvent unable to parse the intent.

            // Only the payload does not have custom content and it has at least one
            // of the notification properties, the notification will be shown.
            // Otherwise, an Intent will be sent.
            boolean doNotify = !gcmPayload.getMmx().containsKey(GCMPayload.KEY_CUSTOM_CONTENT)
                    && (gcmPayload.getTitle() != null || gcmPayload.getBody() != null
                            || gcmPayload.getIcon() != null || gcmPayload.getBadge() != null
                            || gcmPayload.getSound() != null);
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "handleMMXInternalPush(): doNotify=" + doNotify);
            }
            if (doNotify) {
                invokeNotificationForPush(gcmPayload);
                handled = true;
            }
            //      if (TextUtils.isEmpty(typeName)) { // "mmx:p"
            //        String text = gcmPayload.getBody();
            //        if (Log.isLoggable(TAG, Log.DEBUG)) {
            //          Log.d(TAG, "handleMMXInternalPush(): Received notification with body: " + text);
            //        }
            //        invokeWakeupHandlerForPush(gcmPayload);
            //        if (!TextUtils.isEmpty(urlString)) {
            //          invokeCallback(urlString);
            //        }
            //        return true;
            //      }
        }

        // Do the URL callback if it is specified.
        if (!TextUtils.isEmpty(urlString)) {
            invokeCallback(urlString);
        }
        return handled;
    }
}