Add UXRestrictions to ActionButtonsPreferences
Add functionality for ActionButtonPreferences to handle UxRestriction
changes by enabling, disabling and showing a toast when relevant.
Bug: 177076110
Test: manual, atest CarSettingsUnitTests
Change-Id: Iac3729f5ab4103e782d6a96e6e981aa370896c52
Merged-In: Iac3729f5ab4103e782d6a96e6e981aa370896c52
diff --git a/res/layout/action_buttons_preference.xml b/res/layout/action_buttons_preference.xml
index 69a6420..c0326cd 100644
--- a/res/layout/action_buttons_preference.xml
+++ b/res/layout/action_buttons_preference.xml
@@ -36,7 +36,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
- <LinearLayout
+ <com.android.car.ui.uxr.DrawableStateLinearLayout
android:id="@+id/button1"
style="?android:attr/buttonBarButtonStyle"
android:background="?android:attr/selectableItemBackground"
@@ -45,22 +45,22 @@
android:layout_weight="1"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <ImageView
+ <com.android.car.ui.uxr.DrawableStateImageView
android:id="@+id/button1Icon"
style="@style/PreferenceButtonIconAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/action_buttons_icon_margin_end"
android:selectable="false"/>
- <TextView
+ <com.android.car.ui.uxr.DrawableStateTextView
android:id="@+id/button1Text"
style="@style/PreferenceButtonTextAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:selectable="false"/>
- </LinearLayout>
+ </com.android.car.ui.uxr.DrawableStateLinearLayout>
- <LinearLayout
+ <com.android.car.ui.uxr.DrawableStateLinearLayout
android:id="@+id/button2"
style="?android:attr/buttonBarButtonStyle"
android:background="?android:attr/selectableItemBackground"
@@ -69,22 +69,22 @@
android:layout_weight="1"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <ImageView
+ <com.android.car.ui.uxr.DrawableStateImageView
android:id="@+id/button2Icon"
style="@style/PreferenceButtonIconAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/action_buttons_icon_margin_end"
android:selectable="false"/>
- <TextView
+ <com.android.car.ui.uxr.DrawableStateTextView
android:id="@+id/button2Text"
style="@style/PreferenceButtonTextAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:selectable="false"/>
- </LinearLayout>
+ </com.android.car.ui.uxr.DrawableStateLinearLayout>
- <LinearLayout
+ <com.android.car.ui.uxr.DrawableStateLinearLayout
android:id="@+id/button3"
style="?android:attr/buttonBarButtonStyle"
android:background="?android:attr/selectableItemBackground"
@@ -93,22 +93,22 @@
android:layout_weight="1"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <ImageView
+ <com.android.car.ui.uxr.DrawableStateImageView
android:id="@+id/button3Icon"
style="@style/PreferenceButtonIconAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/action_buttons_icon_margin_end"
android:selectable="false"/>
- <TextView
+ <com.android.car.ui.uxr.DrawableStateTextView
android:id="@+id/button3Text"
style="@style/PreferenceButtonTextAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:selectable="false"/>
- </LinearLayout>
+ </com.android.car.ui.uxr.DrawableStateLinearLayout>
- <LinearLayout
+ <com.android.car.ui.uxr.DrawableStateLinearLayout
android:id="@+id/button4"
style="?android:attr/buttonBarButtonStyle"
android:background="?android:attr/selectableItemBackground"
@@ -117,20 +117,20 @@
android:layout_weight="1"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <ImageView
+ <com.android.car.ui.uxr.DrawableStateImageView
android:id="@+id/button4Icon"
style="@style/PreferenceButtonIconAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/action_buttons_icon_margin_end"
android:selectable="false"/>
- <TextView
+ <com.android.car.ui.uxr.DrawableStateTextView
android:id="@+id/button4Text"
style="@style/PreferenceButtonTextAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:selectable="false"/>
- </LinearLayout>
+ </com.android.car.ui.uxr.DrawableStateLinearLayout>
</LinearLayout>
diff --git a/src/com/android/car/settings/common/ActionButtonInfo.java b/src/com/android/car/settings/common/ActionButtonInfo.java
index 7c3580c..266e557 100644
--- a/src/com/android/car/settings/common/ActionButtonInfo.java
+++ b/src/com/android/car/settings/common/ActionButtonInfo.java
@@ -23,11 +23,14 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
+import com.android.car.settings.R;
+
import java.lang.ref.WeakReference;
/**
@@ -42,13 +45,17 @@
private CharSequence mText;
private Drawable mIcon;
private View.OnClickListener mListener;
+ private boolean mIsPreferenceRestricted = false;
private boolean mIsEnabled = true;
private boolean mIsVisible = true;
private WeakReference<ButtonInfoChangeListener> mButtonInfoChangeListener;
+ private String mMessageToShowWhenUxRestrictedPreferenceClicked;
ActionButtonInfo(Context context, ButtonInfoChangeListener changeListener) {
mContext = context;
mButtonInfoChangeListener = new WeakReference<>(changeListener);
+ mMessageToShowWhenUxRestrictedPreferenceClicked = context.getString(
+ R.string.car_ui_restricted_while_driving);
}
/**
@@ -116,16 +123,24 @@
return this;
}
- void setButtonView(View view) {
+ ActionButtonInfo setButtonView(View view) {
mButtonView = view;
+ return this;
}
- void setButtonTextView(TextView textView) {
+ ActionButtonInfo setButtonTextView(TextView textView) {
mButtonTextView = textView;
+ return this;
}
- void setButtonIconView(ImageView iconView) {
+ ActionButtonInfo setButtonIconView(ImageView iconView) {
mButtonIconView = iconView;
+ return this;
+ }
+
+ ActionButtonInfo setPreferenceRestricted(boolean isRestricted) {
+ mIsPreferenceRestricted = isRestricted;
+ return this;
}
/**
@@ -171,11 +186,10 @@
void setUpButton() {
mButtonTextView.setText(mText);
mButtonIconView.setImageDrawable(mIcon);
- mButtonView.setOnClickListener(mListener);
+ mButtonView.setOnClickListener(this::performClick);
- mButtonView.setEnabled(mIsEnabled);
- mButtonTextView.setEnabled(mIsEnabled);
- mButtonIconView.setEnabled(mIsEnabled);
+ mButtonView.setEnabled(isEnabled() || mIsPreferenceRestricted);
+
mButtonIconView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
if (shouldBeVisible()) {
@@ -185,6 +199,24 @@
}
}
+ @VisibleForTesting
+ void performClick(View v) {
+ if (!isEnabled()) {
+ return;
+ }
+ if (mListener == null) {
+ return;
+ }
+ if (mIsPreferenceRestricted) {
+ if (!TextUtils.isEmpty(mMessageToShowWhenUxRestrictedPreferenceClicked)) {
+ Toast.makeText(mContext, mMessageToShowWhenUxRestrictedPreferenceClicked,
+ Toast.LENGTH_LONG).show();
+ }
+ return;
+ }
+ mListener.onClick(v);
+ }
+
/**
* By default, four buttons are visible.
* However, there are two cases which button should be invisible.
diff --git a/src/com/android/car/settings/common/ActionButtonsPreference.java b/src/com/android/car/settings/common/ActionButtonsPreference.java
index 9434a38..2ebe68f 100644
--- a/src/com/android/car/settings/common/ActionButtonsPreference.java
+++ b/src/com/android/car/settings/common/ActionButtonsPreference.java
@@ -23,10 +23,10 @@
import android.widget.ImageView;
import android.widget.TextView;
-import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.car.settings.R;
+import com.android.car.ui.preference.CarUiPreference;
/**
* Preference that provides a four button layout.
@@ -40,7 +40,7 @@
* Preference_allowDividerAbove and Preference_allowDividerBelow attributes. The dividers are
* enabled by default.
*/
-public class ActionButtonsPreference extends Preference implements
+public class ActionButtonsPreference extends CarUiPreference implements
ActionButtonInfo.ButtonInfoChangeListener {
/**
@@ -106,18 +106,29 @@
holder.findViewById(R.id.bottomDivider).setVisibility(
mAllowDividerBelow ? View.VISIBLE : View.GONE);
- mButton1Info.setButtonView(holder.findViewById(R.id.button1));
- mButton1Info.setButtonTextView((TextView) holder.findViewById(R.id.button1Text));
- mButton1Info.setButtonIconView((ImageView) holder.findViewById(R.id.button1Icon));
- mButton2Info.setButtonView(holder.findViewById(R.id.button2));
- mButton2Info.setButtonTextView((TextView) holder.findViewById(R.id.button2Text));
- mButton2Info.setButtonIconView((ImageView) holder.findViewById(R.id.button2Icon));
- mButton3Info.setButtonView(holder.findViewById(R.id.button3));
- mButton3Info.setButtonTextView((TextView) holder.findViewById(R.id.button3Text));
- mButton3Info.setButtonIconView((ImageView) holder.findViewById(R.id.button3Icon));
- mButton4Info.setButtonView(holder.findViewById(R.id.button4));
- mButton4Info.setButtonTextView((TextView) holder.findViewById(R.id.button4Text));
- mButton4Info.setButtonIconView((ImageView) holder.findViewById(R.id.button4Icon));
+ mButton1Info
+ .setButtonView(holder.findViewById(R.id.button1))
+ .setButtonTextView((TextView) holder.findViewById(R.id.button1Text))
+ .setButtonIconView((ImageView) holder.findViewById(R.id.button1Icon))
+ .setPreferenceRestricted(isUxRestricted());
+
+ mButton2Info
+ .setButtonView(holder.findViewById(R.id.button2))
+ .setButtonTextView((TextView) holder.findViewById(R.id.button2Text))
+ .setButtonIconView((ImageView) holder.findViewById(R.id.button2Icon))
+ .setPreferenceRestricted(isUxRestricted());
+
+ mButton3Info
+ .setButtonView(holder.findViewById(R.id.button3))
+ .setButtonTextView((TextView) holder.findViewById(R.id.button3Text))
+ .setButtonIconView((ImageView) holder.findViewById(R.id.button3Icon))
+ .setPreferenceRestricted(isUxRestricted());
+
+ mButton4Info
+ .setButtonView(holder.findViewById(R.id.button4))
+ .setButtonTextView((TextView) holder.findViewById(R.id.button4Text))
+ .setButtonIconView((ImageView) holder.findViewById(R.id.button4Icon))
+ .setPreferenceRestricted(isUxRestricted());
mButton1Info.setUpButton();
mButton2Info.setUpButton();
diff --git a/tests/unit/src/com/android/car/settings/common/ActionButtonsPreferenceTest.java b/tests/unit/src/com/android/car/settings/common/ActionButtonsPreferenceTest.java
index fc28143..b75746c 100644
--- a/tests/unit/src/com/android/car/settings/common/ActionButtonsPreferenceTest.java
+++ b/tests/unit/src/com/android/car/settings/common/ActionButtonsPreferenceTest.java
@@ -20,21 +20,33 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.preference.PreferenceViewHolder;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.car.settings.R;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
@RunWith(AndroidJUnit4.class)
public class ActionButtonsPreferenceTest {
@@ -43,12 +55,22 @@
private View mRootView;
private ActionButtonsPreference mPref;
private PreferenceViewHolder mHolder;
+ private MockitoSession mSession;
@Before
public void setUp() {
mRootView = View.inflate(mContext, R.layout.action_buttons_preference, /* parent= */ null);
mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPref = new ActionButtonsPreference(mContext);
+
+ mSession = ExtendedMockito.mockitoSession().mockStatic(Toast.class).startMocking();
+ }
+
+ @After
+ public void tearDown() {
+ if (mSession != null) {
+ mSession.finishMocking();
+ }
}
@Test
@@ -222,6 +244,63 @@
}
@Test
+ public void onButtonClicked_shouldOnlyTriggerListenerIfEnabled() {
+ mPref.getButton(ActionButtons.BUTTON1).setEnabled(true);
+ mPref.getButton(ActionButtons.BUTTON2).setEnabled(false);
+
+ View.OnClickListener enabledListener = mock(View.OnClickListener.class);
+ View.OnClickListener disabledListener = mock(View.OnClickListener.class);
+ mPref.getButton(ActionButtons.BUTTON1).setOnClickListener(enabledListener);
+ mPref.getButton(ActionButtons.BUTTON2).setOnClickListener(disabledListener);
+
+ mPref.onBindViewHolder(mHolder);
+
+ mPref.getButton(ActionButtons.BUTTON1).performClick(null);
+ verify(enabledListener).onClick(any());
+
+ mPref.getButton(ActionButtons.BUTTON2).performClick(null);
+ verify(disabledListener, never()).onClick(any());
+ }
+
+ @Test
+ @UiThreadTest
+ public void onButtonClicked_makesToastIfPreferenceRestricted() {
+ Toast mockToast = mock(Toast.class);
+ ExtendedMockito.when(Toast.makeText(any(), anyString(), anyInt())).thenReturn(mockToast);
+
+ mPref.setUxRestricted(true);
+ mPref.getButton(ActionButtons.BUTTON1).setEnabled(true);
+
+ View.OnClickListener listener = mock(View.OnClickListener.class);
+ mPref.getButton(ActionButtons.BUTTON1).setOnClickListener(listener);
+
+ mPref.onBindViewHolder(mHolder);
+
+ mPref.getButton(ActionButtons.BUTTON1).performClick(null);
+ verify(listener, never()).onClick(any());
+
+ verify(mockToast).show();
+ }
+
+ @Test
+ @UiThreadTest
+ public void onButtonClicked_disabled_uxRestricted_shouldDoNothing() {
+ mPref.setUxRestricted(true);
+ mPref.getButton(ActionButtons.BUTTON1).setEnabled(false);
+
+ View.OnClickListener listener = mock(View.OnClickListener.class);
+ mPref.getButton(ActionButtons.BUTTON1).setOnClickListener(listener);
+
+ mPref.onBindViewHolder(mHolder);
+
+ mPref.getButton(ActionButtons.BUTTON1).performClick(null);
+ verify(listener, never()).onClick(any());
+
+ ExtendedMockito.verify(
+ () -> Toast.makeText(any(), anyString(), anyInt()), never());
+ }
+
+ @Test
public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
mPref.getButton(ActionButtons.BUTTON1).setText(R.string.settings_label).setIcon(0);