1078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningpackage com.android.systemui.statusbar.car;
2078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
3078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport android.app.ActivityManager;
4d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenningimport android.content.ComponentName;
5078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport android.content.Context;
6078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport android.content.Intent;
7078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport android.content.pm.PackageManager;
8078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport android.content.pm.ResolveInfo;
9a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenningimport android.view.Display;
107e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenningimport android.view.View;
11078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
12078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport java.util.HashMap;
13078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport java.util.List;
14078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningimport java.util.Set;
15078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
16078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning/**
17078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
18078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning * application on screen is associated with it. This is basically a similar concept to a radio
19078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning * button group.
20078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning */
21078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenningpublic class CarFacetButtonController {
22078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
23078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
24078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
25d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning    protected HashMap<String, CarFacetButton> mButtonsByComponentName = new HashMap<>();
26078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    protected CarFacetButton mSelectedFacetButton;
27078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    protected Context mContext;
28078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
29078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    public CarFacetButtonController(Context context) {
30078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        mContext = context;
31078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    }
32078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
33078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    /**
3438b46f82f718891de608ca31e49034d824a88a12Brad Stenning     * Add facet button to this controller. The expected use is for the facet button
3538b46f82f718891de608ca31e49034d824a88a12Brad Stenning     * to get a reference to this controller via {@link com.android.systemui.Dependency}
3638b46f82f718891de608ca31e49034d824a88a12Brad Stenning     * and self add.
3738b46f82f718891de608ca31e49034d824a88a12Brad Stenning     * @param facetButton
38078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning     */
3938b46f82f718891de608ca31e49034d824a88a12Brad Stenning    public void addFacetButton(CarFacetButton facetButton) {
4038b46f82f718891de608ca31e49034d824a88a12Brad Stenning        String[] categories = facetButton.getCategories();
41d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        for (int i = 0; i < categories.length; i++) {
42d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning            mButtonsByCategory.put(categories[i], facetButton);
4338b46f82f718891de608ca31e49034d824a88a12Brad Stenning        }
44078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
4538b46f82f718891de608ca31e49034d824a88a12Brad Stenning        String[] facetPackages = facetButton.getFacetPackages();
46d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        for (int i = 0; i < facetPackages.length; i++) {
47d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning            mButtonsByPackage.put(facetPackages[i], facetButton);
48d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        }
49d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        String[] componentNames = facetButton.getComponentName();
50d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        for (int i = 0; i < componentNames.length; i++) {
51d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning            mButtonsByComponentName.put(componentNames[i], facetButton);
52078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        }
53a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        // Using the following as a default button for display id info it's not
54a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        // attached to a screen at this point so it can't be extracted here.
55a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        mSelectedFacetButton = facetButton;
56078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    }
57078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
58224b5b39a274623e92e9238e112181eb086379adBrad Stenning    public void removeAll() {
59224b5b39a274623e92e9238e112181eb086379adBrad Stenning        mButtonsByCategory.clear();
60224b5b39a274623e92e9238e112181eb086379adBrad Stenning        mButtonsByPackage.clear();
61d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        mButtonsByComponentName.clear();
62224b5b39a274623e92e9238e112181eb086379adBrad Stenning        mSelectedFacetButton = null;
63224b5b39a274623e92e9238e112181eb086379adBrad Stenning    }
64224b5b39a274623e92e9238e112181eb086379adBrad Stenning
65078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    /**
66078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning     * This will unselect the currently selected CarFacetButton and determine which one should be
67078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning     * selected next. It does this by reading the properties on the CarFacetButton and seeing if
68a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning     * they are a match with the supplied StackInfo list.
69a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning     * The order of selection detection is ComponentName, PackageName then Category
70a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning     * They will then be compared with the supplied StackInfo list.
71a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning     * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
72a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning     * for consideration if it has the same displayId as the CarFacetButtons.
73078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning     * @param taskInfo of the currently running application
74078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning     */
75a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning    public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
76a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        int displayId = getDisplayId();
777e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        ActivityManager.StackInfo validStackInfo = null;
78a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        for (ActivityManager.StackInfo stackInfo :stackInfoList) {
797e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            // If the display id is unknown or it matches the stack, it's valid for use
807e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            if ((displayId == -1 || displayId == stackInfo.displayId) &&
817e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning                    stackInfo.topActivity != null) {
827e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning                validStackInfo = stackInfo;
837e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning                break;
84a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning            }
857e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        }
86078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
877e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        if (validStackInfo == null) {
887e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            // No stack was found that was on the same display as the facet buttons thus return
897e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            return;
907e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        }
91a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning
927e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        if (mSelectedFacetButton != null) {
937e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            mSelectedFacetButton.setSelected(false);
947e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        }
95a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning
967e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        String packageName = validStackInfo.topActivity.getPackageName();
977e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        CarFacetButton facetButton = findFacetButtongByComponentName(validStackInfo.topActivity);
987e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        if (facetButton == null) {
997e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            facetButton = mButtonsByPackage.get(packageName);
1007e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        }
101a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning
1027e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        if (facetButton == null) {
1037e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            String category = getPackageCategory(packageName);
1047e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            if (category != null) {
1057e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning                facetButton = mButtonsByCategory.get(category);
106a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning            }
107a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        }
1087e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning
1097e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        if (facetButton != null && facetButton.getVisibility() == View.VISIBLE) {
1107e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            facetButton.setSelected(true);
1117e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning            mSelectedFacetButton = facetButton;
1127e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning        }
1137e4118135a6e2b685a98073e26637d5d89fdec06Brad Stenning
114a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning    }
115a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning
116a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning    private int getDisplayId() {
117a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        if (mSelectedFacetButton != null) {
118a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning            Display display = mSelectedFacetButton.getDisplay();
119a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning            if (display != null) {
120a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning                return display.getDisplayId();
121078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            }
122078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        }
123a5e714cafa091cafef1d89af8f41291ca773a058Brad Stenning        return -1;
124078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    }
125078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning
126d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning    private CarFacetButton findFacetButtongByComponentName(ComponentName componentName) {
127d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        CarFacetButton button = mButtonsByComponentName.get(componentName.flattenToShortString());
128d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning        return (button != null) ? button :
129d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning                mButtonsByComponentName.get(componentName.flattenToString());
130d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning    }
131d450ad6d1b2c2f12c5b1968f6ba1190e6d9c912bBrad Stenning
132078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    protected String getPackageCategory(String packageName) {
133078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        PackageManager pm = mContext.getPackageManager();
134078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        Set<String> supportedCategories = mButtonsByCategory.keySet();
135078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        for (String category : supportedCategories) {
136078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            Intent intent = new Intent();
137078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            intent.setPackage(packageName);
138078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            intent.setAction(Intent.ACTION_MAIN);
139078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            intent.addCategory(category);
140078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
141078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            if (list.size() > 0) {
142078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning                // Cache this package name into facetPackageMap, so we won't have to query
143078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning                // all categories next time this package name shows up.
144078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
145078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning                return category;
146078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning            }
147078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        }
148078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning        return null;
149078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning    }
150078235b17d3aadc78196e41c21759fa40c0df5d4Brad Stenning}
151