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.PreferenceControllerMixin; 32 import com.android.settings.core.instrumentation.MetricsFeatureProvider; 33 import com.android.settings.overlay.FeatureFactory; 34 import com.android.settingslib.core.AbstractPreferenceController; 35 36 import libcore.util.Objects; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME; 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 OverlayManager mOverlayService; 50 private final PackageManager mPackageManager; 51 52 public ThemePreferenceController(Context context) { 53 this(context, ServiceManager.getService(Context.OVERLAY_SERVICE) != null 54 ? new OverlayManager() : null); 55 } 56 57 @VisibleForTesting 58 ThemePreferenceController(Context context, OverlayManager overlayManager) { 59 super(context); 60 mOverlayService = overlayManager; 61 mPackageManager = context.getPackageManager(); 62 mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 63 } 64 65 @Override 66 public String getPreferenceKey() { 67 return KEY_THEME; 68 } 69 70 @Override 71 public boolean handlePreferenceTreeClick(Preference preference) { 72 if (KEY_THEME.equals(preference.getKey())) { 73 mMetricsFeatureProvider.action(mContext, ACTION_THEME); 74 } 75 return false; 76 } 77 78 @Override 79 public void updateState(Preference preference) { 80 ListPreference pref = (ListPreference) preference; 81 String[] pkgs = getAvailableThemes(); 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 112 public boolean onPreferenceChange(Preference preference, Object newValue) { 113 String current = getTheme(); 114 if (Objects.equal(newValue, current)) { 115 return true; 116 } 117 try { 118 mOverlayService.setEnabledExclusive((String) newValue, true, UserHandle.myUserId()); 119 } catch (RemoteException e) { 120 return false; 121 } 122 return true; 123 } 124 125 private boolean isChangeableOverlay(String packageName) { 126 try { 127 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 128 return pi != null && !pi.isStaticOverlay; 129 } catch (PackageManager.NameNotFoundException e) { 130 return false; 131 } 132 } 133 134 private String getTheme() { 135 try { 136 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 137 UserHandle.myUserId()); 138 for (int i = 0, size = infos.size(); i < size; i++) { 139 if (infos.get(i).isEnabled() && 140 isChangeableOverlay(infos.get(i).packageName)) { 141 return infos.get(i).packageName; 142 } 143 } 144 } catch (RemoteException e) { 145 } 146 return null; 147 } 148 149 @Override 150 public boolean isAvailable() { 151 if (mOverlayService == null) return false; 152 String[] themes = getAvailableThemes(); 153 return themes != null && themes.length > 1; 154 } 155 156 157 @VisibleForTesting 158 String getCurrentTheme() { 159 return getTheme(); 160 } 161 162 @VisibleForTesting 163 String[] getAvailableThemes() { 164 try { 165 List<OverlayInfo> infos = mOverlayService.getOverlayInfosForTarget("android", 166 UserHandle.myUserId()); 167 List<String> pkgs = new ArrayList(infos.size()); 168 for (int i = 0, size = infos.size(); i < size; i++) { 169 if (isChangeableOverlay(infos.get(i).packageName)) { 170 pkgs.add(infos.get(i).packageName); 171 } 172 } 173 return pkgs.toArray(new String[pkgs.size()]); 174 } catch (RemoteException e) { 175 } 176 return new String[0]; 177 } 178 179 public static class OverlayManager { 180 private final IOverlayManager mService; 181 182 public OverlayManager() { 183 mService = IOverlayManager.Stub.asInterface( 184 ServiceManager.getService(Context.OVERLAY_SERVICE)); 185 } 186 187 public void setEnabledExclusive(String pkg, boolean enabled, int userId) 188 throws RemoteException { 189 mService.setEnabledExclusive(pkg, enabled, userId); 190 } 191 192 public List<OverlayInfo> getOverlayInfosForTarget(String target, int userId) 193 throws RemoteException { 194 return mService.getOverlayInfosForTarget(target, userId); 195 } 196 } 197 } 198