I've implemented GCM in my app and I've come across a strange issue.
When my overriding IntentService for GCMBaseIntentService sits in my root package it works fine. I reference it in my manifest with .MyGCMIntentService.
The root package would be com.example.rootpackage
When I move my intent service to a different package, such as com.example.rootpackage.service the intent service is never called. At this point I would update my manifest to point to com.example.rootpackage.service.MyGCMIntentService and no dice.
Am I missing something in Google's documentation on locating it or is this just how it works?
Yes, it should be in the root package :
This intent service will be called by the GCMBroadcastReceiver (which is provided by the GCM library), as shown in the next step. It must be a subclass of com.google.android.gcm.GCMBaseIntentService, must contain a public constructor, and should be named my_app_package.GCMIntentService (unless you use a subclass of GCMBroadcastReceiver that overrides the method used to name the service).
(quote taken from here)
EDIT :
As the documentation says, you can change it if you use a subclass of GCMBroadcastReceiver which overrides getDefaultIntentServiceClassName :
public class GCMBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "GCMBroadcastReceiver";
private static boolean mReceiverSet = false;
#Override
public final void onReceive(Context context, Intent intent) {
Log.v(TAG, "onReceive: " + intent.getAction());
// do a one-time check if app is using a custom GCMBroadcastReceiver
if (!mReceiverSet) {
mReceiverSet = true;
String myClass = getClass().getName();
if (!myClass.equals(GCMBroadcastReceiver.class.getName())) {
GCMRegistrar.setRetryReceiverClassName(myClass);
}
}
String className = getGCMIntentServiceClassName(context);
Log.v(TAG, "GCM IntentService class: " + className);
// Delegates to the application-specific intent service.
GCMBaseIntentService.runIntentInService(context, intent, className);
setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
}
/**
* Gets the class name of the intent service that will handle GCM messages.
*/
protected String getGCMIntentServiceClassName(Context context) {
return getDefaultIntentServiceClassName(context);
}
/**
* Gets the default class name of the intent service that will handle GCM
* messages.
*/
static final String getDefaultIntentServiceClassName(Context context) {
String className = context.getPackageName() +
DEFAULT_INTENT_SERVICE_CLASS_NAME;
return className;
}
}
Related
I have a BroadcastReceiver here:
NotificationServiceReceiver:
public class NotificationServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RestService.ACTION_PENDING_REMINDERS_UPDATED)) {
//Reminders updated
NotificationServer.startNotificationWorkRequest(context);
}
}
A Notification Server:
public class NotificationServer extends IntentService {
private static final String LOG_TAG = "NotificationService";
public static final String ACTION_SHOW_NOTIFICATION = "com.android.actions.SHOW_NOTIFICATION";
// this is a bypass used for unit testing - we don't want to trigger this service when the calendar updates during
// the intergration tests
public static boolean sIgnoreIntents = false;
private WorkManager mWorkManager;
private LiveData<List<WorkStatus>> mSavedWorkStatus;
public NotificationServer() {
super(NotificationServer.class.getName());
mWorkManager = WorkManager.getInstance();
}
/**
* Handles all intents for the update services. Intents are available to display a particular notification, clear all
* notifications, refresh the data backing the notification service and initializing our timer. The latter is safe to
* call always, it will check the current state of on-device notifications and update its timers appropriately.
*
* #param intent - the intent to handle. One of ACTION_SHOW_NOTIFICATION,
* ACTION_REFRESH_DATA or ACTION_INIT_TIMER.
*/
#Override
protected void onHandleIntent(Intent intent) {
startNotificationWorkRequest(this);
}
public void startNotificationWorkRequest(Context context) {
WorkContinuation continuation = mWorkManager
.beginUniqueWork(IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker.class));
}
}
I want to start a WorkManager task onReceive of the Broadcast Receiver. The problem is I can't do this statically as I need access to the current WorkManager object. The example code that Google provides here: https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurActivity.java
Grabs the ViewModel like this: ViewModelProviders.of(this).get(BlurViewModel.class);
I can't do this obviously because my notification server class is not a view model. How should I approach this problem?
For anyone that sees this, you can use WorkManager.getInstance() to get the WorkManager object statically. There is only one instance of WorkManager, just make sure you initialize it like this on the start of your application: WorkManager.initialize(this, new Configuration.Builder().build());
Android Custom Work Manager Config official documentation
I am having an issue getting my ActivityRecognition Service to remain running. I currently have a service (GService) that runs continuously in the background. I want to start the ActivityRecognition service within GService, and have the ActivityRecognition service broadcast the activity result back to GService. I am able to start the service and receive feedback that it is running, and I also get one result from the intent handler (no actual data), but never again.
Here is the section of code from my continuous service setting up the intent, pending intent:
#Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
startLocationUpdates();
//start process to receive activity updates
Intent intent = new Intent(this, DetectedActivitiesIntentService.class);
PendingIntent mActivityRecognitionPendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, ActivityConstants.DETECTION_INTERVAL_MILLISECONDS_MOVING,
mActivityRecognitionPendingIntent).setResultCallback(this);
startService(intent); // this should start the DetectedActivitiesIntentService
This is the Broadcast receiver within GService:
public class ActivityDetectionBroadcastReceiver extends BroadcastReceiver {
protected static final String TAG_AR = "ADRR";
#Override
public void onReceive(Context context, Intent intent){
//ArrayList<DetectedActivity> updatedActivities =
// intent.getParcelableArrayListExtra(ActivityConstants.ACTIVITY_EXTRA);
//updateDetectedActivitiesList(updatedActivities);
String action = intent.getAction();
if(action.equals("com.gt.useractivity"))
{
Log.d(TAG_AR, "received broadcast from Activity service");
// below line should grab the resulting string activity from the intent and log it.
Log.d(TAG_AR, "activity is : " + intent.getExtras().getString(ActivityConstants.ACTIVITY_EXTRA));
}
}
}
Here is the ActivityRecognition Service code:
public class DetectedActivitiesIntentService extends IntentService {
protected static final String TAG = "ADIS";
/**
* This constructor is required, and calls the super IntentService(String)
* constructor with the name for a worker thread.
*/
public DetectedActivitiesIntentService() {
// Use the TAG to name the worker thread.
super(TAG);
Log.d(TAG, "Activity service started....");
}
#Override
public void onCreate() {
super.onCreate();
}
/**
* Handles incoming intents.
* #param intent The Intent is provided (inside a PendingIntent) when requestActivityUpdates()
* is called.
*/
#Override
protected void onHandleIntent(Intent intent) {
if(ActivityRecognitionResult.hasResult(intent))
{
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
Intent localIntent = new Intent(ActivityConstants.BROADCAST_ACTION);
// Get the list of the probable activities associated with the current state of the
// device. Each activity is associated with a confidence level, which is an int between
// 0 and 100.
ArrayList<DetectedActivity> detectedActivities = (ArrayList) result.getProbableActivities();
// Log each activity.
Log.i(TAG, "activities detected");
for (DetectedActivity da: detectedActivities) {
Log.i(TAG, ActivityConstants.getActivityString(da.getType()) + " " + da.getConfidence() + "%");
}
String activity = result.getMostProbableActivity().toString(); // get the activity and convert to string
// Broadcast the list of detected activities.
//localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, detectedActivities);
//localIntent.setAction("com.gt.useractivity");
localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, activity); // set the activity string to be transmitted
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
else{
Log.d(TAG, "Intent had no activity data....");
}
}
}
This Activity recognition sample is based from the Google Github sample.
All the examples I have found when using the PendingIntent is being called from a main activity, not from a service. I'm obviously doing something incorrect, but I can't figure it out. Any advice would be appreciated. I should also note that I have 2 broadcast receivers within my GService. I don't know if this would cause an issue or not.
It looks like I have solved the problem. I have a second intent within my GService used for broadcasting. From what I can tell from this thread (Pending intent works correctly for first notification but not for the rest) if there are multiple intents being used, they have to be unique. Thus, I added one line of code when declaring my intent intent.setAction(Long.toString(System.currentTimeMillis())); which is enough to differentiate it from the other intent to the system. Once I did that, I began to receive the Activity broadcasts from the intent service, as well as still receiving the location requests from within the GService routine.
I'm using an intent service to communicate with a server to get data for an app. I'd like the app to wait until the data it requests has been sent back (hopefully meaning that the IntentService the data has been requested from has finished running) before the app attempts to access or use the variables the data is to be stored in. How would I go about doing this? thanks!
Easiest way is to have your IntentService send a Broadcast once it is done gathering data from the server, which you can listen to in your UI thread (e.g. Activity).
public final class Constants {
...
// Defines a custom Intent action
public static final String BROADCAST_ACTION =
"com.example.yourapp.BROADCAST";
...
// Defines the key for the status "extra" in an Intent
public static final String EXTENDED_DATA_STATUS =
"com.example.yourapp.STATUS";
...
}
public class MyIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
...
// Do work here, based on the contents of dataString
// E.g. get data from a server in your case
...
// Puts the status into the Intent
String status = "..."; // any data that you want to send back to receivers
Intent localIntent =
new Intent(Constants.BROADCAST_ACTION)
.putExtra(Constants.EXTENDED_DATA_STATUS, status);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
}
Then create your broadcast receiver (either a separate class or inner class within your Activity)
E.g.
// Broadcast receiver for receiving status updates from the IntentService
private class MyResponseReceiver extends BroadcastReceiver {
// Called when the BroadcastReceiver gets an Intent it's registered to receive
#
public void onReceive(Context context, Intent intent) {
...
/*
* You get notified here when your IntentService is done
* obtaining data form the server!
*/
...
}
}
Now the final step is to register the BroadcastReceiver in your Activity:
IntentFilter statusIntentFilter = new IntentFilter(
Constants.BROADCAST_ACTION);
MyResponseReceiver responseReceiver =
new MyResponseReceiver();
// Registers the MyResponseReceiver and its intent filters
LocalBroadcastManager.getInstance(this).registerReceiver(
responseReceiver, statusIntentFilter );
What I have:
I have a library running on a process using aidl.
I have an app that uses this library, and on the messaging activity, I connect with the service to send messaging and I have a broadcast receiver to manage the incoming messages.
The problem?
if this library are going to be used by two apps on the same device the broadcast actions are going to be the same, and I will have problems when I send a broadcast.
What is my doubt?
What is the best way to "listen" the new incoming messages that I receive on my library and send it to the App.
Maybe a callback? or there are any better solution?
More information
The library provides a few methods to start a session, and other methods for send different type of messages (images, text, locations, etc...) and I receive a callback from another library, that uses C and C++, when a new message is incoming.
If you need more information feel free to ask.
My Code:
IRemote.aidl
interface IRemote
{
int sendTextMessage(String to, String message);
}
WrapperLibrary.java
public class MyLibrary extends Service {
// Current context, used to sendbroadcast() from #Callbacks
private Context mContext = this;
private static MyLibrary instance = new MyLibrary();
//Executor to start a new thread from the service.
final ExecutorService service;
#Override
public IBinder onBind(Intent arg0) {
//Return the interface.
return mBinder;
}
/** Return the current instance */
public static WrapperLibrary getInstance() {
return instance;
}
private final IRemote.Stub mBinder = new IRemote.Stub() {
#Override
public int sendTextMessage(String to, String message)
throws RemoteException {
Log.d(TAG, "Send Text Message. ");
int i = -1;
Future<Integer> task;
task = service.submit(new Callable<Integer>() {
public Integer call() {
return tu.tu_message_send_text(to, message);
}
});
try {
i = task.get();
} catch (Exception e) {
Log.e(TAG, "Send Text Message: EXCEPTION *** " + e.getMessage());
}
Log.d(TAG, "Send Text Message: Status Code: " + i);
return 0;
}
}
Callbacks.java
public class Callbacks extends JNICallback {
private Context mContext;
public Callbacks(Context context) {
this.mContext = context;
}
public void on_incoming_text_message(final String from, final String message) {
Log.d(TAG, " Incoming TEXT message from:" + from + " with message: " + message);
Intent i = new Intent(BroadcastActions.INCOMING_TEXT_MESSAGE);
i.putExtra("from", from);
i.putExtra("message", message);
mContext.sendBroadcast(i);
}
}
MainActivity.java
On this activity I have a broadcast receiver and I can update the UI with a new message
public class MessageReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extra = intent.getExtras();
String incomingMessage = "";
if(extra != null) {
incomingMessage = extra.getString("message");
addNewMessage(new Message(incomingMessage, false));
}
Toast.makeText(MessagingActivity.this, "Incoming Message", Toast.LENGTH_LONG).show();
}
};
I was going to suggest to use LocalBroadcastManager or if it becomes messy EventBus, but if the service runs in its own process (which is not something I'm sure about) the messaging will not be passed from one process to the other.
So I would suggest to define the Broadcast Action from the service in strings.xml and make it different for every app. Of course, you'll have to be careful as you'll need also to update the receiver action for every app.
Well, finally I will use a Callbacks implementation.
The architecture is going to be like this:
App
Main Activity (Connect with the LibService)
LibService (is going to have the callbacks and broadcast receivers)
Library
Callbacks and interfaces but not running in a service.
This is the best approach to me to the future integration on other projects, and the library is going to be more simple without aidl and services.
I thought the use of the receivers was a very good options, but thinking about the integration on other projects this is the best way to do it (for me).
I have an application which is keeping a log of internally developed applications installed on the device. Upon installation a broadcast receiver for Intent.PACKAGE_ADDED is invoked and records the package name using the following code:
public class NewInstallReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Bundle b = intent.getExtras();
int uid = b.getInt(Intent.EXTRA_UID);
String[] packages = context.getPackageManager().getPackagesForUid(uid);
ApplicationService appService = new ApplicationService(context);
appService.ApplicationInstalled(packages);
}
}
The problem I'm facing is when using a broadcast receiver for Intent.PACKAGE_REMOVED, all reference to the package via the unique Id (UID) comes back with null information (As you would expect, given its already been uninstalled). I have a temporary solution for the meantime, but its not very elegant, and for the next version I would like to have cleaner code. An example of how the code should work:
public class RemoveApplicationReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Bundle b = intent.getExtras();
int uid = b.getInt(Intent.EXTRA_UID);
String[] packages = context.getPackageManager().getPackagesForUid(uid);
ApplicationService appService = new ApplicationService(context);
appService.ApplicationRemoved(packages);
}
}
So to recap, the question is:
How, after a program has been removed, can I reference the package name in a broadcast receiver for Intent.PACKAGE_REMOVED.
Thanks
The package names are in the Intent you got from BroadcasReceiver, use the "getData()" function, there is the ComponentMame of the installed/uninstalled package.