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

Commit 0be85b08 authored by Vadim Caen's avatar Vadim Caen Committed by Android (Google) Code Review
Browse files

Merge "Add Settings for back animation developer option DO NOT MERGE" into tm-dev

parents e83d2a5e d268b11e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -649,6 +649,11 @@
            android:title="@string/enable_non_resizable_multi_window"
            android:summary="@string/enable_non_resizable_multi_window_summary" />

        <SwitchPreference
            android:key="back_navigation_animation"
            android:title="@string/back_navigation_animation"
            android:summary="@string/back_navigation_animation_summary" />

        <Preference
            android:key="reset_shortcut_manager_throttling"
            android:title="@string/reset_shortcut_manager_throttling" />
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.development;

import android.content.Context;
import android.provider.Settings;

import androidx.preference.Preference;
import androidx.preference.SwitchPreference;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import java.util.Objects;

/**
 * PreferenceController for enabling/disabling animation related to back button and back gestures.
 */
public class BackAnimationPreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {

    private static final String BACK_NAVIGATION_ANIMATION_KEY =
            "back_navigation_animation";

    private static final int SETTING_VALUE_OFF = 0;
    private static final int SETTING_VALUE_ON = 1;
    private final DevelopmentSettingsDashboardFragment mFragment;

    @VisibleForTesting
    BackAnimationPreferenceController(Context context) {
        super(context);
        mFragment = null;
    }


    public BackAnimationPreferenceController(Context context,
            DevelopmentSettingsDashboardFragment fragment) {
        super(context);
        Objects.requireNonNull(fragment);
        mFragment = fragment;
    }

    @Override
    public String getPreferenceKey() {
        return BACK_NAVIGATION_ANIMATION_KEY;
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean isEnabled = (Boolean) newValue;
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION,
                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
        if (mFragment != null && isEnabled) {
            BackAnimationPreferenceDialog.show(mFragment);
        }
        return true;
    }

    @Override
    public void updateState(Preference preference) {
        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
        ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        super.onDeveloperOptionsSwitchDisabled();
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
        ((SwitchPreference) mPreference).setChecked(false);
    }
}
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.development;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;

import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;

/**
 * Information dialog shown when enabling back animations
 */
public class BackAnimationPreferenceDialog extends InstrumentedDialogFragment
        implements DialogInterface.OnClickListener {

    public static final String TAG = "BackAnimationDlg";

    private BackAnimationPreferenceDialog() {
    }

    /**
     * Show this dialog.
     */
    public static void show(@NonNull Fragment host) {
        FragmentActivity activity = host.getActivity();
        if (activity == null) {
            return;
        }
        FragmentManager manager = activity.getSupportFragmentManager();
        if (manager.findFragmentByTag(TAG) == null) {
            BackAnimationPreferenceDialog dialog = new BackAnimationPreferenceDialog();
            dialog.setTargetFragment(host, 0 /* requestCode */);
            dialog.show(manager, TAG);
        }
    }

    @Override
    public int getMetricsCategory() {
        return SettingsEnums.DIALOG_BACK_ANIMATIONS;
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.back_navigation_animation)
                .setMessage(R.string.back_navigation_animation_dialog)
                .setPositiveButton(android.R.string.ok, this /* onClickListener */)
                .create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // Do nothing
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -607,6 +607,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        controllers.add(new OverlaySettingsPreferenceController(context));
        controllers.add(new StylusHandwritingPreferenceController(context));
        controllers.add(new IngressRateLimitPreferenceController((context)));
        controllers.add(new BackAnimationPreferenceController(context, fragment));

        return controllers;
    }
+149 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.development;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.app.Instrumentation;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;

import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@RunWith(AndroidJUnit4.class)
public class BackAnimationPreferenceControllerTest {

    private static final int SETTING_VALUE_OFF = 0;
    private static final int SETTING_VALUE_ON = 1;

    private SwitchPreference mPreference;

    private Context mContext;
    private BackAnimationPreferenceController mController;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        mContext = instrumentation.getTargetContext();
        mController = new BackAnimationPreferenceController(mContext);
        mPreference = new SwitchPreference(mContext);
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }

        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, -1);

        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
        mPreference.setKey(mController.getPreferenceKey());
        screen.addPreference(mPreference);
        mController.displayPreference(screen);
    }

    @Test
    public void onPreferenceChange_switchEnabled_shouldEnableBackAnimations() {
        mController.onPreferenceChange(mPreference, true /* new value */);

        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
        assertThat(mode).isEqualTo(SETTING_VALUE_ON);
    }

    @Test
    public void onPreferenceChange_switchDisabled_shouldDisableBackAnimations() {
        mController.onPreferenceChange(mPreference, false /* new value */);

        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
    }

    @Test
    public void updateState_settingEnabled_preferenceShouldBeChecked() {
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_ON);
        mController.updateState(mPreference);
        assertTrue(mPreference.isChecked());
    }

    @Test
    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);

        mController.updateState(mPreference);
        assertFalse(mPreference.isChecked());
    }

    @Test
    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference()
            throws InterruptedException {
        ContentResolver contentResolver = mContext.getContentResolver();
        int mode = doAndWaitForSettingChange(() -> mController.onDeveloperOptionsSwitchDisabled(),
                contentResolver);
        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
        assertFalse(mPreference.isEnabled());
        assertFalse(mPreference.isChecked());
    }

    private int doAndWaitForSettingChange(Runnable runnable, ContentResolver contentResolver) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ContentObserver settingsObserver =
                new ContentObserver(new Handler(Looper.myLooper())) {
                    @Override
                    public void onChange(boolean selfChange, Uri uri) {
                        countDownLatch.countDown();
                    }
                };
        contentResolver.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.ENABLE_BACK_ANIMATION),
                false, settingsObserver, UserHandle.USER_SYSTEM
        );
        runnable.run();
        try {
            countDownLatch.await(500, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Assert.fail(e.getMessage());
        }
        return Settings.Global.getInt(contentResolver,
                Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
    }
}