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 17 package com.android.car.apps.common; 18 19 import android.car.drivingstate.CarUxRestrictions; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.os.Handler; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import android.widget.TextView; 26 import android.widget.Toast; 27 28 import androidx.annotation.Nullable; 29 30 import com.android.car.ui.utils.CarUxRestrictionsUtil; 31 32 /** 33 * UX Restrictions compliant Button. 34 * This class will automatically listen to Car UXRestrictions, and respond to click event 35 * accordingly. You can set one or multiple restrictions in the layout file, e.g., 36 * app:carUxRestrictions="UX_RESTRICTIONS_NO_SETUP|UX_RESTRICTIONS_NO_KEYBOARD" 37 * If not set, it'll use UX_RESTRICTIONS_FULLY_RESTRICTED as fallback. 38 * If no restriction is enforced, this Button will work as a normal Button; otherwise, its 39 * OnClickListener will be disabled if any, and a blocking message will be displayed. 40 * 41 * This class extends from TextView instead of Button because only TextView supports gradient 42 * truncate for now. 43 */ 44 public class UxrButton extends TextView { 45 private static final int[] STATE_UX_RESTRICTED = {R.attr.state_ux_restricted}; 46 47 private CarUxRestrictionsUtil mCarUxRestrictionsUtil; 48 private CarUxRestrictions mActiveCarUxRestrictions; 49 private View.OnClickListener mOnClickListenerDelegate; 50 51 @CarUxRestrictions.CarUxRestrictionsInfo 52 private int mRestrictions; 53 54 private final Handler mHandler = new Handler(); 55 56 private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener mListener = 57 this::updateActiveCarUxRestrictions; 58 59 private final View.OnClickListener mOnClickListenerWrapper = (View v) -> { 60 if (mOnClickListenerDelegate == null) { 61 return; 62 } 63 if (isRestricted()) { 64 showBlockingMessage(); 65 } else { 66 mOnClickListenerDelegate.onClick(v); 67 } 68 }; 69 UxrButton(Context context)70 public UxrButton(Context context) { 71 super(context); 72 init(context, null); 73 } 74 UxrButton(Context context, AttributeSet attrs)75 public UxrButton(Context context, AttributeSet attrs) { 76 super(context, attrs); 77 init(context, attrs); 78 } 79 UxrButton(Context context, AttributeSet attrs, int defStyleAttr)80 public UxrButton(Context context, AttributeSet attrs, int defStyleAttr) { 81 super(context, attrs, defStyleAttr); 82 init(context, attrs); 83 } 84 UxrButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)85 public UxrButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 86 super(context, attrs, defStyleAttr, defStyleRes); 87 init(context, attrs); 88 } 89 init(Context context, AttributeSet attrs)90 private void init(Context context, AttributeSet attrs) { 91 mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(context); 92 super.setOnClickListener(mOnClickListenerWrapper); 93 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UxrButton); 94 try { 95 mRestrictions = a.getInteger(R.styleable.UxrButton_carUxRestrictions, 96 CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 97 } finally { 98 a.recycle(); 99 } 100 } 101 102 @Override onCreateDrawableState(int extraSpace)103 public int[] onCreateDrawableState(int extraSpace) { 104 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 105 if (isRestricted()) { 106 mergeDrawableStates(drawableState, STATE_UX_RESTRICTED); 107 } 108 return drawableState; 109 } 110 111 @Override setOnClickListener(@ullable View.OnClickListener listener)112 public void setOnClickListener(@Nullable View.OnClickListener listener) { 113 mOnClickListenerDelegate = listener; 114 } 115 116 @Override onAttachedToWindow()117 protected void onAttachedToWindow() { 118 super.onAttachedToWindow(); 119 mCarUxRestrictionsUtil.register(mListener); 120 } 121 122 @Override onDetachedFromWindow()123 protected void onDetachedFromWindow() { 124 super.onDetachedFromWindow(); 125 mCarUxRestrictionsUtil.unregister(mListener); 126 } 127 128 /** 129 * Set the UX restriction mode for this button 130 */ setUxRestrictions(int uxRestrictions)131 public void setUxRestrictions(int uxRestrictions) { 132 mRestrictions = uxRestrictions; 133 mHandler.post(() -> refreshDrawableState()); 134 } 135 isRestricted()136 private boolean isRestricted() { 137 return CarUxRestrictionsUtil.isRestricted(mRestrictions, mActiveCarUxRestrictions); 138 } 139 updateActiveCarUxRestrictions(CarUxRestrictions carUxRestrictions)140 private void updateActiveCarUxRestrictions(CarUxRestrictions carUxRestrictions) { 141 mActiveCarUxRestrictions = carUxRestrictions; 142 mHandler.post(() -> refreshDrawableState()); 143 } 144 145 /** 146 * Shows a message to inform the user that the current feature is not available when driving. 147 */ showBlockingMessage()148 protected void showBlockingMessage() { 149 Toast.makeText(getContext(), R.string.restricted_while_driving, 150 Toast.LENGTH_SHORT).show(); 151 } 152 } 153