Reputation: 5344
My current Android Application needs to call
ActivityManager.clearApplicationUserData()
to simulate the user clearing App storage
Which works fine.
A side effect of calling clearApplicationUserData()
is that the App is (understandably) closed.
Which gives a poor user experience.
I am having difficulty in restarting my Application once I have called clearApplicationUserData()
.
I have tried using startActivity
, Alarm Manager
with Pending Intent
, Foreground
/Background
service.
Nothing works.
Is it impossible to restart an Android App having called clearApplicationUserData()
?
Upvotes: 12
Views: 4162
Reputation: 4788
(2nd answer: I need much more contribution on it)
After 8 hours of researching in Android OS and Android Developers Website in order to find a solution to restart activity when clearApplicationUserData
is invoked. Finally, I would be able to find a nice/hacking solution.
This solution looks like Zidane dribble :)
Let's introduce the solution. at first, clearApplicationUserData
clears all the clues of the application when is invoked like tasks, notifications, alarms and etc. therefore, explicit Activity calling is impossible.
implicit way is the only possible way of calling activity.
After a couple of tests I found that application manifest
registered intent-filters
wouldn't be removed and they are able to listen for incoming system broadcasts.
Approximately, 98% of system broadcasts wouldn't be received by cleared application and that 2% remained might not be broadcasted very soon.
so what to do? hmmm? come on man I must find a solution ...
bingo, ** I must trigger something in order to system broadcast it** <= looks hacking :)
so I decide to choose WIFI_STATE_CHANGED because
manifest.xml
<receiver
android:name=".PackageDataClearedReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityManager am;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (am != null) {
ExecutorService pool = Executors.newFixedThreadPool(2);
final Collection<Future> futures = new HashSet<Future>();
futures.add(pool.submit(new Runnable() {
@Override
public void run() {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);
wifiManager.setWifiEnabled(false);
am.clearApplicationUserData();
}
}));
for (Future future : futures) {
future.isDone();
}
}
}
}).start();
}
});
}
}
demo
keep in mind, it's just A minimum viable product that needs to be developed more in order to make it work perfectly.
Upvotes: 2
Reputation: 4788
(1st answer: this answer only works on limited situations. it's not a complete answer)
public boolean clearApplicationUserData ()
Return: true
if the application successfully requested that the application's data be erased; false
otherwise.
As the reference website stated, we have a returnee before the application is being closed. so, we are going to use this returnee in order to restart the app.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
}
when Activity onDestroy()
and onStop()
are called restart app.
@Override
protected void onDestroy() {
super.onDestroy();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
@Override
protected void onStop() {
super.onStop();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
We put restart action in both onDestroy()
and onStop()
in order to make sure the app will be restarted again.
And also, I think it's a good idea to force stop activity before OS stops it.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
finish(); <= i mean this
}
it's because it makes sure that onDestroy()
and onStop()
will be invoked.
Upvotes: 6
Reputation: 1224
Clearing the app data on the device through the API clearApplicationUserData()
resets the app as if it were just installed. As you have found, any alarms and broadcasts registered with your app are also cleared. The most efficient way to keep your app in the foreground would be to clear the data yourself, as others have pointed out, rather than using the API. Here is an example: https://stackoverflow.com/a/9073473/949224
However, if you are determined to use the API (which does guarantee all data is cleared) and the app is force-stopped, I have a suggestion:
Create a small companion app that can be launched just prior to you clearing your app data. The companion app simply re-launches your app, possibly after a short time-out.
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.testrelaunchapp");
if (launchIntent != null) {
startActivity(launchIntent);//null pointer check in case package name was not found
} else {
Log.w( TAG, "Unable to resolve launch activity of relauncher companion app");
}
((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
.clearApplicationUserData();
The companion app itself needs to close afterwards, and ideally should be hidden from the Activity Stack etc..
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.yourmainapp");
if (launchIntent != null) {
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.i( TAG, "About to act on launchIntent");
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(launchIntent);
finish();
System.exit(0);
}
}, 1000);
}
}
I have seen this work with Android 6.0, but no guarantees it would be versatile and work across the board. There would be more to do to make the companion app UI-less if desired and to be hidden from the phone's launcher. You would also probably want to bundle the APK as a file within your own app and install it upon first run, which would need the user to enable installation from "Unknown sources" (non-Play store). It can be done through intents to the right System settings, if needed, but users would need a good explanation why this is needed.
So, as I was saying, the simpler approach is to clear the data and app permissions yourself.
Upvotes: -2
Reputation: 32221
My suggestion might sound trivial but, have you consider not calling ActivityManager.clearApplicationUserData()
?
Here what the docs says about this method:
Permits an application to erase its own data from disk. This is equivalent to the user choosing to clear the app's data from within the device settings UI. It erases all dynamic data associated with the app -- its private data and data in its private area on external storage -- but does not remove the installed application itself, nor any OBB files.
So in order to mimic this behavior you just need to clear you internal and external storage directories. No permissions are needed to access any of those.
Upvotes: 4