1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.customization.model.theme;
17 
18 import android.content.Context;
19 import android.content.om.OverlayInfo;
20 import android.content.om.OverlayManager;
21 import android.os.UserHandle;
22 
23 import androidx.annotation.Nullable;
24 
25 import com.android.customization.model.ResourceConstants;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 
33 /**
34  * Wrapper over {@link OverlayManager} that abstracts away its internals and can be mocked for
35  * testing.
36  */
37 public class OverlayManagerCompat {
38     private final OverlayManager mOverlayManager;
39     private final String[] mTargetPackages;
40     private Map<Integer, Map<String, List<OverlayInfo>>> mOverlayByUser;
41 
OverlayManagerCompat(Context context)42     public OverlayManagerCompat(Context context) {
43         mOverlayManager = context.getSystemService(OverlayManager.class);
44         mTargetPackages = ResourceConstants.getPackagesToOverlay(context);
45     }
46 
isAvailable()47     public boolean isAvailable() {
48         return mOverlayManager != null;
49     }
50 
51     /**
52      * Enables the overlay provided by the given package for the given user Id
53      * @return true if the operation succeeded
54      */
setEnabledExclusiveInCategory(String packageName, int userId)55     public boolean setEnabledExclusiveInCategory(String packageName, int userId) {
56         mOverlayManager.setEnabledExclusiveInCategory(packageName, UserHandle.of(userId));
57         return true;
58     }
59 
60     /**
61      * Disables the overlay provided by the given package for the given user Id
62      * @return true if the operation succeeded
63      */
disableOverlay(String packageName, int userId)64     public boolean disableOverlay(String packageName, int userId) {
65         mOverlayManager.setEnabled(packageName, false, UserHandle.of(userId));
66         return true;
67     }
68 
69     /**
70      * @return the package name of the currently enabled overlay for the given target package, in
71      * the given category, or {@code null} if none is currently enabled.
72      */
73     @Nullable
getEnabledPackageName(String targetPackageName, String category)74     public String getEnabledPackageName(String targetPackageName, String category) {
75         // Can't use mOverlayByUser map as the enabled state might change
76         List<OverlayInfo> overlayInfos = getOverlayInfosForTarget(targetPackageName,
77                 UserHandle.myUserId());
78         for (OverlayInfo overlayInfo : overlayInfos) {
79             if (category.equals(overlayInfo.getCategory()) && overlayInfo.isEnabled()) {
80                 return overlayInfo.getPackageName();
81             }
82         }
83         return null;
84     }
85 
86     /**
87      * @return a Map of Category -> PackageName of all the overlays enabled for the given target
88      * packages. It might be empty if no overlay is enabled for those targets.
89      */
getEnabledOverlaysForTargets(String... targetPackages)90     public Map<String, String> getEnabledOverlaysForTargets(String... targetPackages) {
91         Map<String, String> overlays = new HashMap<>();
92         for (String target : targetPackages) {
93             addAllEnabledOverlaysForTarget(overlays, target);
94         }
95         return overlays;
96     }
97 
getOverlayPackagesForCategory(String category, int userId, String... targetPackages)98     public List<String> getOverlayPackagesForCategory(String category, int userId,
99             String... targetPackages) {
100         List<String> overlays = new ArrayList<>();
101         ensureCategoryMapForUser(userId);
102         for (String target : targetPackages) {
103             for (OverlayInfo info
104                     : mOverlayByUser.get(userId).getOrDefault(target, Collections.emptyList())) {
105                 if (category.equals(info.getCategory())) {
106                     overlays.add(info.getPackageName());
107                 }
108             }
109         }
110         return overlays;
111     }
112 
ensureCategoryMapForUser(int userId)113     private void ensureCategoryMapForUser(int userId) {
114         if (mOverlayByUser == null) {
115             mOverlayByUser = new HashMap<>();
116         }
117         if (!mOverlayByUser.containsKey(userId)) {
118             Map<String, List<OverlayInfo>> overlaysByTarget = new HashMap<>();
119             for (String target : mTargetPackages) {
120                 overlaysByTarget.put(target, getOverlayInfosForTarget(target, userId));
121             }
122             mOverlayByUser.put(userId, overlaysByTarget);
123         }
124     }
125 
126 
getOverlayInfosForTarget(String targetPackageName, int userId)127     private List<OverlayInfo> getOverlayInfosForTarget(String targetPackageName, int userId) {
128         return mOverlayManager.getOverlayInfosForTarget(targetPackageName, UserHandle.of(userId));
129     }
130 
addAllEnabledOverlaysForTarget(Map<String, String> overlays, String target)131     private void addAllEnabledOverlaysForTarget(Map<String, String> overlays, String target) {
132         // Can't use mOverlayByUser map as the enabled state might change
133         for (OverlayInfo overlayInfo : getOverlayInfosForTarget(target, UserHandle.myUserId())) {
134             if (overlayInfo.isEnabled()) {
135                 overlays.put(overlayInfo.getCategory(), overlayInfo.getPackageName());
136             }
137         }
138     }
139 }