how to resolve Android gcm notifications duplicate messages - android

Hi am getting duplicate notification from gcm server , how to resolve it
public class RegisterActivity{
private GoogleCloudMessaging gcm;
Context context;
String regId;
private static final String REG_ID = "regId";
private static final String APP_VERSION = "appVersion";
static final String TAG = "Register Activity";
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private GCMRegistrationCallBack registeredActivity;
private Error error;
public void registerGCM(Context _context)
{
context = _context;
if(!checkPlayServices()){
Log.e(TAG, "This device is not supported.");
return;
}
registeredActivity = (GCMRegistrationCallBack) _context;
gcm = GoogleCloudMessaging.getInstance(context);
regId = getRegistrationId(context);
if(TextUtils.isEmpty(regId))
{
registerInBackground();
Log.d("RegisterActivity",
"registerGCM - successfully registered with GCM server - regId: "
+ regId);
}
else
{
registeredActivity.didFinishRegisteringWithGCM(regId);
}
}
private String getRegistrationId(Context context)
{
final SharedPreferences prefs = context.getSharedPreferences(
GCMRegistrationCallBack.class.getSimpleName(), Context.MODE_PRIVATE);
String registrationId = prefs.getString(REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
int registeredVersion = prefs.getInt(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 (NameNotFoundException e) {
Log.d("RegisterActivity",
"I never expected this! Going down, going down!" + e);
throw new RuntimeException(e);
}
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regId = gcm.register(Config.GOOGLE_PROJECT_ID);
storeRegistrationId(context, regId);
}
catch(UnknownHostException _exception)
{
error = new Error("UnknownHostException");
callErrorCallBackOnMainThread();
}
catch(SocketTimeoutException _exception)
{
error = new Error("SocketTimeoutException");
callErrorCallBackOnMainThread();
}
catch (IOException _exception) {
error = new Error(_exception.getMessage());
callErrorCallBackOnMainThread();
}
catch(Exception _exception)
{
error = new Error("unknown exception");
callErrorCallBackOnMainThread();
}
return regId;
}
#Override
protected void onPostExecute(String msg) {
if(!TextUtils.isEmpty(msg))
registeredActivity.didFinishRegisteringWithGCM(regId);
}
}.execute(null, null, null);
}
private void callErrorCallBackOnMainThread()
{
Activity regAvtivity = (Activity)registeredActivity;
regAvtivity.runOnUiThread(new Runnable()
{
public void run()
{
registeredActivity.didFailedToReceiveRegistrationId(error);
}
});
}
private void storeRegistrationId(Context context, String regId) {
final SharedPreferences prefs = context.getSharedPreferences(
GCMRegistrationCallBack.class.getSimpleName(), Context.MODE_PRIVATE);
int appVersion = getAppVersion(context);
Log.i(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(REG_ID, regId);
editor.putInt(APP_VERSION, appVersion);
editor.commit();
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, (Activity)context,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(TAG, "This device is not supported.");
}
return false;
}
return true;
}
}
Above code for getting registration number please correct me anything wrong... but some time am getting duplicate notification.
thanks

You should use Canonical IDs for this. This might happen because you registered with several IDs for the same device. Using canonical IDs will set your ID to be the last registration you've made.
As per the GCM reference about this:
Canonical IDs
On the server side, as long as the application is behaving well, everything should work normally. However, if a bug in the application triggers multiple registrations for the same device, it can be hard to reconcile state and you might end up with duplicate messages.
GCM provides a facility called "canonical registration IDs" to easily recover from these situations. A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.
If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the registration_id field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.
More info here.

Related

QuickBlox notifications: push token disappears

I've been working on an Android app that uses QuickBlox for chatting. The chat is working fine, but I am encountering difficulties with the push notifications for logged out users.
The problem is that some devices sometimes stop receiving notifications. It often works fine for a couple of hours, but then suddenly completely stops working for that device. A full manual uninstall + reinstall (which then logs the user in again to QuickBlox + GCM) usually solves the problem.
Looking at the QuickBlox admin panel, I can see that some subscriptions lose their push token.
User 1 registered on device A. Push token has disappeared.
http://i.stack.imgur.com/YUkyw.png
User 1 registered on device B. Push token still present.
http://i.stack.imgur.com/Yu5IQ.png
I register with GCM + QuickBlox push messages once the user is logged in to QuickBlox. This code was largely taken from the QuickBlox chat sample.
ChatService:
private void loginToChat(final Context context, final QBUser user, final QBEntityCallback callback)
{
this.qbChatService.login(user, new QBEntityCallbackImpl()
{
#Override
public void onSuccess()
{
checkPlayServices(context);
try
{
qbChatService.startAutoSendPresence(AUTO_PRESENCE_INTERVAL_IN_SECONDS);
}
catch (SmackException.NotLoggedInException e)
{
e.printStackTrace();
}
if (callback != null)
{
callback.onSuccess();
}
}
#Override
public void onSuccess(Object result, Bundle params)
{
super.onSuccess(result, params);
checkPlayServices(context);
}
#Override
public void onError(List errors)
{
if (callback != null)
{
callback.onError(errors);
}
}
});
}
private void checkPlayServices(Context context)
{
// Register with GCM after the session has been created
PlayServicesHelper playServicesHelper = new PlayServicesHelper(context);
playServicesHelper.checkPlayServices();
}
PlayServicesHelper:
public class PlayServicesHelper
{
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final String PROPERTY_REG_ID = "registration_id";
private static final String TAG = "PlayServicesHelper";
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private GoogleCloudMessaging googleCloudMessaging;
private Context activity;
private String regId;
private String projectNumber;
public PlayServicesHelper(Context activity)
{
this.activity = activity;
this.projectNumber = activity.getString(R.string.gcm_project_number);
checkPlayService();
}
private void checkPlayService()
{
// Check device for Play Services APK. If check succeeds, proceed with
// GCM registration.
if (checkPlayServices())
{
googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
regId = getRegistrationId();
if (regId.isEmpty())
{
registerInBackground();
}
}
else
{
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
public boolean checkPlayServices()
{
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity);
if (resultCode != ConnectionResult.SUCCESS)
{
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode))
{
GooglePlayServicesUtil.getErrorDialog(resultCode, activity, PLAY_SERVICES_RESOLUTION_REQUEST).show();
}
return false;
}
return true;
}
/**
* Gets the current registration ID for application on GCM service.
* <p/>
* If result is empty, the app needs to register.
*
* #return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId()
{
final SharedPreferences prefs = getGCMPreferences();
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 = getAppVersionCode();
if (registeredVersion != currentVersion)
{
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
/**
* Registers the application with GCM servers asynchronously.
* <p/>
* Stores the registration ID and app versionCode in the application's
* shared preferences.
*/
private void registerInBackground()
{
new AsyncTask<Void, Void, String>()
{
#Override
protected String doInBackground(Void... params)
{
String msg = "";
try
{
if (googleCloudMessaging == null)
{
googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
}
regId = googleCloudMessaging.register(projectNumber);
msg = "Device registered, registration ID=" + regId;
// You should send the registration ID to your server over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your app.
Handler h = new Handler(activity.getMainLooper());
h.post(new Runnable()
{
#Override
public void run()
{
subscribeToPushNotifications(regId);
}
});
// Persist the regID - no need to register again.
storeRegistrationId(regId);
}
catch (IOException ex)
{
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg)
{
Log.i(TAG, msg + "\n");
}
}.execute(null, null, null);
}
/**
* #return Application's {#code SharedPreferences}.
*/
private SharedPreferences getGCMPreferences()
{
return activity.getSharedPreferences(activity.getPackageName(), Context.MODE_PRIVATE);
}
/**
* Subscribe to Push Notifications
*
* #param regId registration ID
*/
private void subscribeToPushNotifications(String regId)
{
String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null)
{
deviceId = mTelephony.getDeviceId();
}
else
{
deviceId = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID);
}
QBMessages.subscribeToPushNotificationsTask(regId, deviceId, QBEnvironment.PRODUCTION, new QBEntityCallbackImpl<ArrayList<QBSubscription>>()
{
#Override
public void onSuccess(ArrayList<QBSubscription> qbSubscriptions, Bundle bundle)
{
}
#Override
public void onError(List<String> strings)
{
}
});
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param regId registration ID
*/
private void storeRegistrationId(String regId)
{
final SharedPreferences prefs = getGCMPreferences();
int appVersion = getAppVersionCode();
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.apply();
}
private int getAppVersionCode()
{
try
{
PackageInfo packageInfo = this.activity.getPackageManager().getPackageInfo(this.activity.getPackageName(), 0);
return packageInfo.versionCode;
}
catch (PackageManager.NameNotFoundException e)
{
return 0;
}
}
}
To allow a user to receive notifications, I log him out every time the app goes to the background (using onStop in a BaseActivity) and then log back in when the app resumes (which then again starts the PlayServicesHelper).
ChatService.getInstance().logout(null);
The app also uses Parse (with GCM) for push notifications, but the problem still seemed to occur when I disabled Parse.
Could the problem be caused by having one user registered on multiple devices at the same time? Could the frequent app installs (without manually removing the previous installation) result in this behavior?
Push token can disappear if 2 users use the same device
for example, User 1 subscribed for pushes on deviceA
Then User 2 subscribed for pushes on deviceA (the same device)
After this only User 2 will be receiving pushes, not User 1
Is there a chance to have such logic in your case?

When multiple apps which listen to same GCM channel are installed, How to make sure the notification is shown only once?

I'm going to publish a series of apps and I want to be able to display notifications to users who have installed any of these apps via GCM. but when there are multiple apps installed on each device, multiple notifications are displayed to users. I want to make sure that only one notification is displayed to users, regardless of count of apps they have installed.
I have tried to put a unique Id in each GCM message that I send and in the application when the Broadcast message is received save it in a file in a specific folder in internal storage, so I can keep track of the message and avoid displaying the same message from multiple apps. but I think this is unreliable and better methods should exist.
All the apps receive the messages from the same GCM channel.
I ended up using a file on external storage as a way to check wether the current GCM message is handled or not, I tested it throughly and in works.
public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = -3;
private NotificationManager mNotificationManager;
public static final String TAG = "CGMIntentService";
public GcmIntentService() {
super("GcmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) { // has effect of unparcelling Bundle
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Post notification of received message.
sendNotification("Received: " + extras.toString(), extras);
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void sendNotification(String msg, final Bundle bundle) {
ATImg atImg = new ATImg();
//use a file lock as way to check whether another app is displaying this gcm message
String fileName = DirectoryHelper.getMCDirectory() + "/" + DirectoryHelper.handledIdsFileName;
if (DirectoryHelper.isFileLocked(fileName))
return;//file is locked, it means another app has caught this gcm message prior to this app
try {
atImg.randomAccessFile = new RandomAccessFile(fileName, "rw");
atImg.lock= atImg.randomAccessFile.getChannel().lock();// lock the file so no other app access it
} catch (FileNotFoundException e) {//again this means file is locked
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
atImg.bundle = bundle;
atImg.doInBackground("");
}
public class ATImg extends AsyncTask<String, String, String> {
Bundle bundle;
RandomAccessFile randomAccessFile;
FileLock lock;
#Override
protected void onPostExecute(String reult){
//release the lock on file
try {
lock.release();
randomAccessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected String doInBackground(String... strings) {
String id = bundle.getString("id");
//CHECK IF THE MESSAGE IS HANDLED
Gson gson = new Gson();
List<String> ids = null;
try {
Type concerete = new TypeToken<List<String>>() {
}.getType();
randomAccessFile.seek(0);
ids = gson.fromJson(randomAccessFile.readUTF(), concerete);
if (ids != null && ids.contains(id)) {
return "";//the message has already been shown
}
}
catch (Exception ex){
ex.printStackTrace();
return "";
}
//write the new id to the end of the file
if (ids == null)
ids = new ArrayList<>();
ids.add(id);
try {
randomAccessFile.seek(0);
randomAccessFile.writeUTF(gson.toJson(ids));
} catch (IOException e) {
e.printStackTrace();
}
//END MASSAGE HANDLING CODE
//write code to show the message
}
}
}

Android root activity sometimes started spontaneously without any user actions

I have a strange issue which I cannot explain. In the manifest file the launch activity of my app is defined as follows:
<activity
android:name="com.xxx.xxx.xxx.StartupActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.Light.NoTitleBar"
android:screenOrientation="sensorPortrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
In the StartUpActivity the following check is performed:
protected void startIntent() {
Intent intent;
if (checkCurrentProfile()) {
Notifier.showHttpToast(R.string.toastLoggedIn);
//try to update the device token in the database
Request.updateDeviceToken();
intent = new Intent(this, GameListActivity.class);
} else {
intent = new Intent(this, RegisterActivity.class);
}
startActivity(intent);
finish();
}
So if the user has a valid account the GameListActivity is shown as root activity:
<activity
android:name="com.xxx.xxx.xxx.xxx.GameListActivity"
android:label="#string/app_name"
android:theme="#style/MyTheme"
android:screenOrientation="sensorPortrait" >
</activity>
The issue now is the following: sometimes the system brings the root activity to the front spontaneously without any user actions. It only occurs sometimes, but I can't figure out the cause. Can anyone help me out here?
The StartUpActivity looks like this:
public class StartupActivity extends StartupCoreActivity implements OnRegisterGCMListener {
private IabHelper mHelper;
private IabHelper.QueryInventoryFinishedListener mQueryInventoryFinishedListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerOnRegisterGCMListener(this);
if (!registerGCMneeded()) {
initInAppBilling();
}
}
#Override
public void registerGCMfinished() {
initInAppBilling();
}
private void initInAppBilling() {
boolean hasPremium = Prefs.getBoolValue(getResources().getString(R.string.pref_key_upgrade_premium), false);
if (hasPremium) {
//unlocking contents not needed
startIntent();
} else {
Prefs.storeValue(getResources().getString(R.string.pref_key_upgrade_premium), false);
mHelper = new IabHelper(this, C.Billing.BILLING_KEY);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (result.isSuccess()) {
queryInventory();
} else {
startIntent();
}
}
});
}
}
private void queryInventory() {
String[] products = {C.Billing.ITEM_SKU};
mQueryInventoryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (result.isSuccess()) {
checkPremiumVersion(inv);
} else {
startIntent();
}
}
};
mHelper.queryInventoryAsync(true, Arrays.asList(products), mQueryInventoryFinishedListener);
}
private void checkPremiumVersion(Inventory inv) {
if (inv.hasPurchase(C.Billing.ITEM_SKU)) {
Request.updPremiumVersion();
Prefs.storeValue(getResources().getString(R.string.pref_key_upgrade_premium), true);
Notifier.showHttpToast(R.string.toastPremiumContentsUnlocked);
}
startIntent();
}
}
And the StartupCoreActivity looks like this:
public class StartupCoreActivity extends Activity {
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = xxxx;
GoogleCloudMessaging mGcm;
Context mContext;
String mRegId;
/**
* Substitute you own sender ID here. This is the project number you got
* from the API Console, as described in "Getting Started."
*/
String SENDER_ID = "xxxxxxxxxx";
private OnRegisterGCMListener mOnRegisterGCMListener = null;
public void registerOnRegisterGCMListener(OnRegisterGCMListener listener) {
mOnRegisterGCMListener = listener;
}
protected boolean registerGCMneeded() {
mContext = getApplicationContext();
// Check device for Play Services APK.
if (checkPlayServices()) {
// If this check succeeds, proceed with normal processing.
// Otherwise, prompt user to get valid Play Services APK.
mGcm = GoogleCloudMessaging.getInstance(this);
mRegId = getRegistrationId(mContext);
if (mRegId.isEmpty()) {
registerInBackground();
return true;
} else {
// note we never called setContentView()
return false;
}
}
return false;
}
protected void startIntent() {
Intent intent;
if (checkCurrentProfile()) {
Notifier.showHttpToast(R.string.toastLoggedIn);
//try to update the device token in the database
Request.updateDeviceToken();
intent = new Intent(this, GameListActivity.class);
} else {
intent = new Intent(this, RegisterActivity.class);
}
startActivity(intent);
finish();
}
private boolean checkCurrentProfile() {
KUPlayer me = G.getMySelf();
if (me.getPlayerId() <= 0) {
//database was not present yet and has been created
//or database was present, but profile cannot be read anymore
// make sure Login screen appears ONLY if PlayerID cannot be retrieved anymore
if (G.getPlayerID() <=0) {
Prefs.storeValue(Prefs.PREF_KEY_PWD_SAVED, false);
return false;
}
}
return true;
}
// You need to do the Play Services APK check here too.
#Override
protected void onResume() {
super.onResume();
checkPlayServices();
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
finish();
}
return false;
}
return true;
}
/**
* Registers the application with mGcm servers asynchronously.
* <p>
* Stores the registration ID and the app versionCode in the application's
* shared preferences.
*/
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (mGcm == null) {
mGcm = GoogleCloudMessaging.getInstance(mContext);
}
mRegId = mGcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + mRegId;
// Persist the mRegId - no need to register again.
storeRegistrationId(mContext, mRegId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
// note we never called setContentView()
if (mOnRegisterGCMListener != null) {
mOnRegisterGCMListener.registerGCMfinished();
}
}
}.execute(null, null, null);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersionCode(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
private static String getAppVersionName(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionName;
} catch (NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
/**
* Stores the registration ID and the app versionCode in the application's
* {#code SharedPreferences}.
*
* #param mContext application's mContext.
* #param mRegId registration ID
*/
private void storeRegistrationId(Context context, String regId) {
Prefs.storeValue(Prefs.PREF_KEY_DEVICE_TOKEN, regId);
Prefs.storeValue(Prefs.PREF_KEY_APP_VERSION_CODE, getAppVersionCode(context));
Prefs.storeValue(Prefs.PREF_KEY_APP_VERSION_NAME, getAppVersionName(context));
AppRate.resetAfterUpdate();
}
/**
* Gets the current registration ID for application on mGcm service, if there is one.
* <p>
* If result is empty, the app needs to register.
*
* #return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId(Context context) {
String registrationId = Prefs.getStringValue(Prefs.PREF_KEY_DEVICE_TOKEN);
if (registrationId.isEmpty()) {
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing mRegId is not guaranteed to work with the new
// app version.
int registeredVersion = Prefs.getIntValue(Prefs.PREF_KEY_APP_VERSION_CODE);
int currentVersion = getAppVersionCode(context);
if (registeredVersion != currentVersion) {
return "";
}
return registrationId;
}
}
I guess it has to do with the following known Android bug:
How to prevent multiple instances of an activity when it is launched with different intents
And as suggested put the following code in the onCreate method of the rootActivity:
// Possible work around for market launches. See http://code.google.com/p/android/issues/detail?id=2373
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
}
I tested it and when you start your app from Google Play store in stead of launch screen, then indeed finish() is called from above code.

java.lang.NoClassDefFoundError: com.google.android.gms.gcm.GoogleCloudMessaging

I am developing the GCM sample app using http://javapapers.com/android/google-cloud-messaging-gcm-for-android-and-push-notifications/#comment-103785.
I am unable to register gcm server.I am getting the following error.I have goggled it .but i couldn't find the solution and all the solutions were about jar files i have done all the changes but i couldn't get it.
Error
FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: com.google.android.gms.gcm.GoogleCloudMessaging
com.sanjeev.integrationapp.RegisterActivity.registerGCM(RegisterActivity.java:80)
com.sanjeev.integrationapp.RegisterActivity$1.onClick(RegisterActivity.java:47)
at android.view.View.performClick(View.java:4084)
at android.view.View$PerformClick.run(View.java:16966)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
And my class code follows
public class RegisterActivity extends Activity {
Button btnGCMRegister;
Button btnAppShare;
GoogleCloudMessaging gcm;
Context context;
String regId;
public static final String REG_ID = "regId";
private static final String APP_VERSION = "appVersion";
static final String TAG = "Register Activity";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
context = getApplicationContext();
btnGCMRegister = (Button) findViewById(R.id.btnGCMRegister);
btnGCMRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
if (TextUtils.isEmpty(regId)) {
regId = registerGCM();
Log.d("RegisterActivity", "GCM RegId: " + regId);
} else {
Toast.makeText(getApplicationContext(),
"Already Registered with GCM Server!",
Toast.LENGTH_LONG).show();
}
}
});
btnAppShare = (Button) findViewById(R.id.btnAppShare);
btnAppShare.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
if (TextUtils.isEmpty(regId)) {
Toast.makeText(getApplicationContext(), "RegId is empty!",
Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent(getApplicationContext(),
MainActivity.class);
i.putExtra("regId", regId);
Log.d("RegisterActivity",
"onClick of Share: Before starting main activity.");
startActivity(i);
finish();
Log.d("RegisterActivity", "onClick of Share: After finish.");
}
}
});
}
public String registerGCM() {
// below line I am getting error
gcm = GoogleCloudMessaging.getInstance(this);
regId = getRegistrationId(context);
if (TextUtils.isEmpty(regId)) {
registerInBackground();
Log.d("RegisterActivity",
"registerGCM - successfully registered with GCM server - regId: "
+ regId);
} else {
Toast.makeText(getApplicationContext(),
"RegId already available. RegId: " + regId,
Toast.LENGTH_LONG).show();
}
return regId;
}
#SuppressLint("NewApi")
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getSharedPreferences(
MainActivity.class.getSimpleName(), Context.MODE_PRIVATE);
String registrationId = prefs.getString(REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
int registeredVersion = prefs.getInt(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 (NameNotFoundException e) {
Log.d("RegisterActivity","I never expected this! Going down, going down!" + e);
throw new RuntimeException(e);
}
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regId = gcm.register(Config.GOOGLE_PROJECT_ID);
Log.d("RegisterActivity", "registerInBackground - regId: "
+ regId);
msg = "Device registered, registration ID=" + regId;
storeRegistrationId(context, regId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
Log.d("RegisterActivity", "Error: " + msg);
}
Log.d("RegisterActivity", "AsyncTask completed: " + msg);
return msg;
}
#Override
protected void onPostExecute(String msg) {
Toast.makeText(getApplicationContext(),
"Registered with GCM Server." + msg,
Toast.LENGTH_LONG)
.show();
}
}.execute(null, null, null);
}
private void storeRegistrationId(Context context, String regId) {
final SharedPreferences prefs = getSharedPreferences(
MainActivity.class.getSimpleName(), Context.MODE_PRIVATE);
int appVersion = getAppVersion(context);
Log.i(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(REG_ID, regId);
editor.putInt(APP_VERSION, appVersion);
editor.commit();
}
}
Here, no need to import google_play_services_lib in your workspace, google_play_services_lib has a jar named google-play-services.jar which needs to be included in your GCM App.
google-play-services.jar contains all GCM related classes, thats why google_play_services_lib has been added as dependency on GCM Android Project. The simplest thing you can do is to add that jar on your GCM project instead of adding dependency of google_play_services_lib project.
To add jar in android project you have to do following things,
1. First create a libs folder under you project, its a libs folder and not lib.
2. Then copy google-play-services.jar inside libs folder.
3. Then right click on the google-play-services.jar and select Build Path > Add to Build Path.
You can see Android Private Libraries created under your project's root folder which will contain this jar.
Note: Path of jar file - sdk\extras\google\google_play_services\libproject\google-play-services_lib\libs

How to get RegistrationID using GCM in android

I am trying to do push notification in android using GCM. I read the Google docs for GCM and their demo application. I created the client side program mentioned here
http://android.amolgupta.in/. But i am not getting registration ID. Also I am not getting some points like:
do i need to server program too with this
on Google demo app they mention that i need to change api key at "samples/gcm-demo-server/WebContent/WEB-INF/classes/api.key" is it necessary to do it every time as i am creating new project
Can any one provide me proper project other than google provided so that i clear my concepts.
Any help will be appreciated.
Here I have written a few steps for How to Get RegID and Notification starting from scratch
Create/Register App on Google Cloud
Setup Cloud SDK with Development
Configure project for GCM
Get Device Registration ID
Send Push Notifications
Receive Push Notifications
You can find a complete tutorial here:
Getting Started with Android Push Notification : Latest Google Cloud
Messaging (GCM) - step by step complete tutorial
Code snippet to get Registration ID (Device Token for Push Notification).
Configure project for GCM
Update AndroidManifest file
To enable GCM in our project we need to add a few permissions to our manifest file. Go to AndroidManifest.xml and add this code:
Add Permissions
<uses-permission android:name="android.permission.INTERNET”/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name=“.permission.RECEIVE" />
<uses-permission android:name=“<your_package_name_here>.permission.C2D_MESSAGE" />
<permission android:name=“<your_package_name_here>.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
Add GCM Broadcast Receiver declaration in your application tag:
<application
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" ]]>
<intent-filter]]>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="" />
</intent-filter]]>
</receiver]]>
<application/>
Add GCM Service declaration
<application
<service android:name=".GcmIntentService" />
<application/>
Get Registration ID (Device Token for Push Notification)
Now Go to your Launch/Splash Activity
Add Constants and Class Variables
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private final static String TAG = "LaunchActivity";
protected String SENDER_ID = "Your_sender_id";
private GoogleCloudMessaging gcm =null;
private String regid = null;
private Context context= null;
Update OnCreate and OnResume methods
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch);
context = getApplicationContext();
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
} else {
Log.d(TAG, "No valid Google Play Services APK found.");
}
}
}
#Override
protected void onResume() {
super.onResume();
checkPlayServices();
}
// # Implement GCM Required methods(Add below methods in LaunchActivity)
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.d(TAG, "This device is not supported - Google Play Services.");
finish();
}
return false;
}
return true;
}
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
Log.d(TAG, "Registration ID not found.");
return "";
}
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.d(TAG, "App version changed.");
return "";
}
return registrationId;
}
private SharedPreferences getGCMPreferences(Context context) {
return getSharedPreferences(LaunchActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (NameNotFoundException e) {
throw new RuntimeException("Could not get package name: " + e);
}
}
private void registerInBackground() {
new AsyncTask() {
#Override
protected Object doInBackground(Object...params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
Log.d(TAG, "########################################");
Log.d(TAG, "Current Device's Registration ID is: " + msg);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return null;
}
protected void onPostExecute(Object result) {
//to do here
};
}.execute(null, null, null);
}
Note : please store REGISTRATION_KEY, it is important for sending PN Message to GCM. Also keep in mind: this key will be unique for all devices and GCM will send Push Notifications by REGISTRATION_KEY only.
In response to your first question: Yes, you have to run a server app to send the messages, as well as a client app to receive them.
In response to your second question: Yes, every application needs its own API key. This key is for your server app, not the client.
Use this code to get Registration ID using GCM
String regId = "", msg = "";
public void getRegisterationID() {
new AsyncTask() {
#Override
protected Object doInBackground(Object...params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(Login.this);
}
regId = gcm.register(YOUR_SENDER_ID);
Log.d("in async task", regId);
// try
msg = "Device registered, registration ID=" + regId;
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
}.execute(null, null, null);
}
and don't forget to write permissions in manifest...
I hope it helps!

Categories

Resources