1 /*
2  * Copyright (C) 2020 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.settings.common;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.drawable.Drawable;
22 import android.text.TextUtils;
23 import android.view.View;
24 import android.widget.ImageView;
25 import android.widget.TextView;
26 import android.widget.Toast;
27 
28 import androidx.annotation.DrawableRes;
29 import androidx.annotation.StringRes;
30 import androidx.annotation.VisibleForTesting;
31 
32 import com.android.car.settings.R;
33 import com.android.car.ui.utils.CarUiUtils;
34 
35 import java.lang.ref.WeakReference;
36 
37 /**
38  * Class representing a button item for an {@link ActionButtonsPreference}
39  */
40 public class ActionButtonInfo {
41     private static final Logger LOG = new Logger(ActionButtonInfo.class);
42     private final Context mContext;
43     private View mButtonView;
44     private ImageView mButtonIconView;
45     private TextView mButtonTextView;
46     private CharSequence mText;
47     private Drawable mIcon;
48     private View.OnClickListener mListener;
49     private boolean mIsPreferenceRestricted = false;
50     private boolean mIsEnabled = true;
51     private boolean mIsVisible = true;
52     private WeakReference<ButtonInfoChangeListener> mButtonInfoChangeListener;
53     private String mMessageToShowWhenUxRestrictedPreferenceClicked;
54 
ActionButtonInfo(Context context, ButtonInfoChangeListener changeListener)55     ActionButtonInfo(Context context, ButtonInfoChangeListener changeListener) {
56         mContext = context;
57         mButtonInfoChangeListener = new WeakReference<>(changeListener);
58         mMessageToShowWhenUxRestrictedPreferenceClicked = context.getString(
59                 R.string.car_ui_restricted_while_driving);
60     }
61 
62     /**
63      * Set the visibility state.
64      */
setVisible(boolean isVisible)65     public ActionButtonInfo setVisible(boolean isVisible) {
66         if (isVisible != mIsVisible) {
67             mIsVisible = isVisible;
68             update();
69         }
70         return this;
71     }
72 
73     /**
74      * Sets the text to be displayed.
75      */
setText(@tringRes int textResId)76     public ActionButtonInfo setText(@StringRes int textResId) {
77         final String newText = mContext.getString(textResId);
78         if (!TextUtils.equals(newText, mText)) {
79             mText = newText;
80             update();
81         }
82         return this;
83     }
84 
85     /**
86      * Sets the drawable to be displayed above of text.
87      */
setIcon(@rawableRes int iconResId)88     public ActionButtonInfo setIcon(@DrawableRes int iconResId) {
89         if (iconResId == 0) {
90             return this;
91         }
92 
93         final Drawable icon;
94         try {
95             icon = mContext.getDrawable(iconResId);
96             mIcon = icon;
97             update();
98         } catch (Resources.NotFoundException exception) {
99             LOG.e("Resource does not exist: " + iconResId);
100         }
101         return this;
102     }
103 
104     /**
105      * Set the enabled state.
106      */
setEnabled(boolean isEnabled)107     public ActionButtonInfo setEnabled(boolean isEnabled) {
108         if (isEnabled != mIsEnabled) {
109             mIsEnabled = isEnabled;
110             update();
111         }
112         return this;
113     }
114 
115     /**
116      * Register a callback to be invoked when clicked.
117      */
setOnClickListener( View.OnClickListener listener)118     public ActionButtonInfo setOnClickListener(
119             View.OnClickListener listener) {
120         if (listener != mListener) {
121             mListener = listener;
122             update();
123         }
124         return this;
125     }
126 
setButtonView(View view)127     ActionButtonInfo setButtonView(View view) {
128         mButtonView = view;
129         return this;
130     }
131 
setButtonTextView(TextView textView)132     ActionButtonInfo setButtonTextView(TextView textView) {
133         mButtonTextView = textView;
134         return this;
135     }
136 
setButtonIconView(ImageView iconView)137     ActionButtonInfo setButtonIconView(ImageView iconView) {
138         mButtonIconView = iconView;
139         return this;
140     }
141 
setPreferenceRestricted(boolean isRestricted)142     ActionButtonInfo setPreferenceRestricted(boolean isRestricted) {
143         mIsPreferenceRestricted = isRestricted;
144         return this;
145     }
146 
147     /**
148      * Get the current button text.
149      */
150     @VisibleForTesting
getText()151     public CharSequence getText() {
152         return mText;
153     }
154 
155     /**
156      * Get the current button icon.
157      */
158     @VisibleForTesting
getIcon()159     public Drawable getIcon() {
160         return mIcon;
161     }
162 
163     /**
164      * Get the current button click listener.
165      */
166     @VisibleForTesting
getOnClickListener()167     public View.OnClickListener getOnClickListener() {
168         return mListener;
169     }
170 
171     /**
172      * Get the current button enabled state.
173      */
174     @VisibleForTesting
isEnabled()175     public boolean isEnabled() {
176         return mIsEnabled;
177     }
178 
179     /**
180      * Get the current button visibility.
181      */
182     @VisibleForTesting
isVisible()183     public boolean isVisible() {
184         return shouldBeVisible();
185     }
186 
setUpButton()187     void setUpButton() {
188         mButtonTextView.setText(mText);
189         mButtonIconView.setImageDrawable(mIcon);
190         mButtonView.setOnClickListener(this::performClick);
191 
192         boolean enabled = isEnabled() || mIsPreferenceRestricted;
193         mButtonView.setEnabled(enabled);
194         CarUiUtils.makeAllViewsEnabledAndUxRestricted(mButtonView, isEnabled(),
195                 mIsPreferenceRestricted);
196 
197         mButtonIconView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
198 
199         if (shouldBeVisible()) {
200             mButtonView.setVisibility(View.VISIBLE);
201         } else {
202             mButtonView.setVisibility(View.GONE);
203         }
204     }
205 
206     @VisibleForTesting
performClick(View v)207     void performClick(View v) {
208         if (!isEnabled()) {
209             return;
210         }
211         if (mListener == null) {
212             return;
213         }
214         if (mIsPreferenceRestricted) {
215             if (!TextUtils.isEmpty(mMessageToShowWhenUxRestrictedPreferenceClicked)) {
216                 Toast.makeText(mContext, mMessageToShowWhenUxRestrictedPreferenceClicked,
217                         Toast.LENGTH_LONG).show();
218             }
219             return;
220         }
221         mListener.onClick(v);
222     }
223 
224     /**
225      * By default, four buttons are visible.
226      * However, there are two cases which button should be invisible.
227      *
228      * 1. User set invisible for this button. ex: mIsVisible = false.
229      * 2. User didn't set any title or icon.
230      */
shouldBeVisible()231     private boolean shouldBeVisible() {
232         return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null);
233     }
234 
update()235     private void update() {
236         ButtonInfoChangeListener listener = mButtonInfoChangeListener.get();
237         if (listener != null) {
238             listener.onButtonInfoChange(this);
239         }
240     }
241 
242     /**
243      * Listener that is notified when a button has been updated.
244      */
245     interface ButtonInfoChangeListener {
onButtonInfoChange(ActionButtonInfo buttonInfo)246         void onButtonInfoChange(ActionButtonInfo buttonInfo);
247     }
248 }
249