1package com.android.systemui.statusbar.car;
2
3import android.app.ActivityManager;
4import android.content.ComponentName;
5import android.content.Context;
6import android.content.Intent;
7import android.content.pm.PackageManager;
8import android.content.pm.ResolveInfo;
9import android.view.Display;
10import android.view.View;
11
12import java.util.HashMap;
13import java.util.List;
14import java.util.Set;
15
16/**
17 * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
18 * application on screen is associated with it. This is basically a similar concept to a radio
19 * button group.
20 */
21public class CarFacetButtonController {
22
23    protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
24    protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
25    protected HashMap<String, CarFacetButton> mButtonsByComponentName = new HashMap<>();
26    protected CarFacetButton mSelectedFacetButton;
27    protected Context mContext;
28
29    public CarFacetButtonController(Context context) {
30        mContext = context;
31    }
32
33    /**
34     * Add facet button to this controller. The expected use is for the facet button
35     * to get a reference to this controller via {@link com.android.systemui.Dependency}
36     * and self add.
37     * @param facetButton
38     */
39    public void addFacetButton(CarFacetButton facetButton) {
40        String[] categories = facetButton.getCategories();
41        for (int i = 0; i < categories.length; i++) {
42            mButtonsByCategory.put(categories[i], facetButton);
43        }
44
45        String[] facetPackages = facetButton.getFacetPackages();
46        for (int i = 0; i < facetPackages.length; i++) {
47            mButtonsByPackage.put(facetPackages[i], facetButton);
48        }
49        String[] componentNames = facetButton.getComponentName();
50        for (int i = 0; i < componentNames.length; i++) {
51            mButtonsByComponentName.put(componentNames[i], facetButton);
52        }
53        // Using the following as a default button for display id info it's not
54        // attached to a screen at this point so it can't be extracted here.
55        mSelectedFacetButton = facetButton;
56    }
57
58    public void removeAll() {
59        mButtonsByCategory.clear();
60        mButtonsByPackage.clear();
61        mButtonsByComponentName.clear();
62        mSelectedFacetButton = null;
63    }
64
65    /**
66     * This will unselect the currently selected CarFacetButton and determine which one should be
67     * selected next. It does this by reading the properties on the CarFacetButton and seeing if
68     * they are a match with the supplied StackInfo list.
69     * The order of selection detection is ComponentName, PackageName then Category
70     * They will then be compared with the supplied StackInfo list.
71     * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
72     * for consideration if it has the same displayId as the CarFacetButtons.
73     * @param taskInfo of the currently running application
74     */
75    public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
76        int displayId = getDisplayId();
77        ActivityManager.StackInfo validStackInfo = null;
78        for (ActivityManager.StackInfo stackInfo :stackInfoList) {
79            // If the display id is unknown or it matches the stack, it's valid for use
80            if ((displayId == -1 || displayId == stackInfo.displayId) &&
81                    stackInfo.topActivity != null) {
82                validStackInfo = stackInfo;
83                break;
84            }
85        }
86
87        if (validStackInfo == null) {
88            // No stack was found that was on the same display as the facet buttons thus return
89            return;
90        }
91
92        if (mSelectedFacetButton != null) {
93            mSelectedFacetButton.setSelected(false);
94        }
95
96        String packageName = validStackInfo.topActivity.getPackageName();
97        CarFacetButton facetButton = findFacetButtongByComponentName(validStackInfo.topActivity);
98        if (facetButton == null) {
99            facetButton = mButtonsByPackage.get(packageName);
100        }
101
102        if (facetButton == null) {
103            String category = getPackageCategory(packageName);
104            if (category != null) {
105                facetButton = mButtonsByCategory.get(category);
106            }
107        }
108
109        if (facetButton != null && facetButton.getVisibility() == View.VISIBLE) {
110            facetButton.setSelected(true);
111            mSelectedFacetButton = facetButton;
112        }
113
114    }
115
116    private int getDisplayId() {
117        if (mSelectedFacetButton != null) {
118            Display display = mSelectedFacetButton.getDisplay();
119            if (display != null) {
120                return display.getDisplayId();
121            }
122        }
123        return -1;
124    }
125
126    private CarFacetButton findFacetButtongByComponentName(ComponentName componentName) {
127        CarFacetButton button = mButtonsByComponentName.get(componentName.flattenToShortString());
128        return (button != null) ? button :
129                mButtonsByComponentName.get(componentName.flattenToString());
130    }
131
132    protected String getPackageCategory(String packageName) {
133        PackageManager pm = mContext.getPackageManager();
134        Set<String> supportedCategories = mButtonsByCategory.keySet();
135        for (String category : supportedCategories) {
136            Intent intent = new Intent();
137            intent.setPackage(packageName);
138            intent.setAction(Intent.ACTION_MAIN);
139            intent.addCategory(category);
140            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
141            if (list.size() > 0) {
142                // Cache this package name into facetPackageMap, so we won't have to query
143                // all categories next time this package name shows up.
144                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
145                return category;
146            }
147        }
148        return null;
149    }
150}
151