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.om.IOverlayManager; 18 import android.content.om.OverlayInfo; 19 import android.content.pm.PackageInfo; 20 import android.content.pm.PackageManager; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.os.RemoteException; 23 import android.os.ServiceManager; 24 import android.os.UserHandle; 25 import android.support.annotation.VisibleForTesting; 26 import android.support.v7.preference.ListPreference; 27 import android.support.v7.preference.Preference; 28 import android.text.TextUtils; 29 30 import com.android.settings.R; 31 import com.android.settings.core.PreferenceController; 32 import com.android.settings.core.instrumentation.MetricsFeatureProvider; 33 import com.android.settings.overlay.FeatureFactory; 34 35 import libcore.util.Objects; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 40 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME; 41 42 public class ThemePreferenceController extends PreferenceController implements 43 Preference.OnPreferenceChangeListener { 44 45 private static final String KEY_THEME = "theme"; 46 47 private final MetricsFeatureProvider mMetricsFeatureProvider; 48 private final OverlayManager 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 OverlayManager() : null); 54 } 55 56 @VisibleForTesting ThemePreferenceController(Context context, OverlayManager overlayManager)57 ThemePreferenceController(Context context, OverlayManager 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 if (TextUtils.isEmpty(theme)) { 94 theme = mContext.getString(R.string.default_theme); 95 pref.setSummary(theme); 96 } 97 pref.setSummary(theme); 98 pref.setValue(theme); 99 } 100 101 @Override onPreferenceChange(Preference preference, Object newValue)102 public boolean onPreferenceChange(Preference preference, Object newValue) { 103 String current = getTheme(); 104 if (Objects.equal(newValue, current)) { 105 return true; 106 } 107 try { 108 mOverlayService.setEnabledExclusive((String) newValue, true, UserHandle.myUserId()); 109 } catch (RemoteException e) { 110 return false; 111 } 112 return true; 113 } 114 isChangeableOverlay(String packageName)115 private boolean isChangeableOverlay(String packageName) { 116 try { 117 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 118 return pi != null && !pi.isStaticOverlay; 119 } catch (PackageManager.NameNotFoundException e) { 120 return false; 121 } 122 } 123 getTheme()124 private String getTheme() { 125 try { 126 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 127 UserHandle.myUserId()); 128 for (int i = 0, size = infos.size(); i < size; i++) { 129 if (infos.get(i).isEnabled() && 130 isChangeableOverlay(infos.get(i).packageName)) { 131 return infos.get(i).packageName; 132 } 133 } 134 } catch (RemoteException e) { 135 } 136 return null; 137 } 138 139 @Override isAvailable()140 public boolean isAvailable() { 141 if (mOverlayService == null) return false; 142 String[] themes = getAvailableThemes(); 143 return themes != null && themes.length > 1; 144 } 145 146 147 @VisibleForTesting getCurrentTheme()148 String getCurrentTheme() { 149 return getTheme(); 150 } 151 152 @VisibleForTesting getAvailableThemes()153 String[] getAvailableThemes() { 154 try { 155 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 156 UserHandle.myUserId()); 157 List<String> pkgs = new ArrayList(infos.size()); 158 for (int i = 0, size = infos.size(); i < size; i++) { 159 if (isChangeableOverlay(infos.get(i).packageName)) { 160 pkgs.add(infos.get(i).packageName); 161 } 162 } 163 return pkgs.toArray(new String[pkgs.size()]); 164 } catch (RemoteException e) { 165 } 166 return new String[0]; 167 } 168 169 public static class OverlayManager { 170 private final IOverlayManager mService; 171 OverlayManager()172 public OverlayManager() { 173 mService = IOverlayManager.Stub.asInterface( 174 ServiceManager.getService(Context.OVERLAY_SERVICE)); 175 } 176 setEnabledExclusive(String pkg, boolean enabled, int userId)177 public void setEnabledExclusive(String pkg, boolean enabled, int userId) 178 throws RemoteException { 179 mService.setEnabledExclusive(pkg, enabled, userId); 180 } 181 getOverlayInfosForTarget(String target, int userId)182 public List<OverlayInfo> getOverlayInfosForTarget(String target, int userId) 183 throws RemoteException { 184 return mService.getOverlayInfosForTarget(target, userId); 185 } 186 } 187 } 188