Android:如何更改默认的主屏幕应用程序

23

为了满足某个具体的需求,我需要将Android默认的主屏应用程序更改为我的自定义主屏应用程序(在我的应用程序中有一个设置选项,可以切换默认主屏 = 我的应用程序或上一个主屏)。

我不希望用户浏览非常复杂的Android设置。

有没有人能够帮助我找出注册launcher.apk作为默认主屏应用程序的位置,或者如何更改它?

我唯一找到的是这个旧问题:如何更改默认的Android桌面应用程序?

但没有任何答案。

我看到市场上有HomeSwitcher可以解决问题,但对于可能很忙碌的开发人员来说,没有答案。

编辑

我在网上找到了这篇文章:http://www.mail-archive.com/android-developers@googlegroups.com/msg74167.html

但我遇到了同样的问题:

这是我的代码:

    private void makePreferred() {
            PackageManager pm = getPackageManager();
            IntentFilter f = new IntentFilter("android.intent.action.MAIN");
            f.addCategory("android.intent.category.HOME");
            f.addCategory("android.intent.category.DEFAULT");
            ComponentName cn = new ComponentName("com.example.android.home", "com.example.android.home.Home");
            pm.addPreferredActivity(f, IntentFilter.MATCH_CATEGORY_EMPTY, null, cn);

我在清单文件中设置了android.permission.SET_PREFERRED_APPLICATIONS。执行上述代码后,日志显示已添加所期望的内容(与从IntentResolver的列表中取消"设置为默认值"时相同)。但是,当我点击主页继续操作时,该列表仍然显示,而日志则显示:

INFO/PackageManager(52): Result set changed, dropping preferred
activity for Intent { act=android.intent.action.MAIN cat=
[android.intent.category.HOME] flg=0x10200000 } type null

看起来解析器删除了默认条目。我是做错了什么吗?还是这是一种安全措施?背后的想法是什么?


1
我非常确定你无法强制用户将你的意图设置为任何事情的默认值——这应该始终是他们的选择。将其注册为主页意图应该足以让用户获得对话框,询问他们想要启动哪个主页应用程序。 - Kyle P
除非您正在使用手机的主要构建版本,否则这是不可能的,即使在这种情况下仍然存在风险。考虑告诉请求者NO,因为从应用程序的角度来看,这不是预期的行为。 - JoxTraex
4个回答

9
我对此进行了广泛的研究,从2.2版本开始就没有办法做到这一点。唯一的方法是使用一些黑客手段,但这种应用程序最近导致三星手机陷入无限循环,因此这是一种风险较高的方法。
如果您查看froyo源代码中的packagemanager类(在此处),您将看到addPreferredActivity方法中的这个小条件。
if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
                     < Build.VERSION_CODES.FROYO) {
                 Slog.w(TAG, "Ignoring addPreferredActivity() from uid"
                        + Binder.getCallingUid());
                 return;
             }

由于API的更改,HomeSwitcher在2.2上无法正常工作。开发者在应用页面上做出了评论:“Froyo(2.2)不受支持”


OP没有遇到那个问题。他的错误是“放弃首选活动...”,这是在代码中进一步生成的。所有这些检查所做的就是防止您从未针对Froyo构建的应用程序调用addPreferredActivity。 - Airsource Ltd

7
"结果集已更改"表示与您创建默认值时指定的集合不同,匹配该意图的软件包集已更改 - 因此默认值不再有效。您的组件列表(目前设置为null)需要包含设备上存在的所有主屏幕应用程序,而不仅仅是您自己的应用程序。
以下是我测试过并用于设置默认浏览器的示例代码(使用adb shell am start http://www.google.co.uk/)。XXX代表我必须打码的客户名称。
请注意,为了调用addPreferredActivity,您必须针对最低SDK版本8(2.2)进行编译,并且必须指定SET_PREFERRED_APPLICATIONS权限。该权限的保护级别为2,因此您需要使用与PackageManager相同的证书进行签名。"
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.VIEW");
filter.addCategory("android.intent.category.DEFAULT");
filter.addDataScheme("http");
Context context = getApplicationContext();
ComponentName component = new ComponentName("com.opera.mini.XXX", "com.opera.mini.XXX.CustomerBrowser");
ComponentName[] components = new ComponentName[] {new ComponentName("com.android.browser", "com.android.browser.BrowserActivity"),
                                                  component};
pm.addPreferredActivity(filter, IntentFilter.MATCH_CATEGORY_SCHEME, components, component);

如果您标记了这个回复,请告诉我原因。我上面发布的代码已经经过测试并且可以正常工作...


6
如何使用相同的证书进行签署? - hoffmanc
我也想实现它,我想在第一次启动后更改默认的启动器。我已经将代码放到PackageManagerService的构造函数中,但它总是失败。你能告诉我如果我想这样做,应该把代码放在哪个位置吗?非常感谢! - alexunder
@hoffmanc,您需要使用操作系统构建的平台密钥。除非您正在构建和签署自己的ROM,否则您将无法访问这些密钥。 - amitavk
什么是pm?它没有被初始化。 - Pavel Kostal

2
startActivity(new Intent(Settings.ACTION_HOME_SETTINGS));

这需要访问设置并允许您手动设置主屏幕启动器,但无法通过编程将其设置回默认启动器。 - saiyancoder

0
这段代码在我的ICS设备上运行良好:我使用了一个对某些调用敏感的服务,其中之一被称为SET_PREFERRED_LAUNCHER,将您的新默认启动器包(PREFERRED_PACKAGE_KEY)和其活动(PREFERRED_ACTIVITY_KEY)放入bundle中。
Method installPackageMethod = null;
Method deletePackageMethod = null;
Method setPreferredActivityMethod = null;
Method replacePreferredActivityMethod = null;
Object pm = null;


    @Override
public void onCreate() {
    super.onCreate();
        if (pm == null)
        pm = getPackageManager();
    try {
        if (setPreferredActivityMethod == null)
            setPreferredActivityMethod = pm.getClass().getMethod(
                    "addPreferredActivity", IntentFilter.class, int.class,
                    ComponentName[].class, ComponentName.class);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
}


private final class ServiceHandler extends Handler {
    private Context context;

    public ServiceHandler(Looper looper, Context ctx) {
        super(looper);
        context = ctx;
    }

    @Override
    public void handleMessage(Message msg) {
        Intent intent = (Intent) msg.getData().getParcelable(
                UPDATER_SERVICE_ACTION);
        int request = intent.getIntExtra(
                REQUEST_KEY,
                REQUEST_UNKNOWN);
        Bundle bundle = intent.getExtras();

        switch (request) {
        case INSTALL_APPLICATION: {
            if (bundle != null) {
                String appPath = bundle
                        .getString(APP_PATH_KEY);
                if (appPath != null) {
                    LogUtil.e(TAG, "try to install " + appPath);

                    try {
                        am.installPackage(appPath);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    LogUtil.e(TAG, "install of " + appPath + " done");
                }
            }
            break;
        }
        case UNISTALL_PACKAGE: {
            if (bundle != null) {
                String packagename = bundle
                        .getString(PACKAGE_NAME_KEY);
                if (packagename != null) {
                    LogUtil.e(TAG, "unistall " + packagename);

                    try {
                        deletePackageMethod
                                .invoke(pm, packagename, null, 0);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            break;
        }
        case SET_PREFERRED_LAUNCHER: {
            if (bundle != null) {
                String package_name = bundle
                        .getString(PREFERRED_PACKAGE_KEY);
                if (package_name == null) {
                    LogUtil.e(TAG,
                            "WARNING: setDefaultActivity cannot continue, package is NULL");
                    return;
                }
                String activity_name = bundle
                        .getString(PREFERRED_ACTIVITY_KEY);
                if (activity_name == null) {
                    LogUtil.e(TAG,
                            "WARNING: setDefaultActivity cannot continue, activity is NULL");
                    return;
                }

                LogUtil.e(TAG, "setDefaultActivity activity="
                        + activity_name + " package=" + package_name);

                IntentFilter filter = new IntentFilter(
                        "android.intent.action.MAIN");
                filter.addCategory("android.intent.category.HOME");
                filter.addCategory("android.intent.category.DEFAULT");
                ComponentName[] components = new ComponentName[] {
                        new ComponentName("com.android.launcher",
                                "com.android.launcher2.Launcher"),
                        new ComponentName(package_name, activity_name) };
                ComponentName activity = new ComponentName(package_name,
                        activity_name);
                try {
                    setPreferredActivityMethod.invoke(pm, filter,
                            IntentFilter.MATCH_CATEGORY_EMPTY, components,
                            activity);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        }

    }

}

请记得在您的清单文件中添加此权限:

    <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"/>

使用方法:

    public void setPreferredLauncher(String activity_name,String package_name)
{
    Intent intent = new Intent(UPDATER_SERVICE_ACTION);
    intent.putExtra(REQUEST_KEY, SET_PREFERRED_LAUNCHER);
    intent.putExtra(PREFERRED_ACTIVITY_KEY, activity_name);
    intent.putExtra(PREFERRED_PACKAGE_KEY, package_name);
    context.startService(intent);
}

其中:

    public static final String _UPDATER_SERVICE_ACTION = "com.android.updaterservice.ACTION";
public static final String REQUEST_KEY = "com.android.updaterservice.REQUEST_KEY";
public static final String PACKAGE_NAME_KEY = "com.android.updaterservice.PACKAGE_NAME_KEY";
public static final String APP_PATH_KEY = "com.android.updaterservice.APP_PATH_KEY";
public static final String PREFERRED_ACTIVITY_KEY = "com.android.updaterservice.PREFERRED_ACTIVITY_KEY";
public static final String PREFERRED_PACKAGE_KEY = "com.android.updaterservice.PREFERRED_PACKAGE_KEY";
public static final String INSTALL_PACKAGE_RESULT = "com.android.updaterservice.INSTALL_PACKAGE_RESULT";
public static final String PACKAGE_NAME = "PACKAGE_NAME";
public static final String INSTALL_SUCCEEDED = "INSTALL_SUCCEEDED";
public static final int REQUEST_UNKNOWN = -1;
public static final int INSTALL_APPLICATION = 1;
public static final int UNISTALL_PACKAGE = 2;
public static final int SET_PREFERRED_LAUNCHER = 3;

1
请注意,只有使用与固件相同签名密钥签署的应用程序才能拥有“SET_PREFERRED_APPLICATIONS”权限。 - CommonsWare
在am.installPackage()中,am是什么?需要声明吗?并且没有使用serviceHandler,请正确更新它。 - Nirav Dangi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接