1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.applications.appinfo; 18 19 import static android.os.UserHandle.getUserHandleForUid; 20 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE; 21 22 import android.app.ActivityManager; 23 import android.app.AppGlobals; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.IPackageManager; 27 import android.content.pm.LauncherApps; 28 import android.content.pm.PackageManager; 29 import android.os.RemoteException; 30 import android.provider.DeviceConfig; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 35 import com.android.car.settings.common.Logger; 36 37 /** 38 * Manager for handling app aspect ratio override behavior 39 */ 40 public final class AspectRatioManager { 41 private static final Logger LOG = new Logger(AspectRatioManager.class); 42 private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = true; 43 private static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS = 44 "enable_app_compat_aspect_ratio_user_settings"; 45 46 private final Context mContext; 47 private final IPackageManager mIPm; 48 private final ActivityManager mActivityManager; 49 AspectRatioManager(Context context)50 public AspectRatioManager(Context context) { 51 mContext = context; 52 mIPm = AppGlobals.getPackageManager(); 53 mActivityManager = mContext.getSystemService(ActivityManager.class); 54 } 55 56 /** Determines whether an app should have aspect ratio settings */ shouldShowAspectRatioSettingsForApp(ApplicationInfo appInfo)57 public boolean shouldShowAspectRatioSettingsForApp(ApplicationInfo appInfo) { 58 if (appInfo == null) { 59 return false; 60 } 61 62 return isFeatureEnabled() && canDisplayAspectRatioUi(appInfo); 63 } 64 65 /** 66 * @return {@link PackageManager.UserMinAspectRatio} override for an app 67 */ 68 @PackageManager.UserMinAspectRatio getUserMinAspectRatioValue(@onNull String packageName, int uid)69 public int getUserMinAspectRatioValue(@NonNull String packageName, int uid) 70 throws RemoteException { 71 int aspectRatio = mIPm.getUserMinAspectRatio(packageName, uid); 72 return aspectRatio; 73 } 74 75 /** 76 * Sets user-specified {@link PackageManager.UserMinAspectRatio} override for an app 77 */ setUserMinAspectRatio(@onNull String packageName, int uid, @PackageManager.UserMinAspectRatio int aspectRatio)78 public void setUserMinAspectRatio(@NonNull String packageName, int uid, 79 @PackageManager.UserMinAspectRatio int aspectRatio) throws RemoteException { 80 mIPm.setUserMinAspectRatio(packageName, uid, aspectRatio); 81 stopApp(packageName); 82 } 83 stopApp(@onNull String packageName)84 private void stopApp(@NonNull String packageName) { 85 mActivityManager.forceStopPackage(packageName); 86 } 87 88 /** 89 * Whether user aspect ratio settings is enabled for device. 90 */ isFeatureEnabled()91 private boolean isFeatureEnabled() { 92 final boolean isBuildTimeFlagEnabled = mContext.getResources().getBoolean( 93 com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled); 94 return getValueFromDeviceConfig(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, 95 DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS) && isBuildTimeFlagEnabled; 96 } 97 getValueFromDeviceConfig(String name, boolean defaultValue)98 private boolean getValueFromDeviceConfig(String name, boolean defaultValue) { 99 return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, name, defaultValue); 100 } 101 102 /** 103 * Whether an app's aspect ratio can be overridden by user. Only apps with launcher entry 104 * will be overridable. 105 */ canDisplayAspectRatioUi(@onNull ApplicationInfo appInfo)106 private boolean canDisplayAspectRatioUi(@NonNull ApplicationInfo appInfo) { 107 Boolean appAllowsUserAspectRatioOverride = readComponentProperty( 108 mContext.getPackageManager(), appInfo.packageName, 109 PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE); 110 return !(Boolean.FALSE.equals(appAllowsUserAspectRatioOverride)) 111 && hasLauncherEntry(appInfo); 112 } 113 hasLauncherEntry(@onNull ApplicationInfo app)114 private boolean hasLauncherEntry(@NonNull ApplicationInfo app) { 115 LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class); 116 if (launcherApps == null) { 117 return false; 118 } 119 120 return !launcherApps 121 .getActivityList(app.packageName, getUserHandleForUid(app.uid)) 122 .isEmpty(); 123 } 124 125 @Nullable readComponentProperty(PackageManager pm, String packageName, String propertyName)126 private static Boolean readComponentProperty(PackageManager pm, String packageName, 127 String propertyName) { 128 try { 129 return pm.getProperty(propertyName, packageName).getBoolean(); 130 } catch (PackageManager.NameNotFoundException e) { 131 LOG.e("Error getting property " + propertyName + " for package: " + packageName + " " 132 + e); 133 } 134 return null; 135 } 136 } 137