How can I maintain a persistent BroadcastReceiver in a Dashclock extension? - java

Here's the scaffolding code of my Dashclock extention. It receives messages from GCM and displays the information.
public class ComputerWidget extends DashClockExtension {
private MessageReceiver objMessageReceiver;
private class MessageReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context ctxContext, Intent ittIntent) {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(ctxContext);
String strType = gcm.getMessageType(ittIntent);
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(strType)) {
showInformation(ittIntent.getExtras());
}
}
}
#Override
protected void onInitialize(boolean booReconnect) {
super.onInitialize(booReconnect);
if (objMessageReceiver != null) {
try {
unregisterReceiver(objMessageReceiver);
} catch (Exception e) {
e.printStackTrace();
}
}
IntentFilter intentFilter = new IntentFilter("com.google.android.c2dm.intent.RECEIVE");
intentFilter.addCategory("com.something.something");
objMessageReceiver = new MessageReceiver();
registerReceiver(objMessageReceiver, intentFilter);
}
public void onCreate() {
super.onCreate();
}
protected void showInformation(Bundle bndBundle) {
final ExtensionData edtInformation = new ExtensionData();
setUpdateWhenScreenOn(true);
try {
if (bndBundle.getString("event").equalsIgnoreCase("logon")) {
edtInformation.visible(true);
edtInformation.expandedTitle(bndBundle.getString("machine"));
edtInformation.expandedBody(getString(R.string.logon, bndBundle.getString("username")));
}
} catch (Exception e) {
edtInformation.visible(false);
}
edtInformation.icon(R.drawable.ic_dashclock);
publishUpdate(edtInformation);
}
public void onDestroy() {
super.onDestroy();
if (objMessageReceiver != null) {
try {
unregisterReceiver(objMessageReceiver);
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
protected void onUpdateData(int arg0) {
setUpdateWhenScreenOn(true);
System.out.println("Update");
}
}
As you can see, I'm registering my GCM BroadcastReceiver when my extension is initialized and I'm unregistering it when the extension is destroyed.
This piece of code works just fine as long as the receiver is initialized but Dashclock, after a period of inactivity, destroys the extension and therefore I can't receive any GCM messages.
How can I keep the BroadcastReceiver alive so that I can receive GCM messages? Is there a way to accomplish this programatically and not through the manifest file?
I came to this solution after my last question regarding BroadcastRecievers — How do I publish an update to Dashclock when my application receives an Intent?

How can I keep the BroadcastReceiver alive so that I can receive GCM messages?
Register it in the manifest, the way the GCM samples show you.
Is there a way to accomplish this programatically and not through the manifest file?
No, but you can enable and disable the manifest-registered receiver via PackageManager and setComponentEnabledSetting().

Related

Android Request to server before application compeletly closed

I have just one Activity , when user close the application (from os clear list of recent apps) I want to send a request to my server api and change user status.
so I make IntentService and call it in my onDestroy() method of activity, but it dosn't work. how do it? is there any way else to do this(send request to server before application killed compeletly)?
my code :
Activity:
#Override
protected void onDestroy() {
Intent intent = new Intent(this, MakeOfflineIntentService.class);
intent.putExtra(Variables.INTENT_TOKEN, Token);
intent.setAction("ACTION_MAKE_OFFLINE");
startService(intent);
super.onDestroy();
}
and in my IntentService:
public class MakeOfflineIntentService extends IntentService {
private static final String ACTION_MAKE_OFFLINE = "ACTION_MAKE_OFFLINE";
private static final String EXTRA_TOKEN = Variables.INTENT_TOKEN;
public MakeOfflineIntentService() {
super("MakeOfflineIntentService");
}
public static void startActionFoo(Context context, String param1) {
Intent intent = new Intent(context, MakeOfflineIntentService.class);
intent.setAction(ACTION_MAKE_OFFLINE);
intent.putExtra(EXTRA_TOKEN, param1);
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_MAKE_OFFLINE.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_TOKEN);
retrofitBaseInformationChange(param1,Variables.OFFLINE,1);
}
}
}
private void retrofitBaseInformationChange(final String Token, final int online, int vehicle){
RetrofitCallServer retrofitCallServer = new RetrofitCallServer(WebServiceUrls.RETROFIT_INFORMATION_CHEETAH_MAN);
OnCallBackRetrofit onCallBackRetrofit = retrofitCallServer.getResponse();
Call<OBRbaseInfromationChange> call = onCallBackRetrofit.askBaseInformationChange(Token,online,vehicle);
call.enqueue(new Callback<OBRbaseInfromationChange>() {
#Override
public void onResponse(Call<OBRbaseInfromationChange> call, Response<OBRbaseInfromationChange> response) {
/*response gotten maybe success or not*/
if (response.isSuccessful()){
OBRbaseInfromationChange obr = response.body();
if(obr.code == 200){
Log.i(Variables.APP_TAG,"BaseInformationChange successful");
}
else{
Log.i(Variables.APP_TAG,"BaseInformationChange error code: "+obr.code);
}
}// end if response successful
else {
Log.i(Variables.APP_TAG,"BaseInformationChange not Successful: "+response.code());
}
}
#Override
public void onFailure(Call<OBRbaseInfromationChange> call, Throwable t) {
/*our request not sent or conversion problem*/
Log.i(Variables.APP_TAG,"onFailure BaseInformationChange: "+t.getMessage());
}
});
}
// end retrofitBaseInformationChange()
}
and finally here is in my manifest:
<service
android:name=".Services.MakeOfflineIntentService"
android:exported="false"
android:stopWithTask="false"/>
Have you tried to return START_STICKY in the onStartCommand override?
After you sent your request you can then call stopService to stop yourself.
As far as I know, even sticky services might be "recreated" when you kill the app. So maybe, an Intent is not the best way to use here.
I'd go with SharedPreferences here:
The onCreate of your app sets the key "app_offline" to "false"
The onDestroy sets this key to "true" and starts the service
The service is START_STICKY and when it finds the "app_offline" as true, sends its request, updates "app_offline" to false (resets it) and then performs a self-shutdown.
Something like that.
Hope this helps, cheers, Gris
thanks for Grisgram answer, I solve the issue and paste my code here for more complete answer :
I make a variable in SharedPreferences name IS_APP_CLOSED.
when application open in onCreate :
saveL.saveInLocalStorage(Variables.IS_APP_CLOSED,false);
startServiceToMakeOffline();
method startServiceToMakeOffline() is :
private void startServiceToMakeOffline(){
Intent intent= new Intent(this, MakeOfflineService.class);
startService(intent);
}
in onDestroy of this activity :
#Override
protected void onDestroy() {
saveL.saveInLocalStorage(Variables.IS_APP_CLOSED,true);
super.onDestroy();
}
and here is my service class :
public class MakeOfflineService extends Service {
private boolean isAppClosed = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
loadInfoFromLocalStorage();
if(isAppClosed){
askServer();
}
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void loadInfoFromLocalStorage() {
SharedPreferences prefs = getApplicationContext().getSharedPreferences(Variables.CHEETAH_NORMAL, 0);
isAppClosed = prefs.getBoolean(Variables.IS_APP_CLOSED, false);
prefs = null;
}
// end loadInfoFromLocalStorage()
private void askServer() {
//TODO: request server than when result gotten:
stopSelf();
}
}
and here is my manifest :
<service
android:name=".Services.MakeOfflineService"
android:stopWithTask="false"/>

Detect USB STATE CHANGE android java

I want to be able to trigger an event when a user connects or disconnects a USB device. I have the following code:
public static boolean isConnected(Context context) {
Intent intent = context.registerReceiver(null, new IntentFilter("android.hardware.usb.action.USB_STATE"));
return intent.getExtras().getBoolean("connected");
}
The above is from another StackOverflow question.
How can I go about the problem I'm facing? I don't think it would be practical to check every x second if the state has changed. So, I checked online for an event listener about USB_CONNECTION, but I can't seem to find any. Actually, I did find a few but they were confusing and quite old. (A year or two ago.)
TRIED CODE
while(notFinished) {
if (!isConnected(this)) {
pause();
} else if (!mediaPlayer.isPlaying() && isConnected(this)) {
play();
} else {
Log.e("[ERROR]: ", "An error has occured at startSong!");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
^^^ Clearly not efficient, but it does work (somewhat). So, that's why I came over here to know if there was a listener for USB_CONNECTION_STATUS. I think this would be a lot faster.
You can use BroadcastReceiver for receiving connected and disconnected actions:
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras().getBoolean("connected")) {
//start doing something for state - connected
} else {
//start doing something for state - disconnected
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction("android.hardware.usb.action.USB_STATE");
registerReceiver(receiver, filter);
This BroadcastReceiver will receive actions every time wen usb connected or disconnected.

Destroying activity causes service to lose data

Whenever my application is minimized I start a service that is sending pull requests to my HTTP server to check for notifications, when the application is brought back up the service gets terminated (along with the scheduled runnable). All works well until I decided to kill the application (slide it off the screen from the running apps list). Then for some reason the properties of the service get reset (even the static ones) and onStartCommand gets called again with it's first parameter Intent as null which is weird for me.
Here are some parts of the code
public class DnActivity extends Activity {
protected String cookieString = "";
protected String userAgent = "";
protected WebView webview;
#Override
protected void onStop() {
super.onStop();
try {
Intent mServiceIntent = new Intent(this, PullService.class);
mServiceIntent.putExtra("cookieString", cookieString);
mServiceIntent.putExtra("userAgent", userAgent);
startService(mServiceIntent);
} catch (Exception e) {
Log.d("DNev", e.getMessage());
}
}
#Override
protected void onStart() {
super.onStart();
Intent mServiceIntent = new Intent(this, PullService.class);
stopService(mServiceIntent);
}
#Override
public void onCreate(Bundle savedInstanceState) {
...
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
try {
cookieString = getCookieFromAppCookieManager(url);
} catch (Throwable e) {
Log.e("DNev", e.getMessage());
}
}
});
}
}
And the service
public class PullService extends Service {
protected static String cookieString;
protected static String userAgent = "Mobile APP for Android";
protected Service PullService = this;
protected ScheduledFuture interval;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (intent.hasExtra("cookieString")) {
cookieString = intent.getStringExtra("cookieString");
}
if (intent.hasExtra("userAgent")) {
userAgent = intent.getStringExtra("userAgent");
}
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
interval.cancel(true);
}
#Override
public void onCreate() {
super.onCreate();
Log.d("DNev", String.valueOf(cookieString));
Log.d("DNev", String.valueOf(userAgent));
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
interval = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
Log.d("DNev", "1");
Log.d("DNev", String.valueOf(cookieString));
Log.d("DNev", String.valueOf(userAgent));
...
As I said, everything works fine until I destroy the activity, then the interval keeps running but cookieString and userAgent become their default values.
I need to be able to persist these values when the activity gets destroyed, how can I do that?
I'm not experienced in neither android nor java development, and I want to apologize if my code made anyone cry blood.
Here is the manifest entry for the service, it resides in <application
<service android:name=".PullService" android:exported="false"/>
All works well until I decided to kill the application (slide it off the screen from the running apps list).
When you kill the app (which I assume Force Stop from i.e. Settings -> Apps) then WHOLE app gets terminated, including its services. Everything stored in variables will go away with the process. If you want it to survive, you need to store it in persistent storage (i.e. in database or shared preferences).
Also I'd save this data once I received it, in onStartCommand() because if onDestroy() will not be called (which is not unlikely for abruptly killed process) then your data would be lost.
I start a service that is sending pull requests to my HTTP server to check for notifications
Don't. Use GCM to actually push notification to the app. Do not pull.
in the DnActivity.onDestroy() method, save the info somewhere, you could have the "shutting down" of the activity control the mServiceIntent and do alterations to it (like shutting it down as well)
For instance:
DnActivity.onDestroy(){
super.onDestroy();
stopService(mServiceIntent);
Intent mServiceIntent = new Intent(this, PullService.class);
mServiceIntent.putExtra("some_value", the_value);
mServiceIntent.putExtra("some_other_value", the_other_value);
startService(mServiceIntent);
}

Google Cloud Messaging - Check if device is already registered

I just set up GCM in my Android App. But I have the problem that I don't know how to check if the device is already registered. I work with the new google play services library.
The register part looks like this:
#Override
protected String doInBackground(String... arg0) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context_app);
}
regid = gcm.register(SENDER_ID);
msg = "Dvice registered, registration ID=" + regid;
Log.d("111", msg);
sendRegistrationIdToBackend(regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
How can I modify this that it checks if the device is already registered?
Store the registration id in a databade table or shared preference and when app starting..check whether it is null or not
Google has provided very clear documentation with code.You should use following code:
// 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);
registerReceiver(mHandleMessageReceiver,
new IntentFilter(DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
mDisplay.append(getString(R.string.already_registered) + "\n");
} 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) {
boolean registered =
ServerUtilities.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
#Override
protected void onDestroy() {
if (mRegisterTask != null) {
mRegisterTask.cancel(true);
}
unregisterReceiver(mHandleMessageReceiver);
GCMRegistrar.onDestroy(this);
super.onDestroy();
}
private final BroadcastReceiver mHandleMessageReceiver =
new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
mDisplay.append(newMessage + "\n");
}
};
when you get registration Id, Store it in SharedPreferences, for example:
SharedPreferences shp = context.getSharedPreferences("anyNameYouLike",MODE_PRIVATE);
SharedPreferences.Editor editor=shp.edit();
editor.putString("RegID",registrationID).commit;
In the next time before you register check the "anyNameYouLike" if it contain field called RegID Like this:
private boolean isRegistered(Context context){
SharedPreferences shp = context.getSharedPreferences("anyNameYouLike",PRIVATE_MODE);
return shp.contains("RegID");
}

Local Service Binding Code from Activity to Service

The client code for binding to a service, which is normally in the activity class; I'm trying to move it to the service class, so that the activity class would be as clean and small as possible.
i.e. basically trying to merge the code in the second box here into the first box = as much of it into the service class as possible
Single Line in Activity for Binding to Service
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Bind to service with this line only:
AService.bindService(this);
}
}
Static bindService and ServiceConnection Moved to Service
public class AService extends Service {
public String test = "I want to see this";
public static AService aService;
private static boolean isBound;
private static Context context;
// ... IBinder, onBind etc also here on service side
public static void bindService(Context context) {
try {
Log.i(TAG, "bindService Start");
if (!isBound && context != null) {
Log.i(TAG, "Binding");
context.bindService(new Intent(context, AService.class),
serviceConnection, Context.BIND_AUTO_CREATE);
isBound = true;
Log.i(TAG, "Bound");
}
} catch (Exception e) {
Log.e(TAG, "bindService", e);
}
}
private static ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
try {
Log.i(TAG, "onServiceConnected Start");
aService = ((AService.LocalBinder) service).getService();
if (aService != null)
Log.i(TAG, aService.test);
Log.i(TAG, "onServiceConnected Finish");
} catch (Exception e) {
Log.e(TAG, "onServiceConnected", e);
}
}
public void onServiceDisconnected(ComponentName className) {
try {
Log.i(TAG, "onServiceDisconnected");
aService = null;
} catch (Exception e) {
Log.e(TAG, "onServiceDisconnected", e);
}
}
};
public static void unbind() {
try {
Log.i(TAG, "unbind start");
if (isBound && context != null) {
Log.i(TAG, "Unbinding");
context.unbindService(serviceConnection);
isBound = false;
context = null;
Log.i(TAG, "Unbound");
}
} catch (Exception e) {
Log.e(TAG, "unbind", e);
}
}
}
But onServiceConnected is Never Called?
The log shows everything up to:
...
Bound
But NOT onServiceConnected Start or beyond
and no exceptions.
Note that when the same code was in the Activity, it works (when called with MyActivity.this)
What am I doing wrong?
Is this
AService.bindService(this);
much better than this?
bindService(new Intent(context, AService.class),
serviceConnection, Context.BIND_AUTO_CREATE);
And does ServiceConnection implementation sit in Activity really annoying your so much? I doubt that.
I don't see any point centralize everything into Service and then call a static method in the actual Service to start this Service from Activity. The best practice is to follow the standard way that Google's recommended to do things, by doing this in your way, you make your code obscure and confuse other people when reading your code (if you work in a team). It doesn't make any sense IMO.
Instead of put all your effort into isolate every single bit of service from activity, I would rather consider more on how to isolate business logic from activity and centralize them into service, and let Activity mostly focus on UI stuff.
Really hope that would help you.

Categories