How to programmatically clear application data - android

I am developing automated tests for an android application (using Robotium). In order to ensure the consistency and reliability of tests, I would like to start each test with clean state (of the application under test). In order to do so, I need to clear the app data. This can be done manually in Settings/Applications/Manage Applications/[My App]/Clear data
What is the recommended way to get this done programmatically?

You can use the package-manager tool to clear data for installed apps (similar to pressing the 'clear data' button in the app settings on your device).
So using adb you could do:
adb shell pm clear my.wonderful.app.package

Following up to #edovino's answer, the way of clearing all of an application's preferences programmatically would be
private void clearPreferences() {
try {
// clearing app data
Runtime runtime = Runtime.getRuntime();
runtime.exec("pm clear YOUR_APP_PACKAGE_GOES HERE");
} catch (Exception e) {
e.printStackTrace();
}
}
Warning: the application will force close.

you can clear SharedPreferences app-data with this
Editor editor =
context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
and for clearing app db, this answer is correct -> Clearing Application database

From API version 19 it is possible to call ActivityManager.clearApplicationUserData().
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).clearApplicationUserData();

Check this code to:
#Override
protected void onDestroy() {
// closing Entire Application
android.os.Process.killProcess(android.os.Process.myPid());
Editor editor = getSharedPreferences("clear_cache", Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
trimCache(this);
super.onDestroy();
}
public static void trimCache(Context context) {
try {
File dir = context.getCacheDir();
if (dir != null && dir.isDirectory()) {
deleteDir(dir);
}
} catch (Exception e) {
// TODO: handle exception
}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// <uses-permission
// android:name="android.permission.CLEAR_APP_CACHE"></uses-permission>
// The directory is now empty so delete it
return dir.delete();
}

If you have just a couple of shared preferences to clear, then this solution is much nicer.
#Override
protected void setUp() throws Exception {
super.setUp();
Instrumentation instrumentation = getInstrumentation();
SharedPreferences preferences = instrumentation.getTargetContext().getSharedPreferences(...), Context.MODE_PRIVATE);
preferences.edit().clear().commit();
solo = new Solo(instrumentation, getActivity());
}

Using Context,We can clear app specific files like preference,database file.
I have used below code for UI testing using Espresso.
#Rule
public ActivityTestRule<HomeActivity> mActivityRule = new ActivityTestRule<>(
HomeActivity.class);
public static void clearAppInfo() {
Activity mActivity = testRule.getActivity();
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(mActivity);
prefs.edit().clear().commit();
mActivity.deleteDatabase("app_db_name.db");
}

if android version is above kitkat you may use this as well
public void onClick(View view) {
Context context = getApplicationContext(); // add this line
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) {
((ActivityManager) context.getSystemService(ACTIVITY_SERVICE))
.clearApplicationUserData();
return;
}

What is the recommended way to get this done programmatically?
The only possible option is to run ADB command adb shell pm clear package before the test. The biggest problem is that it's kind of headache combining tests execution and shell commands.
However, we (at Mediafe) came with some solution that can work for you on regular unrooted device. All you need to do is to add an annotation. All the rest is done by running simple bash script.
Just add #ClearData annotation before ANY of your tests and tada 🎉, ADB clear command will be executed before the test execution.
This is an example of such test:
#Test
#ClearData
public void someTest() {
// your test
}
The idea is as follows
Read all tests by using adb shell am instrument -e log true
Build execution plan by parsing the output from (1)
Run the execution plan line by line
Using the same idea these are all options you can easily support:
Clear data
Clear notification bar
Parameterize
Filter and run by tags
Use only annotations. Like this:
#Test
#ClearData
#Tags(tags = {"sanity", "medium"})
#Parameterized.Repeat(count = 3)
public void myTest() throws Exception {
String param = params[index];
// ...
}
Bonus! 🎁 For each failed test:
Collect Logcat + stacktrace
Record video (mp4)
Dump DB (sqlite)
Dump default shared preferences (xml)
Collect dumpsys files like: battery, netstats and other.
In general, it's easy to add more options, since the tests are executed one by one from bash script rather than from gradle task.
📗 The full blog post: https://medium.com/medisafe-tech-blog/running-android-ui-tests-53e85e5c8da8
📘 The source code with examples: https://github.com/medisafe/run-android-tests
Hope this answers 6 years question ;)

This way added by Sebastiano was OK, but it's necessary, when you run tests from i.e. IntelliJ IDE to add:
try {
// clearing app data
Runtime runtime = Runtime.getRuntime();
runtime.exec("adb shell pm clear YOUR_APP_PACKAGE_GOES HERE");
}
instead of only "pm package..."
and more important: add it before driver.setCapability(App_package, package_name).

Related

How do I save app logs locally on Android?

I want to save the logs generated by my application locally on the android device and view them in an instance of a crash.
Using the "Take Bug Report" under the developer options gives the entire system logs which are irrelevant to me. I am looking only for those logs created by my application when it runs.
Is there any application that does this? Or are there any libraries I could include in my application code to satisfy my requirement?
You may just add firebase to your project, and everything will be done automatically.
Or if need it to be "locally", can use the Thread.UncaughtExceptionHandler to save crash log. Register it when your application onCreate.
private static UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
public static void registerUncaughtExceptionHandler() {
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable ex) {
// Save Log
saveLog(ex);
// Throw system
mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
}
});
}
private static void saveLog(Throwable exception) {
try {
String stackTrace = Log.getStackTraceString(exception);
// Save it to SharedPreferences or DB as you like
} catch (Exception e) {
}
}
Then can extract the last crash log, submit to your server or display in logcat when app starts.
It is much better to use Third Party libraries such as Firebase Crashlytics or Sentry Crash Report or AppMetrica for crash reports.
just add these libraries and make an account on one of these sites, then you can have a full report of crashes if happen.
but if you want to save the logs on the device, you can refer to this question :
Saving Logcat to a text file in Android Device
You can try this
fun writeLog(context: Context) {
try {
val path = File(context.filesDir, "log_files")
if (!path.exists()) {
path.mkdir()
}
val fileName = "your_filename.txt"
Runtime.getRuntime().exec("logcat -v time -f $fileName")
} catch (e: IOException) {
}
}
Or you can change logcat command based on your requirements: refer to this https://developer.android.com/studio/command-line/logcat
You can check it at data/data/{applicationId}/files/log_files/

How to prevent app from being cached in android

After some research I found that Android likes to cache some parts of an app while installing to improve the performance while runtime.
Is there a way to prevent Android from caching things from my app?
I am sharing my app via my website and users install and update the app manually. As soon as I update my app some Activities and Code-parts seems to be cached on their devices.
You could delete your cache everytime before you update or before you close you application.
Code to clear the cache:
public static void trimCache(Context context) {
try {
File dir = context.getCacheDir();
if (dir != null && dir.isDirectory()) {
deleteDir(dir);
}
} catch (Exception e) {
// TODO: handle exception
}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
Call Trimcache when you want to clear the cache (before you update perhaps? or override the onStop() method, to clear the cache when the application is going to close.
The way I see you should also have a cache problem when you're NOT updating your app.
If you want to force a server fetch when you upgrade your app you could store a boolean in Shared Preferences, using a key associated with the app version. So when you install version 1 you fetch and make putsBoolean(version, true) where version == "1". When version 2 is installed you'll find a false, which will trigger a fetch, followed by setting putsBoolean(version, true) - but in this case version == "2".
If you are reusing information that have a different meaning when you update, the solution is storing the information in a different part of the cache, so it's not sent to an activity that is not prepared to display it.
Otherwise the solution by #jordy-dieltjens seems a good one.

Dropbox Sync Api. Not seeing the File on Dropbox the first time round

I have a DropboxHelper Class that is handling downloading and uploading from dropbox.
Downloading works fine but when I try to upload from dropbox the first time the code is called. The following Line is false
if (dropboxFileSystem.isFile(dropboxPath)) {
}
It returns false. Tell the app to try again and this time it sees the file and uploads it to the app. Below is some of the code I am using for the class. Debug seems to incdicate the dropbox api has not completing started / synced the first time
public class DropBoxHelper {
public DropBoxHelper(Context pContext) {
context = pContext;
defineVariables();
}
private void defineVariables() {
dropboxAccountManager = DbxAccountManager.getInstance(context.getApplicationContext(), DROPBOX_APP_KEY, DROPBOX_APP_SECRET);
dropboxPath = new DbxPath(DbxPath.ROOT, DROPBOX_FILE_NAME);
}
public boolean importFromDropbox() {
try {
dropboxFileSystem = DbxFileSystem.forAccount(dropboxAccountManager.getLinkedAccount());
if (dropboxFileSystem.isFile(dropboxPath)) {
DbxFile databaseFileonDropbox = dropboxFileSystem.open(dropboxPath);
try {
// Do Copy
} finally {
Log.i(DEBUG_TAG, "Closing File");
databaseFileonDropbox.close();
}
}
Any ideas on why the copy fails first time.
Thanks
I'm not 100% sure, but I believe you need to use dropboxFileSystem.awaitFirstSync() to make sure at least one sync with the server has happened before you try to find the file.
An alternative might be to just call dropboxFileSystem.open(...) directly and handle the exception that's raised if the file doesn't exist.

Requesting root access for android app

I know there are already questions about that here, but I tried all the answers given without success.
There's a simple CheckBoxPreference (titled "Root"):
<CheckBoxPreference
android:key="root"
android:title="#string/root"
android:summary="#string/root_summary"
android:defaultValue="false" />
Now I need to set the OnPreferenceChangeListener on it and gain root access. If so the checkbox should be checked, otherwise it should not:
public class Settings extends PreferenceActivity implements OnPreferenceChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
findPreference("root").setOnPreferenceChangeListener(this);
}
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if ("root".equals(key) && !((CheckBoxPreference) preference).isChecked()) {
try {
Process p = Runtime.getRuntime().exec("su");
p.waitFor();
Log.d("Settings", Integer.toString(p.exitValue()));
if (p.exitValue() == 255) {
Log.d("Settings", "###NO ROOT###");
return false;
}
} catch (Exception e) {
Log.d("Settings", "###NO ROOT###");
Log.d("Settings", e.getMessage());
return false;
}
Log.d("Settings", "!!!ROOT!!!");
}
return true;
}
}
Superuser prompts correctly for root access. Denying however also returns true, as exitValue is 1 (???) and allowing freezes the whole app (I guess at p.waitFor).
I'm currently running Superuser 3.1.3 with su binary 3.1.1 (newest versions).
Taking a look into logcat I can see the following message: Activity pause timeout for ActivityRecord{42c0ebb8 com.example/.gui.Settings}
The command you're running is just su which will, I suspect, run a shell as superuser. You're waiting (indefinitely) for that shell to finish.
You need to specify su some-command-here-which-needs-to-run-as-root.
Unfortunately, there is no way to achieve superuser permissions for the Java code within your Android project. The root-ness applies only to commands which are spawned by su itself.

AsyncTask and getInstalledPackages() fail

I don't really understand what is happening and maybe you can give me an idea. I use getInstalledPackages() in order to get a list of all user installed applications and their permissions. Here is how I do it:
private PackageManager pm;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pm = getPackageManager();
getListData();
}
private void getListData()
{
backTask = new BackTask();
backTask.execute();
}
protected class BackTask extends AsyncTask<Context, String, ArrayList<App>>
{
....
#Override
protected ArrayList<App> doInBackground(Context... param)
{
try {
//get a list of installed apps.
List<PackageInfo> packages = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS);
for (PackageInfo packageInfo : packages) {
ApplicationInfo application = packageInfo.applicationInfo;
....
}
I need to refresh the list displayed based on user interaction. The problem appears at this line in AsyncTask:
List<PackageInfo> packages = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS);
Sometimes packages contains all the installed apps sometimes is EMPTY. Let's say for example out of 3 times, the 3rd time is empty. Taking this code out of AsyncTask and put it on getListData() works every single time without any problems. The issue is that I get a little freeze because operation is not on a back thread.
So what's the deal with AsyncTask ? Am I doing something wrong ?
Basically, this exception was fixed on Android 5.1 (or at least that's what I've read somewhere), but for pre-5.1, you need to do something on your own.
You can avoid having a crash of this exception and get a full list of the installed apps by using the shell, as I've shown here:
https://code.google.com/p/android/issues/detail?id=172058
Here's the code:
public static List<PackageInfo> getInstalledPackages(Context context,int flags)
{
final PackageManager pm=context.getPackageManager();
//if it's Android 5.1, no need to do any special work
if(VERSION.SDK_INT>=VERSION_CODES.LOLLIPOP_MR1)
return pm.getInstalledPackages(flags);
//else, protect against exception, and use a fallback if needed:
try
{
return pm.getInstalledPackages(flags);
}
catch(Exception ignored)
{
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
Process process;
List<PackageInfo> result=new ArrayList<>();
BufferedReader bufferedReader=null;
try
{
process=Runtime.getRuntime().exec("pm list packages");
bufferedReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line=bufferedReader.readLine())!=null)
{
final String packageName=line.substring(line.indexOf(':')+1);
final PackageInfo packageInfo=pm.getPackageInfo(packageName,flags);
result.add(packageInfo);
}
process.waitFor();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(bufferedReader!=null)
try
{
bufferedReader.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return result;
}
Check out a similar SO question here, CommonsWare argues that this method need to be involved in UI thread, As far as I can see, this is the only reasonable explanation.
I'm proposing a different explanation:
The IPC buffer runs full and on earlier Android versions (sub API15) just an empty list is returned while on newer Android versions a TransactionTooLargeException is thrown.
Also see:
PackageManager.getInstalledPackages() returns empty list

Categories

Resources