Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f677476 authored by Menghan Li's avatar Menghan Li
Browse files

Add controller for magnification feedback preference

The controller's logic determines the visibility of this preference
based on the availability of surveys, as reported by
SurveyFeatureProvider.isSurveyAvailable.

Bug: 380346799
Test: atest MagnificationFeedbackPreferenceControllerTest
Flag: com.android.server.accessibility.enable_low_vision_hats
Change-Id: I7fe7aa4418a6be38e9e7af7efc76a9a25266198b
parent f4dfdda4
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -5084,6 +5084,12 @@
    <string name="accessibility_tap_assistance_title">Timing controls</string>
    <!-- Title for the accessibility system controls page. [CHAR LIMIT=50] -->
    <string name="accessibility_system_controls_title">System controls</string>
    <!-- Title for the accessibility feedback preference. [CHAR LIMIT=50] -->
    <string name="accessibility_feedback_title">Feedback</string>
    <!-- Summary for the accessibility feedback preference. [CHAR LIMIT=100] -->
    <string name="accessibility_feedback_summary">Help improve by taking a survey</string>
    <!-- Summary for the accessibility feedback preference is disabled. [CHAR LIMIT=100] -->
    <string name="accessibility_feedback_disabled_summary">No surveys available</string>
    <!-- Title for the accessibility preference category of services downloaded by the user. [CHAR LIMIT=50] -->
    <string name="user_installed_services_category_title">Downloaded apps</string>
    <!-- Title for the accessibility preference category of settings considered to be experimental, meaning they might be changed or removed in the future. [CHAR LIMIT=50] -->
+85 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+121 −0
Original line number Diff line number Diff line
/*
 * 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<Boolean> 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<Boolean> 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);
    }
}