Android10定制Google开机向导

1.简介

Google开机向导com.google.android.setupwizard是没有源码的,但Google提供了客制化接口,可以做OEM定制。开机向导应用以APK的形式存放在vendor/partner_gms/apps/SetupWizard

下面以Android10为例,讲述开机向导客制化的方法。

2.修改wizard_script.xml

vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml

--- a/idh.code/vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml
+++ b/idh.code/vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml
@@ -190,11 +190,13 @@
     <WizardAction id="qr_provision_flow"
       wizard:script="android.resource://com.google.android.gmsintegration/raw/wizard_script_qr_provision_flow" />

     <!-- OEM completion [CUSTOMIZABLE] -->
     <WizardAction id="oem_post_setup"
         wizard:uri="intent:#Intent;action=com.android.setupwizard.OEM_POST_SETUP;end" />
 
+    <!-- MY completion [CUSTOMIZABLE] -->
+    <WizardAction id="my_post_setup"
+        wizard:uri="intent:#Intent;action=com.android.setupwizard.MY_POST_SETUP;end" />

参考oem_post_setup,在后面添加自定义的action。注意,如果客制化应用有多个Activity,就要在这里定义多个action,不能在自己的应用内跳转!

3.编写自己的Activity

怎样编写Activity属于应用开发,不是本篇讨论的范畴。我们主要看下如何才能将自己的Activity添加到开机向导。

3.1 AndroidManifest.xml声明action

        <activity android:name="com.my.mysetupwizard.Activity" >
            <intent-filter>
                <action android:name="com.android.setupwizard.MY_POST_SETUP" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

测试一下,看看能否通过命令调起这个Activity:

am start -a com.android.setupwizard.MY_POST_SETUP

到这一步,客制化Activity已经成功地添加到开机向导了,它将在Google开机向导的最后一步被调起。但还有一个问题,此时无法退出开机向导。

3.2 如何退出Activity

(1) 首先想到的是finish()。然而事与愿违,finish()是不行的。测试结果:finish()结束了当前Activity之后,会跳回上一个页面,而不是结束开机向导;此时点击下一步,又进入了客制化Activity;这样就进入了死循环,开机向导永远无法结束。

(2) 参考博客 https://blog.csdn.net/u012824529/article/details/90109914 添加下面这段跳转代码,实测点击跳转,开机向导会crash。看来Android10已经不能这么干了(其他Android版本没有验证)。

    // Google Wizard Manager Constants
    private static final String ACTION_NEXT = "com.android.wizard.NEXT";
    private static final String EXTRA_ACTION_ID = "actionId";
    private static final String EXTRA_SCRIPT_URI = "scriptUri";
    private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";

    public static void onNext(Intent intent, Activity activity, int resultCode) {
        Intent nextIntent = new Intent(ACTION_NEXT);
        nextIntent.putExtra(EXTRA_SCRIPT_URI, intent.getStringExtra(EXTRA_SCRIPT_URI));
        nextIntent.putExtra(EXTRA_ACTION_ID, intent.getStringExtra(EXTRA_ACTION_ID));
        nextIntent.putExtra(EXTRA_THEME, intent.getStringExtra(EXTRA_THEME));
        nextIntent.putExtra(EXTRA_RESULT_CODE, resultCode);
        activity.startActivityForResult(nextIntent, 10000);
    }

(3) 正确的跳转办法,使用Android原生方法WizardManagerHelper.getNextIntent

    class MyClickListner implements OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.button:
                try {
                    onNext(Activity.RESULT_OK);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
            }

        }
    }

    public void onNext(int requestCode) {
        int resultCode = Activity.RESULT_OK;
        Intent intent = WizardManagerHelper.getNextIntent(getIntent(), resultCode);
        try {
            startActivityForResult(intent, requestCode);
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, e.getMessage());
        }
    }

关键代码是这句:

Intent intent = WizardManagerHelper.getNextIntent(getIntent(), resultCode);

代码位于frameworks/opt/setupwizard/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java

遗憾的是,这个方法没有开放到sdk里面供普通应用使用。既然没法直接调用,我们就把它抽出来,做成一个工具类吧。

自定义WizardManagerHelper.java

package com.my.mysetupwizard;

import java.util.Arrays;

import android.content.Intent;

public class WizardManagerHelper {

    private static final String ACTION_NEXT = "com.android.wizard.NEXT";
    static final String EXTRA_SCRIPT_URI = "scriptUri";
    static final String EXTRA_ACTION_ID = "actionId";
    private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
    public static final String EXTRA_THEME = "theme";
    static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
    static final String EXTRA_IS_FIRST_RUN = "firstRun";
    static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
    static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
    public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";

    public static Intent getNextIntent(Intent originalIntent, int resultCode) {
        return getNextIntent(originalIntent, resultCode, null);
    }
    
    public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) {
        Intent intent = new Intent(ACTION_NEXT);
        copyWizardManagerExtras(originalIntent, intent);
        intent.putExtra(EXTRA_RESULT_CODE, resultCode);
        if (data != null && data.getExtras() != null) {
          intent.putExtras(data.getExtras());
        }
        intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME));

        return intent;
      }
    
    public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
        dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
        for (String key :
            Arrays.asList(
                EXTRA_IS_FIRST_RUN,
                EXTRA_IS_DEFERRED_SETUP,
                EXTRA_IS_PRE_DEFERRED_SETUP,
                EXTRA_IS_SETUP_FLOW)) {
          dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
        }

        for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
          dstIntent.putExtra(key, srcIntent.getStringExtra(key));
        }
      }
    
}

4 开机向导调试方法

开机向导只在首次开机时启动,想要再次启动开机向导,只能重刷固件或者恢复出厂设置,这个太麻烦了,不适合调试。

Google开机向导SetupWizard没有源码,不好分析。我们从Android原生开机向导Provision的代码里面得到了一些信息。

packages/apps/Provision/src/com/android/provision/DefaultActivity.java

    private void setProvision() {
        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);

        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();
        ComponentName name = new ComponentName(this, DefaultActivity.class);
        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);

        // terminate the activity.
        finish();
    }

可见,完成开机向导的时候,做了3件事,

(1) Settings.Global.DEVICE_PROVISIONED 设置为1;

(2) Settings.Secure.USER_SETUP_COMPLETE 设置为1;

(3) DISABLE自己。

根据以上分析,想要使能开机向导,我们做一下这3件事的反向操作,就可以了。

1.通过命令使能开机向导
adb shell
settings put global device_provisioned 0
settings put secure user_setup_complete 0
pm enable com.google.android.setupwizard/com.google.android.setupwizard.SetupWizardActivity
sync
reboot

2.查询settings的值
settings get global device_provisioned
settings get secure user_setup_complete

3.通过代码实现
+Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
+Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0);
+ComponentName name = new ComponentName("com.google.android.setupwizard", "com.google.android.setupwizard.SetupWizardActivity");
+mContext.getPackageManager().setComponentEnabledSetting(name,PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值