package com.kidozen.client.push; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.provider.Settings; import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.gcm.GoogleCloudMessaging; import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import kidozen.client.KZApplication; import kidozen.client.ServiceEvent; import kidozen.client.ServiceEventListener; /** * Created by christian on 7/8/14. */ public class GCM { private String TAG = this.getClass().getSimpleName(); private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; private static final long PULL_SUBSCRIPTION_TIMEOUT = 5; public static final String PROPERTY_REG_ID = "registration_id"; private static final String PROPERTY_APP_VERSION = "appVersion"; private String mRegistrationId; private GoogleCloudMessaging mGcm; private KZApplication mKido; private Context mContext; private Activity mActivity; private String mSenderId; private String mAndroidId; private IGcmEvents mGCMEvents; private JSONArray mDeviceSubscriptions = new JSONArray(); private boolean mInitializationSuccess = false; private boolean mLastSubscriptionSucess = false; private boolean mLastPushSucess = false; /** * Constructor * * @param activity the associated activity * @param kidoApp an initialized instance of KidoZen Application * @param SenderId the GCM project identifier */ public GCM(Activity activity, KZApplication kidoApp, String SenderId) { mActivity = activity; mContext = activity.getApplicationContext(); mKido = kidoApp; mSenderId = SenderId; mAndroidId = Settings.Secure.getString(mContext.getContentResolver(),Settings.Secure.ANDROID_ID); } /** * Initializes and checks the registration process. * This include: * - checking Google Play Services availability * - getting a valid registration ID from GCM * - retrieve or store it in Shared Preferences * - register with KidoZen services * * Fires the onInitializationComplete method */ public void Initialize() { try { mKido.Notification().GetSubscriptions(mAndroidId, new ServiceEventListener() { @Override public void onFinish(ServiceEvent e) { Boolean success = (e.StatusCode == HttpStatus.SC_OK); if (success) mDeviceSubscriptions = (JSONArray) e.Response; if (checkPlayServices()) { mGcm = GoogleCloudMessaging.getInstance(mContext); mRegistrationId = getRegistrationId(mContext); if (mRegistrationId.isEmpty()) { registerInBackground(); } else { mInitializationSuccess = true; if (mGCMEvents!=null) mGCMEvents.onInitializationComplete(true, "RegistrationId obtained from SharedPreferences", mRegistrationId, mAndroidId); } } } }); } catch (Exception ex) { String message = "Error :" + ex.getMessage(); if (mGCMEvents!=null) mGCMEvents.onInitializationComplete(false, message, mRegistrationId, mAndroidId); } } /** * Subscribe the device to the specified KidoZen channel * @param channel * * Fires the onSubscriptionComplete method */ public void SubscribeToChannel(String channel) { if (!mInitializationSuccess) throw new IllegalStateException("You must invoke Initialize method before call this method"); try { if (isDeviceRegisterInChannel(channel)) { if (mGCMEvents!=null) mGCMEvents.onSubscriptionComplete(true, "Device already registered in channel"); } else mKido.Notification().Subscribe(mAndroidId,channel,mRegistrationId,new SubscribeToChannelEventListener()); } catch (Exception ex) { if (mGCMEvents!=null) mGCMEvents.onSubscriptionComplete(false, ex.getMessage()); } } /** * Removes subscription from KidoZen channel * @param channel * * Fires the onRemoveSubscriptionComplete method */ public void RemoveSubscription(String channel) { if (!mInitializationSuccess) throw new IllegalStateException("You must invoke Initialize method before call this method"); try { if (isDeviceRegisterInChannel(channel)) { mKido.Notification().Unsubscribe(channel, mRegistrationId, new UnSubscribeToChannelEventListener()); } } catch (Exception ex) { if (mGCMEvents!=null) mGCMEvents.onSubscriptionComplete(false, ex.getMessage()); } } /** * Sends a message in the channel specified * @param channel * @param data * * Fires the onPushMessageComplete method */ public void PushMessage(String channel, JSONObject data) { if (!mInitializationSuccess) throw new IllegalStateException("You must invoke Initialize method before call this method"); try { mKido.Notification().Push(channel,data,new PushMessageEventListener()); } catch (Exception ex) { if (mGCMEvents!=null) mGCMEvents.onPushMessageComplete(false, ex.getMessage()); } } /** * Returns all the channels that the device has subscribed * * Fires the onGetSubscriptionsComplete method */ public void GetSubscriptions() { try { mKido.Notification().GetSubscriptions(mAndroidId, new ServiceEventListener() { @Override public void onFinish(ServiceEvent e) { Boolean success = (e.StatusCode == HttpStatus.SC_OK); if (success) mDeviceSubscriptions = (JSONArray) e.Response; if (mGCMEvents != null) mGCMEvents.onGetSubscriptionsComplete(true, mDeviceSubscriptions.toString()); } }); } catch (Exception ex) { String message = "Error :" + ex.getMessage(); if (mGCMEvents!=null) mGCMEvents.onGetSubscriptionsComplete(false, message); } } /** * Setup the class that implements the IGcmEvents * @param mGCMEvents */ public void setGCMEvents(IGcmEvents mGCMEvents) { this.mGCMEvents = mGCMEvents; } private boolean isDeviceRegisterInChannel(String channel) { Boolean isRegister = false; getSubscriptionsSync(); for (int i = 0; i < mDeviceSubscriptions.length(); ++i) { JSONObject object = null; try { object = mDeviceSubscriptions.getJSONObject(i); if (object.getString("channelName").equals(channel) && object.getString("applicationName").equals(mKido.getApplicationName()) && object.getString("subscriptionId").equals(mRegistrationId)) { isRegister = true; break; }; } catch (JSONException e) { break; } } return isRegister; } private void getSubscriptionsSync() { try { CountDownLatch cdl = new CountDownLatch(1); mKido.Notification().GetSubscriptions(mAndroidId, new ServiceEventListener() { @Override public void onFinish(ServiceEvent e) { Boolean success = (e.StatusCode == HttpStatus.SC_OK); if (success) mDeviceSubscriptions = (JSONArray) e.Response; } }); cdl.await(PULL_SUBSCRIPTION_TIMEOUT, TimeUnit.SECONDS); } catch (Exception ignore) {} } private void registerInBackground() { new AsyncTask<Void,Void,String>() { Boolean mSuccess = true; String mRegisterMessage = ""; @Override protected String doInBackground(Void... params) { try { if (mGcm == null) { mGcm = GoogleCloudMessaging.getInstance(mContext); } mRegistrationId = mGcm.register(mSenderId); mRegisterMessage = "Device registered, registration ID=" + mRegistrationId; // Persist the regID - no need to register again. storeRegistrationId(mContext, mRegistrationId); } catch (IOException ex) { mRegisterMessage = "Error :" + ex.getMessage(); mSuccess = false; } return mRegisterMessage; } @Override protected void onPostExecute(String msg) { Log.i(TAG, "mRegisterMessage :" + msg); mInitializationSuccess = mSuccess; if (mGCMEvents!=null) mGCMEvents.onInitializationComplete(mSuccess, msg, mRegistrationId, mAndroidId); } }.execute(null, null, null); } private String getRegistrationId(Context context) { final SharedPreferences prefs = getGCMPreferences(context); String registrationId = prefs.getString(PROPERTY_REG_ID, ""); if (registrationId.isEmpty()) { Log.i(TAG, "Registration not found."); return ""; } // Check if app was updated; if so, it must clear the registration ID // since the existing regID is not guaranteed to work with the new // app version. int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); int currentVersion = getAppVersion(context); if (registeredVersion != currentVersion) { Log.i(TAG, "App version changed."); return ""; } return registrationId; } private static int getAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(context.getPackageName(), 0); return packageInfo.versionCode; } catch (PackageManager.NameNotFoundException e) { // should never happen throw new RuntimeException("Could not get package name: " + e); } } private SharedPreferences getGCMPreferences(Context context) { // This sample app persists the registration ID in shared preferences, but // how you store the regID in your app is up to you. return mContext.getSharedPreferences(this.getClass().getSimpleName(), Context.MODE_PRIVATE); } private void storeRegistrationId(Context context, String regId) { final SharedPreferences prefs = getGCMPreferences(context); int appVersion = getAppVersion(context); Log.i(TAG, "Saving regId on app version " + appVersion); SharedPreferences.Editor editor = prefs.edit(); editor.putString(PROPERTY_REG_ID, regId); editor.putInt(PROPERTY_APP_VERSION, appVersion); editor.commit(); mInitializationSuccess = true; } private boolean checkPlayServices() { int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); if (resultCode != ConnectionResult.SUCCESS) { if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { GooglePlayServicesUtil.getErrorDialog(resultCode, mActivity, PLAY_SERVICES_RESOLUTION_REQUEST).show(); } else { Log.i(TAG, "This device is not supported."); //finish(); } return false; } return true; } /* * */ private class SubscribeToChannelEventListener implements ServiceEventListener { Boolean mSuccess = false; String mMessage = ""; @Override public void onFinish(ServiceEvent e) { mSuccess = (e.StatusCode == HttpStatus.SC_CREATED); mLastSubscriptionSucess = mSuccess; mMessage = e.Body; if (mGCMEvents!=null) mGCMEvents.onSubscriptionComplete(mSuccess, mMessage); } } /* * */ private class PushMessageEventListener implements ServiceEventListener { Boolean mSuccess = false; String mMessage = ""; @Override public void onFinish(ServiceEvent e) { mSuccess = (e.StatusCode == HttpStatus.SC_NO_CONTENT); mLastPushSucess = mSuccess; mMessage = e.Body; if (mGCMEvents!=null) mGCMEvents.onSubscriptionComplete(mSuccess, mMessage); } } /* * */ private class UnSubscribeToChannelEventListener implements ServiceEventListener { Boolean mSuccess = false; String mMessage = ""; @Override public void onFinish(ServiceEvent e) { mSuccess = (e.StatusCode == HttpStatus.SC_NO_CONTENT); mLastSubscriptionSucess = mSuccess; mMessage = e.Body; if (mGCMEvents!=null) mGCMEvents.onRemoveSubscriptionComplete(mSuccess, mMessage); } } }