diff --git a/res/values/strings.xml b/res/values/strings.xml index 236a66183ce..5163dc67e32 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5084,6 +5084,12 @@ Timing controls System controls + + Feedback + + Help improve by taking a survey + + No surveys available Downloaded apps diff --git a/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceController.java b/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceController.java new file mode 100644 index 00000000000..bfcd2930513 --- /dev/null +++ b/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceController.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2025 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.settings.accessibility; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.overlay.SurveyFeatureProvider; + +/** + * PreferenceController for magnification feedback preference. This controller manages the + * visibility and click behavior of the preference based on the availability of a user survey + * related to magnification. + */ +public class MagnificationFeedbackPreferenceController extends BasePreferenceController + implements DefaultLifecycleObserver { + private static final String TAG = "MagnificationFeedbackPreferenceController"; + public static final String PREF_KEY = "magnification_feedback"; + public static final String FEEDBACK_KEY = "A11yMagnificationUser"; + private final DashboardFragment mParent; + private final @Nullable SurveyFeatureProvider mSurveyFeatureProvider; + + public MagnificationFeedbackPreferenceController(@NonNull Context context, + @NonNull DashboardFragment parent, @NonNull String preferenceKey) { + super(context, preferenceKey); + mParent = parent; + mSurveyFeatureProvider = + FeatureFactory.getFeatureFactory().getSurveyFeatureProvider(context); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(@NonNull Preference preference) { + super.updateState(preference); + if (mSurveyFeatureProvider != null) { + mSurveyFeatureProvider.checkSurveyAvailable( + mParent.getViewLifecycleOwner(), + FEEDBACK_KEY, + enabled -> { + final String summary = mContext.getString(enabled + ? R.string.accessibility_feedback_summary + : R.string.accessibility_feedback_disabled_summary); + preference.setSummary(summary); + preference.setEnabled(enabled); + }); + } else { + Log.w(TAG, "SurveyFeatureProvider is not ready"); + } + } + + @Override + public boolean handlePreferenceTreeClick(@NonNull Preference preference) { + if (mSurveyFeatureProvider != null) { + mSurveyFeatureProvider.sendActivityIfAvailable(FEEDBACK_KEY); + } + return true; + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceControllerTest.java new file mode 100644 index 00000000000..389e12756bf --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationFeedbackPreferenceControllerTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2025 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.settings.accessibility; + +import static com.android.settings.accessibility.MagnificationFeedbackPreferenceController.FEEDBACK_KEY; +import static com.android.settings.accessibility.MagnificationFeedbackPreferenceController.PREF_KEY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.core.util.Consumer; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.SurveyFeatureProvider; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link MagnificationFeedbackPreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class MagnificationFeedbackPreferenceControllerTest { + + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private PreferenceScreen mScreen; + @Mock private PreferenceManager mPreferenceManager; + @Mock private DashboardFragment mFragment; + private SurveyFeatureProvider mSurveyFeatureProvider; + private MagnificationFeedbackPreferenceController mController; + private Preference mPreference; + + @Before + public void setUp() { + FakeFeatureFactory.setupForTest(); + mSurveyFeatureProvider = + FakeFeatureFactory.getFeatureFactory().getSurveyFeatureProvider(mContext); + mController = new MagnificationFeedbackPreferenceController(mContext, mFragment, PREF_KEY); + mPreference = new Preference(mContext); + when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); + when(mPreferenceManager.findPreference(PREF_KEY)).thenReturn(mPreference); + when(mFragment.getPreferenceScreen()).thenReturn(mScreen); + } + + @Test + public void getAvailabilityStatus_shouldAlwaysBeAvailable() { + assertThat(mController.getAvailabilityStatus()).isEqualTo( + MagnificationFeedbackPreferenceController.AVAILABLE); + } + + @Test + public void updateState_surveyAvailable_preferenceEnabledWithSummary() { + doAnswer(invocation -> { + Consumer consumer = invocation.getArgument(2); + consumer.accept(true); + return null; + }).when(mSurveyFeatureProvider).checkSurveyAvailable(any(), eq(FEEDBACK_KEY), any()); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.getSummary()).isEqualTo( + mContext.getString(R.string.accessibility_feedback_summary)); + } + + @Test + public void updateState_surveyUnavailable_preferenceDisabledWithSummary() { + doAnswer(invocation -> { + Consumer consumer = invocation.getArgument(2); + consumer.accept(false); + return null; + }).when(mSurveyFeatureProvider).checkSurveyAvailable(any(), eq(FEEDBACK_KEY), any()); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.getSummary()).isEqualTo( + mContext.getString(R.string.accessibility_feedback_disabled_summary)); + } + + @Test + public void handlePreferenceTreeClick_shouldStartSurvey() { + mController.handlePreferenceTreeClick(mPreference); + + verify(mSurveyFeatureProvider).sendActivityIfAvailable(FEEDBACK_KEY); + } +}