diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm index 9811576..261d49f 100644 Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm index a9809b3..19d2337 100644 Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 54665c7..a66dcf1 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -33,5 +33,5 @@ ] } ], - "minSdkVersionForDexing": 24 + "minSdkVersionForDexing": 26 } \ No newline at end of file diff --git a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/domain/UseCasesModule.kt b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/domain/UseCasesModule.kt index e16b72f..13fbe01 100644 --- a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/domain/UseCasesModule.kt +++ b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/domain/UseCasesModule.kt @@ -4,6 +4,7 @@ import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase +import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import dagger.Module import dagger.Provides @@ -31,4 +32,10 @@ class UseCasesModule { fun provideStorageFileManagementUseCase(): StorageFileManagementUseCase { return StorageFileManagementUseCase() } + + @Provides + @Singleton + fun provideRenameStorageUseCase(): RenameStorageUseCase { + return RenameStorageUseCase() + } } \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/storages/local/LocalStorage.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/storages/local/LocalStorage.kt index 0658ec1..df26fff 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/storages/local/LocalStorage.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/storages/local/LocalStorage.kt @@ -35,7 +35,7 @@ class LocalStorage( private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher) override val accessor: IStorageAccessor = _accessor override val isVirtualStorage: Boolean = false - private val metaInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX" + private val metaInfoFileName: String = "$uuid$STORAGE_INFO_FILE_POSTFIX" suspend fun init() { _accessor.init() @@ -91,7 +91,7 @@ class LocalStorage( } companion object { - const val ENC_INFO_FILE_POSTFIX = ".enc-info" + const val STORAGE_INFO_FILE_POSTFIX = ".storage-info" private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() } } } \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/usecases/RenameStorageUseCase.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/usecases/RenameStorageUseCase.kt new file mode 100644 index 0000000..d82cee9 --- /dev/null +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/usecases/RenameStorageUseCase.kt @@ -0,0 +1,12 @@ +package com.github.nullptroma.wallenc.domain.usecases + +import com.github.nullptroma.wallenc.domain.interfaces.IStorage +import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo + +class RenameStorageUseCase { + suspend fun rename(storage: IStorageInfo, newName: String) { + when (storage) { + is IStorage -> storage.rename(newName) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/ViewModelBase.kt similarity index 86% rename from presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt rename to presentation/src/main/java/com/github/nullptroma/wallenc/presentation/ViewModelBase.kt index c92ff4d..c8f87cf 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/ViewModelBase.kt @@ -1,4 +1,4 @@ -package com.github.nullptroma.wallenc.presentation.viewmodel +package com.github.nullptroma.wallenc.presentation import androidx.lifecycle.ViewModel diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/WallencViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/WallencViewModel.kt index 16fc821..28e0df7 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/WallencViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/WallencViewModel.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.viewmodel.compose.saveable import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute -import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel import kotlin.collections.set diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt new file mode 100644 index 0000000..2a0f4a6 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt @@ -0,0 +1,188 @@ +package com.github.nullptroma.wallenc.presentation.elements + +import android.app.Dialog +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Divider +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.PlatformTextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.github.nullptroma.wallenc.domain.datatypes.Tree +import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo +import com.github.nullptroma.wallenc.presentation.R + +@Composable +fun StorageTree( + modifier: Modifier, + tree: Tree, + onClick: (Tree) -> Unit, + onRename: (Tree, String) -> Unit, +) { + val cur = tree.value + val cardShape = RoundedCornerShape(30.dp) + Column(modifier) { + Card( + modifier = Modifier + .fillMaxWidth() + .clip(cardShape) + .clickable { + onClick(tree) + //viewModel.printStorageInfoToLog(cur) + }, + shape = cardShape, + elevation = CardDefaults.cardElevation( + defaultElevation = 4.dp + ), + ) { + val available by cur.isAvailable.collectAsStateWithLifecycle() + val numOfFiles by cur.numberOfFiles.collectAsStateWithLifecycle() + val size by cur.size.collectAsStateWithLifecycle() + val metaInfo by cur.metaInfo.collectAsStateWithLifecycle() + + Row(modifier = Modifier.height(IntrinsicSize.Min)) { + Column(modifier = Modifier.padding(8.dp)) { + Text(metaInfo.name ?: stringResource(R.string.no_name)) + Text( + text = "IsAvailable: $available" + ) + Text("Files: $numOfFiles") + Text("Size: $size") + Text("IsVirtual: ${cur.isVirtualStorage}") + } + Column( + modifier = Modifier, + horizontalAlignment = Alignment.End + ) { + Box(modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 0.dp)) { + var expanded by remember { mutableStateOf(false) } + var showRenameDialog by remember { mutableStateOf(false) } + IconButton(onClick = { expanded = !expanded }) { + Icon( + Icons.Default.MoreVert, + contentDescription = stringResource(R.string.show_storage_item_menu) + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem( + onClick = { + expanded = false + showRenameDialog = true + }, + text = { Text(stringResource(R.string.rename)) } + ) + } + + if (showRenameDialog) { + RenameDialog( + onDismiss = { showRenameDialog = false }, + onConfirmation = { newName -> + showRenameDialog = false + onRename(tree, newName) + }, + startName = tree.value.metaInfo.value.name ?: "" + ) + } + } + Spacer(modifier = Modifier.weight(1f)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(0.dp, 0.dp, 12.dp, 8.dp) + .align(Alignment.End), + text = cur.uuid.toString(), + textAlign = TextAlign.End, + fontSize = 8.sp, + style = LocalTextStyle.current.copy( + platformStyle = PlatformTextStyle( + includeFontPadding = true + ) + ) + ) + } + } + } + for (i in tree.children ?: listOf()) { + StorageTree(Modifier.padding(16.dp, 0.dp, 0.dp, 0.dp), i, onClick, onRename) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RenameDialog(onDismiss: () -> Unit, onConfirmation: (String) -> Unit, startName: String = "") { + var name by remember { mutableStateOf(startName) } + BasicAlertDialog( + onDismissRequest = { onDismiss() } + ) { + Card { + Column(modifier = Modifier.padding(12.dp)) { + Text("New name", style = MaterialTheme.typography.titleLarge) + TextField(name, { + name = it + }) + Spacer(modifier = Modifier.height(24.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button(modifier = Modifier.weight(1f), onClick = onDismiss) { + Text("Cancel") + } + Spacer(modifier = Modifier.width(12.dp)) + Button(modifier = Modifier.weight(1f), onClick = { + onConfirmation(name) + }) { + Text("Ok") + } + } + } + } + } +} diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt index e76f001..843191a 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.viewmodel.compose.saveable import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute -import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase +import com.github.nullptroma.wallenc.presentation.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel @HiltViewModel diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt index 368e5e3..f0b2441 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt @@ -47,6 +47,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.presentation.R +import com.github.nullptroma.wallenc.presentation.elements.StorageTree import kotlin.random.Random @OptIn(ExperimentalMaterial3Api::class) @@ -54,7 +55,7 @@ import kotlin.random.Random fun LocalVaultScreen( modifier: Modifier = Modifier, viewModel: LocalVaultViewModel = hiltViewModel(), - openTextEdit: (String)->Unit + openTextEdit: (String) -> Unit ) { val uiState by viewModel.state.collectAsStateWithLifecycle() @@ -68,66 +69,19 @@ fun LocalVaultScreen( } }) { innerPadding -> LazyColumn(modifier = Modifier.padding(innerPadding)) { - items(uiState.storagesList) { tree -> - Storage(Modifier.padding(8.dp), tree) { - openTextEdit(it.value.uuid.toString()) - } + items(uiState.storagesList) { listItem -> + StorageTree( + modifier = Modifier.padding(8.dp), + tree = listItem, + onClick = { + openTextEdit(it.value.uuid.toString()) + }, + onRename = { tree, newName -> + viewModel.rename(tree.value, newName) + } + ) } } } } -@Composable -fun Storage(modifier: Modifier, tree: Tree, onClick: (Tree) -> Unit) { - val cur = tree.value - val cardShape = RoundedCornerShape(30.dp) - Column(modifier) { - Card( - modifier = Modifier - .fillMaxWidth() - .clip(cardShape) - .clickable { - onClick(tree) - //viewModel.printStorageInfoToLog(cur) - }, - shape = cardShape, - elevation = CardDefaults.cardElevation( - defaultElevation = 4.dp - ), - ) { - val available by cur.isAvailable.collectAsStateWithLifecycle() - val numOfFiles by cur.numberOfFiles.collectAsStateWithLifecycle() - val size by cur.size.collectAsStateWithLifecycle() - val metaInfo by cur.metaInfo.collectAsStateWithLifecycle() - - Row { - Column(modifier = Modifier.padding(8.dp)) { - Text(metaInfo.name ?: stringResource(R.string.no_name)) - Text( - text = "IsAvailable: $available" - ) - Text("Files: $numOfFiles") - Text("Size: $size") - Text("IsVirtual: ${cur.isVirtualStorage}") - Text( - modifier = Modifier - .fillMaxWidth() - .padding(0.dp,0.dp,8.dp,0.dp) - .align(Alignment.End), - text = cur.uuid.toString(), - textAlign = TextAlign.End, - fontSize = 8.sp, - style = LocalTextStyle.current.copy( - platformStyle = PlatformTextStyle( - includeFontPadding = true - ) - ) - ) - } - } - } - for(i in tree.children ?: listOf()) { - Storage(Modifier.padding(16.dp,0.dp,0.dp,0.dp), i, onClick) - } - } -} diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt index 3836730..95bc770 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt @@ -1,7 +1,6 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault import androidx.lifecycle.viewModelScope -import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IFile @@ -9,9 +8,10 @@ import com.github.nullptroma.wallenc.domain.interfaces.ILogger import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase +import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.presentation.extensions.toPrintable -import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase +import com.github.nullptroma.wallenc.presentation.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -24,6 +24,7 @@ class LocalVaultViewModel @Inject constructor( private val manageLocalVaultUseCase: ManageLocalVaultUseCase, private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase, private val storageFileManagementUseCase: StorageFileManagementUseCase, + private val renameStorageUseCase: RenameStorageUseCase, private val logger: ILogger ) : ViewModelBase(LocalVaultScreenState(listOf())) { init { @@ -75,4 +76,10 @@ class LocalVaultViewModel @Inject constructor( manageLocalVaultUseCase.createStorage() } } + + fun rename(storage: IStorageInfo, newName: String) { + viewModelScope.launch { + renameStorageUseCase.rename(storage, newName) + } + } } \ No newline at end of file diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsViewModel.kt index e556f69..ca94c35 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsViewModel.kt @@ -1,6 +1,6 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes -import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase +import com.github.nullptroma.wallenc.presentation.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/settings/SettingsViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/settings/SettingsViewModel.kt index 658e01c..9ba5264 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/settings/SettingsViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/settings/SettingsViewModel.kt @@ -1,6 +1,6 @@ package com.github.nullptroma.wallenc.presentation.screens.settings -import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase +import com.github.nullptroma.wallenc.presentation.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel @HiltViewModel diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index d65a8c1..8f6c55a 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -7,4 +7,7 @@ Settings Screen Title! <noname> + Show storage item menu + Rename + \ No newline at end of file