ListView won't update onNewIntent() - android

I'm developing a quite simple application that receives GCM's push notifications. There are similar questions here in SO, but this is the only one I found that tries to update the ListView in the onNewIntent() hook, but it wasn't helpful.
The "business logic" should be:
If a notification is received when the application isn't in foreground, add it to the ListView (so, the ListView will have only one item).
If a notification is received when the application is in foreground, add it to the ListView (so, it will have n>1 items).
This is the Activity that holds the ListView:
package ar.edu.uca.ingenieria.notificaciones;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import ar.edu.uca.ingenieria.notificaciones.adapter.NotificacionesAdapter;
import ar.edu.uca.ingenieria.notificaciones.gcm.GcmIntentService;
import ar.edu.uca.ingenieria.notificaciones.model.Notificacion;
/**
* Main UI for the demo app.
*/
public class MainActivity extends Activity {
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private static final String TAG = "GCM Demo";
/**
* Es el project number obtenido en la API Console, como se explica en "Getting Started."
*/
String senderId;
TextView mDisplay;
List<Notificacion> notificaciones;
private ListView notificacionesListView;
GoogleCloudMessaging gcm;
Context context;
String regid;
private NotificacionesAdapter notificacionesAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplay = (TextView) findViewById(R.id.display);
context = getApplicationContext();
senderId = getSenderId();
intentarRegistrarGooglePlayServices();
Notificacion notificacion = getNotificacionFromIntent();
this.notificaciones = new ArrayList<Notificacion>();
inicializarListView(notificacion);
}
private void inicializarListView(Notificacion notificacion) {
this.notificaciones.add(notificacion);
// Create the adapter to convert the array to views
notificacionesAdapter = new NotificacionesAdapter(this, this.notificaciones);
// Attach the adapter to a ListView
this.notificacionesListView = (ListView) findViewById(R.id.lista_notificaciones);
this.notificacionesListView.setAdapter(notificacionesAdapter);
}
private void agregarNotificacion(Notificacion notificacion) {
this.notificaciones.add(notificacion);
// this.notificacionesAdapter.add(notificacion);
this.notificacionesAdapter.notifyDataSetChanged();
}
/**
* Verifica si Google Play Services está instalado en el dispositivo. De ser así, intenta
* registrarse y guarda el regid obtenido.
*/
private void intentarRegistrarGooglePlayServices() {
// Check device for Play Services APK. If check succeeds, proceed with GCM registration.
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
Log.i(TAG, "regid: " + this.regid);
}
private Notificacion getNotificacionFromIntent() {
String tituloNotificacion = (String) getIntent().getCharSequenceExtra(GcmIntentService.GCM_TITULO);
Log.d(TAG, "Título: " + tituloNotificacion);
String mensajeNotificacion = (String) getIntent().getCharSequenceExtra(GcmIntentService.GCM_MENSAJE);
Log.d(TAG, "Mensaje: " + mensajeNotificacion);
// TODO esto muestra "1/1/1970" si no viene la fecha...
long fechaMilisegundos = getIntent().getLongExtra(GcmIntentService.GCM_FECHA, new Date(0).getTime());
Date fechaNotificacion = new Date(fechaMilisegundos);
return new Notificacion(tituloNotificacion, mensajeNotificacion, fechaNotificacion);
}
#Override
protected void onResume() {
super.onResume();
// Check device for Play Services APK.
checkPlayServices();
Log.i(TAG, "regid: " + this.regid);
Log.d(TAG, "onResume");
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent");
this.setIntent(intent);
this.agregarNotificacion(this.getNotificacionFromIntent());
}
/**
* 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 {
Log.i(TAG, "This device is not supported.");
finish();
}
return false;
}
return true;
}
/**
* Stores the registration ID and the app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
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.apply();
}
/**
* Gets the current registration ID for application on GCM 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) {
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;
}
/**
* Registers the application with GCM 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 (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(senderId);
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.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device will send
// upstream messages to a server that echo back the message using the
// 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, 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) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersion(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);
}
}
/**
* #return Application's {#code SharedPreferences}.
*/
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 getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
// TODO implementar esto del lado del server
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP or CCS to send
* messages to your app. Not needed for this demo since the device sends upstream messages
* to a server that echoes back the message using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
}
// TODO: esto no es responsabilidad de esta clase...
// Tal vez un "setting provider" o similar
private String getSenderId() {
Resources res = getResources();
return res.getString(R.string.senderId);
}
}
However, although the agregarNotificacion() method is called with the correct notification, it won't update the ListView items.
I did quite a bit googling, and read that methods like myAdapter.add() should be used, but it didn't work either (that's why I left it commented).
So, in this particular situation (creating the ListView in one Activity hook, and updating it in another), how could I refresh my ListView?
Two final considerations:
There's a small bug: if the application is run for the first time, and a push is received then, it won't be added (since the getNotificacionFromIntent() method is returning an object with null fields, instead of a null Notificacion). I'm aware of it, but running and exiting the app is a quick possible workaround.
Sorry for the "spanglish": this is a university project, so I'm using Spanish names in the variables and classes. But also, there's some copy-paste involved, that I haven't refactored yet.
Any help or direction will be highly appreciated.
Regards.

The solution was to extend from ListActivity, and sending the new items via: this.notificacionesAdapter.add(newItem).

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?

gcm registers again when regid for that user already exists on my db

I was referring to link for gcm implementation and I got it to work 8 months ago.
Today when I tried sending the message to my device it worked but also registered new rigid and null,null email and username when I received the message and saw it in Mainacitvity that Attempt #1 to register was displayed in textview above edittext. But this email id and rigid already exists in db. Also after this process a new rigid with both username and email id was null as mentioned above.
I would like to know what is the right way to implement the gcm service so that the id just updates the previous rigid .But also previous regid also works now so why does gcm re-register gcm-id
final String regId = GCMRegistrar.getRegistrationId(this);
The code above returns null and I have to re-register the user who already exists in my db?
I really appreciate any help. Any other approach to gcm which is easier to implement on client and on my db please let me know.
MainActivity.java
package com.androidhive.pushnotifications;
import static com.androidhive.pushnotifications.CommonUtilities.DISPLAY_MESSAGE_ACTION;
import static com.androidhive.pushnotifications.CommonUtilities.EXTRA_MESSAGE;
import static com.androidhive.pushnotifications.CommonUtilities.SENDER_ID;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gcm.GCMRegistrar;
public class MainActivity extends Activity {
// label to display gcm messages
TextView lblMessage;
// Asyntask
AsyncTask<Void, Void, Void> mRegisterTask;
// Alert dialog manager
AlertDialogManager alert = new AlertDialogManager();
// Connection detector
ConnectionDetector cd;
public static String name;
public static String email;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cd = new ConnectionDetector(getApplicationContext());
// Check if Internet present
if (!cd.isConnectingToInternet()) {
// Internet Connection is not present
alert.showAlertDialog(MainActivity.this,
"Internet Connection Error",
"Please connect to working Internet connection", false);
// stop executing code by return
return;
}
// Getting name, email from intent
Intent i = getIntent();
name = i.getStringExtra("name");
email = i.getStringExtra("email");
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(this);
lblMessage = (TextView) findViewById(R.id.lblMessage);
registerReceiver(mHandleMessageReceiver, new IntentFilter(
DISPLAY_MESSAGE_ACTION));
// Get GCM registration id
final String regId = GCMRegistrar.getRegistrationId(this);
// Check if regid already presents
if (regId.equals("")) {
// Registration is not present, register now with GCM
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
Toast.makeText(getApplicationContext(), "Already registered with GCM", Toast.LENGTH_LONG).show();
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// Register on our server
// On server creates a new user
ServerUtilities.register(context, name, email, regId);
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
}
/**
* Receiving push messages
* */
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
// Waking up mobile if it is sleeping
WakeLocker.acquire(getApplicationContext());
/**
* Take appropriate action on this message
* depending upon your app requirement
* For now i am just displaying it on the screen
* */
// Showing received message
lblMessage.append(newMessage + "\n");
Toast.makeText(getApplicationContext(), "New Message: " + newMessage, Toast.LENGTH_LONG).show();
// Releasing wake lock
WakeLocker.release();
}
};
#Override
protected void onDestroy() {
if (mRegisterTask != null) {
mRegisterTask.cancel(true);
}
try {
unregisterReceiver(mHandleMessageReceiver);
GCMRegistrar.onDestroy(this);
} catch (Exception e) {
Log.e("UnRegister Receiver Error", "> " + e.getMessage());
}
super.onDestroy();
}
}
ServerUtilities.java
package com.androidhive.pushnotifications;
import static com.androidhive.pushnotifications.CommonUtilities.SERVER_URL;
import static com.androidhive.pushnotifications.CommonUtilities.TAG;
import static com.androidhive.pushnotifications.CommonUtilities.displayMessage;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import android.content.Context;
import android.util.Log;
import com.google.android.gcm.GCMRegistrar;
public final class ServerUtilities {
private static final int MAX_ATTEMPTS = 5;
private static final int BACKOFF_MILLI_SECONDS = 2000;
private static final Random random = new Random();
/**
* Register this account/device pair within the server.
*
*/
static void register(final Context context, String name, String email, final String regId) {
Log.i(TAG, "registering device (regId = " + regId + ")");
String serverUrl = SERVER_URL;
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId);
params.put("name", name);
params.put("email", email);
long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
// Once GCM returns a registration id, we need to register on our server
// As the server might be down, we will retry it a couple
// times.
for (int i = 1; i <= MAX_ATTEMPTS; i++) {
Log.d(TAG, "Attempt #" + i + " to register");
try {
displayMessage(context, context.getString(
R.string.server_registering, i, MAX_ATTEMPTS));
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, true);
String message = context.getString(R.string.server_registered);
CommonUtilities.displayMessage(context, message);
return;
} catch (IOException e) {
// Here we are simplifying and retrying on any error; in a real
// application, it should retry only on unrecoverable errors
// (like HTTP error code 503).
Log.e(TAG, "Failed to register on attempt " + i + ":" + e);
if (i == MAX_ATTEMPTS) {
break;
}
try {
Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
Thread.sleep(backoff);
} catch (InterruptedException e1) {
// Activity finished before we complete - exit.
Log.d(TAG, "Thread interrupted: abort remaining retries!");
Thread.currentThread().interrupt();
return;
}
// increase backoff exponentially
backoff *= 2;
}
}
String message = context.getString(R.string.server_register_error,
MAX_ATTEMPTS);
CommonUtilities.displayMessage(context, message);
}
/**
* Unregister this account/device pair within the server.
*/
static void unregister(final Context context, final String regId) {
Log.i(TAG, "unregistering device (regId = " + regId + ")");
String serverUrl = SERVER_URL + "/unregister";
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId);
try {
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, false);
String message = context.getString(R.string.server_unregistered);
CommonUtilities.displayMessage(context, message);
} catch (IOException e) {
// At this point the device is unregistered from GCM, but still
// registered in the server.
// We could try to unregister again, but it is not necessary:
// if the server tries to send a message to the device, it will get
// a "NotRegistered" error message and should unregister the device.
String message = context.getString(R.string.server_unregister_error,
e.getMessage());
CommonUtilities.displayMessage(context, message);
}
}
/**
* Issue a POST request to the server.
*
* #param endpoint POST address.
* #param params request parameters.
*
* #throws IOException propagated from POST.
*/
private static void post(String endpoint, Map<String, String> params)
throws IOException {
URL url;
try {
url = new URL(endpoint);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("invalid url: " + endpoint);
}
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
// constructs the POST body using the parameters
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
bodyBuilder.append(param.getKey()).append('=')
.append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString();
Log.v(TAG, "Posting '" + body + "' to " + url);
byte[] bytes = body.getBytes();
HttpURLConnection conn = null;
try {
Log.e("URL", "> " + url);
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
// post the request
OutputStream out = conn.getOutputStream();
out.write(bytes);
out.close();
// handle the response
int status = conn.getResponseCode();
if (status != 200) {
throw new IOException("Post failed with error code " + status);
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}
The link you are referring to is from 2012, and it uses a deprecated class - GCMRegistrar.
You are advised to refer to a current and more official demo, using the GoogleCloudMessaging class.
As to why final String regId = GCMRegistrar.getRegistrationId(this) returns null, as you can see below, this method searches your shared preferences for a previously stored registration ID. If it doesn't find it (which might happen if your app was uninstalled and installed again, or if you cleared your app's data at some point), or if it detects that the app was upgraded since the last time it was stored, it returns null, and you have to register again to GCM.
public static String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
// check if app was updated; if so, it must clear registration id to
// avoid a race condition if GCM sends a message
int oldVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int newVersion = getAppVersion(context);
if (oldVersion != Integer.MIN_VALUE && oldVersion != newVersion) {
Log.v(TAG, "App version changed from " + oldVersion + " to " +
newVersion + "; resetting registration id");
clearRegistrationId(context);
registrationId = "";
}
return registrationId;
}

how to resolve Android gcm notifications duplicate messages

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.

GCM Console shows 0 (zero) requests after a successful

From my ASP.NET web service and using my GCM Browser API key I can successfully send a GCM Push Notification getting the following success response:
{"multicast_id":4623804699821154941,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1393876717064721%59cd098ff9fd7ecd"}
Two Problems:
1) The Google Developers Console shows 0 (zero) Requests and 0 Errors even after multiple 'Sends' and browser refreshes of the console page. Shouldn't the count change with every GCM Push Notification?
2) My Android device did not receive the push notification.
My code is modeled after:
remote server returned an error: (401) unathorized in C# GCM response
The passed in Android device ID was copied from a successful Android device registration.
My Server (web-service) code is below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Text;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Collections.Specialized;
public class AndroidGCMPushNotification
{
public AndroidGCMPushNotification()
{
//
// TODO: Add constructor logic here
//
}
public string SendNotification(string deviceId, string message)
{
string BrowserAPIKey = "xxxxxxxxxxxxxxxxxxxxxxxxx"; // GCM Browser Key
string tickerText = "ticker test GCM";
string contentTitle = "content title GCM";
string postData = "{ \"registration_ids\": [ \"" + deviceId + "\" ], \"data\": {\"tickerText\":\"" + tickerText + "\", \"contentTitle\":\"" + contentTitle + "\", \"message\": \"" + message + "\"}}";
string sResponseFromServer = SendGCMNotification(BrowserAPIKey, postData);
return sResponseFromServer;
}
private string SendGCMNotification(string apiKey, string postData, string postDataContentType = "application/json")
{
String sResponseFromServer = "";
// from here:
// https://stackoverflow.com/questions/11431261/unauthorized-when-calling-google-gcm
//
// original:
// http://www.codeproject.com/Articles/339162/Android-push-notification-implementation-using-ASP
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateServerCertificate);
//
// MESSAGE CONTENT
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
//
// CREATE REQUEST
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://android.googleapis.com/gcm/send");
Request.Method = "POST";
Request.KeepAlive = false;
Request.ContentType = postDataContentType;
//Request.Headers.Add(string.Format("Authorization: key={0}", apiKey));
Request.Headers.Add(HttpRequestHeader.Authorization, String.Format("key={0}", apiKey));
Request.ContentLength = byteArray.Length;
//Stream dataStream;
try
{
Stream dataStream = Request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
catch (Exception e)
{
sResponseFromServer = e.Message;
}
//
// SEND MESSAGE
try
{
WebResponse Response = Request.GetResponse();
HttpStatusCode ResponseCode = ((HttpWebResponse)Response).StatusCode;
if (ResponseCode.Equals(HttpStatusCode.Unauthorized) || ResponseCode.Equals(HttpStatusCode.Forbidden))
{
sResponseFromServer = "Unauthorized - need new token";
}
else if (!ResponseCode.Equals(HttpStatusCode.OK))
{
sResponseFromServer = "Response from web service isn't OK";
}
StreamReader Reader = new StreamReader(Response.GetResponseStream());
sResponseFromServer = Reader.ReadToEnd();
Reader.Close();
}
catch (Exception e)
{
sResponseFromServer = e.Message;
}
return sResponseFromServer;
}
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
On the Android client side I have the following notification-receive code:
package com.MichaelResslerFineArt.eclipmessenger;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity;
import com.MichaelResslerFineArt.eclipmessenger.R;
import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmBroadcastReceiver;
import com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmIntentService;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.app.Activity;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class ProfileActivity extends Activity {
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 int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
String SENDER_ID = "594966827111"; // GCM Project Number
public static final String LOG_MSG_TAG = "eClipMessenger";
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
SharedPreferences prefs;
Context context;
String regid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
context = getApplicationContext();
// Check device for Play Services APK.
GcmApi gcmApi = new GcmApi();
if (gcmApi.checkPlayServices(this)) {
// If this check succeeds, proceed with normal processing.
// Otherwise, prompt user to get valid Play Services APK.
Toast.makeText(this, "Success checking APK.", Toast.LENGTH_LONG).show();
Log.i(LOG_MSG_TAG, "Success checking APK.");
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
else {
Log.i(LOG_MSG_TAG, "GCM Reg ID: " + regid);
}
} else {
Log.i(LOG_MSG_TAG, "No valid Google Play Services APK found.");
}
}
// You need to do the Play Services APK check here too.
#Override
protected void onResume() {
super.onResume();
GcmApi gcmApi = new GcmApi();
gcmApi.checkPlayServices(this);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.profile, menu);
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(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(LOG_MSG_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(LOG_MSG_TAG, "App version changed.");
return "";
}
return registrationId;
}
/**
* #return Application's {#code SharedPreferences}.
*/
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 getSharedPreferences(ProfileActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersion(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);
}
}
/**
* 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 (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
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.
// The request to your server should be authenticated if your app
// is using accounts.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, 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(LOG_MSG_TAG, msg);
//mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
* or CCS to send messages to your app. Not needed for this demo since the
* device sends upstream messages to a server that echoes back the message
* using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
private void storeRegistrationId(Context context, String regId) {
Log.i(LOG_MSG_TAG, "entered storeRegistrationId()");
final SharedPreferences prefs = getGCMPreferences(context);
Log.i(LOG_MSG_TAG, "retrieved preferences");
int appVersion = getAppVersion(context);
Log.i(LOG_MSG_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();
}
/*
public void sendGcm(final View view) {
if (view == findViewById(R.id.button2)) {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
Bundle data = new Bundle();
data.putString("my_message", "Hello World");
data.putString("my_action",
"com.google.android.gcm.demo.app.ECHO_NOW");
String id = Integer.toString(msgId.incrementAndGet());
gcm.send(SENDER_ID + "#gcm.googleapis.com", id, data);
msg = "Sent message";
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Log.i(LOG_MSG_TAG, msg);
//mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
// } else if (view == findViewById(R.id.textView2)) {
// mDisplay.setText("");
}
}
*/
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(LOG_MSG_TAG, "onReceive");
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
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
/*
* 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 you're not interested in, or that you don't
* recognize.
*/
if (GoogleCloudMessaging.
MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server: " +
extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// This loop represents the service doing some work.
for (int i=0; i<5; i++) {
Log.i(LOG_MSG_TAG, "Working... " + (i+1)
+ "/5 # " + SystemClock.elapsedRealtime());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
Log.i(LOG_MSG_TAG, "Completed work # " + SystemClock.elapsedRealtime());
// Post notification of received message.
sendNotification("Received: " + extras.toString());
Log.i(LOG_MSG_TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
// Put the message into a notification and post it.
// This is just one simple example of what you might choose to do with
// a GCM message.
private void sendNotification(String msg) {
Log.i(LOG_MSG_TAG, "sendNotification");
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, ProfileActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("GCM Notification")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
}
And this separate class for checking the Play Services:
package com.MichaelResslerFineArt.eclipmessenger;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
public class GcmApi {
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 int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
String SENDER_ID = "594966827111"; // GCM Project Number
/**
* 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(ProfileActivity currActivity) {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(currActivity);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, currActivity,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(ProfileActivity.LOG_MSG_TAG, "This device is not supported.");
Toast.makeText(currActivity, "This device is not supported.", Toast.LENGTH_LONG).show();
currActivity.finish();
}
return false;
}
return true;
}
}
And the Android client side manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.MichaelResslerFineArt.eclipmessenger"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission android:name="com.MichaelResslerFineArt.eclipmessenger.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.MichaelResslerFineArt.eclipmessenger.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.MichaelResslerFineArt.eclipmessenger.ProfileActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<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="com.MichaelResslerFineArt.eclipmessenger" />
</intent-filter>
</receiver>
<service android:name=".GcmIntentService" />
</application>
</manifest>
GcmBroadcastReceiver and GcmIntentService shouldn't be inner classes of your activity class. They should be regular (top level) classes.
When you declare them as .GcmBroadcastReceiver and .GcmIntentService in your manifest, in means they should be located at com.MichaelResslerFineArt.eclipmessenger.GcmBroadcastReceiver and com.MichaelResslerFineArt.eclipmessenger.GcmIntentService, but since you implemented them as inner classes, they are actually located in com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmBroadcastReceiver and com.MichaelResslerFineArt.eclipmessenger.ProfileActivity.GcmIntentService, so they cannot be found when the message reaches your device.

java.lang.NoClassDefFoundError: com.google.android.gms.common.GooglePlayServicesUtil

I'm trying to make a basic Google Cloud Messaging application, However, I get following error:
02-07 12:53:51.510 642-642/com.youtubemp3downloader E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: com.google.android.gms.common.GooglePlayServicesUtil
at com.youtubemp3downloader.MainActivity.checkPlayServices(MainActivity.java:222)
at com.youtubemp3downloader.MainActivity.onCreate(MainActivity.java:53)
at android.app.Activity.performCreate(Activity.java:5008)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
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)
Here is the code MainActivity.java :
package com.youtubemp3downloader;
import android.annotation.TargetApi;
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.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import org.w3c.dom.Text;
import java.io.IOException;
public class MainActivity extends Activity {
private static final String TAG = "MyActivity";
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
GoogleCloudMessaging gcm;
Context context;
String regid;
String SENDER_ID="201069056364";
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
if (checkPlayServices()) {
// If this check succeeds, proceed with normal processing.
// Otherwise, prompt user to get valid Play Services APK.
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
}
}
/**
* 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.
*/
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
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.");
Toast.makeText(getApplicationContext(), "Registration not found.", 10).show();
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.");
Toast.makeText(getApplicationContext(), "App version changed.", 10).show();
return "";
}
return registrationId;
}
/**
* #return Application's {#code SharedPreferences}.
*/
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 getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
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);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
switch (item.getItemId()) {
case R.id.action_settings:
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* 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() {
#Override
protected String doInBackground(Object... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
Toast.makeText(getApplicationContext(), msg, 10).show();
// 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.
// The request to your server should be authenticated if your app
// is using accounts.
// sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, 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(Object msg) {
}
}.execute(null, null, null);
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
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();
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}
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.i("MyActivity", "This device is not supported.");
Toast.makeText(getApplicationContext(), "This devise is not supported.", 10).show();
finish();
}
return false;
}
return true;
}
}
Here is my build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
}
}
apply plugin: 'android'
repositories {
mavenCentral()
}
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
minSdkVersion 9
targetSdkVersion 16
}
buildTypes {
release {
runProguard true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:+'
compile 'com.google.android.gms:play-services:4.0.30'
}
I am using Android Studio. Can someone help me solve the problem?
try adding this dependency in gradle:
compile 'com.android.support:support-v4:18.0.0'
or else Upgrade to Android Studio 0.4.0 or later, or clean and rebuild your project if you want to stay on 0.3.2. There's a bug in the 0.6 versions of the Gradle plugin (https://code.google.com/p/android/issues/detail?id=63366) that can cause newly-added dependencies to not get linked into your APK unless you clean your project after doing so.
Make sure you have this in your SDK directory
$ANDROID_SDK_DIR\extras\google\m2repository\com\google\android\gms\play-services\4.0.30
Also upgrade your Android Studio to 0.4.3. In 0.4.2 your code will not find the symbols in java class files but will compile fine.

Categories

Resources