Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move conversation to folder from other user profile [WPB-15479] [WPB-14630] #3849

Merged
merged 5 commits into from
Jan 30, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Merge branch 'develop' into feat/other-user-profile-move-to-folder
Garzas committed Jan 30, 2025
commit 5960905391b43c04d23370ba1b0528aae2d3c5fa
2 changes: 1 addition & 1 deletion .github/workflows/gradle-run-unit-tests.yml
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ jobs:
merge-multiple: true

- name: Upload code coverage to codecov
uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: "app/build/reports/kover/report.xml"
11 changes: 10 additions & 1 deletion app/src/main/kotlin/com/wire/android/WireApplication.kt
Original file line number Diff line number Diff line change
@@ -254,7 +254,16 @@ class WireApplication : BaseApp() {
.isAppVisibleFlow()
.filter { isVisible -> isVisible }
.collect {
AnonymousAnalyticsManagerImpl.sendEvent(AnalyticsEvent.AppOpen)
val currentSessionResult = coreLogic.get().getGlobalScope().session.currentSessionFlow().first()
val isTeamMember = if (currentSessionResult is CurrentSessionResult.Success) {
coreLogic.get().getSessionScope(currentSessionResult.accountInfo.userId).team.isSelfATeamMember()
} else {
null
}

AnonymousAnalyticsManagerImpl.sendEvent(
AnalyticsEvent.AppOpen(isTeamMember)
)
}
}
}
15 changes: 0 additions & 15 deletions app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt
Original file line number Diff line number Diff line change
@@ -326,21 +326,6 @@ class UseCaseModule {
): ObserveSecurityClassificationLabelUseCase =
coreLogic.getSessionScope(currentAccount).observeSecurityClassificationLabel

@ViewModelScoped
@Provides
fun provideCreateBackupUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) =
coreLogic.getSessionScope(currentAccount).backup.create

@ViewModelScoped
@Provides
fun provideVerifyBackupUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) =
coreLogic.getSessionScope(currentAccount).backup.verify

@ViewModelScoped
@Provides
fun provideRestoreBackupUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) =
coreLogic.getSessionScope(currentAccount).backup.restore

@ViewModelScoped
@Provides
fun provideUpdateApiVersionsScheduler(@KaliumCoreLogic coreLogic: CoreLogic) =
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.di.accountScoped

import com.wire.android.di.CurrentAccount
import com.wire.android.di.KaliumCoreLogic
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.backup.BackupScope
import com.wire.kalium.util.DelicateKaliumApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@Module
@InstallIn(ViewModelComponent::class)
class BackupModule {

@ViewModelScoped
@Provides
fun provideBackupScope(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId): BackupScope =
coreLogic.getSessionScope(currentAccount).backup

@ViewModelScoped
@Provides
fun provideCreateBackupUseCase(backupScope: BackupScope) =
backupScope.create

@ViewModelScoped
@Provides
fun provideVerifyBackupUseCase(backupScope: BackupScope) =
backupScope.verify

@ViewModelScoped
@Provides
fun provideRestoreBackupUseCase(backupScope: BackupScope) =
backupScope.restore

@OptIn(DelicateKaliumApi::class)
@ViewModelScoped
@Provides
fun provideOnboardingBackupUseCase(backupScope: BackupScope) =
backupScope.createUnEncryptedCopy
}
Original file line number Diff line number Diff line change
@@ -46,7 +46,12 @@ class AudioWavesMaskHelper @Inject constructor(
if (this.isEmpty()) return listOf()

val divider = max() / (WAVE_MAX - 1)
return map { (it / divider).roundToInt() + 1 }

return if (divider == 0.0) {
map { 1 }
} else {
map { (it / divider).roundToInt() + 1 }
}
}

private fun List<Int>.averageWavesMask(): List<Double> {
Original file line number Diff line number Diff line change
@@ -93,5 +93,7 @@ enum class NotificationIds {
MIGRATION_NOTIFICATION_ID,
SINGLE_USER_MIGRATION_NOTIFICATION_ID,
MIGRATION_ERROR_NOTIFICATION_ID,
DELETING_CONVERSATION_NOTIFICATION_ID,
PERSISTENT_CHECK_NOTIFICATION_ID,
PLAYING_AUDIO_MESSAGE_ID
}
67 changes: 63 additions & 4 deletions app/src/main/kotlin/com/wire/android/ui/debug/DebugScreen.kt
Original file line number Diff line number Diff line change
@@ -18,10 +18,12 @@

package com.wire.android.ui.debug

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@@ -30,6 +32,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
@@ -40,6 +43,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.wire.android.BuildConfig
import com.wire.android.R
import com.wire.android.di.hiltViewModelScoped
import com.wire.android.model.Clickable
import com.wire.android.navigation.BackStackMode
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
@@ -49,8 +54,12 @@ import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.topappbar.NavigationIconType
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.wire.android.ui.destinations.MigrationScreenDestination
import com.wire.android.util.AppNameUtil
import com.wire.android.ui.home.conversationslist.common.FolderHeader
import com.wire.android.ui.home.settings.SettingsItem
import com.wire.android.ui.home.settings.backup.BackupAndRestoreDialog
import com.wire.android.ui.home.settings.backup.rememberBackUpAndRestoreStateHolder
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.AppNameUtil
import com.wire.android.util.getMimeType
import com.wire.android.util.getUrisOfFilesInDirectory
import com.wire.android.util.multipleFileSharingIntent
@@ -60,7 +69,10 @@ import java.io.File
@RootNavGraph
@WireDestination
@Composable
fun DebugScreen(navigator: Navigator, userDebugViewModel: UserDebugViewModel = hiltViewModel()) {
fun DebugScreen(
navigator: Navigator,
userDebugViewModel: UserDebugViewModel = hiltViewModel(),
) {
UserDebugContent(
onNavigationPressed = navigator::navigateBack,
onManualMigrationPressed = {
@@ -74,7 +86,7 @@ fun DebugScreen(navigator: Navigator, userDebugViewModel: UserDebugViewModel = h
state = userDebugViewModel.state,
onLoggingEnabledChange = userDebugViewModel::setLoggingEnabledState,
onDeleteLogs = userDebugViewModel::deleteLogs,
onDatabaseLoggerEnabledChanged = userDebugViewModel::setDatabaseLoggerEnabledState
onDatabaseLoggerEnabledChanged = userDebugViewModel::setDatabaseLoggerEnabledState,
)
}

@@ -121,6 +133,53 @@ internal fun UserDebugContent(
onCopyText = debugContentState::copyToClipboard,
onManualMigrationPressed = onManualMigrationPressed
)
DangerOptions()
}
}
}
}

@Composable
fun DangerOptions(
modifier: Modifier = Modifier,
exportObfuscatedCopyViewModel: ExportObfuscatedCopyViewModel =
hiltViewModelScoped<ExportObfuscatedCopyViewModelImpl, ExportObfuscatedCopyViewModel, ExportObfuscatedCopyArgs>(
ExportObfuscatedCopyArgs
),
) {

Column(modifier = modifier) {
FolderHeader("Danger Zone DO NOT TOUCH")
@SuppressLint("ComposeViewModelInjection")
if (BuildConfig.PRIVATE_BUILD) {
val backupAndRestoreStateHolder = rememberBackUpAndRestoreStateHolder()

SettingsItem(
text = "Create Obfuscated Database Copy",
onRowPressed = Clickable(enabled = true, onClick = backupAndRestoreStateHolder::showBackupDialog),
modifier = Modifier.background(Color.Red)
)
when (backupAndRestoreStateHolder.dialogState) {
BackupAndRestoreDialog.CreateBackup -> {
CreateObfuscatedCopyFlow(
backUpAndRestoreState = exportObfuscatedCopyViewModel.state,
backupPasswordTextState = exportObfuscatedCopyViewModel.createBackupPasswordState,
onCreateBackup = exportObfuscatedCopyViewModel::createObfuscatedCopy,
onSaveBackup = exportObfuscatedCopyViewModel::saveCopy,
onShareBackup = exportObfuscatedCopyViewModel::shareCopy,
onCancelCreateBackup = {
backupAndRestoreStateHolder.dismissDialog()
exportObfuscatedCopyViewModel.cancelBackupCreation()
},
onPermissionPermanentlyDenied = {}
)
}

BackupAndRestoreDialog.None -> {
/*no-op*/
}

BackupAndRestoreDialog.RestoreBackup -> TODO("Restore backup not implemented")
}
}
}
@@ -184,6 +243,6 @@ internal fun PreviewUserDebugContent() = WireTheme {
onManualMigrationPressed = {},
onLoggingEnabledChange = {},
onDeleteLogs = {},
onDatabaseLoggerEnabledChanged = {}
onDatabaseLoggerEnabledChanged = {},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.debug

import android.net.Uri
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.appLogger
import com.wire.android.di.ScopedArgs
import com.wire.android.di.ViewModelScopedPreview
import com.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl
import com.wire.android.feature.analytics.model.AnalyticsEvent
import com.wire.android.ui.home.settings.backup.BackupAndRestoreState
import com.wire.android.ui.home.settings.backup.BackupCreationProgress
import com.wire.android.util.FileManager
import com.wire.android.util.dispatchers.DefaultDispatcherProvider
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.feature.backup.CreateBackupResult
import com.wire.kalium.logic.feature.backup.CreateObfuscatedCopyUseCase
import com.wire.kalium.util.DelicateKaliumApi
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import javax.inject.Inject

@ViewModelScopedPreview
interface ExportObfuscatedCopyViewModel {

val state: BackupAndRestoreState get() = BackupAndRestoreState.INITIAL_STATE

val createBackupPasswordState: TextFieldState
get() = TextFieldState()

fun createObfuscatedCopy() {}
fun shareCopy() {}
fun saveCopy(uri: Uri) {}
fun cancelBackupCreation() {}
}

@HiltViewModel
class ExportObfuscatedCopyViewModelImpl @OptIn(DelicateKaliumApi::class) @Inject constructor(
private val createUnencryptedCopy: CreateObfuscatedCopyUseCase,
private val dispatcher: DispatcherProvider = DefaultDispatcherProvider(),
private val fileManager: FileManager,
) : ViewModel(), ExportObfuscatedCopyViewModel {

override var state by mutableStateOf(BackupAndRestoreState.INITIAL_STATE)

override val createBackupPasswordState: TextFieldState = TextFieldState()

@VisibleForTesting
internal var latestCreatedBackup: BackupAndRestoreState.CreatedBackup? = null

@OptIn(DelicateKaliumApi::class)
override fun createObfuscatedCopy() {
viewModelScope.launch {

when (val result = createUnencryptedCopy(null)) {
is CreateBackupResult.Success -> {
state = state.copy(backupCreationProgress = BackupCreationProgress.Finished(result.backupFileName))
latestCreatedBackup = BackupAndRestoreState.CreatedBackup(
result.backupFilePath,
result.backupFileName,
result.backupFileSize,
false
)
}

is CreateBackupResult.Failure -> {
state = state.copy(backupCreationProgress = BackupCreationProgress.Failed)
appLogger.e("Failed to create backup: ${result.coreFailure}")
AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupExportFailed)
}
}
}
}

override fun shareCopy() {
viewModelScope.launch {
latestCreatedBackup?.let { backupData ->
withContext(dispatcher.io()) {
fileManager.shareWithExternalApp(backupData.path, backupData.assetName) {}
}
}
state = state.copy(
backupCreationProgress = BackupCreationProgress.InProgress(),
)
}
}

override fun saveCopy(uri: Uri) {
viewModelScope.launch {
latestCreatedBackup?.let { backupData ->
fileManager.copyToUri(backupData.path, uri, dispatcher)
}
state = state.copy(
backupCreationProgress = BackupCreationProgress.InProgress(),
)
}
}

override fun cancelBackupCreation() {
viewModelScope.launch(dispatcher.main()) {
createBackupPasswordState.clearText()
}
}
}

@Serializable
object ExportObfuscatedCopyArgs : ScopedArgs {
override val key = "ExportObfuscatedCopyArgsKey"
}
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.