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 */
14package com.android.settings.display;
15
16import android.content.Context;
17import android.content.om.IOverlayManager;
18import android.content.om.OverlayInfo;
19import android.content.pm.PackageInfo;
20import android.content.pm.PackageManager;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.os.UserHandle;
25import android.support.annotation.VisibleForTesting;
26import android.support.v7.preference.ListPreference;
27import android.support.v7.preference.Preference;
28import android.text.TextUtils;
29
30import com.android.settings.R;
31import com.android.settings.core.PreferenceController;
32import com.android.settings.core.instrumentation.MetricsFeatureProvider;
33import com.android.settings.overlay.FeatureFactory;
34
35import libcore.util.Objects;
36
37import java.util.ArrayList;
38import java.util.List;
39
40import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME;
41
42public 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
51    public ThemePreferenceController(Context context) {
52        this(context, ServiceManager.getService(Context.OVERLAY_SERVICE) != null
53                ? new OverlayManager() : null);
54    }
55
56    @VisibleForTesting
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
65    public String getPreferenceKey() {
66        return KEY_THEME;
67    }
68
69    @Override
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
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
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
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
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
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
148    String getCurrentTheme() {
149        return getTheme();
150    }
151
152    @VisibleForTesting
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
172        public OverlayManager() {
173            mService = IOverlayManager.Stub.asInterface(
174                    ServiceManager.getService(Context.OVERLAY_SERVICE));
175        }
176
177        public void setEnabledExclusive(String pkg, boolean enabled, int userId)
178                throws RemoteException {
179            mService.setEnabledExclusive(pkg, enabled, userId);
180        }
181
182        public List<OverlayInfo> getOverlayInfosForTarget(String target, int userId)
183                throws RemoteException {
184            return mService.getOverlayInfosForTarget(target, userId);
185        }
186    }
187}
188