package com.appboy; import android.app.Notification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.NotificationManagerCompat; import android.util.Log; import com.appboy.configuration.AppboyConfigurationProvider; import com.appboy.push.AppboyNotificationActionUtils; import com.appboy.push.AppboyNotificationUtils; import com.appboy.support.AppboyLogger; public final class AppboyGcmReceiver extends BroadcastReceiver { private static final String TAG = String.format("%s.%s", Constants.APPBOY_LOG_TAG_PREFIX, AppboyGcmReceiver.class.getName()); private static final String GCM_RECEIVE_INTENT_ACTION = "com.google.android.c2dm.intent.RECEIVE"; private static final String GCM_REGISTRATION_INTENT_ACTION = "com.google.android.c2dm.intent.REGISTRATION"; private static final String GCM_ERROR_KEY = "error"; private static final String GCM_REGISTRATION_ID_KEY = "registration_id"; private static final String GCM_UNREGISTERED_KEY = "unregistered"; private static final String GCM_MESSAGE_TYPE_KEY = "message_type"; private static final String GCM_DELETED_MESSAGES_KEY = "deleted_messages"; private static final String GCM_NUMBER_OF_MESSAGES_DELETED_KEY = "total_deleted"; public static final String CAMPAIGN_ID_KEY = Constants.APPBOY_PUSH_CAMPAIGN_ID_KEY; @Override public void onReceive(Context context, Intent intent) { AppboyLogger.i(TAG, String.format("Received broadcast message. Message: %s", intent.toString())); String action = intent.getAction(); if (GCM_REGISTRATION_INTENT_ACTION.equals(action)) { handleRegistrationEventIfEnabled(new AppboyConfigurationProvider(context), context, intent); } else if (GCM_RECEIVE_INTENT_ACTION.equals(action)) { handleAppboyGcmReceiveIntent(context, intent); } else if (Constants.APPBOY_CANCEL_NOTIFICATION_ACTION.equals(action)) { AppboyNotificationUtils.handleCancelNotificationAction(context, intent); } else if (Constants.APPBOY_ACTION_CLICKED_ACTION.equals(action)) { AppboyNotificationActionUtils.handleNotificationActionClicked(context, intent); } else if (Constants.APPBOY_PUSH_CLICKED_ACTION.equals(action)) { AppboyNotificationUtils.handleNotificationOpened(context, intent); } else { AppboyLogger.w(TAG, "The GCM receiver received a message not sent from Appboy. Ignoring the message."); } } /** * Processes the registration/unregistration result returned from the GCM servers. If the * registration/unregistration is successful, this will store/clear the registration ID from the * device. Otherwise, it will log an error message and the device will not be able to receive GCM * messages. */ boolean handleRegistrationIntent(Context context, Intent intent) { String error = intent.getStringExtra(GCM_ERROR_KEY); String registrationId = intent.getStringExtra(GCM_REGISTRATION_ID_KEY); if (error != null) { if ("SERVICE_NOT_AVAILABLE".equals(error)) { Log.e(TAG, "Unable to connect to the GCM registration server. Try again later."); } else if ("ACCOUNT_MISSING".equals(error)) { Log.e(TAG, "No Google account found on the phone. For pre-3.0 devices, a Google account is required on the device."); } else if ("AUTHENTICATION_FAILED".equals(error)) { Log.e(TAG, "Unable to authenticate Google account. For Android versions <4.0.4, a valid Google Play account " + "is required for Google Cloud Messaging to function. This phone will be unable to receive Google Cloud " + "Messages until the user logs in with a valid Google Play account or upgrades the operating system on this device."); } else if ("INVALID_SENDER".equals(error)) { Log.e(TAG, "One or multiple of the sender IDs provided are invalid."); } else if ("PHONE_REGISTRATION_ERROR".equals(error)) { Log.e(TAG, "Device does not support GCM."); } else if ("INVALID_PARAMETERS".equals(error)) { Log.e(TAG, "The request sent by the device does not contain the expected parameters. This phone does not " + "currently support GCM."); } else { AppboyLogger.w(TAG, String.format("Received an unrecognised GCM registration error type. Ignoring. Error: %s", error)); } } else if (registrationId != null) { Appboy.getInstance(context).registerAppboyPushMessages(registrationId); } else if (intent.hasExtra(GCM_UNREGISTERED_KEY)) { AppboyLogger.w(TAG, "The device was un-registered from GCM."); } else { AppboyLogger.w(TAG, "The GCM registration message is missing error information, registration id, and unregistration " + "confirmation. Ignoring."); return false; } return true; } /** * Handles both Appboy data push GCM messages and notification messages. Notification messages are * posted to the notification center if the GCM message contains a title and body and the payload * is sent to the application via an Intent. Data push messages do not post to the notification * center, although the payload is forwarded to the application via an Intent as well. */ boolean handleAppboyGcmMessage(Context context, Intent intent) { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); String messageType = intent.getStringExtra(GCM_MESSAGE_TYPE_KEY); if (GCM_DELETED_MESSAGES_KEY.equals(messageType)) { int totalDeleted = intent.getIntExtra(GCM_NUMBER_OF_MESSAGES_DELETED_KEY, -1); if (totalDeleted == -1) { Log.e(TAG, String.format("Unable to parse GCM message. Intent: %s", intent.toString())); } else { AppboyLogger.i(TAG, String.format("GCM deleted %d messages. Fetch them from Appboy.", totalDeleted)); } return false; } else { Bundle gcmExtras = intent.getExtras(); AppboyLogger.i(TAG, String.format("Push message payload received: %s", gcmExtras)); // Parsing the Appboy data extras (data push). // We convert the JSON in the extras key into a Bundle. Bundle appboyExtras = AppboyNotificationUtils.getAppboyExtrasWithoutPreprocessing(gcmExtras); gcmExtras.putBundle(Constants.APPBOY_PUSH_EXTRAS_KEY, appboyExtras); if (AppboyNotificationUtils.isNotificationMessage(intent)) { AppboyLogger.d(TAG, "Received notification push"); int notificationId = AppboyNotificationUtils.getNotificationId(gcmExtras); gcmExtras.putInt(Constants.APPBOY_PUSH_NOTIFICATION_ID, notificationId); AppboyConfigurationProvider appConfigurationProvider = new AppboyConfigurationProvider(context); IAppboyNotificationFactory appboyNotificationFactory = AppboyNotificationUtils.getActiveNotificationFactory(); Notification notification = appboyNotificationFactory.createNotification(appConfigurationProvider, context, gcmExtras, appboyExtras); if (notification == null) { AppboyLogger.d(TAG, "Notification created by notification factory was null. Not displaying notification."); return false; } notificationManager.notify(Constants.APPBOY_PUSH_NOTIFICATION_TAG, notificationId, notification); AppboyNotificationUtils.sendPushMessageReceivedBroadcast(context, gcmExtras); // Since we have received a notification, we want to wake the device screen. AppboyNotificationUtils.wakeScreenIfHasPermission(context, gcmExtras); // Set a custom duration for this notification. if (gcmExtras != null && gcmExtras.containsKey(Constants.APPBOY_PUSH_NOTIFICATION_DURATION_KEY)) { int durationInMillis = Integer.parseInt(gcmExtras.getString(Constants.APPBOY_PUSH_NOTIFICATION_DURATION_KEY)); AppboyNotificationUtils.setNotificationDurationAlarm(context, this.getClass(), notificationId, durationInMillis); } return true; } else { AppboyLogger.d(TAG, "Received data push"); AppboyNotificationUtils.sendPushMessageReceivedBroadcast(context, gcmExtras); AppboyNotificationUtils.requestGeofenceRefreshIfAppropriate(context, gcmExtras); return false; } } } /** * Runs the handleAppboyGcmMessage method in a background thread in case of an image push * notification, which cannot be downloaded on the main thread. */ public class HandleAppboyGcmMessageTask extends AsyncTask<Void, Void, Void> { private final Context mContext; private final Intent mIntent; public HandleAppboyGcmMessageTask(Context context, Intent intent) { mContext = context; mIntent = intent; execute(); } @Override protected Void doInBackground(Void... voids) { try { handleAppboyGcmMessage(mContext, mIntent); } catch (Exception e) { AppboyLogger.e(TAG, "Failed to create and display notification.", e); } return null; } } void handleAppboyGcmReceiveIntent(Context context, Intent intent) { if (AppboyNotificationUtils.isAppboyPushMessage(intent)) { new HandleAppboyGcmMessageTask(context, intent); } } boolean handleRegistrationEventIfEnabled(AppboyConfigurationProvider appConfigurationProvider, Context context, Intent intent) { // Only handle GCM registration events if GCM registration handling is turned on in the // configuration file. if (appConfigurationProvider.isGcmMessagingRegistrationEnabled()) { handleRegistrationIntent(context, intent); return true; } return false; } }