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 import android.content.Context; 17 import android.content.pm.PackageInfo; 18 import android.content.pm.PackageManager; 19 import android.content.pm.PackageManager.NameNotFoundException; 20 import android.os.RemoteException; 21 import android.os.ServiceManager; 22 import android.os.UserHandle; 23 import android.support.annotation.VisibleForTesting; 24 import android.support.v7.preference.ListPreference; 25 import android.support.v7.preference.Preference; 26 import android.text.TextUtils; 27 28 import com.android.settings.R; 29 import com.android.settings.core.PreferenceControllerMixin; 30 import com.android.settings.overlay.FeatureFactory; 31 import com.android.settings.wrapper.OverlayManagerWrapper; 32 import com.android.settings.wrapper.OverlayManagerWrapper.OverlayInfo; 33 import com.android.settingslib.core.AbstractPreferenceController; 34 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 40 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME; 41 42 public class ThemePreferenceController extends AbstractPreferenceController implements 43 PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 44 45 private static final String KEY_THEME = "theme"; 46 47 private final MetricsFeatureProvider mMetricsFeatureProvider; 48 private final OverlayManagerWrapper mOverlayService; 49 private final PackageManager mPackageManager; 50 ThemePreferenceController(Context context)51 public ThemePreferenceController(Context context) { 52 this(context, ServiceManager.getService(Context.OVERLAY_SERVICE) != null 53 ? new OverlayManagerWrapper() : null); 54 } 55 56 @VisibleForTesting ThemePreferenceController(Context context, OverlayManagerWrapper overlayManager)57 ThemePreferenceController(Context context, OverlayManagerWrapper overlayManager) { 58 super(context); 59 mOverlayService = overlayManager; 60 mPackageManager = context.getPackageManager(); 61 mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 62 } 63 64 @Override getPreferenceKey()65 public String getPreferenceKey() { 66 return KEY_THEME; 67 } 68 69 @Override handlePreferenceTreeClick(Preference preference)70 public boolean handlePreferenceTreeClick(Preference preference) { 71 if (KEY_THEME.equals(preference.getKey())) { 72 mMetricsFeatureProvider.action(mContext, ACTION_THEME); 73 } 74 return false; 75 } 76 77 @Override updateState(Preference preference)78 public void updateState(Preference preference) { 79 ListPreference pref = (ListPreference) preference; 80 String[] pkgs = getAvailableThemes(); 81 CharSequence[] labels = new CharSequence[pkgs.length]; 82 for (int i = 0; i < pkgs.length; i++) { 83 try { 84 labels[i] = mPackageManager.getApplicationInfo(pkgs[i], 0) 85 .loadLabel(mPackageManager); 86 } catch (NameNotFoundException e) { 87 labels[i] = pkgs[i]; 88 } 89 } 90 pref.setEntries(labels); 91 pref.setEntryValues(pkgs); 92 String theme = getCurrentTheme(); 93 CharSequence themeLabel = null; 94 95 for (int i = 0; i < pkgs.length; i++) { 96 if (TextUtils.equals(pkgs[i], theme)) { 97 themeLabel = labels[i]; 98 break; 99 } 100 } 101 102 if (TextUtils.isEmpty(themeLabel)) { 103 themeLabel = mContext.getString(R.string.default_theme); 104 } 105 106 pref.setSummary(themeLabel); 107 pref.setValue(theme); 108 } 109 110 @Override onPreferenceChange(Preference preference, Object newValue)111 public boolean onPreferenceChange(Preference preference, Object newValue) { 112 String current = getTheme(); 113 if (Objects.equals(newValue, current)) { 114 return true; 115 } 116 mOverlayService.setEnabledExclusiveInCategory((String) newValue, UserHandle.myUserId()); 117 return true; 118 } 119 isTheme(OverlayInfo oi)120 private boolean isTheme(OverlayInfo oi) { 121 if (!OverlayInfo.CATEGORY_THEME.equals(oi.category)) { 122 return false; 123 } 124 try { 125 PackageInfo pi = mPackageManager.getPackageInfo(oi.packageName, 0); 126 return pi != null && !pi.isStaticOverlayPackage(); 127 } catch (PackageManager.NameNotFoundException e) { 128 return false; 129 } 130 } 131 getTheme()132 private String getTheme() { 133 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 134 UserHandle.myUserId()); 135 for (int i = 0, size = infos.size(); i < size; i++) { 136 if (infos.get(i).isEnabled() && isTheme(infos.get(i))) { 137 return infos.get(i).packageName; 138 } 139 } 140 return null; 141 } 142 143 @Override isAvailable()144 public boolean isAvailable() { 145 if (mOverlayService == null) return false; 146 String[] themes = getAvailableThemes(); 147 return themes != null && themes.length > 1; 148 } 149 150 151 @VisibleForTesting getCurrentTheme()152 String getCurrentTheme() { 153 return getTheme(); 154 } 155 156 @VisibleForTesting getAvailableThemes()157 String[] getAvailableThemes() { 158 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 159 UserHandle.myUserId()); 160 List<String> pkgs = new ArrayList<>(infos.size()); 161 for (int i = 0, size = infos.size(); i < size; i++) { 162 if (isTheme(infos.get(i))) { 163 pkgs.add(infos.get(i).packageName); 164 } 165 } 166 return pkgs.toArray(new String[pkgs.size()]); 167 } 168 } 169