I am looking for the best way to notify a user that they need to install a package by handling the possibility of an external package not being found.
In this particular case I am wishing to implement the TODO: if intent not found, notification on need to have GSF in the C2DMMessaging class
public static void register(Context context,
String senderId) {
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.setPackage(GSF_PACKAGE);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, senderId);
context.startService(registrationIntent);
// TODO: if intent not found, notification on need to have GSF
}
I'm thinking that I should look for the error W/ActivityManager( 60): Unable to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gsf (has extras) }: not found
But how to trap that error?
UPDATE - Just found that the call to startService returns a ComponentName instance which is null if the service failed to start so my code now looks like this
ComponentName name = context.startService(registrationIntent);
// TODO: if intent not found, notification on need to have GSF
if(name == null){
Util.log_debug_message("#### REG INTENT FAILED");
}else{
Util.log_debug_message("#### REG INTENT SUCCEEDED");
}
(For anyone looking for this solution Util.log_debug is just a function I created in a util class to call Log.d so just replace this with a call to Log.d)
Which seems to work just fine so I guess I need to send a broadcast message with an extra to indicate the package needs to be installed. The receiver could then show an alert dialog explaining that the user must install!
What does the user need to install? and how would the user install whatever is needed to be installed?
Thanks in advance for any tips, pointers code snippets and help
I have resolved this issue - In the activity that calls the C2DM registration event I have this code
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
register();
}
protected void register() {
String reg_id = C2DMessaging.getRegistrationId(this);
String email = Util.getEmail(this);
if(reg_id == null || reg_id == ""){
Util.log_debug_message("#### Registering with C2DM");
Toast.makeText(this, "Registering with C2DM", Toast.LENGTH_LONG).show();
if(C2DMessaging.register(this, Config.C2DM_SENDER)){
showLoadingDialog();
}else{
showInstallGSFDialog();
}
}else if(email == null || email =="-1"){
Util.log_debug_message("**** Updating server with new auth token");
register_with_server();
}
}
private void showInstallGSFDialog(){
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("ERROR!");
alertDialog.setMessage("Please ensure you have a valid GMail account set up on your phone." +
" This application needs to use Google's C2DM service");
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
} });
alertDialog.show();
}
and I changed the C2DMessaging.register to a boolean method and added the check to ensure that the service started like so...
/**
* Initiate c2d messaging registration for the current application
*/
public static boolean register(Context context, String senderId) {
boolean res = false;
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.setPackage(GSF_PACKAGE);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, senderId);
ComponentName name = context.startService(registrationIntent);
// if intent not found, notification on need to have GSF by NOT setting resukt of this function to true
if(name == null){
Util.log_debug_message("#### REG INTENT FAILED");
}else{
Util.log_debug_message("#### REG INTENT SUCCEEDED");
res = true;
}
return res;
}
Related
How to open app when notification received without user interaction
I am using react native push notification library for Push notification. App should auto start from background and quit state without user interaction and getInitialNotification method should be called. I want to invoke app on specific notification type.
===============================================
File: RNPushNotificationHelper
===============================================
public void invokeApp(Bundle bundle) {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
Class<?> activityClass = Class.forName(className);
Intent activityIntent = new Intent(context, activityClass);
if(bundle != null) {
activityIntent.putExtra("notification", bundle);
}
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activityIntent);
} catch(Exception e) {
Log.e(LOG_TAG, "Class not found", e);
return;
}
}
===============================================
File: RNReceivedMessageHandler
===============================================
// If notification ID is not provided by the user for push notification, generate one at random
if (bundle.getString("id") == null) {
SecureRandom randomNumberGenerator = new SecureRandom();
bundle.putString("id", String.valueOf(randomNumberGenerator.nextInt()));
}
Application applicationContext = (Application) context.getApplicationContext();
RNPushNotificationConfig config = new RNPushNotificationConfig(mFirebaseMessagingService.getApplication());
RNPushNotificationHelper pushNotificationHelper = new RNPushNotificationHelper(applicationContext);
boolean isForeground = pushNotificationHelper.isApplicationInForeground();
RNPushNotificationJsDelivery jsDelivery = new RNPushNotificationJsDelivery(context);
bundle.putBoolean("foreground", isForeground);
bundle.putBoolean("userInteraction", false);
jsDelivery.notifyNotification(bundle);
// If contentAvailable is set to true, then send out a remote fetch event
if (bundle.getString("contentAvailable", "false").equalsIgnoreCase("true")) {
jsDelivery.notifyRemoteFetch(bundle);
}
Log.v(LOG_TAG, "invokeApp: " + bundle);
pushNotificationHelper.invokeApp(bundle);
if (config.getNotificationForeground() || !isForeground) {
Log.v(LOG_TAG, "sendNotification: " + bundle);
pushNotificationHelper.sendToNotificationCentre(bundle);
}
But It does not seem to work in Background OR Quit state. I checked logcat it showing logs when in foreground.
Notification actions
I am assuming that this is not possible because it would be a major security gap.
In the Android docs, only the tap on the notification and notification actions are listed as options for actions in the background.
The section.
Restrictions bypasses
There is a so-called "Restrictions on starting activities from the background" from Android 10 (API level 29).
However, these have several exceptions.
I think the most interesting for you are these:
The app receives a notification PendingIntent from the system. In the
case of pending intents for services and broadcast receivers, the app
can start activities for a few seconds after the pending intent is
sent.
The app has been granted the SYSTEM_ALERT_WINDOW permission by the
user.
I am making calling app using twilio sdk i have integrated all stuff and it is in wokring condition. But the problem is when my android app remains in the background for long time then app didn't receive incoming call request , pending intent not invoked by Twilio Here is the code:
if (!Twilio.isInitialized()) {
/*
* Needed for setting/abandoning audio focus during call
*/
Twilio.initialize(context, new Twilio.InitListener() {
/*
* Now that the SDK is initialized we can register using a Capability Token.
* A Capability Token is a JSON Web Token (JWT) that specifies how an associated Device
* can interact with Twilio services.
*/
#Override
public void onInitialized() {
isInitialized = true;
Twilio.setLogLevel(Log.VERBOSE);
/*
* Retrieve the Capability Token from your own web server
*/
retrieveCapabilityToken();
}
#Override
public void onError(Exception e) {
isInitialized = false;
Logging.e(TAG, e.toString());
notifyOnStopListening(null,-1,context.getString(R.string.error_message_not_connecting));
}
});
} else {
retrieveCapabilityToken();
}
}
private void retrieveCapabilityToken() {
// in the success of api response
if (clientDevice == null) {
clientDevice = Twilio.createDevice(capabilityToken,deviceListener);
}
if(clientDevice != null) {
Intent intent = new Intent(context, ReceiveCallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
clientDevice.setIncomingIntent(pendingIntent);
isIntentAdded = true;
Logging.e(TAG,"in addPendingIntent intent added");
}
}
Any suggestion , help would be appreciated.
Last year when you added a Cloud Endpoints plus GCM module to an Android Studio project, the IDE created some sample code both in the backend and the app that showed how to use GCM with Cloud Endpoints.
However, with the newer versions of Android Studio you only get the backend part added for you. So I went back into my old projects and dug up some of the convenient app code which registered, and sent GCM push notifications in Android.
Here is what that code looks like:
GcmBroadcastReceiver.java
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// 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);
}
}
GcmIntentService.java
public class GcmIntentService extends IntentService {
android.support.v4.app.NotificationCompat.Builder notification;
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 != null && !extras.isEmpty()) { // has effect of unparcelling Bundle
// Since we're not using two way messaging, this is all we really to check for
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
Logger.getLogger("GCM_RECEIVED").log(Level.INFO, extras.toString());
showToast(extras.getString("message"));
sendNotification(extras.getString("message"));
}
}
//call to the API and get new data.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
protected void showToast(final String message) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
});
}
private void sendNotification(String msg) {
notification = new android.support.v4.app.NotificationCompat.Builder(this);
//set number of notifications count
//notification.setNumber(x);
//cancels notification when app is opened.
notification.setAutoCancel(true);
//build the notification
notification.setSmallIcon(R.drawable.greenicon);
notification.setTicker("This is the ticker!");
//set time
notification.setWhen(System.currentTimeMillis());
notification.setContentTitle("New message!");
notification.setContentText(msg);
notification.setSound((Settings.System.DEFAULT_NOTIFICATION_URI));
//LED
notification.setLights(Color.RED, 3000, 3000);
// intent
Intent intent = new Intent(this, MainActivity.class);
//give phone access to perform this intent b/c they may be in another part of their phone.
//aka gives phone access to the intents in our app
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//what to do when notification is clicked:
notification.setContentIntent(pendingIntent);
//Builds notification and issues it (sends it to device). Can build and send out notifcations
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//send out notification with uniqueID
nm.notify(2158, notification.build());
}
}
GcmRegistrationAsyncTask
class GcmRegistrationAsyncTask extends AsyncTask<Void, Void, String> {
private static Registration regService = null;
private GoogleCloudMessaging gcm;
private Context context;
// TODO: change to your own sender ID to Google Developers Console project number, as per instructions above
private static final String SENDER_ID = "1026567774990";
public GcmRegistrationAsyncTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(Void... params) {
if (regService == null) {
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// Need setRootUrl and setGoogleClientRequestInitializer only for local testing,
// otherwise they can be skipped
.setRootUrl("https://push-notif-45657747.appspot.com/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest)
throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
}) ;
// end of optional local run code
regService = builder.build();
}
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
String 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.
regService.register(regId).execute();
} catch (IOException ex) {
ex.printStackTrace();
msg = "Error: " + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Logger.getLogger("REGISTRATION").log(Level.INFO, msg);
}
}
However, I am getting some deprecated errors in Android Studio now:
gcm.register(SENDER_ID); is deprecated and so is GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.
This GCM stuff is pretty confusing to begin with and while there is some information here on how to use it, I was wondering if anyone had any currently working non-deprecated examples or maybe you could suggest some edits to the above code if you know what you are doing...? Much thanks!
Wanted to give people a little guide here in case they were lost.
First check out and stay up to date with this Google Cloud Messaging Android example:
https://github.com/google/gcm
To make it work you will have to generate a google-services.json file which you can do here:
https://developers.google.com/mobile/add
Make sure you are logged into the google developers console before you go that link. It will load your projects for you and automatically set up the gcm api key for you in your projects credentials.
Copy/paste the google-services.json into the /app directory of your Android project.
Add a cloud endpoints with gcm module to the android project.
Enter your gcm api key (which you can view on your credentials page on developers console) into the webapp-WEB_INF/appengine-web.xml file in your cloud endpoints backend:
<property name="gcm.api.key" value="your-api-key-here"/>
This way, inside the Android client and MessagingEndpoint the code will automatically get the api key (in the endpoint it will be the line Sender sender = new Sender(API_KEY); for example, which will just retrieve it for you).
Run the sample gcm android project and it should work. Send a push notification with the API's explorer you deployed.
BIG NOTE: when you are ready to use the sample code in your own app make sure the RegistrationIntentService is in the root of your package or it won't work! Took a while to figure that out... Not sure if it is a bug or what.
I have been using parse for about 2 months for android and am very happy with it. I have built most of the app, just need to get push notifications finalized. I read the parse push guide and am able to receive and customize them.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
ParseObject.registerSubclass(ParseFeedBack.class);
ParseObject.registerSubclass(ParseReportIssue.class);
ParseObject.registerSubclass(ParseOrder.class);
ParseObject.registerSubclass(ParseAddress.class);
Parse.enableLocalDatastore(getApplicationContext());
Parse.initialize(this, "ID", "ID");
ParseInstallation.getCurrentInstallation().saveInBackground();
}}
So the questions is
Do I leave ParseInstallation.getCurrentInstallation() in the application class itself? Or shift it out to the main activity and register over there since I also want to identify the user associated with it, hence ParseInstallation.put("userPointer", ParseUser.getCurrentUser) will need to be added on my main activity.
Does this function have to be called everytime the user opens the app or only the first time? In which case I will place the code in the registration page so its called only once.
What happens If user A uses user B's phone to login. In that case this code ParseInstallation.put("userPointer", ParseUser.getCurrentUser) will associate installation on B with user A as he had logged in last. So If i send a targeted push notification to user A, it will be received on his as well as User B phone.
In ParsePushBroadcastReceiver, I am overriding onPushRecieve, and building my notification over there. Im not placing any pending intent with the notification but instead overriding onPushOpen(). Is this recommended or should I just add a pending intent in the builder itself and leave onPushOpen() empty.
Google metioned that on October 15th. I noticed in manifest parse still uses C2DM. Have they released any blog about the changes they will implement?
.
public class MyPushReceiver extends ParsePushBroadcastReceiver {
final Integer PRIORITY_MAX=2;
NotificationCompat.Builder builder;
String message;
public MyPushReceiver() {
super();
}
#Override
protected void onPushReceive(Context context, Intent intent) {
super.onPushReceive(context, intent);
builder=new NotificationCompat.Builder(context);
try {
JSONObject jsonObject=new JSONObject(intent.getExtras().getString("com.parse.Data"));
message=jsonObject.getString("alert");
builder.setContentText(message);
builder.setContentTitle(jsonObject.getString("title"));
builder.setPriority(PRIORITY_MAX);
int mNotificationId = 001;
NotificationManager mNotifyMgr =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyMgr.notify(mNotificationId, builder.build());
Log.d("PUSH ", jsonObject.toString());
Log.d("s", intent.toString());
}catch (JSONException e){
e.printStackTrace();
}
}
#Override
protected void onPushOpen(Context context, Intent intent) {
super.onPushOpen(context, intent);
Toast.makeText(context, "Opening push!", Toast.LENGTH_LONG).show();
Intent i=new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
public class SessionManager extends BroadcastReceiver{
Date timeOff;
Date timeOn;
#Override
public void onReceive(Context context, Intent intent) {
if( "android.intent.action.SCREEN_OFF".equals(intent.getAction())) {
Log.i("MobileViaNetReceiver", "Screen off - start time to end session");
timeOff = Calendar.getInstance().getTime();
}
if( "android.intent.action.ACTION_SHUTDOWN".equals(intent.getAction())) {
// DO WHATEVER YOU NEED TO DO HERE
Log.i("MobileViaNetReceiver", "Shut down - log off user");
DbAdapter_User db = new DbAdapter_User(context);
db.open();
db.handleLogout();
db.close();
}
if( "android.intent.action.SCREEN_ON".equals(intent.getAction())) {
timeOn = Calendar.getInstance().getTime();
long diffInMs = timeOn.getTime()-timeOff.getTime();
// convert it to Minutes
long diffInMins = TimeUnit.MILLISECONDS.toMinutes(diffInMs);
if ((int) (diffInMins) > 15) {
//log out user
Log.i("MobileViaNetReceiver", "User inactive for 15 minutes - logout user");
DbAdapter_User db = new DbAdapter_User(context);
db.open(); // ******* HERE *************
db.handleLogout();
db.close();
} else {
Log.i("MobileViaNetReceiver", "User still active");
}
}
}
When the screen is turned ON I am checking if the user has turned scrren off for more than 15 mins, if yes, logout him. And go to LonIn screen.
I want to start an intent when I call that handleLogout() (marked * HERE **)
Can I do that when class extends BroadcastReceiver ? If no, what else can I do ?
You need to remember intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Blessings and happiness to the Google engineer who wrote the followint error message "E/AndroidRuntime( 2339): java.lang.RuntimeException: Unable to start receiver android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?"
public class AgeingAutoStartBroadcast extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent intent1 = new Intent(context,MyMainClass.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
);
}
}
Thanks to everyone who writes helpful messages here
Yes, you can do that. You just use the context that was passed into your onRecieve function when you're creating your Intent. Once you have the Intent, make this call:
Context.startActivity(yourIntent);
You certainly can. Try
Intent yourIntent = new Intent(context, YourActivity.class);
startActivity(yourIntent);
If you want to do it in your handleLogout method, pass in the Context.
private method handleLogout(Context context) {
...
}