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

Commit e46cab2a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Migrate Remove Animations preference to Catalyst backend." into main

parents 74fea8cc 9ef696d0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@

    <SwitchPreferenceCompat
        android:icon="@drawable/ic_accessibility_animation"
        android:key="toggle_disable_animations"
        android:key="animator_duration_scale"
        android:persistent="false"
        android:summary="@string/accessibility_disable_animations_summary"
        android:title="@string/accessibility_disable_animations"
+3 −1
Original line number Diff line number Diff line
@@ -37,7 +37,9 @@ class ColorAndMotionScreen : PreferenceScreenCreator {

    override fun fragmentClass() = ColorAndMotionFragment::class.java

    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
        +RemoveAnimationsPreference();
    }

    companion object {
        const val KEY = "accessibility_color_and_motion"
+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.Arrays;
import java.util.List;

// LINT.IfChange
/** A toggle preference controller for disable animations. */
public class DisableAnimationsPreferenceController extends TogglePreferenceController implements
        LifecycleObserver, OnStart, OnStop {
@@ -123,3 +124,4 @@ public class DisableAnimationsPreferenceController extends TogglePreferenceContr
        mContentResolver.unregisterContentObserver(mSettingsContentObserver);
    }
}
// LINT.ThenChange(src/com/android/settings/accessibility/RemoveAnimationsPreference.kt)
 No newline at end of file
+122 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.annotation.DrawableRes
import android.content.Context
import android.provider.Settings
import com.android.settings.R
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.datastore.SettingsGlobalStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.SwitchPreference
import com.android.settingslib.preference.SwitchPreferenceBinding

class RemoveAnimationsPreference :
    SwitchPreference(
        KEY,
        R.string.accessibility_disable_animations,
        R.string.accessibility_disable_animations_summary
    ),
    SwitchPreferenceBinding,
    PreferenceLifecycleProvider {

    private var mSettingsKeyedObserver: KeyedObserver<String?>? = null

    override val icon: Int
        @DrawableRes get() = R.drawable.ic_accessibility_animation

    override fun onStart(context: PreferenceLifecycleContext) {
        mSettingsKeyedObserver = object : KeyedObserver<String?> {
            override fun onKeyChanged(key: String?, reason: Int) {
                context.notifyPreferenceChange(KEY)
            }
        }
        mSettingsKeyedObserver?.let {
            for (key in TOGGLE_ANIMATION_KEYS) {
                SettingsGlobalStore.get(context).addObserver(key, it, HandlerExecutor.main)
            }
        }
    }

    override fun onStop(context: PreferenceLifecycleContext) {
        mSettingsKeyedObserver?.let {
            SettingsGlobalStore.get(context).removeObserver(it)
            mSettingsKeyedObserver = null
        }
    }

    override fun storage(context: Context): KeyValueStore = RemoveAnimationsStorage(context)

    private class RemoveAnimationsStorage(private val context: Context) :
        NoOpKeyedObservable<String>(), KeyValueStore {
        override fun contains(key: String) = key == KEY

        override fun <T : Any> getValue(key: String, valueType: Class<T>) =
            when {
                key == KEY && valueType == Boolean::class.javaObjectType ->
                    !isAnimationEnabled(context) as T

                else -> null
            }

        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
            if (key == KEY && value is Boolean) {
                setAnimationEnabled(context, !value)
            }
        }
    }

    companion object {
        // This KEY must match the key used in accessibility_color_and_motion.xml for this
        // preference, at least until the entire screen is migrated to Catalyst and that XML
        // is deleted. Use any key from the set of 3 toggle animation keys.
        const val KEY = Settings.Global.ANIMATOR_DURATION_SCALE

        const val ANIMATION_ON_VALUE: Float = 1.0f
        const val ANIMATION_OFF_VALUE: Float = 0.0f

        val TOGGLE_ANIMATION_KEYS: List<String> = listOf(
            Settings.Global.WINDOW_ANIMATION_SCALE, Settings.Global.TRANSITION_ANIMATION_SCALE,
            Settings.Global.ANIMATOR_DURATION_SCALE
        )

        fun isAnimationEnabled(context: Context): Boolean {
            // This pref treats animation as enabled if *any* of the animation types are enabled.
            for (animationSetting in TOGGLE_ANIMATION_KEYS) {
                val animationValue: Float? =
                    SettingsGlobalStore.get(context).getFloat(animationSetting)
                // Animation is enabled by default, so treat null as enabled.
                if (animationValue == null || animationValue > ANIMATION_OFF_VALUE) {
                    return true
                }
            }
            return false
        }

        fun setAnimationEnabled(context: Context, enabled: Boolean) {
            val value = if (enabled) ANIMATION_ON_VALUE else ANIMATION_OFF_VALUE;
            for (animationSetting in TOGGLE_ANIMATION_KEYS) {
                SettingsGlobalStore.get(context).setFloat(animationSetting, value)
            }
        }
    }
}
 No newline at end of file
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 androidx.preference.SwitchPreferenceCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.accessibility.RemoveAnimationsPreference.Companion.ANIMATION_ON_VALUE
import com.android.settings.accessibility.RemoveAnimationsPreference.Companion.TOGGLE_ANIMATION_KEYS
import com.android.settingslib.datastore.SettingsGlobalStore
import com.android.settingslib.preference.createAndBindWidget
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class RemoveAnimationsPreferenceTest {

    private val appContext: Context = ApplicationProvider.getApplicationContext()

    private val removeAnimationsPreference =
        RemoveAnimationsPreference()

    private fun getSwitchPreferenceCompat(): SwitchPreferenceCompat =
        removeAnimationsPreference.createAndBindWidget(appContext)

    @Test
    fun animationOff_switchPreferenceIsChecked() {
        RemoveAnimationsPreference.setAnimationEnabled(appContext, false)

        assertThat(getSwitchPreferenceCompat().isChecked).isTrue()
    }

    @Test
    fun animationOn_switchPreferenceIsNotChecked() {
        RemoveAnimationsPreference.setAnimationEnabled(appContext, true)

        assertThat(getSwitchPreferenceCompat().isChecked).isFalse()
    }

    @Test
    fun oneAnimationValueOn_switchPreferenceIsNotChecked() {
        // Animation is disabled, except for one value.
        RemoveAnimationsPreference.setAnimationEnabled(appContext, false)
        SettingsGlobalStore.get(appContext).setFloat(TOGGLE_ANIMATION_KEYS[0], ANIMATION_ON_VALUE)

        assertThat(getSwitchPreferenceCompat().isChecked).isFalse()
    }

    @Test
    fun storageSetTrue_turnsOffAnimation() {
        RemoveAnimationsPreference.setAnimationEnabled(appContext, true)

        storageSetValue(true)

        assertThat(RemoveAnimationsPreference.isAnimationEnabled(appContext)).isFalse()
    }

    @Test
    fun storageSetFalse_turnsOnAnimation() {
        RemoveAnimationsPreference.setAnimationEnabled(appContext, false)

        storageSetValue(false)

        assertThat(RemoveAnimationsPreference.isAnimationEnabled(appContext)).isTrue()
    }

    private fun storageSetValue(enabled: Boolean) = removeAnimationsPreference.storage(appContext)
        .setValue(RemoveAnimationsPreference.KEY, Boolean::class.javaObjectType, enabled)
}
 No newline at end of file