Java tutorial
/* * Copyright (C) 2012-2014 Gregory S. Meiste <http://gregmeiste.com> * * 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.meiste.greg.ptw.gcm; 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.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.text.format.DateUtils; import com.google.android.gms.gcm.GoogleCloudMessaging; import com.google.android.gms.tagmanager.Container; import com.meiste.greg.ptw.EditPreferences; import com.meiste.greg.ptw.GAE; import com.meiste.greg.ptw.GAE.GaeListener; import com.meiste.greg.ptw.GtmHelper; import com.meiste.greg.ptw.GtmHelper.OnContainerAvailableListener; import com.meiste.greg.ptw.MainActivity; import com.meiste.greg.ptw.PTW; import com.meiste.greg.ptw.PlayerAdapter; import com.meiste.greg.ptw.PlayerHistory; import com.meiste.greg.ptw.R; import com.meiste.greg.ptw.Races; import com.meiste.greg.ptw.Util; import com.meiste.greg.ptw.tab.RuleBook; import com.meiste.greg.ptw.tab.Standings; public class GcmIntentService extends IntentService implements OnContainerAvailableListener { private static final int MAX_ATTEMPTS = 5; private static final int PI_REQ_CODE = 426801; private static final int BACKOFF_MILLI_SECONDS = 2000; private static final long WAIT_TIMEOUT = DateUtils.MINUTE_IN_MILLIS; private static final String MSG_KEY = "collapse_key"; private static final String MSG_KEY_SYNC = "ptw_sync"; private static final String MSG_KEY_HISTORY = "ptw_history"; private static final String MSG_KEY_RULES = "ptw_rules"; private final Object mSync = new Object(); private boolean mGaeSuccess = false; private Container mContainer; public GcmIntentService() { super(GcmIntentService.class.getSimpleName()); } @Override public void onCreate() { super.onCreate(); GtmHelper.getInstance(getApplicationContext()).getContainer(this); } @Override protected void onHandleIntent(final Intent intent) { final Bundle extras = intent.getExtras(); final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); final String messageType = gcm.getMessageType(intent); synchronized (mSync) { if (mContainer == null) { try { mSync.wait(); } catch (final InterruptedException e) { } } } if (!extras.isEmpty()) { /* * Filter messages based on message type. Since it is likely that GCM will be * extended in the future with new message types, just ignore any message types * not recognized. */ if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { Util.log("GCM send error: " + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) { Util.log("GCM deleted messages on server: " + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { if (intent.hasExtra(MSG_KEY)) { final String message = intent.getStringExtra(MSG_KEY); Util.log("Received " + message + " message from GCM"); if (MSG_KEY_SYNC.equals(message)) { getFromServer("schedule", scheduleListener, false); getFromServer("standings", standingsListener, true); } else if (MSG_KEY_HISTORY.equals(message)) { getFromServer("history", historyListener, true); } else if (MSG_KEY_RULES.equals(message)) { getFromServer("rule_book", rulesListener, false); } else { Util.log("Message type unknown. Ignoring..."); } } } } // Release the wake lock provided by the WakefulBroadcastReceiver. GcmBroadcastReceiver.completeWakefulIntent(intent); } @Override public void onContainerAvailable(final Container container) { synchronized (mSync) { mContainer = container; mSync.notify(); } } private void getFromServer(final String page, final GcmGaeListener l, final boolean accountRequired) { long backoff = BACKOFF_MILLI_SECONDS; mGaeSuccess = false; for (int i = 1; i <= MAX_ATTEMPTS; i++) { if (accountRequired && GAE.isAccountSetupNeeded(this)) { Util.log("Skipping " + page + " sync since account not setup"); break; } Util.log("Attempt #" + i + " to get " + page + " from PTW server"); synchronized (mSync) { GAE.getInstance(getApplicationContext()).getPage(l, page); try { mSync.wait(WAIT_TIMEOUT); } catch (final InterruptedException e) { } } if (mGaeSuccess || (i == MAX_ATTEMPTS)) break; try { Util.log("Sleeping for " + backoff + " ms before retry"); Thread.sleep(backoff); } catch (final InterruptedException e) { } // increase backoff exponentially backoff *= 2; } } private final GcmGaeListener scheduleListener = new GcmGaeListener() { @Override public void onGet(final Context context, final String json) { Util.log("scheduleListener: onGet"); Races.update(context, json); super.onGet(context, json); } }; private final GcmGaeListener standingsListener = new GcmGaeListener() { @Override public void onGet(final Context context, final String json) { Util.log("standingsListener: onGet"); final PlayerAdapter pAdapter = new PlayerAdapter(getApplicationContext()); final int beforeUpdate = pAdapter.getRaceAfterNum(); Standings.update(context, json); sendBroadcast(new Intent(PTW.INTENT_ACTION_STANDINGS)); pAdapter.notifyDataSetChanged(); final int afterUpdate = pAdapter.getRaceAfterNum(); if ((beforeUpdate != afterUpdate) && (afterUpdate > 0)) { Util.log("Notifying user of standings update"); showResultsNotification(context, pAdapter.getRaceAfterName()); } super.onGet(context, json); } }; private final GcmGaeListener historyListener = new GcmGaeListener() { @Override public void onGet(final Context context, final String json) { Util.log("historyListener: onGet"); PlayerHistory.fromJson(json).commit(context); sendBroadcast(new Intent(PTW.INTENT_ACTION_HISTORY)); super.onGet(context, json); } }; private final GcmGaeListener rulesListener = new GcmGaeListener() { @Override public void onGet(final Context context, final String json) { Util.log("rulesListener: onGet"); RuleBook.update(context, json); super.onGet(context, json); } }; private class GcmGaeListener implements GaeListener { @Override public void onGet(final Context context, final String json) { synchronized (mSync) { mGaeSuccess = true; mSync.notify(); } } @Override public void onFailedConnect(final Context context) { Util.log("GcmGaeListener: onFailedConnect"); synchronized (mSync) { mGaeSuccess = false; mSync.notify(); } } @Override public void onLaunchIntent(final Intent launch) { // Should never happen synchronized (mSync) { mGaeSuccess = false; mSync.notify(); } } @Override public void onConnectSuccess(final Context context, final String json) { // Should never happen synchronized (mSync) { mGaeSuccess = false; mSync.notify(); } } } private void showResultsNotification(final Context context, final String race) { // Only show notification if user wants results notifications final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); if (prefs.getBoolean(EditPreferences.KEY_NOTIFY_RESULTS, true) && mContainer.getBoolean(GtmHelper.KEY_GAME_ENABLED)) { final Intent notificationIntent = new Intent(context, MainActivity.class); notificationIntent.putExtra(PTW.INTENT_EXTRA_TAB, 2); final PendingIntent pi = PendingIntent.getActivity(context, PI_REQ_CODE, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); int defaults = 0; if (prefs.getBoolean(EditPreferences.KEY_NOTIFY_VIBRATE, true)) defaults |= Notification.DEFAULT_VIBRATE; if (prefs.getBoolean(EditPreferences.KEY_NOTIFY_LED, true)) defaults |= Notification.DEFAULT_LIGHTS; final NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_stat_steering_wheel) .setTicker(context.getString(R.string.remind_results_notify, race)) .setContentTitle(context.getString(R.string.app_name)) .setContentText(context.getString(R.string.remind_results_notify, race)).setContentIntent(pi) .setAutoCancel(true).setDefaults(defaults).setSound(Uri .parse(prefs.getString(EditPreferences.KEY_NOTIFY_RINGTONE, PTW.DEFAULT_NOTIFY_SND))); getNM(context).notify(R.string.remind_results_notify, builder.build()); } } public static void clearNotification(final Context context) { getNM(context).cancel(R.string.remind_results_notify); } private static NotificationManager getNM(final Context context) { return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } }