Fix UXRE toast behavior

Move UX restriction message logic into PreferenceControllers so it can
be added and removed whenever a preference refreshes, ensuring the
toast is enable/disabled accurately.

Bug: 170122685
Bug: 168823984
Test: manual, atest CarSettingsUnitTests
Change-Id: I5b26413f1b2a528a4547561dacb82184997e129d
diff --git a/src/com/android/car/settings/common/PreferenceController.java b/src/com/android/car/settings/common/PreferenceController.java
index c2796f5..ce4d527 100644
--- a/src/com/android/car/settings/common/PreferenceController.java
+++ b/src/com/android/car/settings/common/PreferenceController.java
@@ -27,6 +27,7 @@
 import androidx.preference.Preference;
 
 import com.android.car.settings.R;
+import com.android.car.ui.preference.DisabledPreferenceCallback;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -140,6 +141,7 @@
     private final Context mContext;
     private final String mPreferenceKey;
     private final FragmentController mFragmentController;
+    private final String mRestrictedWhileDrivingMessage;
 
     private CarUxRestrictions mUxRestrictions;
     private V mPreference;
@@ -159,6 +161,8 @@
                 mContext.getResources().getStringArray(R.array.config_ignore_ux_restrictions)));
         mAlwaysIgnoreUxRestrictions =
                 mContext.getResources().getBoolean(R.bool.config_always_ignore_ux_restrictions);
+        mRestrictedWhileDrivingMessage =
+                mContext.getResources().getString(R.string.restricted_while_driving);
     }
 
     /**
@@ -446,10 +450,18 @@
      * additional driving restrictions.
      */
     protected void onApplyUxRestrictions(CarUxRestrictions uxRestrictions) {
+        String message = "";
         if (!isUxRestrictionsIgnored(mAlwaysIgnoreUxRestrictions,
                 mPreferencesIgnoringUxRestrictions)
                 && CarUxRestrictionsHelper.isNoSetup(uxRestrictions)) {
             mPreference.setEnabled(false);
+            if (getAvailabilityStatus() != AVAILABLE_FOR_VIEWING) {
+                message = mRestrictedWhileDrivingMessage;
+            }
+        }
+        if (mPreference instanceof DisabledPreferenceCallback) {
+            ((DisabledPreferenceCallback) mPreference)
+                    .setMessageToShowWhenDisabledPreferenceClicked(message);
         }
     }
 
diff --git a/src/com/android/car/settings/common/SettingsFragment.java b/src/com/android/car/settings/common/SettingsFragment.java
index f6f4fcf..949c042 100644
--- a/src/com/android/car/settings/common/SettingsFragment.java
+++ b/src/com/android/car/settings/common/SettingsFragment.java
@@ -41,7 +41,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.car.settings.R;
-import com.android.car.ui.preference.DisabledPreferenceCallback;
 import com.android.car.ui.preference.PreferenceFragment;
 import com.android.car.ui.toolbar.MenuItem;
 import com.android.car.ui.toolbar.Toolbar;
@@ -82,7 +81,6 @@
 
     private CarUxRestrictions mUxRestrictions;
     private int mCurrentRequestIndex = 0;
-    private String mRestrictedWhileDrivingMessage;
 
     /**
      * Returns the resource id for the preference XML of this fragment.
@@ -165,8 +163,6 @@
             mPreferenceControllersLookup.computeIfAbsent(controller.getClass(),
                     k -> new ArrayList<>(/* initialCapacity= */ 1)).add(controller);
         });
-
-        mRestrictedWhileDrivingMessage = context.getString(R.string.restricted_while_driving);
     }
 
     @Override
@@ -192,12 +188,6 @@
             Preference pref = screen.findPreference(controller.getPreferenceKey());
 
             controller.setPreference(pref);
-
-            if (pref instanceof DisabledPreferenceCallback && controller.getAvailabilityStatus()
-                    != PreferenceController.AVAILABLE_FOR_VIEWING) {
-                ((DisabledPreferenceCallback) pref).setMessageToShowWhenDisabledPreferenceClicked(
-                        mRestrictedWhileDrivingMessage);
-            }
         }
     }
 
diff --git a/tests/unit/src/com/android/car/settings/common/PreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/common/PreferenceControllerTest.java
new file mode 100644
index 0000000..725f3ac
--- /dev/null
+++ b/tests/unit/src/com/android/car/settings/common/PreferenceControllerTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.settings.common;
+
+import static org.mockito.Mockito.verify;
+
+import android.car.drivingstate.CarUxRestrictions;
+import android.content.Context;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.car.ui.preference.CarUiPreference;
+import com.android.car.ui.preference.DisabledPreferenceCallback;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferenceControllerTest {
+
+    private static final CarUxRestrictions NO_SETUP_UX_RESTRICTIONS =
+            new CarUxRestrictions.Builder(/* reqOpt= */ true,
+                    CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP, /* timestamp= */ 0).build();
+
+    private static final CarUxRestrictions BASELINE_UX_RESTRICTIONS =
+            new CarUxRestrictions.Builder(/* reqOpt= */ true,
+                    CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
+
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+
+    private Context mContext = ApplicationProvider.getApplicationContext();
+    private FakePreferenceController mPreferenceController;
+
+    @Mock
+    private FragmentController mFragmentController;
+    @Mock
+    private CarUiPreference mPreference;
+
+    @Before
+    @UiThreadTest
+    public void setUp() {
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+
+        MockitoAnnotations.initMocks(this);
+
+        mPreferenceController = new FakePreferenceController(mContext, /* preferenceKey= */ "key",
+                mFragmentController, BASELINE_UX_RESTRICTIONS);
+    }
+
+    @Test
+    public void onUxRestrictionsChanged_restricted_RestrictedMessageSet() {
+        mPreferenceController.setPreference(mPreference);
+        mPreferenceController.onCreate(mLifecycleOwner);
+
+        Mockito.reset(mPreference);
+        mPreferenceController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
+
+        verify((DisabledPreferenceCallback) mPreference)
+                .setMessageToShowWhenDisabledPreferenceClicked(
+                        getResourceString("restricted_while_driving"));
+    }
+
+    @Test
+    public void onUxRestrictionsChanged_restricted_viewOnly_restrictedMessageUnset() {
+        mPreferenceController.setPreference(mPreference);
+        mPreferenceController.setAvailabilityStatus(PreferenceController.AVAILABLE_FOR_VIEWING);
+        mPreferenceController.onCreate(mLifecycleOwner);
+
+        Mockito.reset(mPreference);
+        mPreferenceController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
+
+        verify((DisabledPreferenceCallback) mPreference)
+                .setMessageToShowWhenDisabledPreferenceClicked("");
+    }
+
+    @Test
+    public void onCreate_unrestricted_disabled_restrictedMessageUnset() {
+        mPreference.setEnabled(false);
+        mPreferenceController.setPreference(mPreference);
+        mPreferenceController.onCreate(mLifecycleOwner);
+
+        verify((DisabledPreferenceCallback) mPreference)
+                .setMessageToShowWhenDisabledPreferenceClicked("");
+    }
+
+    private String getResourceString(String name) {
+        return mContext.getResources().getString(mContext.getResources().getIdentifier(
+                name, /* type= */ "string", mContext.getPackageName()));
+    }
+
+    private static class FakePreferenceController extends
+            PreferenceController<Preference> {
+
+        private int mAvailabilityStatus;
+
+        FakePreferenceController(Context context, String preferenceKey,
+                FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
+            super(context, preferenceKey, fragmentController, uxRestrictions);
+            mAvailabilityStatus = AVAILABLE;
+        }
+
+        @Override
+        protected Class<Preference> getPreferenceType() {
+            return Preference.class;
+        }
+
+        @Override
+        protected int getAvailabilityStatus() {
+            return mAvailabilityStatus;
+        }
+
+        public void setAvailabilityStatus(int availabilityStatus) {
+            mAvailabilityStatus = availabilityStatus;
+        }
+    }
+}