1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.settings.display; 15 16 17 import android.app.settings.SettingsEnums; 18 import android.content.Context; 19 import android.content.om.IOverlayManager; 20 import android.content.om.OverlayInfo; 21 import android.content.pm.PackageInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.os.RemoteException; 25 import android.os.ServiceManager; 26 import android.os.UserHandle; 27 import android.text.TextUtils; 28 29 import androidx.annotation.VisibleForTesting; 30 import androidx.preference.ListPreference; 31 import androidx.preference.Preference; 32 33 import com.android.settings.R; 34 import com.android.settings.core.PreferenceControllerMixin; 35 import com.android.settings.overlay.FeatureFactory; 36 import com.android.settingslib.core.AbstractPreferenceController; 37 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Objects; 42 43 public class ThemePreferenceController extends AbstractPreferenceController implements 44 PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 45 46 private static final String KEY_THEME = "theme"; 47 48 private final MetricsFeatureProvider mMetricsFeatureProvider; 49 private final IOverlayManager mOverlayService; 50 private final PackageManager mPackageManager; 51 ThemePreferenceController(Context context)52 public ThemePreferenceController(Context context) { 53 this(context, IOverlayManager.Stub 54 .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))); 55 } 56 57 @VisibleForTesting ThemePreferenceController(Context context, IOverlayManager overlayManager)58 ThemePreferenceController(Context context, IOverlayManager overlayManager) { 59 super(context); 60 mOverlayService = overlayManager; 61 mPackageManager = context.getPackageManager(); 62 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 63 } 64 65 @Override getPreferenceKey()66 public String getPreferenceKey() { 67 return KEY_THEME; 68 } 69 70 @Override handlePreferenceTreeClick(Preference preference)71 public boolean handlePreferenceTreeClick(Preference preference) { 72 if (KEY_THEME.equals(preference.getKey())) { 73 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_THEME); 74 } 75 return false; 76 } 77 78 @Override updateState(Preference preference)79 public void updateState(Preference preference) { 80 ListPreference pref = (ListPreference) preference; 81 String[] pkgs = getAvailableThemes(false /* currentThemeOnly */); 82 CharSequence[] labels = new CharSequence[pkgs.length]; 83 for (int i = 0; i < pkgs.length; i++) { 84 try { 85 labels[i] = mPackageManager.getApplicationInfo(pkgs[i], 0) 86 .loadLabel(mPackageManager); 87 } catch (NameNotFoundException e) { 88 labels[i] = pkgs[i]; 89 } 90 } 91 pref.setEntries(labels); 92 pref.setEntryValues(pkgs); 93 String theme = getCurrentTheme(); 94 CharSequence themeLabel = null; 95 96 for (int i = 0; i < pkgs.length; i++) { 97 if (TextUtils.equals(pkgs[i], theme)) { 98 themeLabel = labels[i]; 99 break; 100 } 101 } 102 103 if (TextUtils.isEmpty(themeLabel)) { 104 themeLabel = mContext.getString(R.string.default_theme); 105 } 106 107 pref.setSummary(themeLabel); 108 pref.setValue(theme); 109 } 110 111 @Override onPreferenceChange(Preference preference, Object newValue)112 public boolean onPreferenceChange(Preference preference, Object newValue) { 113 String current = getCurrentTheme(); 114 if (Objects.equals(newValue, current)) { 115 return true; 116 } 117 try { 118 mOverlayService.setEnabledExclusiveInCategory((String) newValue, UserHandle.myUserId()); 119 } catch (RemoteException re) { 120 throw re.rethrowFromSystemServer(); 121 } 122 return true; 123 } 124 isTheme(OverlayInfo oi)125 private boolean isTheme(OverlayInfo oi) { 126 if (!OverlayInfo.CATEGORY_THEME.equals(oi.category)) { 127 return false; 128 } 129 try { 130 PackageInfo pi = mPackageManager.getPackageInfo(oi.packageName, 0); 131 return pi != null && !pi.isStaticOverlayPackage(); 132 } catch (PackageManager.NameNotFoundException e) { 133 return false; 134 } 135 } 136 137 @Override isAvailable()138 public boolean isAvailable() { 139 if (mOverlayService == null) return false; 140 String[] themes = getAvailableThemes(false /* currentThemeOnly */); 141 return themes != null && themes.length > 1; 142 } 143 144 145 @VisibleForTesting getCurrentTheme()146 String getCurrentTheme() { 147 String[] themePackages = getAvailableThemes(true /* currentThemeOnly */); 148 return themePackages.length < 1 ? null : themePackages[0]; 149 } 150 151 @VisibleForTesting getAvailableThemes(boolean currentThemeOnly)152 String[] getAvailableThemes(boolean currentThemeOnly) { 153 List<OverlayInfo> infos; 154 List<String> pkgs; 155 try { 156 infos = mOverlayService.getOverlayInfosForTarget("android", UserHandle.myUserId()); 157 pkgs = new ArrayList<>(infos.size()); 158 for (int i = 0, size = infos.size(); i < size; i++) { 159 if (isTheme(infos.get(i))) { 160 if (infos.get(i).isEnabled() && currentThemeOnly) { 161 return new String[] {infos.get(i).packageName}; 162 } else { 163 pkgs.add(infos.get(i).packageName); 164 } 165 } 166 } 167 } catch (RemoteException re) { 168 throw re.rethrowFromSystemServer(); 169 } 170 171 // Current enabled theme is not found. 172 if (currentThemeOnly) { 173 return new String[0]; 174 } 175 return pkgs.toArray(new String[pkgs.size()]); 176 } 177 } 178