I am somewhat new to android development, this is my first time trying to provide a list of installed applications in a preference screen. The selected value from this list will later be used to launch an app of the user's choosing.
I have created the following class:
public class AppSelectorPreference extends ListPreference {
public AppSelectorPreference(Context context, AttributeSet attrs) {
super(context,attrs);
PackageManager pm = context.getPackageManager();
List<PackageInfo> appListInfo = pm.getInstalledPackages(0);
CharSequence[] entries = new CharSequence[appListInfo.size()];
CharSequence[] entryValues = new CharSequence[appListInfo.size()];
try {
int i = 0;
for (PackageInfo p : appListInfo) {
if (p.applicationInfo.uid > 10000) {
entries[i] = p.applicationInfo.loadLabel(pm).toString();
entryValues[i] = p.applicationInfo.packageName.toString();
Log.d(BT,"Label: " + entries[i]);
Log.d(BT,"PName: " + entryValues[i]);
i++;
}
}
} catch (Exception e) {
Log.e(BT,"ER> Put descriptive error message here");
e.printStackTrace();
}
setEntries(entries);
setEntryValues(entryValues);
}
}
This is added to my PreferenceScreen with the following XML:
<PreferenceCategory android:title="Launch Application">
<CheckBoxPreference
android:title="Launch Application"
android:summary="Launch an application"
android:defaultValue="false"
android:key="pref_connect_launch_enable"
android:dependency="pref_connect_enable"/>
<com.nirsoftware.AppSelectorPreference
android:title="Select Application"
android:summary="Select application to launch"
android:key="pref_connect_package_name"
android:dependency="pref_connect_launch_enable"/>
</PreferenceCategory>
It appears everything is working correctly, until clicking on the AppSelectorPreference, which throws an NPE in android.preference.ListPreference.findIndexOfValue(ListPreference.java:169). Any suggestions?
I have tried your code, and it works fine for me, both adding the prefence programatically like this:
AppSelectorPreference pref2 = new AppSelectorPreference(this, null);
getPreferenceScreen().addPreference(pref2);
and using xml like you wrote. Which one is the line 169 where the error is appearing?
Also, did you look through logcat to see if any of the app names or labels is giving something like null? The only thing I can think is that your android has different apps from mine.
Edit: Sorry, I tested again and now I got the same error as you. Not sure what I did different, other than select a value
However, I fixed it by overriding the findIndexOfValue method. I just did this to test:
#Override
public int findIndexOfValue(String value) {
return 0;
//return super.findIndexOfValue(value);
}
but of course you will need to implement findind the index for that value. Might be an android bug for very large entries arrays?
i would suggest you to remove the if condition, cause it is what causes a null pointer error since the condition will be false unable to execute the commands null entries a value
try {
int i = 0;
for (PackageInfo p : appListInfo) {
entries[i] = p.applicationInfo.loadLabel(pm).toString();
entryValues[i] = p.applicationInfo.packageName.toString();
Log.d(BT,"Label: " + entries[i]);
Log.d(BT,"PName: " + entryValues[i]);
i++;
}
} catch (Exception e) {
Log.e(BT,"ER> Put descriptive error message here");
e.printStackTrace();
}
Related
In my app I need to monitorize recently added or updated packages, but since Oreo this is a hard task.
To do it I have a service that runs every X time to detect the new installed/updated apps.
The main core of this service is to call the getChangedPackages function from the PackageManager, but this function always returns null, even if I install or update any app from or not from the Play Store in the interval between two consequtive calls to getChangedPackages.
https://developer.android.com/reference/android/content/pm/PackageManager.html#getChangedPackages(int)
I need to request any permission to call this function? Is the getChangedPackages buggy?
private void _doProcess()
{
try
{
PackageManager package_manager = getPackageManager();
int sequence_number = ApplicationPreferences.getInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, 0);
ChangedPackages changed_packages = package_manager.getChangedPackages(sequence_number);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages == null) changed_packages = package_manager.getChangedPackages(0);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages != null)
{
List<String> packages_names = changed_packages.getPackageNames();
LogUtilities.show(this, String.format("%d recently installed/updated apps", packages_names == null ? 0 : packages_names.size()));
if (packages_names != null) for (String package_name : packages_names) PackagesUpdatedReceiver.doProcessPackageUpdate(this, new Intent(isNewInstall(package_manager, package_name) ? Intent.ACTION_PACKAGE_ADDED : Intent.ACTION_PACKAGE_REPLACED).setData(Uri.parse(String.format("package:%s", package_name))));
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", changed_packages.getSequenceNumber()));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, changed_packages.getSequenceNumber());
}
else
{
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", sequence_number + 1));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, sequence_number + 1);
}
}
catch (Exception e)
{
LogUtilities.show(this, e);
}
}
My experimental results so far have shown that this PackageManager API method getChangedPackages() is not reliable: quite often the returned ChangedPackages value contains many unchanged packages. So I’ve decided to implement a similar feature in a class called PackageUtils, as shown below. The idea is to poll for all the installed packages, as shown in method getInstalledPackageNames() below, and compare the string list with a previously saved one. This comparison boils down to comparing 2 string lists, as shown in method operate2StringLists() below. To get a set of removed packages, use GET_1_MINUS_2_OR_REMOVED as operation. To get a set of added packages, use GET_2_MINUS_1_OR_ADDED as operation.
public class PackageUtils {
public static final int GET_1_MINUS_2_OR_REMOVED = 0;
public static final int GET_2_MINUS_1_OR_ADDED = 1;
// Get all the installed package names
public static List<String> getInstalledPackageNames(Context context) {
List<String> installedPackageNames = new ArrayList<>();
try {
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> appInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo appInfo : appInfoList) {
installedPackageNames.add(appInfo.packageName);
}
} catch (Exception e) {
e.printStackTrace();
}
return installedPackageNames;
}
// Compare 2 string lists and return differences.
public static Set<String> operate2StringLists(List<String> pkgList1, List<String> pkgList2, int operation) {
Set<String> result = null;
Set<String> pkgSet1 = new HashSet<String>(pkgList1);
Set<String> pkgSet2 = new HashSet<String>(pkgList2);
switch (operation) {
case GET_1_MINUS_2_OR_REMOVED:
pkgSet1.removeAll(pkgSet2);
result = pkgSet1;
break;
case GET_2_MINUS_1_OR_ADDED:
pkgSet2.removeAll(pkgSet1);
result = pkgSet2;
break;
default:
break;
}
return result;
}
}
The code has been tested on an Android Oreo device. It can reliably detect all added and removed packages between 2 time instances. However, it can’t detect updated packages in-between.
Finally got it. You have to create a variable called sequenceNumber, and update it every time you query changed packages.
private static int sequenceNumber = 0;
...
PackageManager pm = getContext().getPackageManager();
ChangedPackages changedPackages = pm.getChangedPackages(sequenceNumber);
if(changedPackages != null)
sequenceNumber = changedPackages.getSequenceNumber();
I am writing a code to extract the package name, application name, and icon from the last installed app on my phone. I can get the application common name and icon from the application info, but I can't seem to figure out how to get the package name. All the codes that I have found to get the package name give me the package name of MY app, not the last installed app.
It seems like I need to find a method to get the package name, where I can pass in the application info as the parameter (like I do for the application common name and icon).
final PackageManager pm = context.getPackageManager();
ApplicationInfo ai;
try {
ai = pm.getApplicationInfo(intent.getData().getSchemeSpecificPart(), 0);
Log.d("tag_name","Application Info" + ai);
PACKAGE_NAME = context.getApplicationContext().getPackageName();
Log.d("tag_name","Package Name" + PACKAGE_NAME);
} catch (final PackageManager.NameNotFoundException e) {
ai = null;
}
final String applicationName = (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)");
Log.d("tag_name", "Application NAME" + applicationName);
// http://www.carbonrider.com/2016/01/01/extract-app-icon-in-android/
try {
Drawable icon = context.getPackageManager().getApplicationIcon(ai);
Log.d("tag_name", "ICON" + icon);}
catch (Exception e){}
Firstly receive all apps with this Code :
List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA);
Then sort the packages list with this code :
Collections.sort(packages, new Comparator<PackageInfo>() {
#Override
public int compare(PackageInfo p1, PackageInfo p2) {
return Long.toString(p2.firstInstallTime).compareTo(Long.toString(p1.firstInstallTime));
}
});
Then you can receive the package Name of the latest installed app this way:
packages.get(0).packageName
I'm doing an app that need the higher resolution icons of each app.
I have done some research since this morning but none of the answers I found work.
Here are two of them and what is wrong for me.
FYI, I have retrieved all the "packageinfo" from the packagemanager, I'm finding everything I need but the high resolution icon...
1. using getDrawableForDensity:
ActivityInfo info = packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA);
Resources resources;
try {
resources = mPackageManager.getResourcesForApplication(
info.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
resources = null;
}
if (resources != null) {
int iconId = info.getIconResource();
if (iconId != 0) {
Drawable d;
try {
d = resources.getDrawableForDensity(iconId, DisplayMetrics.DENSITY_XHIGH);
} catch (Resources.NotFoundException e) {
d = null;
}
return d;
}
}
My problem for this one is that I don't understand what to put for componentName. How can I "construct" it with the information I got from packagemanager ?
2.Other solution
Retrieving xhdpi app icons from packageManager?
// Get the application's resources
Resources res = mPackageManager.getResourcesForApplication(appInfo);
// Get a copy of the configuration, and set it to the desired resolution
Configuration config = res.getConfiguration();
Configuration originalConfig = new Configuration(config);
config.densityDpi = DisplayMetrics.DENSITY_XHIGH;
// Update the configuration with the desired resolution
DisplayMetrics dm = res.getDisplayMetrics();
res.updateConfiguration(config, dm);
// Grab the app icon
Drawable appIcon = res.getDrawable(appInfo.icon);
// Set our configuration back to what it was
res.updateConfiguration(originalConfig, dm);
for this one I have a problem with this line:
Drawable appIcon = res.getDrawable(appInfo.icon);
Error I get about "icon":
icon cannot be resolved or is not a field
Try this, it is similar to the code that you have but it seems to work for me :
//Get HiRes Icon
Drawable appIcon;
List<Application> applications = new ArrayList<Application>();
for (ApplicationInfo item : getInstalledApplications(packageManager)) {
try {
Resources resourcesForApplication = packageManager.getResourcesForApplication(item);
Configuration config = resourcesForApplication.getConfiguration();
Configuration originalConfig = new Configuration(config);
DisplayMetrics displayMetrics = resourcesForApplication.getDisplayMetrics();
DisplayMetrics originalDisplayMetrics = resourcesForApplication.getDisplayMetrics();
displayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
resourcesForApplication.updateConfiguration(config, displayMetrics);
appIcon = resourcesForApplication.getDrawable(item.icon);
resourcesForApplication.updateConfiguration(originalConfig, originalDisplayMetrics);
} catch (PackageManager.NameNotFoundException e) {
Log.e("check", "error getting Hi Res Icon :", e);
appIcon = item.loadIcon(packageManager);
}
Application app = new Application();
app.setTitle(item.loadLabel(packageManager).toString());
app.setPackageName(item.packageName);
// app.setImage(item.loadIcon(packageManager));
app.setImage(appIcon);
applications.add(app);
}
}
This is only speculation as I'm not a particularly good Android programmer but:
In the first solution, you could probably retrieve a list of every application installed on a device by a method getInstalledApplications(int flags) of PackageManager class.
See
http://developer.android.com/reference/android/content/pm/PackageManager.html#getInstalledApplications(int)
So as a code something like this:
///This retrieves information on every application installed on device, see documentation
///for flags
List<ApplicationInfo> applications = mPackageManager.getInstalledApplications(int flags);
///Then retrieve Resources for every application with looping through all the applications
for (ApplicationInfo info : applications) {
Resources res = mPackageManager.getResourcesForApplication(info);
///And now you can do what you already wrote yourself and try fetching icon for every
///'res' you did in the first solution.
}
This is assuming that you want to show every application.
About solution 2:
I'm assuming appInfo is of 'ApplicationInfo' class. It has a public field 'icon' that is inherited from the 'PackageItemInfo'-class. Make sure your appInfo is any of the sub classes of 'PackageItemInfo' class and it should work. If it does not work, there is probably something wrong with the Android manifest.xml of the given application and it can not retrieve the required integer.
You could try retrieving the icons by 'getApplicationIcon'-method of the 'PackageManager'-class. Once again
List<ApplicationInfo> applications = mPackageManager.getInstalledApplications(int flags);
List<Drawable> icons = new List<Drawable>;
for (ApplicationInfo app : applications) {
icons.add(mPackageManager.getApplicationIcon(app));
}
Of course, this means you are going twice through the list of applications, short of.
The documentation (http://developer.android.com/guide/topics/manifest/manifest-element.html#uid) only states I can't use raw strings and the API level it was added, but doesn't explain why I would want to use it.
If I already set android:sharedUserID to "com.foo.bar" what value should I put in the string referenced by android:sharedUserLabel, and most importantly why!?
Thank you
As far as I understand from the AOSP actually you can use this label just to display a pretty name to a user (if you have several processes in the same uid). For instance, here is a part of code in the RunningState.java file:
// If we couldn't get information about the overall
// process, try to find something about the uid.
String[] pkgs = pm.getPackagesForUid(mUid);
// If there is one package with this uid, that is what we want.
if (pkgs.length == 1) {
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
// If there are multiple, see if one gives us the official name
// for this uid.
for (String name : pkgs) {
try {
PackageInfo pi = pm.getPackageInfo(name, 0);
if (pi.sharedUserLabel != 0) {
CharSequence nm = pm.getText(name,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mDisplayLabel = nm;
mLabel = nm.toString();
mPackageInfo = pi.applicationInfo;
return;
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
Basically, it does the following things. At first, it tries to get information about the overall process. If it has not find, it tries to get information using UID of the application as a parameter (this is a part of code that I've given here). If there is only one package with this UID the information about the process is got from this package. But if there are several packages (using shareUserId) then it iterates and tries to find official (pretty) name.
As a confirmation to my words I found the following string in MediaProvider:
<!-- Label to show to user for all apps using this UID. -->
<string name="uid_label">Media</string>
Thus, all process that uses android:sharedUserId="android.media" will have name Media.
I do not think that this feature will be used a lot by ordinary developers and is useful for them.
If I call
PackageManager pm = getPackageManager () ;
List<PackageInfo> pis = pm.getInstalledPackages (PackageManager.GET_PROVIDERS) ;
I get a list of installed packages, including any provivders they declare (i.e, with pis[i].providers possibly being non-null).
However, if I include PackageManager.GET_ACITIVITIES among the flags, as in
PackageManager pm = getPackageManager () ;
List<PackageInfo> pis = pm.getInstalledPackages (PackageManager.GET_ACTIVITIES | PackageManager.GET_PROVIDERS) ;
I expect to get the "same" list of installed packages, but with pis[i].activities being non-null as well. But I don't, I get an empty list.
Is there something special about including PackageManager.GET_ACTIVITES among the flags that isn't mention in the documentation?
My current work around is to leave PackageManager.GET_ACTIVITIES out of the flags, then loop through the returned list as follows:
for (PackageInfo pi : pis) {
try {
PackageInfo tmp = pm.getPackageInfo (pi.packageName, PackageManager.GET_ACTIVITIES) ;
pi.activities = tmp.activities ;
}
catch (NameNotFoundException e) {
Log.e (TAG, e.getMessage ()) ;
}
But that seems like a real kludge.
The only mention I could find of getInstalledPackages (PackageManager.GET_ACTIVITIES) returning an empty list is here, but the problem in that case seems to have been something about calling getInstalledPackages() outside the main application thread and that is not the situation in my case.
p.s. this is the .602 VZW build of Gingerbread, in case that matters
I came across the same issue and found out a better workaround:
public void listAllActivities() throws NameNotFoundException
{
List<PackageInfo> packages = getPackageManager().getInstalledPackages(0);
for(PackageInfo pack : packages)
{
ActivityInfo[] activityInfo = getPackageManager().getPackageInfo(pack.packageName, PackageManager.GET_ACTIVITIES).activities;
Log.i("Pranay", pack.packageName + " has total " + ((activityInfo==null)?0:activityInfo.length) + " activities");
if(activityInfo!=null)
{
for(int i=0; i<activityInfo.length; i++)
{
Log.i("PC", pack.packageName + " ::: " + activityInfo[i].name);
}
}
}
}
Notice that I need to query PackageManager twice. Once using getPackageManager().getInstalledPackages(...) and again using getPackageManager().getPackageInfo(...)
I hope it helps.