当前位置: 首页 > news >正文

Lineageos 22.1(Android 15) 开机向导制作

一、前言

开机向导原理其实就是将特定的category的Activity加入ComponentResolver,如下

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

然后我们开机启动的时候,FallbackHome结束,然后启动Launcher的时候,就会找到对应的开机向导Activity页面。所以我们现定制我们自己的应用。

二、定制MyApp

1.清单文件,定义好权限和Launcer相关的category

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions" />

    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
        tools:ignore="ProtectedPermissions" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyProvision2"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyProvision2">
            <intent-filter android:priority="2">
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.HOME"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.SETUP_WIZARD"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

2.MainActivity,简单一个页面,点击之后完成配置

package com.example.myprovision2

import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.Bundle
import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.example.myprovision2.ui.theme.MyProvision2Theme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        setContent {
            MyProvision2Theme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                    )


                    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                        Image(modifier=Modifier.fillMaxSize(),painter = painterResource(R.drawable.pic),
                            contentScale = ContentScale.FillBounds,
                            contentDescription = "")
                        OutlinedButton(onClick = {
                            finishSetup()
                        }) {
                            Text(text = "FINISH SET UP",
                            )
                        }

                    }
                }
            }
        }
    }



    private fun finishSetup() {
        setProvisioningState()
        disableSelfAndFinish()
    }

    private fun disableSelfAndFinish() {
        // remove this activity from the package manager.
        val pm = packageManager
        val name: ComponentName = ComponentName(this, MainActivity::class.java)
        pm.setComponentEnabledSetting(
            name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
        )
//         terminate the activity.
        finish()
    }

    private fun setProvisioningState() {
//        Settings
//        Log.i(TAG, "Setting provisioning state")
        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(contentResolver, Settings.Global.DEVICE_PROVISIONED, 1)
        Settings.Secure.putInt(contentResolver, Settings.Secure.USER_SETUP_COMPLETE, 1)
    }

}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyProvision2Theme {
        Greeting("Android")
    }
}

3.这里有特殊的权限,需要在system/etc/permissions内配置,我们准备一下权限的xml
com.example.myprovision2.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2019 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
<permissions>
    <privapp-permissions package="com.example.myprovision2">
        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
    </privapp-permissions>
</permissions>

4.修改Android.bp

android_app_import {
    name: "MyApp",

    // this needs to be a privileged application
    privileged: true,

    // Make sure the build system doesn't try to resign the APK
    dex_preopt: {
        enabled: false,
    },

    arch: {
        arm: {
            apk: "MyApp.apk",
        },
        arm64: {
             apk: "MyApp.apk",
        },
        x86: {
               apk: "MyApp.apk",
        },
        x86_64: {
                apk: "MyApp.apk",
        },
    },
   certificate: "platform",
   required: [
        "MyAppPermissions"
    ],

}


// 定义 XML 文件复制规则 
prebuilt_etc {
    name: "MyAppPermissions",      // 模块唯一标识 
    src: "com.example.myprovision2.xml", // 源文件路径(相对于当前 Android.bp )
    sub_dir: "permissions",        // 指定 system/etc 下的子目录 
    filename: "com.example.myprovision2.xml", // 目标文件名(可重命名)


}


完成之后我们make MyApp就会自动复制权限xml到对应目录下。

三、验证

我们push apk和xml到对应目录下之后,然后重启设备。这里重启之前还需要重置一下变量

adb shell settings put secure user_setup_complete 0

这样解锁有就可以正常启动我们自定义的开机向导了。

相关文章:

  • 【0407】Postgres内核 Condition variables (ConditionVariable)设计机制 ①
  • HDLBits ——> Building Larger Circuits
  • Windows桌面系统管理5:Windows 10操作系统注册表
  • ubuntu源码方式安装TensorRT-LLM推理框架(超详细)
  • 亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!
  • idea连接gitee(使用idea远程兼容gitee)
  • Redis7——基础篇(五)
  • Docker 部署 ollama + DeepSeek
  • buildctl配置镜像加速
  • ES6中Object.defineProperty 的详细用法和使用场景以及例子
  • 在 C++23 中使用智能指针进行现代内存管理 – 第 1 部分
  • MyBatisPlus学习
  • 【llm post-training】从Loss Function设计上看LLM SFT和RL的区别和联系
  • 【练习】【二分】力扣热题100 35. 搜索插入位置]
  • Markdown 常用语法及示例
  • CSS中块级格式化上下文(BFC)详解
  • 【c语言初阶】函数递归
  • 玩机日记 12 在PVE Windows11上部署本地AI模型,使用群晖反代https转发到外网提供服务,配合沉浸式翻译插件翻译网页
  • 复现论文:DPStyler: Dynamic PromptStyler for Source-Free Domain Generalization
  • 蓝桥杯 Java B 组 之堆的基础(优先队列实现 Top K 问题)
  • 《新时代的中国国家安全》白皮书(全文)
  • 言短意长|西湖大学首次“走出西湖”
  • 巫蛊:文化的历史暗流
  • 陈宝良 高寿仙 彭勇︱明清社会的皇权、商帮与市井百态
  • 广西百色通报:极端强对流天气致墙体倒塌,3人遇难7人受伤
  • 巴西总统卢拉将访华