I keep getting this message in LogCat whenever I try to send a message from the demo GoogleAppEngine page that's built into Android Studio.
:GCM message com.objectivetruth.uoitlibrarybooking >0:1408853167972751%99d31532f9fd7ecd
:broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=com.objectivetruth.uoitlibrarybooking (has extras) }
:Unregister application com.objectivetruth.uoitlibrarybooking for user 0
:Implicit intents with startService are not safe: Intent { act=com.google.android.c2dm.intent.UNREGISTER (has extras) } android.content.ContextWrapper.startService:494 ftg.a:1607 ftg.a:361
I'm about to rip my hair out after following this guide from google twice from scratch to make sure I followed it perfectly and I keep getting the same errors on my device
What's crazy is that its growing, If i send another message, there will be a cascade of calls from, what I assume is, all the previous messages that haven't been dealt with as yet. I tried a local GAE instance then I uploaded it to the cloud and still same issue. The registration works fine AFAIK.
I have a toast that tells me the resulting registration code which I assume means its success.
Also, the local server shows me every time a new registration occurs so I think that's fine as well
Here's my app Manifest's relevant parts which I've quadruple checked:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<permission
android:name="com.objectivetruth.uoitlibrarybooking.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.objectivetruth.uoitlibrarybooking.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission
android:name="com.google.android.c2dm.permission.RECEIVE" />
<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.objectivetruth.uoitlibrarybooking" />
</intent-filter>
</receiver>
<service android:name=".GcmIntentService" />
and the GcmBroadcastReceiver
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Timber.i("Please please call me!");
// 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);
}
Here's the GcmIntentService
public class GcmIntentService extends IntentService {
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)) {
Timber.i(extras.toString());
showToast(extras.getString("message"));
}
}
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();
}
});
}
}
The AsyncGcmRegistration is copy/pasted from the guide
Really not sure what else might be wrong..
OMG, if anyone stumbles upon this in the future and is tearing their hair out like I was, the reason you get result=canceled is because there's a legitamate issue with where the intent is going.
DO NOT IGNORE IT.
In my case I had the receive declarations outside my tags so it was not recognizing the pkg correctly.
Do not ignore the manifest, if you have issues with receiving very high chance its because of it.
Related
Manifest.xml of receiving app.
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<receiver android:name=".DemoReceiver"
android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="com.example.myparentapplication.demo"/>
</intent-filter>
</receiver>
Receiver declared in receiving app:
public class DemoReceiver extends BroadcastReceiver {
public DemoReceiver(){}
#Override
public void onReceive(Context context, Intent intent) {
Log.v(TAG, "Received");
String data = intent.getStringExtra("data");
Toast.makeText(context,"opening the intent data : " + data,Toast.LENGTH_LONG ).show();
}
}
Broadcast sent by broadcasting app:
Intent intent = new Intent("com.example.myparentapplication.demo");
intent.putExtra("data","job dispatched");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.setComponent(new ComponentName(getApplicationContext().getPackageName(), "com.example.myparentapplication.DemoReceiver"));
sendBroadcast(intent);
This way I am using broadcast after upgrading my api to Android 'O', but I am not receiving any broadcast. where as in documentation it says that explicit broadcast are allow to register in manifest after Android Oreo. Please guide me appropriately that what is wrong here?
Note: Receiving app is a background app. so I can not register broadcast dynamically. I have tried almost all the solution from stack overflow but still it's not working.
intent.setComponent(new ComponentName(getApplicationContext().getPackageName(), "com.example.myparentapplication.DemoReceiver"));
getApplicationContext().getPackageName() will return the application ID of the sending app. Your ComponentName needs the application ID of the receiving app.
We have implemented GCM to show notifications in android application but only some messages not receiving in devices sent by server to google? Server is sending messages without collapse_key so every message should reach in device without collapsed. Please suggest me to proceed further.
Thanks
Receiver code is below:
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);
}
}
service code is below:
public class GcmIntentService extends IntentService {
NotificationCompat.Builder builder;
public GcmIntentService() {
super("GcmIntentService");
}
public static final String TAG = "GCM Demo";
#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(TAG, "Working... " + (i + 1)
+ "/5 # " + SystemClock.elapsedRealtime());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
generateNotification(this, name, id, message);
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
}
Manifest file is below
<permission
android:name="MY_PACKAGE_NAME.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="MY_PACKAGE_NAME.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<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="MY_PACKAGE_NAME" />
</intent-filter>
</receiver>
<service android:name=".GcmIntentService" />
You need to create a Google account in that device (Settings -> Accounts -> Google). So please check whether it's logged with Google or not :)
When implementing xmpp over the Cloud Connection Server of Google i receive messages that will start an WakefulBroadcastReceiver.
Manifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission
android:name="com.tag.xmppandroid.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.tag.xmppandroid.permission.C2D_MESSAGE"/>
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="com.tag.xmppandroid"/>
</intent-filter>
</receiver>
<service android:name=".GcmIntentService"></service>
WakefulBroadcastReceiver
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);
}
Now this WakefulBroadcastReceiver will start an Intent service called GcmIntentService.class. The purpose of this intentService is receiving the message and directly send a message back as xmpp to the server that send the message. Actually this works, i send the message back with gcm.send(...) and the server receives the messages. But only when im debugging.
The server won't receive the message when the application is not in debugging mode. When the application is in debug mode it will send the message to the xmpp server. It looks like the thread shuts down to early and the message could not be send back to the server. But when im debugging i prevent the service from sleeping and it has time to send the message or anything. Does anyone know why this happens?
Code of my Intentservice:
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
String message_id = null;
String time_to_live = null;
if (!extras.isEmpty()) // has effect of unparcelling Bundle
{
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + extras.toString(), null);
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Get values from intent send over xmpp
message_id = ((intent.getExtras() == null) ? "Empty Bundle" : intent.getExtras().getString("message_id"));
String message = ((intent.getExtras() == null) ? "Empty Bundle" : intent.getExtras().getString("message"));
time_to_live = ((intent.getExtras() == null) ? "Empty Bundle" : intent.getExtras().getString("time_to_live"));
Bundle data = new Bundle();
data.putString("hello", "world");
data.putString("delivered", "true");
try {
gcm.send(Constants.GCM_SENDER_ID + "#gcm.googleapis.com", message_id, Constants.GCM_TIME_TO_LIVE, data);
} catch (IOException e) {
e.printStackTrace();
}
// Send data to the view
sendToView(message_id, message, time_to_live);
// Show notification
sendNotification("Received: " + message, extras);
// Show toast on main thread
Handler mHandler = new Handler(getMainLooper());
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), Constants.GCM_SENDER_ID, Toast.LENGTH_SHORT).show();
}
});
}
} else {
sendToView("empty_bundle", null, null);
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
I hope someone can explain me why this happens, or what i do wrong since i really want to understand why this happens.
I want to put up a notification in the notification bar that will launch my app when pressed. While I have no problems doing this, my users want the notification to come up after a reboot as well. They have an app from another vendor that does this.
Everything I can find states that the app must be running for the notification to display. Any ideas?
You need to add a receiver that launches a Service after a reboot.
In your manifest register for Boot Complete
<service android:name="com.meCorp.service.MeCorpServiceClass"/>
...
<receiver android:name="com.meCorp.receiver.MyRebootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
...
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
In your boot receiver, launch a service.
public class MyRebootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, MeCorpServiceClass.class);
serviceIntent.putExtra("caller", "RebootReceiver");
context.startService(serviceIntent);
}
}
Here is an example for a service class to run in the background.
public class MeCorpServiceClass extends IntentService{
#Override
protected void onHandleIntent(Intent intent){
String intentType = intent.getExtras().getString("caller");
if(intentType == null) return;
if(intentType.Equals("RebootReceiver"))
//Do reboot stuff
//handle other types of callers, like a notification.
}
}
OR Just use a third party like Urban AirShip, which handles all that for you.
I'm monitoring incoming SMSs.
My app is working perfectly with a BroadcastReceiver. However it is working from an Activity and would like to keep the BroadcastReceiver running all the time (and not just when my Activity is running).
How can I achieve this? I've looked through the lifecycle of the BroadcastReceiver but all that is mentioned in the documentation is that the lifecycle is limited to the onReceive method, not the lifecycle of keeping the BroadcastReceiver checking for incoming SMS.
How can I make this persistent?
Thanks
You need to define a receiver in manifest with action name android.intent.action.BOOT_COMPLETED.
<!-- Start the Service if applicable on boot -->
<receiver android:name="com.prac.test.ServiceStarter">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Make sure also to include the completed boot permission.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Use Service for this to make anything persist. And use receivers to receive Boot Up events to restart the service again if system boots..
Code for Starting Service on boot up. Make Service do your work of checking sms or whatever you want. You need to do your work in MyPersistingService define it your self.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceStarter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent("com.prac.test.MyPersistingService");
i.setClass(context, MyPersistingService.class);
context.startService(i);
}
}
Service or Boot Completed is not mandatory
In fact, you don't need to implement a Service or register to android.intent.action.BOOT_COMPLETED
Some examples shows how to register/unregister a BroadcastReceiver when activity is created and destroyed. However, this is useful for intents that you expect only when app is opened (for internal communication between Service/Activity for example).
However, in case of a SMS, you want to listen to the intent all the time (and not only when you app is opened).
There's another way
You can create a class which extends BroadcastReceiver and register to desired intents via AndroidManifest.xml. This way, the BroadcastReceiver will be indepedent from your Activity (and will not depend from Activity's Life Cycle)
This way, your BroadcastReceiver will be notified automatically by Android as soon as an SMS arrive even if your app is closed.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
...
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application>
....
<receiver android:name=".MyCustomBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
MyCustomBroadcastReceiver.java
public class MyCustomBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent != null) {
String action = intent.getAction();
if(action != null) {
if(action.equals("android.provider.Telephony.SMS_RECEIVED")) {
// DO YOUR STUFF
} else if (action.equals("ANOTHER ACTION")) {
// DO ANOTHER STUFF
}
}
}
}
}
Notes
You can add others intent-filters to AndroidManifest and handle all of them in same BroadcastReceiver.
Start a Service only if you will perform a long task. You just need to display a notification or update some database, just use the code above.
Add Broadcast Reciever in manifest:
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
Create Class BootReciever.java
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
// +++ Do Operation Here +++
}
}
}
Beside #Javanator answer I would like to include a case for Android version of (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) In my case this is working for Android SDK 29 (10)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context,FloatingWindow.class));
} else {
context.startService(new Intent(context, FloatingWindow.class));
}
use this code and also mention the broadcast in Manifest also:
public class BootService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
Toast.makeText(context, "Boot Completed", Toast.LENGTH_SHORT).show();
//write code here
}
}
}
I just want to mention that in case of some Chinese phone brands (e.g. MI), you need to go to Settings and give autostart permission to your app.
Otherwise the battery optimisation feature will kill your service in background and broadcast receiver will not work.
So you can redirect your user to Settings and ask them to give that permission.