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