Home | History | Annotate | Download | only in display
      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