Clearing and setting the default home application
Asked Answered
D

4

33

How in the world does Nova manage this? I'm literally trying to do exactly the same thing: provide users with a button to press to clear and pick their new default launcher.

I'm able to get the default app name and display it:

       private String getPrefered(Intent i) {
       PackageManager pm = this.getActivity().getPackageManager();
       final ResolveInfo mInfo = pm.resolveActivity(i, 0);
       return (String) pm.getApplicationLabel(mInfo.activityInfo.applicationInfo);
   }

where Intent i is

Intent home = new Intent("android.intent.action.MAIN");
        home.addCategory("android.intent.category.HOME");

Then I call up the system ResolveActivity,

private void makePrefered() {
       Intent selector = new Intent("android.intent.action.MAIN");
       selector.addCategory("android.intent.category.HOME");                          
       selector.setComponent(new ComponentName("android", "com.android.internal.app.ResolverActivity"));
       startActivity(selector);
   }

The picker comes up and functions correctly, but it doesn't actually set or clear any values. While debugging it, it seems as if I'm missing some extras? When I call the makePrefered method, I get the following log message,

I/ActivityManager(  602): START {act=android.intent.action.MAIN cat=[android.intent.category.HOME] cmp=android/com.android.internal.app.ResolverActivity u=0} from pid 22641

When I use the Nova implementation I see all of this however,

    I/PackageManager(  602): Result set changed, dropping preferred activity for Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 (has extras) } type null
I/ActivityManager(  602): START {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=android/com.android.internal.app.ResolverActivity (has extras) u=0} from pid 22905
I/ActivityManager(  602): START {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.mycolorscreen.canvas/.Launcher (has extras) u=0} from pid 22905
  1. How can I get in there and see what's being sent along with that bundle?
  2. How can I just clear the preferred app? Don't tell me you can't, I've seen enough of those answers. Nova does it and does it exactly the way that I would like to.
Delastre answered 31/10, 2012 at 21:18 Comment(0)
D
61

The code to do this is actually just a very clever work around.

When a component with

        <category android:name="android.intent.category.HOME" />

is enabled, generally from an install of a new home application, the default home app gets cleared.

To take advantage of this by creating an empty activity with the home component like this.

<activity
            android:name="com.t3hh4xx0r.haxlauncher.FakeHome"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>     

When you want to set your new default, you enable this component, then call the home intent and then disable your fake home component again.

public static void makePrefered(Context c) {
       PackageManager p = c.getPackageManager();
       ComponentName cN = new ComponentName(c, FakeHome.class);
       p.setComponentEnabledSetting(cN, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

       Intent selector = new Intent(Intent.ACTION_MAIN);
       selector.addCategory(Intent.CATEGORY_HOME);            
       c.startActivity(selector);

       p.setComponentEnabledSetting(cN, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
   }

The end result is that the system thinks a new home app was installed, so the default is cleared allowing you to set yours with no special permissions.

Thank you to Kevin from TeslaCoil and NovaLauncher for the information on how this is done!

Delastre answered 5/11, 2012 at 20:18 Comment(9)
I've tried this on 4.2.2 and it does not work... Only when installing a new Home Launcher the behavior is as you have described.Inarticulate
Nice one, I couldn't use set_preferred_application() even with permission.Thermic
This example needs to have permission "<uses-permission android:name = "android.permission.WRITE_SETTINGS" />" on some devices.Tessellated
hi r2DoesInc, i also trying to handle the home button, in my application i handle the home key, but out side of application also while pressing home key my launcher is calling. is there any thing i am missed. give me guide lines to me thanks.Hopehopeful
This doesn't seem to work on 4.4.2 when another default launcher is already set (can anyone confirm?). Enabling the fake activity seems to make no difference, the device displays no dialog and returns to the original default app when it reaches c.startActivity(selector); for me.Guyguyana
@r2DoesInc; By doing this it removed my application from android launcher as well. COMPONENT_ENABLED_STATE_DISABLED is used to remove icon from android launcher. But i do not want to remove my app icon. Can you suggest what can help me?Nonobjective
Confirmed that it does not clear other apps default on android 4.4.2. But the trick is still very usefull for other things so I upvoted it nontheless. Think to check if your activity is homescreen and guide user to settings to disable it with a little description beforehand is more the android way and respects the power of the user more.Hubble
for those devices on which this code doesn't work you need to clear default settings & then call the makePrefered() methodFerocious
@r2DoesInc, while it makes itself default launcher, but when i press home button it opens the app, but pressing back button takes to previous default launcher, which i don't want to happen.Gum
T
5

r2DoesInc's solution doesn't work on my 4.2.2 test device.
My solution: Disable then re-enable my app's HomeActivity, it doesn't have to create FakeHome

PackageManager p = getPackageManager();
ComponentName cN = new ComponentName(this, HomeActivity.class);
p.setComponentEnabledSetting(cN, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
p.setComponentEnabledSetting(cN, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Trommel answered 15/1, 2015 at 8:36 Comment(0)
C
5

I use the following code on Android 4.1.2 with a platform-signed kiosk-mode application on an industrial tablet. It uses the deprecated PackageManager.addPreferredActivity(), but the advantage is that it works without user interaction. It even works after the standard Android launcher has been choosen with the "always" option.

// Requires permission SET_PREFERRED_APPLICATIONS.
public static boolean setPreferredHomeActivity (Context context, String packageName, String className) {
   ComponentName oldPreferredActivity = getPreferredHomeActivity(context);
   if (oldPreferredActivity != null && packageName.equals(oldPreferredActivity.getPackageName()) && className.equals(oldPreferredActivity.getClassName())) {
      return false; }
   if (oldPreferredActivity != null) {
      context.getPackageManager().clearPackagePreferredActivities(oldPreferredActivity.getPackageName()); }
   IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
   filter.addCategory(Intent.CATEGORY_HOME);
   filter.addCategory(Intent.CATEGORY_DEFAULT);
   ComponentName[] currentHomeActivities = getActivitiesListByActionAndCategory(context, Intent.ACTION_MAIN, Intent.CATEGORY_HOME);
   ComponentName newPreferredActivity = new ComponentName(packageName, className);
   context.getPackageManager().addPreferredActivity(filter, IntentFilter.MATCH_CATEGORY_EMPTY, currentHomeActivities, newPreferredActivity);
   return true; }

private static ComponentName getPreferredHomeActivity (Context context) {
   ArrayList<IntentFilter> filters = new ArrayList<>();
   List<ComponentName> componentNames = new ArrayList<>();
   context.getPackageManager().getPreferredActivities(filters, componentNames, null);
   for (int i = 0; i < filters.size(); i++) {
      IntentFilter filter = filters.get(i);
      if (filter.hasAction(Intent.ACTION_MAIN) && filter.hasCategory(Intent.CATEGORY_HOME)) {
         return componentNames.get(i); }}
   return null; }

private static ComponentName[] getActivitiesListByActionAndCategory (Context context, String action, String category) {
   Intent queryIntent = new Intent(action);
   queryIntent.addCategory(category);
   List<ResolveInfo> resInfos = context.getPackageManager().queryIntentActivities(queryIntent, PackageManager.MATCH_DEFAULT_ONLY);
   ComponentName[] componentNames = new ComponentName[resInfos.size()];
   for (int i = 0; i < resInfos.size(); i++) {
      ActivityInfo activityInfo = resInfos.get(i).activityInfo;
      componentNames[i] = new ComponentName(activityInfo.packageName, activityInfo.name); }
   return componentNames; }
Concubinage answered 22/8, 2015 at 0:24 Comment(4)
How did you request for permission android.permission.SET_PREFERRED_APPLICATIONS on 4.1.2 ?? I see it is deprecated "This constant was deprecated in API level 7".Buccal
@Buccal I just added SET_PREFERRED_APPLICATIONS to the manifest file. It's deprecated, but it works with API level 16 (Android 4.1.2).Unquestioning
This works for me on Android 4.4.2 when my application is signed as a system app.Swami
I later discovered an issue with the getPreferredHomeActivity method in this post. In the case where a user presses Home and then selects a launcher "Just Once," the method here will return that app from getPreferredHomeActivity while I would instead expect it to return null. The resolveActivity approach mentioned in the original question handled this situation correctly.Swami
S
1

Taking @Bruce's answer (without using fake home activity) further, you can use PackageManager.setComponentEnabledSetting to first disable the component, then resolveActivity for the home intent (rather than using startActivity), then enable the component, and then startActivity with the intent.

Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
PackageManager pm = getPackageManager();
ResolveInfo rInfo = pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);

if (!rInfo.activityInfo.packageName.equals(getPackageName())) { // your app is not the default HOME

    ComponentName cn = <ComponentName object of your home activity>

    pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    pm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
    pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
    startActivity(homeIntent);
}
Selftaught answered 6/9, 2019 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.