I am working on an application for research purposes. We want to track some user activity on the phone: if some actions are easily detactable with broadcast receivers, we want also to check current running applications.
This application works only on Android devices that runs android 5.0 or lower.
My problem is that I post a Runnable Obj in a handler, Runnable posts again itself in handler after HALF_SECOND (see code for details). In runnable I get information and send them to IntentService to perform work.
Everything works fine: app starts at boot, handler and runnable do their job in background UNLESS I open the main Activity.
The app is able to keep going for days, but if I open the main Activity and then close it from "recent open activities" with a swipe, or from the memory task manager, handler and runnable stop, even if they are not called/accessed by the activity (they are in a Separate Service).
Moreover, not always a call to onDestroy (of the activity or Service) is made.
Reading online I understand that swipe or task manager remove the app from memory abrouptly thus not always calling onDestory.
What I want to achive is to make the handler start again soon after the main activity is closed.
What I have tried is to put some check in onPause method of the activity, making sure to remove this check if onStart is called again (like in case the the app switches from vertical to horizontal layout, or if home button is pressed and then app is opend again). Also implemented a way to make the handler send "ImAlive" intent to a broadcast receiver, which should restart the service that starts the handler, if intents do not arrive before a count down is finished. Unfortunately, as soon the main activty stops existing, even the broadcast is automatically unregistered and destroyed.
My question is, is there a way to create something that is able to make my handler restart if the activity is closed? Or is there some other pattern that can help me as workaround for what I want to achieve? Because I am polling data every half second I read is better to use handler, because Timer augments small interval to a greater interval, and AlarmManager is not precise enough for very small interval.
What I want to achieve is something similar to Facebook, Instagram, Whatsapp, Telegram app, that are always in memory, and even if you force to terminate them, after a few seconds are back again there... how?
We are not interested in battery issues because of continuous polling to data. As for research purposes we don't mind if the phone on which we are testing last 2 days straight, 1 day or 12 hours or less.
Here the code: OnBootService is started from broadcast receiver, declared in manifest when onBootCompleted and ShutDown actions are received, in order to start and stop handler.
public class OnBootService extends Service{
private static final Handler handler = new Handler();
private final long HALF_SEC = 500;
private RunnableTest r = null;
private Context myContext = this;
private final String TAG = "BootService";
// Extras
public static final String START = "start";
public static final String STOP = "stop";
#Override
public IBinder onBind(Intent intent){
return null;
}
#Override
public int onStartCommand(Intent intent, int flag, int startId){
String action = intent.getAction();
switch(action){
case START: startHandler();
break;
case STOP: stopHandler();
break;
}
return START_NOT_STICKY;
}
private void startHandler(){
if(r == null){
r = new RunnableTest();
handler.post(r);
Log.i(TAG, "----Handler started!");
}
}
private void stopHandler(){
if(r != null){
Log.i(TAG, "----calling STOP");
handler.removeCallbacks(r);
r = null;
}
}
private class RunnableTest implements Runnable {
private String TAG = "RunnableTest";
public RunnableTest(){}
#Override
public void run(){
handler.removeCallbacks(this);
// Do stuff
Intent i = new Intent(myContext, MyIntentService.class);
i.putExtra("addStuff", myStuff);
myContext.startService(i);
handler.postDelayed(this, HALF_SEC);
}
}
Activity is empty: all method overridden just to understand proper Activity lifecycle, but else is empty for now.
public class MainActivity extends AppCompatActivity {
private final String TAG = "Activity";
private Context myContext = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// access a file and get stored information to show
setContentView(R.layout.activity_main);
Toast.makeText(getBaseContext(), "Application open successfully", Toast.LENGTH_LONG).show();
}
#Override
protected void onRestart(){
super.onRestart();
Log.e(TAG, "----onRestart Called");
}
#Override
protected void onStart(){
super.onStart();
Log.e(TAG, "----onSTART Called");
}
#Override
protected void onResume(){
super.onResume();
Log.e(TAG, "----onRESUME Called");
}
#Override
protected void onPause(){
super.onPause();
Log.e(TAG, "----onPAUSE Called");
}
#Override
protected void onStop(){
super.onStop();
Log.e(TAG, "----onSTOP Called");
}
#Override
protected void onDestroy(){
super.onDestroy();
Log.e(TAG, "----onDestroy Called");
}
}
Any help is really appreciated, if you need some more information on the code, I will update the post.
Thank you!
Android system can restart the service if u return START_STICKY inside onStartCommand().
It works perfectly on all lower version than Lollipop in Android.
No need of CountDownTimer.
Related
I've implemented something similar to Android notification of screen off/on, but it's not working the way it should. All I want to do is stop music when the screen is turned off. I created a screen action class like this
public class ScreenAction extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// DO WHATEVER YOU NEED TO DO HERE
wasScreenOn = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// AND DO WHATEVER YOU NEED TO DO HERE
wasScreenOn = true;
}
}
}
Then, in my main activities on create I have this
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScreenAction();
registerReceiver(mReceiver, filter);
In my main activities onPause, I have something like this:
public void onPause() {
super.onPause();
if (ScreenAction.wasScreenOn) {
final MediaPlayer mp = MediaPlayer.create(this, R.raw.pcmouseclick1);
mp.setVolume(.1f, .1f);
mp.start();
if (buttonState) {
mServ.reduceVolume();
}
}
}
I found this from an online source, but I am having issues. It seems that the screen state is always set as true, and I'm not sure how to change this.
How do I utilize this ScreenAction class to turn off music in on Pause ONLY when the user has locked the screen? I feel like I am missing something in the onPause because in onCreate I link to the class.
#Override
protected void onPause() {
// when the screen is about to turn off
// or when user is switching to another application
super.onPause();
}
#Override
protected void onResume() {
// only when screen turns on
// or when user returns to application
super.onResume();
}
Here you can see the entire lifecycle of an Activity in android:
Activity Lifecycle
Maybe you could also start and stop your music directly from the receiver:
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Pause music player
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// Resume music player
}
}
You're checking ScreenAction.wasScreenOn in onPause, but that's happening before the BroadcastReceiver is called to notify you that the screen is being turned off. So at that point, ScreenAction.wasScreenOn is still true, and then it's set to false, but onPause has already run, so your music never gets paused.
To solve this, you should take the action directly in response to the screen turning off in the BroadcastReceiver. If you need to interact with your UI, consider a solution like LiveData as an abstraction so that you're not reliant on your Activity being paused at the exact moment the screen is turned off (consider also that the onPause solution wouldn't work if your Activity weren't currently visible).
i have this AccessibilityService class for the USSD
public class USSDService extends AccessibilityService {
public static String TAG = "USSDService";
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.d(TAG, "onAccessibilityEvent");
String text = event.getText().toString();
if (event.getClassName().equals("android.app.AlertDialog")) {
performGlobalAction(GLOBAL_ACTION_BACK);
Log.d(TAG, text);
Intent intent = new Intent("com.times.ussd.action.REFRESH");
intent.putExtra("message", text);//this is the value that i want to return to MainActivity
Toast.makeText (this,text,Toast.LENGTH_LONG).show();
}
}
#Override
public void onInterrupt() {
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d(TAG, "onServiceConnected");
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.flags = AccessibilityServiceInfo.DEFAULT;
info.packageNames = new String[]{"com.android.phone"};
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
setServiceInfo(info);
}}
and i invoke in main activity like this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
////////////////USSD////////////////////
Intent mIntent = new Intent(this, USSDService.class);
startService(mIntent);
}
and this is a function that dial ussd
private void dailNumber(String code) {
String ussdCode = "*" + code + Uri.encode("#");
Intent callIntent = new Intent(Intent.ACTION_CALL)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
callIntent.setData(Uri.parse("tel:" + ussdCode));
callIntent.putExtra("simSlot", 0);
startActivity(callIntent);
}
when i call this function by clicking a button, every thing is ok and the response of ussd call show in Toast as i code it in function onAccessibilityEvent
what i want is return the response value to MainActivity
Accessibility services cannot be started in the manner you are attempting to start them. They can only be managed from the Accessibility Settings menu. This is for good reason. Accessibility services can read screen contents. OH, you downloaded my app, that's nice. NOW I'm going to launch an Accessibility Service that watches all of your screen content for things that look like Credit Card numbers.
Intent mIntent = new Intent(this, USSDService.class);
startService(mIntent);
The lines of code above ARE NOT accomplishing what you think they are. The accessibility service will launch (call your service's onCreate), but it will launch in a functionally limited state that does not allow it to bind to applications and the Android Operating System in the way an Accessibility Service normally would.
Once properly configured, go to Settings -> Accessibility and find your service listed next to TalkBack. Then turn it on. Once you do this, in order to communicate between your two processes you're going to need some type of interprocess communication mechanism. For simple communication you can consider using SharePreferences. Otherwise, you're probably going to have to look into BroadCastReceivers.
See how far that gets you, and ask another question if you need!
I'd like to implement an update checker in an application, and I obviously only need this to show up once when you start the application. If I do the call in the onCreate() or onStart() method, it'll be shown every time the activity is created and this is not a viable solution.
So my question is: Is there a way to do something, like check for updates, just once per application start / launch?
I'm sorry if it's a bit hard to understand, I'm having difficulties explaning myself on this one.
SharedPreferences seems like ugly solution to me. It's much more neat when you use application constructor for such purposes.
All you need is to use your own Application class, not default one.
public class MyApp extends Application {
public MyApp() {
// this method fires only once per application start.
// getApplicationContext returns null here
Log.i("main", "Constructor fired");
}
#Override
public void onCreate() {
super.onCreate();
// this method fires once as well as constructor
// but also application has context here
Log.i("main", "onCreate fired");
}
}
Then you should register this class as your application class inside AndroidManifest.xml
<application android:label="#string/app_name" android:name=".MyApp"> <------- here
<activity android:name="MyActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
You even can press Back button, so application go to background, and will not waste your processor resources, only memory resource, and then you can launch it again and constructor still not fire since application was not finished yet.
You can clear memory in Task Manager, so all applications will be closed and then relaunch your application to make sure that your initialization code fire again.
looks like you might have to do something like this
PackageInfo info = getPackageManager().getPackageInfo(PACKAGE_NAME, 0);
int currentVersion = info.versionCode;
this.versionName = info.versionName;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int lastVersion = prefs.getInt("version_code", 0);
if (currentVersion > lastVersion) {
prefs.edit().putInt("version_code", currentVersion).commit();
// do the activity that u would like to do once here.
}
You can do this every time, to check if the app has been upgraded, so it runs only once for app upgrade
The shared preferences approach is messy, and the application class has no access to an activity.
Another alternative I've used is to have a retained fragment instance, and within that instance, a lot more stuff can be done especially if you need access to the main activity UI.
For this example, I've used asynctask within the retained fragment. My AsyncTask has callbacks to the parent activity. It is guaranteed to run only once per application because the fragment is never destroyed-recreated when the same activity is destroyed-recreated. It is a retained fragment.
public class StartupTaskFragment extends Fragment {
public interface Callbacks {
void onPreExecute();
void onProgressUpdate(int percent);
void onCancelled();
void onPostExecute();
}
public static final String TAG = "startup_task_fragment";
private Callbacks mCallbacks;
private StartupTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // this keeps fragment in memory even if parent activity is destroyed
mTask = new StartupTask();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class StartupTask extends AsyncTask<Void, Integer, Void> {
#Override
protected void onPreExecute() {
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
#Override
protected Void doInBackground(Void... ignore) {
// do stuff here
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent[0]);
}
}
#Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore) {
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
}
Then, in main (or parent) activity where you want this startup task fragment to run once.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
StartupTaskFragment st = (StartupTaskFragment) fm.findFragmentByTag(StartupTaskFragment.TAG);
if(st == null) {
fm.beginTransaction().add(mStartupTaskFragment = new StartupTaskFragment(), StartupTaskFragment.TAG).commit();
}
...
}
Ideas for retained fragment came from here: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html. I just figured out its other uses aside from config changes.
Yes you can do it Using SharedPrefernce concept of android. Just create a boolean flag and save it in SharedPrefernce and check its value in your onCreate() method .
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("onlyonce", false)) {
// <---- run your one time code here
// mark once runned.
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("onlyonce", true);
editor.commit();
}
}
This continues on #Vitalii's answer.
After having setup the Application class, if access to the Activity is required, we can use the aptly named android library "Once" https://github.com/jonfinerty/Once.
In the Application class's onCreate method
Once.initialise(this)
In the Activity / Fragment class's onCreate / onViewCreated method.
val helloTag = "hello"
if (!Once.beenDone(Once.THIS_APP_SESSION, helloTag)) {
//Do something that needs to be done only once
Once.markDone(helloTag) //Mark it done
}
I do this the same way as described in the other answer. I just have a global variable in the first activity which matches the release number from the manifest. I increment it for every upgrade and when the check sees a higher number, it executes the one-time code.
If successful, it writes the new number to shared preferences so it wont do it again until the next upgrade.
Make sure you assign the default to -1 when you retrieve the version from shared preferences so that you error on the side of running the code again as opposed to not running it and not having your app update correctly.
Use SharedPreference for this-
If you are not restarting your launcher activity again once your app is active then in that case you case use it.
Use this in a Splash screen if you are implementing it in the app.
If you are not using any splash screen then you need to create a activity with no view set and on it's oncreate call you can do start updation and start your main activity.
you can use counter value or boolean for this.
Here is SharedPreference doc:
http://developer.android.com/reference/android/content/SharedPreferences.html
try {
boolean firstboot = getSharedPreferences("BOOT_PREF",MODE_PRIVATE)
.getBoolean("firstboot", true);
if(firstboot){
//place your code that will run single time
getSharedPreferences("BOOT_PREF",MODE_PRIVATE).edit().
putBoolean("firstboot", false)
.commit();
}
I just solved doing this myself, I reopen my main activity multiple times throughout the application's execution. While the constructor is a valid approach for some things it doesn't let you access the current Application context to write toasts among other things.
My solution was to create a simple 'firstRun' boolean set to true in the class of my MainActivity, from there I run the contents of the if statement then set it to true. Example:
public class MainActivity extends AppCompatActivity
{
private static boolean firstRun = true;
#Override
protected void onCreate(Bundle savedInstanceState)
{
if(firstRun)
{
Toast.makeText(getApplicationContext(), "FIRST RUN", Toast.LENGTH_SHORT).show();
//YOUR FIRST RUN CODE HERE
}
firstRun = false;
super.onCreate(savedInstanceState);
//THE REST OF YOUR CODE
}
I have a service that is, among other things, downloading images from internet. When this is done I want to show this image in a custom Activity that has a dialog theme. But I only want to use this pop up if the app is running, otherwise I just want to create a notification.
But I get an exception when I try to start an activity from my service and i feel that maybe this isn't the right way to do it?
It says:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
So my question is if this is the right way to do this by setting that flag or how should I get my downloaded image from my service to an activity. Can I in some way tell an activity to start a new activity from my service class?
I think using Broadcast Receiver is better option for you.
Add Below Method in Service and call this method when image Download Complete.
private void updateMyActivity(Context context) {
if(MainActivity.activityStatusFlag){
//update the activity if activityStatusFlag=true;
Intent intent = new Intent("mUpdateActivity");
context.sendBroadcast(intent);
}else{
//display notification if activityStatusFlag=false;
}
}
In Activity Add Following Code.
public class MainActivity extends Activity{
public static boolean activityStatusFlag= false;
//define this variable to check if activity is running or not.
#Override
protected void onResume() {
super.onResume();
activityStatusFlag = true;
this.getApplicationContext().
registerReceiver(mMessageReceiver,new IntentFilter("mUpdateActivity"));
}
#Override
protected void onPause() {
super.onPause();
activityStatusFlag = false;
this.getApplicationContext().unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Display Popup or update Activity
}
};
}
Ok I'm working on a splash screen that pauses for 1.5 seconds and works great, except for one thing. Once the timer is started in onCreate if the configuration (orientation) changes then the timer gets reset and then end result is it starts my ParchmentActivity.java twice.
How can I prevent the handler from sending the intent twice?
Thanks in advance!
Full code can be found #: https://github.com/n00bware/android_apps_parchment
Here is my code (from example http://www.anddev.org/novice-tutorials-f8/splash-fade-activity-animations-overridependingtransition-t9464.html):
public class SplashScreen extends Activity {
private static final int SPLASH_DISPLAY_TIME = 1500; /* 1.5 seconds */
private static final String TAG = "Parchment";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Create a new handler with which to start the main activity
and close this splash activity after SPLASH_DISPLAY_TIME has
elapsed. */
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent parchment = new Intent(SplashScreen.this, ParchmentActivity.class);
SplashScreen.this.startActivity(parchment);
SplashScreen.this.finish();
overridePendingTransition(R.anim.fade_main_in, R.anim.fade_splash_out);
}
}, SPLASH_DISPLAY_TIME);
}
/* I found a suggestion to try overriding onConfigurationChanged()
but it doesn't stop a new timer from being started */
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/* I also tried overriding onStart() but this also doesn't stop a
new timer. What exactly is called when an orientation configuration
changes happens? */
#Override
public void onStart() {
super.onStart();
setContentView(R.layout.splash);
}
you can create a new static boolean, set it false and in on create do the Handler action only if the flag is false...
PS: inside the if statement, you must set the boolean flag to true :)
Good luck!
Create the handler in onCreate, release it in onDestroy, send a message / post a runnable in onStart, remove message / runnable in onStop.
This will reset the timer with each rotate, so you could potentially keep the splash screen up if you rotated the device every second.
In Android it can take a second or so to switch rotations, you probably want this behaviour because it would be possible to start app, rotate and not see the splash.