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

Commit 0cd1b994 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Add toast and timeout logic in ringer

Flag: com.android.systemui.volume_redesign
Bug: 369993851
Test: atest VolumeDialogRingerDrawerViewModelTest
Change-Id: I10527a07db808eb0fa4c32db1fade37126611166
parent bf284589
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.volume.dialog.dagger

import com.android.systemui.volume.dialog.dagger.module.VolumeDialogModule
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import dagger.BindsInstance
@@ -27,7 +28,7 @@ import kotlinx.coroutines.CoroutineScope
 * [com.android.systemui.volume.dialog.VolumeDialogPlugin] and lives alongside it.
 */
@VolumeDialogScope
@Subcomponent(modules = [])
@Subcomponent(modules = [VolumeDialogModule::class])
interface VolumeDialogComponent {

    /**
+32 −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.volume.dialog.dagger.module

import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository
import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepositoryImpl
import dagger.Binds
import dagger.Module

/** Dagger module for volume dialog code in the volume package */
@Module
interface VolumeDialogModule {

    @Binds
    fun bindVolumeDialogRingerFeedbackRepository(
        ringerFeedbackRepository: VolumeDialogRingerFeedbackRepositoryImpl
    ): VolumeDialogRingerFeedbackRepository
}
+57 −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.volume.dialog.ringer.data.repository

import android.content.Context
import com.android.systemui.Prefs
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext

interface VolumeDialogRingerFeedbackRepository {

    /** gets number of shown toasts */
    suspend fun getToastCount(): Int

    /** updates number of shown toasts */
    suspend fun updateToastCount(toastCount: Int)
}

class VolumeDialogRingerFeedbackRepositoryImpl
@Inject
constructor(
    @Application private val applicationContext: Context,
    @Background val backgroundDispatcher: CoroutineDispatcher,
) : VolumeDialogRingerFeedbackRepository {

    override suspend fun getToastCount(): Int =
        withContext(backgroundDispatcher) {
            return@withContext Prefs.getInt(
                applicationContext,
                Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
                0,
            )
        }

    override suspend fun updateToastCount(toastCount: Int) {
        withContext(backgroundDispatcher) {
            Prefs.putInt(applicationContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, toastCount + 1)
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository
import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import javax.inject.Inject
@@ -45,6 +46,7 @@ constructor(
    volumeDialogStateInteractor: VolumeDialogStateInteractor,
    private val controller: VolumeDialogController,
    private val audioSystemRepository: AudioSystemRepository,
    private val ringerFeedbackRepository: VolumeDialogRingerFeedbackRepository,
) {

    val ringerModel: Flow<VolumeDialogRingerModel> =
@@ -84,4 +86,12 @@ constructor(
    fun scheduleTouchFeedback() {
        controller.scheduleTouchFeedback()
    }

    suspend fun getToastCount(): Int {
        return ringerFeedbackRepository.getToastCount()
    }

    suspend fun updateToastCount(toastCount: Int) {
        ringerFeedbackRepository.updateToastCount(toastCount)
    }
}
+59 −0
Original line number Diff line number Diff line
@@ -16,17 +16,23 @@

package com.android.systemui.volume.dialog.ringer.ui.viewmodel

import android.content.Context
import android.media.AudioAttributes
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
import android.os.VibrationEffect
import android.widget.Toast
import com.android.internal.R as internalR
import com.android.settingslib.Utils
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.VolumeDialogRingerInteractor
import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
@@ -40,26 +46,37 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

private const val SHOW_RINGER_TOAST_COUNT = 12

class VolumeDialogRingerDrawerViewModel
@AssistedInject
constructor(
    @Application private val applicationContext: Context,
    @VolumeDialog private val coroutineScope: CoroutineScope,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val interactor: VolumeDialogRingerInteractor,
    private val vibrator: VibratorHelper,
    private val volumeDialogLogger: VolumeDialogLogger,
    private val visibilityInteractor: VolumeDialogVisibilityInteractor,
) {

    private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)

    val ringerViewModel: StateFlow<RingerViewModelState> =
        combine(interactor.ringerModel, drawerState) { ringerModel, state ->
                level = ringerModel.level
                levelMax = ringerModel.levelMax
                ringerModel.toViewModel(state)
            }
            .flowOn(backgroundDispatcher)
            .stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)

    // Level and Maximum level of Ring Stream.
    private var level = -1
    private var levelMax = -1

    // Vibration attributes.
    private val sonificiationVibrationAttributes =
        AudioAttributes.Builder()
@@ -71,8 +88,10 @@ constructor(
        if (drawerState.value is RingerDrawerState.Open) {
            Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
            provideTouchFeedback(ringerMode)
            maybeShowToast(ringerMode)
            interactor.setRingerMode(ringerMode)
        }
        visibilityInteractor.resetDismissTimeout()
        drawerState.value =
            when (drawerState.value) {
                is RingerDrawerState.Initial -> {
@@ -201,6 +220,46 @@ constructor(
        }
    }

    private fun maybeShowToast(ringerMode: RingerMode) {
        coroutineScope.launch {
            val seenToastCount = interactor.getToastCount()
            if (seenToastCount > SHOW_RINGER_TOAST_COUNT) {
                return@launch
            }

            val toastText =
                when (ringerMode.value) {
                    RINGER_MODE_NORMAL -> {
                        if (level != -1 && levelMax != -1) {
                            applicationContext.getString(
                                R.string.volume_dialog_ringer_guidance_ring,
                                Utils.formatPercentage(level.toLong(), levelMax.toLong()),
                            )
                        } else {
                            null
                        }
                    }

                    RINGER_MODE_SILENT ->
                        applicationContext.getString(
                            internalR.string.volume_dialog_ringer_guidance_silent
                        )

                    RINGER_MODE_VIBRATE ->
                        applicationContext.getString(
                            internalR.string.volume_dialog_ringer_guidance_vibrate
                        )

                    else ->
                        applicationContext.getString(
                            internalR.string.volume_dialog_ringer_guidance_vibrate
                        )
                }
            toastText?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show() }
            interactor.updateToastCount(seenToastCount)
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(): VolumeDialogRingerDrawerViewModel
Loading