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

Commit b55ca3eb authored by Behnam Heydarshahi's avatar Behnam Heydarshahi Committed by Android (Google) Code Review
Browse files

Merge "Migrate Reduce Bright Colors Tile" into main

parents b83de0bc bb058689
Loading
Loading
Loading
Loading
+87 −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.systemui.qs.tiles.impl.reducebrightness.domain.interactor

import android.os.UserHandle
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toCollection
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {

    private val isAvailable = true
    private val kosmos = Kosmos()
    private val testScope = kosmos.testScope
    private val reduceBrightColorsController = kosmos.reduceBrightColorsController
    private val underTest: ReduceBrightColorsTileDataInteractor =
        ReduceBrightColorsTileDataInteractor(
            testScope.testScheduler,
            isAvailable,
            reduceBrightColorsController
        )

    @Test
    fun alwaysAvailable() =
        testScope.runTest {
            val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())

            assertThat(availability).hasSize(1)
            assertThat(availability.last()).isEqualTo(isAvailable)
        }

    @Test
    fun dataMatchesTheRepository() =
        testScope.runTest {
            val dataList: List<ReduceBrightColorsTileModel> by
                collectValues(
                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
                )
            runCurrent()

            reduceBrightColorsController.isReduceBrightColorsActivated = true
            runCurrent()

            reduceBrightColorsController.isReduceBrightColorsActivated = false
            runCurrent()

            assertThat(dataList).hasSize(3)
            assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false))
        }

    private companion object {
        val TEST_USER = UserHandle.of(1)!!
    }
}
+91 −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.systemui.qs.tiles.impl.reducebrightness.domain.interactor

import android.platform.test.annotations.EnabledOnRavenwood
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {

    private val kosmos = Kosmos()
    private val inputHandler = FakeQSTileIntentUserInputHandler()
    private val controller = kosmos.reduceBrightColorsController

    private val underTest =
        ReduceBrightColorsTileUserActionInteractor(
            inputHandler,
            controller,
        )

    @Test
    fun handleClickWhenEnabled() = runTest {
        val wasEnabled = true
        controller.isReduceBrightColorsActivated = wasEnabled

        underTest.handleInput(QSTileInputTestKtx.click(ReduceBrightColorsTileModel(wasEnabled)))

        assertThat(controller.isReduceBrightColorsActivated).isEqualTo(!wasEnabled)
    }

    @Test
    fun handleClickWhenDisabled() = runTest {
        val wasEnabled = false
        controller.isReduceBrightColorsActivated = wasEnabled

        underTest.handleInput(QSTileInputTestKtx.click(ReduceBrightColorsTileModel(wasEnabled)))

        assertThat(controller.isReduceBrightColorsActivated).isEqualTo(!wasEnabled)
    }

    @Test
    fun handleLongClickWhenDisabled() = runTest {
        val enabled = false

        underTest.handleInput(QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled)))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
            assertThat(it.intent.action).isEqualTo(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
        }
    }

    @Test
    fun handleLongClickWhenEnabled() = runTest {
        val enabled = true

        underTest.handleInput(QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled)))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
            assertThat(it.intent.action).isEqualTo(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
        }
    }
}
+111 −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.systemui.qs.tiles.impl.reducebrightness.ui

import android.graphics.drawable.TestStubDrawable
import android.service.quicksettings.Tile
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val config = kosmos.qsReduceBrightColorsTileConfig

    private lateinit var mapper: ReduceBrightColorsTileMapper

    @Before
    fun setup() {
        mapper =
            ReduceBrightColorsTileMapper(
                context.orCreateTestableResources
                    .apply {
                        addOverride(R.drawable.qs_extra_dim_icon_on, TestStubDrawable())
                        addOverride(R.drawable.qs_extra_dim_icon_off, TestStubDrawable())
                    }
                    .resources,
                context.theme
            )
    }

    @Test
    fun disabledModel() {
        val inputModel = ReduceBrightColorsTileModel(false)

        val outputState = mapper.map(config, inputModel)

        val expectedState =
            createReduceBrightColorsTileState(
                QSTileState.ActivationState.INACTIVE,
            )
        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
    }

    @Test
    fun enabledModel() {
        val inputModel = ReduceBrightColorsTileModel(true)

        val outputState = mapper.map(config, inputModel)

        val expectedState = createReduceBrightColorsTileState(QSTileState.ActivationState.ACTIVE)
        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
    }

    private fun createReduceBrightColorsTileState(
        activationState: QSTileState.ActivationState,
    ): QSTileState {
        val label =
            context.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
        return QSTileState(
            {
                Icon.Loaded(
                    context.getDrawable(
                        if (activationState == QSTileState.ActivationState.ACTIVE)
                            R.drawable.qs_extra_dim_icon_on
                        else R.drawable.qs_extra_dim_icon_off
                    )!!,
                    null
                )
            },
            label,
            activationState,
            context.resources
                .getStringArray(R.array.tile_states_reduce_brightness)[
                    if (activationState == QSTileState.ActivationState.ACTIVE) Tile.STATE_ACTIVE
                    else Tile.STATE_INACTIVE],
            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
            label,
            null,
            QSTileState.SideViewIcon.None,
            QSTileState.EnabledState.ENABLED,
            Switch::class.qualifiedName
        )
    }
}
+43 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.accessibility.qs

import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -40,9 +41,14 @@ import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMap
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileDataInteractor
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.ui.ReduceBrightColorsTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -105,6 +111,7 @@ interface QSAccessibilityModule {
        const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
        const val COLOR_INVERSION_TILE_SPEC = "inversion"
        const val FONT_SCALING_TILE_SPEC = "font_scaling"
        const val REDUCE_BRIGHTNESS_TILE_SPEC = "reduce_brightness"

        @Provides
        @IntoMap
@@ -198,5 +205,41 @@ interface QSAccessibilityModule {
                stateInteractor,
                mapper,
            )

        @Provides
        @IntoMap
        @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
        fun provideReduceBrightColorsTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
            QSTileConfig(
                tileSpec = TileSpec.create(REDUCE_BRIGHTNESS_TILE_SPEC),
                uiConfig =
                    QSTileUIConfig.Resource(
                        iconRes = R.drawable.qs_extra_dim_icon_on,
                        labelRes = com.android.internal.R.string.reduce_bright_colors_feature_name,
                    ),
                instanceId = uiEventLogger.getNewInstanceId(),
            )

        /**
         * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
         * behind a flag.
         */
        @Provides
        @IntoMap
        @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
        fun provideReduceBrightColorsTileViewModel(
            factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
            mapper: ReduceBrightColorsTileMapper,
            stateInteractor: ReduceBrightColorsTileDataInteractor,
            userActionInteractor: ReduceBrightColorsTileUserActionInteractor
        ): QSTileViewModel =
            if (Flags.qsNewTilesFuture())
                factory.create(
                    TileSpec.create(REDUCE_BRIGHTNESS_TILE_SPEC),
                    userActionInteractor,
                    stateInteractor,
                    mapper,
                )
            else StubQSTileViewModel
    }
}
+6 −109
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 * 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.
@@ -16,124 +16,21 @@

package com.android.systemui.qs;

import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.provider.Settings;

import androidx.annotation.NonNull;

import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.util.settings.SecureSettings;

import java.util.ArrayList;

import javax.inject.Inject;

/**
 * @hide
 */
@SysUISingleton
public class ReduceBrightColorsController implements
public interface ReduceBrightColorsController extends
        CallbackController<ReduceBrightColorsController.Listener> {
    private final ColorDisplayManager mManager;
    private final UserTracker mUserTracker;
    private UserTracker.Callback mCurrentUserTrackerCallback;
    private final Handler mHandler;
    private final ContentObserver mContentObserver;
    private final SecureSettings mSecureSettings;
    private final ArrayList<ReduceBrightColorsController.Listener> mListeners = new ArrayList<>();

    @Inject
    public ReduceBrightColorsController(UserTracker userTracker,
            @Background Handler handler,
            ColorDisplayManager colorDisplayManager,
            SecureSettings secureSettings) {
        mManager = colorDisplayManager;
        mUserTracker = userTracker;
        mHandler = handler;
        mSecureSettings = secureSettings;
        mContentObserver = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);
                final String setting = uri == null ? null : uri.getLastPathSegment();
                synchronized (mListeners) {
                    if (setting != null && mListeners.size() != 0) {
                        if (setting.equals(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
                            dispatchOnActivated(mManager.isReduceBrightColorsActivated());
                        }
                    }
                }
            }
        };

        mCurrentUserTrackerCallback = new UserTracker.Callback() {
            @Override
            public void onUserChanged(int newUser, Context userContext) {
                synchronized (mListeners) {
                    if (mListeners.size() > 0) {
                        mSecureSettings.unregisterContentObserver(mContentObserver);
                        mSecureSettings.registerContentObserverForUser(
                                Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                                false, mContentObserver, newUser);
                    }
                }
            }
        };
        mUserTracker.addCallback(mCurrentUserTrackerCallback, new HandlerExecutor(handler));
    }

    @Override
    public void addCallback(@NonNull Listener listener) {
        synchronized (mListeners) {
            if (!mListeners.contains(listener)) {
                mListeners.add(listener);
                if (mListeners.size() == 1) {
                    mSecureSettings.registerContentObserverForUser(
                            Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                            false, mContentObserver, mUserTracker.getUserId());
                }
            }
        }
    }

    @Override
    public void removeCallback(@androidx.annotation.NonNull Listener listener) {
        synchronized (mListeners) {
            if (mListeners.remove(listener) && mListeners.size() == 0) {
                mSecureSettings.unregisterContentObserver(mContentObserver);
            }
        }
    }

    /** Returns {@code true} if Reduce Bright Colors is activated */
    public boolean isReduceBrightColorsActivated() {
        return mManager.isReduceBrightColorsActivated();
    }
    boolean isReduceBrightColorsActivated();

    /** Sets the activation state of Reduce Bright Colors */
    public void setReduceBrightColorsActivated(boolean activated) {
        mManager.setReduceBrightColorsActivated(activated);
    }

    private void dispatchOnActivated(boolean activated) {
        ArrayList<Listener> copy = new ArrayList<>(mListeners);
        for (Listener l : copy) {
            l.onActivated(activated);
        }
    }
    void setReduceBrightColorsActivated(boolean activated);

    /**
     * Listener invoked whenever the Reduce Bright Colors settings are changed.
     */
    public interface Listener {
    interface Listener {
        /**
         * Listener invoked when the activated state changes.
         *
Loading