• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.systemui.statusbar.car;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.res.TypedArray;
22 import android.os.UserHandle;
23 import android.util.AttributeSet;
24 import android.view.Display;
25 import android.view.View;
26 import android.widget.ImageView;
27 import android.widget.LinearLayout;
28 
29 import com.android.keyguard.AlphaOptimizedImageButton;
30 import com.android.systemui.CarSystemUIFactory;
31 import com.android.systemui.R;
32 import com.android.systemui.SystemUIFactory;
33 
34 /**
35  * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
36  * category. It can also render a indicator impling that there are more options of apps to launch
37  * using this component. This is done with a "More icon" currently an arrow as defined in the layout
38  * file. The class is to serve as an example.
39  * Usage example: A button that allows a user to select a music app and indicate that there are
40  * other music apps installed.
41  */
42 public class CarFacetButton extends LinearLayout {
43     private static final String FACET_FILTER_DELIMITER = ";";
44     /**
45      * Extra information to be sent to a helper to make the decision of what app to launch when
46      * clicked.
47      */
48     private static final String EXTRA_FACET_CATEGORIES = "categories";
49     private static final String EXTRA_FACET_PACKAGES = "packages";
50     private static final String EXTRA_FACET_ID = "filter_id";
51     private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
52     private static final String TAG = "CarFacetButton";
53 
54     private Context mContext;
55     private AlphaOptimizedImageButton mIcon;
56     private AlphaOptimizedImageButton mMoreIcon;
57     private boolean mSelected = false;
58     private String[] mComponentNames;
59     /** App categories that are to be used with this widget */
60     private String[] mFacetCategories;
61     /** App packages that are allowed to be used with this widget */
62     private String[] mFacetPackages;
63     private int mIconResourceId;
64     /**
65      * If defined in the xml this will be the icon that's rendered when the button is marked as
66      * selected
67      */
68     private int mSelectedIconResourceId;
69     private boolean mUseMoreIcon = true;
70     private float mSelectedAlpha = 1f;
71     private float mUnselectedAlpha = 1f;
72 
CarFacetButton(Context context, AttributeSet attrs)73     public CarFacetButton(Context context, AttributeSet attrs) {
74         super(context, attrs);
75         mContext = context;
76         View.inflate(context, R.layout.car_facet_button, this);
77         // extract custom attributes
78         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
79         setupIntents(typedArray);
80         setupIcons(typedArray);
81         CarSystemUIFactory factory = SystemUIFactory.getInstance();
82         CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
83                 .getCarFacetButtonController();
84         carFacetButtonController.addFacetButton(this);
85     }
86 
87     /**
88      * Reads the custom attributes to setup click handlers for this component.
89      */
setupIntents(TypedArray typedArray)90     protected void setupIntents(TypedArray typedArray) {
91         String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
92         String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
93         String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
94         String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
95         String componentNameString =
96                 typedArray.getString(R.styleable.CarFacetButton_componentNames);
97         try {
98             final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
99             intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
100 
101             if (packageString != null) {
102                 mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
103                 intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
104             }
105             if (categoryString != null) {
106                 mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
107                 intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
108             }
109             if (componentNameString != null) {
110                 mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
111             }
112 
113             setOnClickListener(v -> {
114                 intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
115                 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
116             });
117 
118             if (longPressIntentString != null) {
119                 final Intent longPressIntent = Intent.parseUri(longPressIntentString,
120                         Intent.URI_INTENT_SCHEME);
121                 setOnLongClickListener(v -> {
122                     mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT);
123                     return true;
124                 });
125             }
126         } catch (Exception e) {
127             throw new RuntimeException("Failed to attach intent", e);
128         }
129     }
130 
setupIcons(TypedArray styledAttributes)131     private void setupIcons(TypedArray styledAttributes) {
132         mSelectedAlpha = styledAttributes.getFloat(
133                 R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
134         mUnselectedAlpha = styledAttributes.getFloat(
135                 R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
136         mIcon = findViewById(R.id.car_nav_button_icon);
137         mIcon.setScaleType(ImageView.ScaleType.CENTER);
138         mIcon.setClickable(false);
139         mIcon.setAlpha(mUnselectedAlpha);
140         mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
141         mIcon.setImageResource(mIconResourceId);
142         mSelectedIconResourceId = styledAttributes.getResourceId(
143                 R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
144 
145         mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
146         mMoreIcon.setClickable(false);
147         mMoreIcon.setAlpha(mSelectedAlpha);
148         mMoreIcon.setVisibility(GONE);
149         mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
150     }
151 
152     /**
153      * @return The app categories the component represents
154      */
getCategories()155     public String[] getCategories() {
156         if (mFacetCategories == null) {
157             return new String[0];
158         }
159         return mFacetCategories;
160     }
161 
162     /**
163      * @return The valid packages that should be considered.
164      */
getFacetPackages()165     public String[] getFacetPackages() {
166         if (mFacetPackages == null) {
167             return new String[0];
168         }
169         return mFacetPackages;
170     }
171 
172     /**
173      * @return The list of component names.
174      */
getComponentName()175     public String[] getComponentName() {
176         if (mComponentNames == null) {
177             return new String[0];
178         }
179         return mComponentNames;
180     }
181 
182     /**
183      * Updates the alpha of the icons to "selected" and shows the "More icon"
184      *
185      * @param selected true if the view must be selected, false otherwise
186      */
setSelected(boolean selected)187     public void setSelected(boolean selected) {
188         super.setSelected(selected);
189         setSelected(selected, selected);
190     }
191 
192     /**
193      * Updates the visual state to let the user know if it's been selected.
194      *
195      * @param selected     true if should update the alpha of the icon to selected, false otherwise
196      * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
197      *                     is ignored if the attribute useMoreIcon is set to false
198      */
setSelected(boolean selected, boolean showMoreIcon)199     public void setSelected(boolean selected, boolean showMoreIcon) {
200         mSelected = selected;
201         mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
202         mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
203         if (mUseMoreIcon) {
204             mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
205         }
206     }
207 
208     /**
209      * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
210      *         a display.
211      */
getDisplayId()212     public int getDisplayId() {
213         Display display = getDisplay();
214         if (display == null) {
215             return Display.INVALID_DISPLAY;
216         }
217         return display.getDisplayId();
218     }
219 }
220