/* * 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.launcher3 import android.content.Context import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.os.Looper import android.provider.Settings import android.provider.Settings.Global.ANIMATOR_DURATION_SCALE import android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE import android.provider.Settings.Global.WINDOW_ANIMATION_SCALE import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.LauncherAppComponent import com.android.launcher3.dagger.LauncherAppSingleton import com.android.launcher3.util.DaggerSingletonObject import com.android.launcher3.util.DaggerSingletonTracker import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject /** Tracker Class for when user turns on/off remove animation setting. */ @LauncherAppSingleton class RemoveAnimationSettingsTracker @Inject constructor(@ApplicationContext val context: Context, tracker: DaggerSingletonTracker) : ContentObserver(Handler(Looper.getMainLooper())) { private val contentResolver = context.contentResolver /** Caches the last seen value for registered keys. */ private val cache: MutableMap = ConcurrentHashMap() init { UI_HELPER_EXECUTOR.execute { contentResolver.registerContentObserver(WINDOW_ANIMATION_SCALE_URI, false, this) contentResolver.registerContentObserver(TRANSITION_ANIMATION_SCALE_URI, false, this) contentResolver.registerContentObserver(ANIMATOR_DURATION_SCALE_URI, false, this) } tracker.addCloseable { UI_HELPER_EXECUTOR.execute { contentResolver.unregisterContentObserver(this) } } } /** * Returns the value for this classes key from the cache. If not in cache, will call * [updateValue] to fetch. */ fun getValue(uri: Uri): Float { return getValue(uri, 1f) } /** * Returns the value for this classes key from the cache. If not in cache, will call * [getValueFromSettingsGlobal] to fetch. */ private fun getValue(uri: Uri, defaultValue: Float): Float { return cache.computeIfAbsent(uri) { getValueFromSettingsGlobal(uri, defaultValue) } } /** Returns if user has opted into having no animation on their device. */ fun isRemoveAnimationEnabled(): Boolean { return getValue(WINDOW_ANIMATION_SCALE_URI) == 0f && getValue(TRANSITION_ANIMATION_SCALE_URI) == 0f && getValue(ANIMATOR_DURATION_SCALE_URI) == 0f } override fun onChange(selfChange: Boolean, uri: Uri?) { if (uri == null) return updateValue(uri) } private fun getValueFromSettingsGlobal(uri: Uri, defaultValue: Float = 1f): Float { return Settings.Global.getFloat(contentResolver, uri.lastPathSegment, defaultValue) } private fun updateValue(uri: Uri, defaultValue: Float = 1f) { val newValue = getValueFromSettingsGlobal(uri, defaultValue) cache[uri] = newValue } companion object { @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getRemoveAnimationSettingsTracker) @JvmField val WINDOW_ANIMATION_SCALE_URI: Uri = Settings.Global.getUriFor(WINDOW_ANIMATION_SCALE) @JvmField val TRANSITION_ANIMATION_SCALE_URI: Uri = Settings.Global.getUriFor(TRANSITION_ANIMATION_SCALE) @JvmField val ANIMATOR_DURATION_SCALE_URI: Uri = Settings.Global.getUriFor(ANIMATOR_DURATION_SCALE) } }