Skip to content

Commit

Permalink
feat(calling): show double tap indicator once per application lifetim…
Browse files Browse the repository at this point in the history
…e (WPB-314) (#1905)

* feat(calling): show double tap indicator once per application lifetime

* chore: detekt

* chore: address comments
  • Loading branch information
ohassine authored Jul 7, 2023
1 parent e04274a commit 751599a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.wire.android.BuildConfig
import com.wire.android.migration.failure.UserMigrationStatus
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import javax.inject.Inject
Expand All @@ -44,13 +45,18 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex
private const val PREFERENCES_NAME = "global_data"

// keys
private val SHOW_CALLING_DOUBLE_TAP_TOAST = booleanPreferencesKey("show_calling_double_tap_toast_")
private val MIGRATION_COMPLETED = booleanPreferencesKey("migration_completed")
private val WELCOME_SCREEN_PRESENTED = booleanPreferencesKey("welcome_screen_presented")
private val IS_LOGGING_ENABLED = booleanPreferencesKey("is_logging_enabled")
private val IS_ENCRYPTED_PROTEUS_STORAGE_ENABLED = booleanPreferencesKey("is_encrypted_proteus_storage_enabled")
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = PREFERENCES_NAME)
private fun userMigrationStatusKey(userId: String): Preferences.Key<Int> = intPreferencesKey("user_migration_status_$userId")
private fun userLastMigrationAppVersion(userId: String): Preferences.Key<Int> = intPreferencesKey("migration_app_version_$userId")
private fun userDoubleTapToastStatusKey(userId: String): Preferences.Key<Boolean> =
booleanPreferencesKey("$SHOW_CALLING_DOUBLE_TAP_TOAST$userId")

private fun userLastMigrationAppVersion(userId: String): Preferences.Key<Int> =
intPreferencesKey("migration_app_version_$userId")
}

suspend fun clear() {
Expand Down Expand Up @@ -120,4 +126,11 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex

suspend fun getUserMigrationAppVersion(userId: String): Int? =
context.dataStore.data.map { it[userLastMigrationAppVersion(userId)] }.firstOrNull()

suspend fun setShouldShowDoubleTapToastStatus(userId: String, shouldShow: Boolean) {
context.dataStore.edit { it[userDoubleTapToastStatusKey(userId)] = shouldShow }
}

suspend fun getShouldShowDoubleTapToast(userId: String): Boolean =
getBooleanPreference(userDoubleTapToastStatusKey(userId), true).first()
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.appLogger
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.di.CurrentAccount
import com.wire.android.navigation.EXTRA_CONVERSATION_ID
import com.wire.android.navigation.NavigationManager
import com.wire.android.ui.calling.model.UICallParticipant
Expand All @@ -36,6 +38,7 @@ import com.wire.android.util.CurrentScreenManager
import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCase
import com.wire.kalium.logic.feature.call.usecase.RequestVideoStreamsUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -50,6 +53,9 @@ import javax.inject.Inject
class OngoingCallViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
qualifiedIdMapper: QualifiedIdMapper,
@CurrentAccount
private val currentUserId: UserId,
private val globalDataStore: GlobalDataStore,
private val navigationManager: NavigationManager,
private val establishedCalls: ObserveEstablishedCallsUseCase,
private val requestVideoStreams: RequestVideoStreamsUseCase,
Expand All @@ -60,7 +66,8 @@ class OngoingCallViewModel @Inject constructor(
savedStateHandle.get<String>(EXTRA_CONVERSATION_ID)!!
)

var shouldShowDoubleTapToast by mutableStateOf(false)
var shouldShowDoubleTapToast: Boolean by mutableStateOf(false)
private set
private var doubleTapIndicatorCountDownTimer: CountDownTimer? = null

init {
Expand Down Expand Up @@ -113,6 +120,9 @@ class OngoingCallViewModel @Inject constructor(

override fun onFinish() {
shouldShowDoubleTapToast = false
viewModelScope.launch {
globalDataStore.setShouldShowDoubleTapToastStatus(currentUserId.toString(), false)
}
}
}
doubleTapIndicatorCountDownTimer?.start()
Expand All @@ -121,13 +131,18 @@ class OngoingCallViewModel @Inject constructor(
private fun showDoubleTapToast() {
viewModelScope.launch {
delay(DELAY_TO_SHOW_DOUBLE_TAP_TOAST)
shouldShowDoubleTapToast = true
startDoubleTapToastDisplayCountDown()
shouldShowDoubleTapToast = globalDataStore.getShouldShowDoubleTapToast(currentUserId.toString())
if (shouldShowDoubleTapToast) {
startDoubleTapToastDisplayCountDown()
}
}
}

fun hideDoubleTapToast() {
shouldShowDoubleTapToast = false
viewModelScope.launch {
globalDataStore.setShouldShowDoubleTapToastStatus(currentUserId.toString(), false)
}
}

private suspend fun navigateBack() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package com.wire.android.ui.calling

import androidx.lifecycle.SavedStateHandle
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.navigation.NavigationManager
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.calling.ongoing.OngoingCallViewModel
Expand All @@ -30,24 +31,19 @@ import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCase
import com.wire.kalium.logic.feature.call.usecase.RequestVideoStreamsUseCase
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.After
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.internal.assertEquals
import org.junit.Test
import org.junit.jupiter.api.BeforeEach

@OptIn(ExperimentalCoroutinesApi::class)
class OngoingCallViewModelTest {

@MockK
Expand All @@ -68,23 +64,26 @@ class OngoingCallViewModelTest {
@MockK
private lateinit var currentScreenManager: CurrentScreenManager

@MockK
private lateinit var globalDataStore: GlobalDataStore

private lateinit var ongoingCallViewModel: OngoingCallViewModel

@BeforeEach
fun setup() {
val scheduler = TestCoroutineScheduler()
Dispatchers.setMain(StandardTestDispatcher(scheduler))
val dummyConversationId = "[email protected]"
MockKAnnotations.init(this)
every { savedStateHandle.get<String>(any()) } returns dummyConversationId
every { savedStateHandle.set(any(), any<String>()) } returns Unit
coEvery {
qualifiedIdMapper.fromStringToQualifiedID("[email protected]")
} returns QualifiedID("some-dummy-value", "some.dummy.domain")
qualifiedIdMapper.fromStringToQualifiedID(dummyConversationId)
} returns conversationId

ongoingCallViewModel = OngoingCallViewModel(
savedStateHandle = savedStateHandle,
qualifiedIdMapper = qualifiedIdMapper,
currentUserId = currentUserId,
globalDataStore = globalDataStore,
navigationManager = navigationManager,
establishedCalls = establishedCall,
requestVideoStreams = requestVideoStreams,
Expand All @@ -105,13 +104,19 @@ class OngoingCallViewModelTest {
coVerify(exactly = 1) { requestVideoStreams(conversationId, expectedClients) }
}

@After
fun tearDown() {
Dispatchers.resetMain()
@Test
fun givenDoubleTabIndicatorIsDisplayed_whenUserTapsOnIt_thenHideIt() = runTest {
coEvery { globalDataStore.setShouldShowDoubleTapToastStatus(currentUserId.toString(), false) } returns Unit

ongoingCallViewModel.hideDoubleTapToast()

assertEquals(false, ongoingCallViewModel.shouldShowDoubleTapToast)
coVerify(exactly = 1) { globalDataStore.setShouldShowDoubleTapToastStatus(currentUserId.toString(), false) }
}

companion object {
val conversationId = ConversationId("some-dummy-value", "some.dummy.domain")
val currentUserId = UserId("userId", "some.dummy.domain")
private val participant1 = UICallParticipant(
id = QualifiedID("value1", "domain"),
clientId = "client-id1",
Expand Down

0 comments on commit 751599a

Please sign in to comment.