AsyncTask and getInstalledPackages() fail - android

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

Related

What's the difference between "getApplicationLabel" and "loadLabel"?

Background
I'm working on my app that is an alternative to the app manager (link here), and wish to optimize it a bit.
As it turns out, one of the slowest things on the app is its bootup, and the main reason for this is getting the app name . I intend on caching it, but I also wish to optimize the way it's being queried, if possible.
The problem
Android has two ways to get the app name: PackageManager.getApplicationLabel and ApplicationInfo.loadLabel .
both have about the same description, but I'm not sure which one should be used.
Not only that, but looking at the code of "ApplicationInfo.loadLabel" , it looks something like this:
public CharSequence loadLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
if (labelRes != 0) {
CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
if (label != null) {
return label.toString().trim();
}
}
if (name != null) {
return name;
}
return packageName;
}
I can't find the code of "PackageManager.getApplicationLabel", as it's abstract.
The question
Is there any difference between the two?
If there is no difference, why do we have 2 very similar methods to get the same app name? I mean, I can use either of them only if I have both applicationInfo object and the PackageManager object, but that's enough to use any of the methods...
If there is difference, which of them is better in terms of speed?
The source of 'PackageManager.getApplicationLabel' is available in 'ApplicationPackageManager.java'. It is as follows;
#Override
public CharSequence getApplicationLabel(ApplicationInfo info) {
return info.loadLabel(this);
}
ApplicationPackageManager.java
I see in AppUtils.java the same wrapping is done as follows;
/** Returns the label for a given package. */
public static CharSequence getApplicationLabel(
PackageManager packageManager, String packageName) {
try {
final ApplicationInfo appInfo =
packageManager.getApplicationInfo(
packageName,
PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_ANY_USER);
return appInfo.loadLabel(packageManager);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to find info for package: " + packageName);
}
return null;
}

Google tasks API. What parameters to use in TasksRequest.setFields(String fields) method?

I'm building a Android app that sync tasks to google tasks. And i used the Google Tasks APIs. My question is what parameters can I use in TasksRequest.setFields(String fields)? I see the sample code uses "items/title". What if i want to get other fields or what if i want to get multiple fields at the same time?
The format of this string is described here: https://developers.google.com/discovery/v1/performance#partial-response
And the APIs Explorer can help you build the string as well using a UI: https://developers.google.com/apis-explorer
You must be referring to this snippet of code from the Google Tasks API sample for Android:
#Override
protected List<String> doInBackground(Void... arg0) {
try {
List<String> result = new ArrayList<String>();
com.google.api.services.tasks.Tasks.TasksOperations.List listRequest = service.tasks().list("#default");
listRequest.setFields("items/title,items/notes,items/completed");
//listRequest.setFields("items/title");
List<Task> tasks = listRequest.execute().getItems();
if (tasks != null) {
for (Task task : tasks) {
result.add(task.getTitle());
}
} else {
result.add("No tasks.");
}
return result;
} catch (IOException e) {
tasksSample.handleGoogleException(e);
return Collections.singletonList(e.getMessage());
} finally {
tasksSample.onRequestCompleted();
}
}
The documentation is very unclear about this, but this link gave me a clue.
Notice that I use
"items/title,items/notes,items/completed"
If you get the strings wrong, you will get
invalidParameter and Invalid field selection note

most efficient way(load speed-performance i mean) to get a list of installed apps on android?

I developed an app with an activity that shows a listview of the installed apps, each row with icon, app label and checkbox. The app works fine on my nexus one and other device, but on some device, like for example an htc legend(froyo), it is extremely slow(1 minute before the activity comes up with the listview).
First i call the app list:
private String[] getAppList(){
ArrayList<String> results=new ArrayList<String>();
PackageManager pm = this.getPackageManager();
List<ApplicationInfo> list = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Collections.sort(list, new ApplicationInfo.DisplayNameComparator(pm));
boolean includeSystemApps = getIntent().getBooleanExtra("sysapps",false);
for (ApplicationInfo AppInfo : list)
{
try
{
if(includeSystemApps || ((AppInfo.flags & ApplicationInfo.FLAG_SYSTEM)!=1))
{
results.add(AppInfo.packageName);
}
}
catch(Exception ex)
{
}
}
return (String[])results.toArray(new String[results.size()]);
}
Then, in the listview item adapter I load icon and label:
String Applabel;
try
{
Applabel=pk.getApplicationLabel(pk.getApplicationInfo(data[position], 0)).toString();
}
catch(Exception ex)
{
Applabel=data[position];
}
holder.text.setText(Applabel);
holder.text.setHint(data[position]);
holder.image.setTag(data[position]);
try
{
holder.image.setImageBitmap(((BitmapDrawable)pk.getApplicationIcon(data[position])).getBitmap());
}
catch(Exception ex)
{
}
I think the long load time, on some device, is given by this call:
this.getPackageManager()
Now I don't think it's an hardware issue(cpu-ram) because the legend it's not a bad device at all.
But there are the crude data:
-nexus one(gingerbred) activity load ~1second
-legend(froyo) activity load ~40/60 secs!
Any suggestion/opinion?
Thanks in advance
This will be due to the Android setup and hardware on the device. The best thing would be to move your loading into an AsyncTask an run it with a ProgressDialog of something like "Please wait, this could take a while"
Joe

How to programmatically clear application data

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).

Retrieve User-Agent programmatically

Is there a way to retrieve Browser's user-agent without having a WebView in activity?
I know it is possible to get it via WebView:
WebView view = (WebView) findViewById(R.id.someview);
String ua = view.getSettings().getUserAgentString() ;
But in my case I don't have/need a webview object and I don't want to create it just for retrieving user-agent string.
If you don't have one you can try taking it like this
String ua=new WebView(this).getSettings().getUserAgentString();
Edit-
The doc for getUserAgentString() says
Return the WebView's user-agent string.
So i don't think you can get it unless you declare one. Some one correct me if i am wrong
There is a much simpler way if you are on Android 2.1 or above. Granted this isn't the exact same User Agent string that a webview would return, but might serve you well enough for your purposes.
As an additional advantage to pulling from web view, you can use this from any thread (not just the UI thread).
There is a system property called http.agent, which can be used to retrieve the User-Agent string.
String userAgent = System.getProperty("http.agent");
See Programmatically get User-Agent String for more details.
I used to use solution proposed by DeRagan. But it turned out that creating a single WebView instance starts a thread "WebViewCoreThread" which stays on the background until application is terminated by the system. Maybe it doesn't consume too much resources but I don't like it anyway. So I use slightly different method now, which tries to avoid WebViewCoreThread creation:
// You may uncomment next line if using Android Annotations library, otherwise just be sure to run it in on the UI thread
// #UiThread
public static String getDefaultUserAgentString(Context context) {
if (Build.VERSION.SDK_INT >= 17) {
return NewApiWrapper.getDefaultUserAgent(context);
}
try {
Constructor<WebSettings> constructor = WebSettings.class.getDeclaredConstructor(Context.class, WebView.class);
constructor.setAccessible(true);
try {
WebSettings settings = constructor.newInstance(context, null);
return settings.getUserAgentString();
} finally {
constructor.setAccessible(false);
}
} catch (Exception e) {
return new WebView(context).getSettings().getUserAgentString();
}
}
#TargetApi(17)
static class NewApiWrapper {
static String getDefaultUserAgent(Context context) {
return WebSettings.getDefaultUserAgent(context);
}
}
It creates WebSettings instance directly using package-visible constructor and if that is not available for some reason (e.g. due to API changes in future Android versions) - silently falls back to "WebView-like" solution.
UPDATE
As pointed by #Skywalker5446, starting from Android 4.2/API 17, there is a public static method to get default user agent value. I've updated my code to use that method on the supported platforms.
Since Android 2.1 you should use System.getProperty("http.agent");
You also dont need to create a WebView first AND , thats the advantage,
you can use it inside a non-uithread.
greetings steve
This is an updated solution based on previous answers that works when you compile for KitKat. Now the WebSettings class is abstract and the WebSettingsClassic class has been removed.
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static String getUserAgent(final Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return WebSettings.getDefaultUserAgent(context);
}
else {
try {
final Class<?> webSettingsClassicClass = Class.forName("android.webkit.WebSettingsClassic");
final Constructor<?> constructor = webSettingsClassicClass.getDeclaredConstructor(Context.class, Class.forName("android.webkit.WebViewClassic"));
constructor.setAccessible(true);
final Method method = webSettingsClassicClass.getMethod("getUserAgentString");
return (String) method.invoke(constructor.newInstance(context, null));
}
catch (final Exception e) {
return new WebView(context).getSettings()
.getUserAgentString();
}
}
}
Thanks to Idolon's answer my app could process this in the background.
But somehow on HTC Inspire 4G from AT&T that runs 2.3.3, it goes to the catch statement and it can be no longer run on the background thread.
My solution for this is the following:
public static String getUserAgent(Context context) {
try {
Constructor<WebSettings> constructor = WebSettings.class.getDeclaredConstructor(Context.class, WebView.class);
constructor.setAccessible(true);
try {
WebSettings settings = constructor.newInstance(context, null);
return settings.getUserAgentString();
} finally {
constructor.setAccessible(false);
}
} catch (Exception e) {
String ua;
if(Thread.currentThread().getName().equalsIgnoreCase("main")){
WebView m_webview = new WebView(context);
ua = m_webview.getSettings().getUserAgentString();
}else{
mContext = context;
((Activity) mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
WebView webview = new WebView(mContext);
mUserAgent = webview.getSettings().getUserAgentString();
}
});
return mUserAgent;
}
return ua;
}
}
(suppose you have mContext and mUserAgent in the field)

Categories

Resources