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);