多種方式實現動態替換Android默認桌面Launcher


多種方式實現動態替換Android默認桌面Launcher
文章目錄
多種方式實現動態替換Android默認桌面Launcher
背景簡介
技術方案
三種方案
方案一
方案二
方案三
風險
背景簡介
Launcher-是安卓系統中的桌面啟動器,安卓系統的桌面UI統稱為Launcher。Launcher是安卓系統中的主要程序組件之一,安卓系統中如果沒有Launcher就無法啟動Android

當前場景
現在安卓設備應用越來越廣泛,有些出廠前可能沒有預留系統OTA升級的入口,但是有app的升級通道,而又在一些情況下需要更改已經出廠的系統默認桌面,所以針對這種情況需要實現一種方案,在不進行系統升級更改的情況下,只通過升級App的方式,實現動態替換默認Launcher的方案
技術方案
三種方案
用代碼設置系統配置,保留兩個Launcher應用進行動態切換

動態刪除添加系統默認/system/priv-app/目錄下的默認Launcher.apk文件

不設置Launcher,通過獲取系統底層對Activity狀態的監控,攔截Launcher

前三種方案都需要系統權限,並在AndroidManifest.xml 中配置Launcher屬性

系統權限,配置shareUserId,並且用系統簽名文件進行簽名
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.launcher"
android:sharedUserId="android.uid.system">
 
新增Launcher屬性
<activity android:name=".MainActivity">
<intent-filter>
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</acivity>
 
方案一
使用代碼配置動態Launcher,需要額外的添加一個系統級別的權限

<permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"/>
1
該權限允許應用程序,進行系統屬性的配置更改,有了該權限后,才可以進行動態的屬性配置。


清除默認Launcher:

安裝運行自定義Launcher應用
遍歷所有配置Launcher屬性的應用
清除除所有應用Launcher屬性
添加自定義桌面應用Launcher屬性配置
private void clear(Context context){

ArrayList<IntentFilter> intentList = new ArrayList<>();
ArrayList<ComponentName> cnList = new ArrayList<>();
context.getPackageManager().getPreferredActivities(intentList, cnList, null);
for(int i = 0; i < cnList.size(); i++) {
IntentFilter dhIF = intentList.get(i);
if(dhIF.hasAction(Intent.ACTION_MAIN) && dhIF.hasCategory(Intent.CATEGORY_HOME)) {//遍歷過濾所有launcher
try {
String name = cnList.get(i).getPackageName();
//清除原有的默認launcher
context.getPackageManager().clearPackagePreferredActivities(name;
}catch (Exception ex){
}
}
}
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);

final int N = mHome.size();
ComponentName[] set = new ComponentName[N];
for (int i = 0; i < N; i++) {
ResolveInfo r = mHome.get(i);
set[i] = new ComponentName(r.activityInfo.packageName,r.activityInfo.name);
}
//設置自定義launcher
ComponentName launcher = new ComponentName(hiBoxLauncher, hiBoxActivity);
context.getPackageManager().addPreferredActivity(filter, 1081344, set, launcher);

恢復默認Launcher方法同上一樣,清除完所有Launcher相關的意圖配置,最后添加上系統Launcher包名。

方案二
通過文件刪除的方式進行Launcher替換,需要文件的讀寫權限,而且系統應用刪除后需要重啟才能生效,所以還需要reboot的權限

<permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<permission android:name="android.permission.REBOOT"/>
 
清除默認Launcher:

安裝運行自定義Launcher應用
復制/system/priv-app/Launcher2.apk保存到sdcrad
刪除/system/priv-app/Launcher2.apk
重啟安卓系統,只留下自定義Launcher應用生效
恢復默認launcher只需要將事先保存的apk,拷貝到系統目錄即可,當前系統會存在兩個Launcher,需要手動選擇一次默認Launcher

方案三
不需要替換Launcher,通過反射的方式注冊系統android.app.
IActivityController的隱藏aidl回調。監聽每個應用的Activity界面的狀態,當回調系統顯示Launcher的時候攔截回調,打開自定義的應用界面,覆蓋系統Launcher,需要系統權限,還需要額外的Activity設置權限

<uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />
1
1.聲明aidl文件
在工程中聲明一個android.app.IActivityController的aidl,用於注冊系統的回調,可以直接從Android源碼中拷貝


interface IActivityController{

boolean activityStarting(in Intent intent, String pkg);

boolean activityResuming(String pkg);

boolean appCrashed(String processName, int pid,
String shortMsg, String longMsg,
long timeMillis, String stackTrace);

int appEarlyNotResponding(String processName, int pid, String annotation);

int appNotResponding(String processName, int pid, String processStats);

2.反射注冊aidl回調代碼:


//在進入頁面時調用setActivityController()方法,注冊aidl回調,當界面有變化時會觸發activityStarting(),activityResuming()事件
public void setActivityController() {
Log.d(TAG,">>>>>>0519,");
try {
Class<?> cActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
Method mGetDefault = cActivityManagerNative.getMethod("getDefault", new Class[]{});
Object oActivityManagerNative = mGetDefault.invoke(null, new Object[]{});
Method mSetActivityController = cActivityManagerNative.getMethod("setActivityController",
Class.forName("android.app.IActivityController"));
mSetActivityController.invoke(oActivityManagerNative, new ActivityController());
} catch (ClassNotFoundException e) {
exceptionWhenSet(e);

3.攔截Launcher
綁定服務,獲取系統回調的Activity狀態接口,進行應用打開操作

private class ActivityController extends android.app.IActivityController.Stub {

public boolean activityStarting(Intent intent, String pkg) {

if(launcher2.equals(pkg)&&isFilter){
startHiBoxLauncher(); }
return true;
}
public boolean activityResuming(String pkg) {
Log.d(TAG,">>>>>>0519,pkg=" + pkg);
if(launcher2.equals(pkg)&&isFilter){
startHiBoxLauncher();
}
return true;
}

如果是管理員進行操作,需要恢復Launcher,可以用過標志位Boolen值來控制是否攔截系統Launcher

風險
1.重啟安卓設備Launcher配置重置,方案一代碼設置生效后,不需要手動進行選擇,可以同時在兩個launcher中切換,但是測試發現,安卓設備重啟后,會清除配置,需要重新選擇默認Launcher

 

 


2.Android系統應用無法刪除,在一些設備上測試發現,即使有了系統權限,只能對/system/priv-app/Launcher2.apk進行拷貝操作,無法進行刪除操作。


3.簽名不一致覆蓋安裝風險,所有的方案都需要重新系統簽名,獲取系統權限,跟之前Android應用簽名不一致,可能會出現覆蓋安裝失敗的問題


————————————————
版權聲明:本文為CSDN博主「ArrayZhang」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/rui1605/article/details/87808581/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM