Android 系统适配无源码app
Android系统,无源码apk分辨率适配
竖屏app在大屏上的应用显示的屏占比问题
针对某个app修改分辨率,下下策 系统中用于强制设置显示尺寸,整个设备都有收到影响
demo1:某个应用的字体大小和UI显示,不适配当前设备的屏幕dpi
先来看一个问题:app启动,onResume中是否可以测量宽高?
如果是activity 启动后第一次进入onResume 生命周期,那么获取到的View的宽高是错误的;
如果是从其他activity回到当前activity而执行的onResume方法,那么就能够获取到View的宽高
为什么?
先找到系统调用 onResume 的地方,read f*** source code
./frameworks/base/core/java/android/app/ActivityThread.java
handleResumeActivity() {
//...
r = performResumeActivity(token, clearHide, reason);//调用 onResume()
//...
wm.addView(decor, l);// WindowManager添加Decor(decor是DecorView)
//...
}
可以看到View的添加是要在执行完onResume()之后的,所以我们调整某个应用的字体大小和UI显示最好是在它之前
frameworks/base/core/java/android/app/Activity.java
+ import android.util.DisplayMetrics;
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
//add text
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(1);
if (runningTasks != null && !runningTasks.isEmpty()) {
ComponentName topActivity = runningTasks.get(0).topActivity;
String packageName = topActivity.getPackageName();
if ("com.android.deskclock".equals(packageName)) {
Resources resources = getResources();
if (resources != null) {
Configuration configuration = resources.getConfiguration();
//适配字体
if(configuration != null){
if(configuration.fontScale != 1.5f){
configuration.fontScale = 1.5f;
resources.updateConfiguration(configuration,resources.getDisplayMetrics());
}
}
if (configuration != null) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int densityDpi = displayMetrics.densityDpi;
// 适配 dpi
if (densityDpi != 240) {
configuration.densityDpi = 240;
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}
}
}
}
}
//add text
}
Android 11 修改指定APP的DPI-CSDN博客
onResume中是否可以测量宽高
Android 屏幕尺寸的获取与变更(Android 12
Android 14 系统修改第三方应用的DPI
//获取屏幕大小 Android12
public void getScreenParams(){
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
WindowMetrics currentWindowMetrics = wm.getCurrentWindowMetrics();
int width = currentWindowMetrics.getBounds().width();
int height = currentWindowMetrics.getBounds().height();
screenInfo.setText("屏幕分辨率为:"+width+"*"+height);
Log.e(getClass().getName(),"screen width:"+width+" screen height:"+height);
}else {
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
screenInfo.setText("屏幕分辨率为:"+width+"*"+height);
Log.e(getClass().getName(),"screen width:"+width+" screen height:"+height);
}
获取Android设备屏幕大小 - xiaowang_lj - 博客园
densityDpi ** 屏幕密度是屏幕密度的绝对值,以每英寸像素数(dpi)表示。 每英寸屏幕中包含的像素数量,密度越大越清晰**。
density 逻辑密度
density = denistyDpi / 160
framework/base/core/java/android/window/WindowMetricsController.java (Android 14)
private WindowMetrics getWindowMetricsInternal(boolean isMaximum) {
final Rect bounds;
final float density;
final boolean isScreenRound;
final int activityType;
synchronized (ResourcesManager.getInstance()) {
final Configuration config = mContext.getResources().getConfiguration();
final WindowConfiguration winConfig = config.windowConfiguration;
bounds = (isMaximum) ? winConfig.getMaxBounds() : winConfig.getBounds();
// Multiply default density scale because WindowMetrics provide the density value with
// the scaling factor for the Density Independent Pixel unit, which is the same unit
// as DisplayMetrics#density
density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
//add text start
if(isMaximum && mContext.getBasePackageName().contains("xxxx")){
float dprVal = 1.6; //density = denistyDpi / 160
ensity = dprVal;
mContext.getResources().getDisplayMetrics().density = dprVal;
}
//add text end
isScreenRound = config.isScreenRound();
activityType = winConfig.getActivityType();
}
final IBinder token = Context.getToken(mContext);
final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
mContext.getDisplayId(), token, bounds, isScreenRound, activityType);
return new WindowMetrics(new Rect(bounds), insetsSupplier, density);
}
无源码app修改在Launcher3上显示的app图标
1.系统层修改,可以让系统内所有显示该app图标的地方都改了
/frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
parseBaseApk(){
...
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
final boolean isCoreApp =
parser.getAttributeBooleanValue(null, "coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
if (result.isError()) {
return result;
}
return input.success(pkg);
} finally {
manifestArray.recycle();
}
...
}
-> parseBaseApkTags() -> parseBaseApplication() -> parseBaseAppBasicFlags(){
...
// Resource ID
.setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
.setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
.setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))// app应用图标,熟悉的AndroidManifest
.setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
.setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
.setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
.setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
// Strings
.setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
...
.setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
//因为它是链式调用,不方便在上面添加拦截函数,最后替换
//add text
String packageName = pkg.getPackageName();
if(packageName != null && packageName.equals("com.android.settings")){
pkg.setIconRes(com.android.internal.R.drawable.perm_group_camera);//ic_battery
}
//add text
}
./frameworks/base/core/res/res/values/symbols.xml:: <java-symbol type="drawable" name="perm_group_camera" />
Android 11.0 PackageManagerService(二)APK扫描过程
2.Launcher3层面修改,在Launcher3层面修改界面上面显示的icon被修改了,setting和系统安装应用界面等还是原来的图
packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java
@UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
|| mDisplay == DISPLAY_TASKBAR;
int flags = useTheme ? FLAG_THEMED : 0;
if (mHideBadge) {
flags |= FLAG_NO_BADGE;
}
FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
mDotParams.appColor = iconDrawable.getIconColor();
mDotParams.dotColor = getContext().getResources()
.getColor(android.R.color.system_accent3_200, getContext().getTheme());
//add text
String pkg = info.getIntent().getComponent().getPackageName();
String cls = info.getIntent().getComponent().getClassName();
if("com.android.text".equals(pkg)){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_player);
setIcon(new FastBitmapDrawable(bitmap));
}else{
setIcon(iconDrawable);
}
//add text
applyLabel(info);
}
Android系统开发
Android第三方无源码应用图标icon定制
无源码app,没有申请对应权限,系统增加授予相关权限
遇到无源码apk,需要某个权限,但是apk内部没有申请,只能让系统适配授予某个权限给apk.
通过PMS(PackageManagerService)解析apk文件,安装apk的过程中来操作.
apk权限的申请通常都在AndroidManifest.xml文件中.
apk解析是PackageParser负责,Activity,Service等组件也是它负责.PackageParser相当于一个解释器.
frameworks/base/core/java/android/content/pm/PackageParser.java
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
...
pkg.mCompileSdkVersion = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
if (pkg.mCompileSdkVersionCodename != null) {
pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
}
pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
sa.recycle();
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
/* @param pkg The package which to populate
* @param acceptedTags Which tags to handle, null to handle all
* @param res Resources against which to resolve values
* @param parser Parser of the manifest
* @param flags Flags about how to parse
* @param outError Human readable error if parsing fails
* @return The package if parsing succeeded or null.
*/
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
...
//关于权限申请的解析
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {
if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION)) {
if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) {
if (!parsePermissionTree(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
...
}
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
...
//add text
android.util.Log.d("tag","name: "+name);
android.util.Log.d("tag",name.intern());
pkg.requestedPermissions.add("android.permission.ACCESS_FINE_LOCATION".intern());
pkg.requestedPermissions.add("android.permission.SYSTEM_ALERT_WINDOW".intern());
//add text
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
} else {
Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+ name + " in package: " + pkg.packageName + " at: "
+ parser.getPositionDescription());
}
return true;
}
无源码应用添加Launcher属性
应用程序的行为通常受到其清单文件(AndroidManifest.xml)中声明的组件和属性的控制.
对于无源码的app,无法直接修改其清单文件,但可以通过修改系统源码来实现特定功能.
app 的Launcher属性 ,由两个关键的 Intent 类别决定:
android.intent.category.HOME//是否为launcherandroid.intent.category.DEFAULTandroid.intent.category.LAUNCHER//是否有图标
还可以通过设置 IntentFilter 的优先级(Priority)来确保应用程序的启动行为符合预期
ParsedActivityUtils 是一个负责解析应用程序组件的工具类,这里对特定的应用组件(Activity)进行拦截,并动态为其添加 Launcher 属性.
(Android T)
frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@NonNull
private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivityImpl activity,
ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
ParseInput input, int parentActivityNameAttr, int permissionAttr,
int exportedAttr) throws IOException, XmlPullParserException {
...
ParsedIntentInfoImpl intentInfo = intentResult.getResult();
if (intentInfo != null) {
IntentFilter intentFilter = intentInfo.getIntentFilter();
//add text start
+ if("com.xx.xxx".equals(activity.getName())){
+ intentFilter.addCategory("android.intent.category.HOME");
+ intentFilter.addCategory("android.intent.category.DEFAULT");
+ intentFilter.setPriority(1000);//设置Priority为1000,确保该启动项的优先级较高
+ }
//add text end
activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
activity.addIntent(intentInfo);
...
}
(Android R)
+++ b/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -333,6 +333,14 @@ public class ParsedActivityUtils {
ParsedIntentInfo intent = intentResult.getResult();
if (intent != null) {
activity.order = Math.max(intent.getOrder(), activity.order);
+ //add text start
+ if(activity.getName().contains("com.example.text_app")){
+ intent.addCategory("android.intent.category.HOME");
+ intent.addCategory("android.intent.category.DEFAULT");
+ intent.setPriority(1000);
+ }
+ //add text end
activity.addIntent(intent);
if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver

浙公网安备 33010602011771号