1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-07 21:37:02 +09:00

DROID-3047 Chats | Enhancement | Clear chat temp folder after sending message (#2513)

This commit is contained in:
Evgenii Kozlov 2025-06-06 17:45:27 +02:00 committed by GitHub
parent e384832a9a
commit 50bc12b04f
Signed by: github
GPG key ID: B5690EEEBB952194
4 changed files with 107 additions and 9 deletions

View file

@ -48,6 +48,7 @@ import com.anytypeio.anytype.domain.objects.CreateObjectFromUrl
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.getTypeOfObject
import com.anytypeio.anytype.feature_chats.BuildConfig
import com.anytypeio.anytype.feature_chats.tools.ClearChatsTempFolder
import com.anytypeio.anytype.feature_chats.tools.DummyMessageGenerator
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.confgs.ChatConfig
@ -101,7 +102,8 @@ class ChatViewModel @Inject constructor(
private val generateSpaceInviteLink: GenerateSpaceInviteLink,
private val makeSpaceShareable: MakeSpaceShareable,
private val getSpaceInviteLink: GetSpaceInviteLink,
private val revokeSpaceInviteLink: RevokeSpaceInviteLink
private val revokeSpaceInviteLink: RevokeSpaceInviteLink,
private val clearChatsTempFolder: ClearChatsTempFolder
) : BaseViewModel(), ExitToVaultDelegate by exitToVaultDelegate {
private val visibleRangeUpdates = MutableSharedFlow<Pair<Id, Id>>(
@ -547,6 +549,8 @@ class ChatViewModel @Inject constructor(
val normalizedMarkup = (markup + parsedUrls).sortedBy { it.range.first }
var shouldClearChatTempFolder = false
chatBoxMode.value = chatBoxMode.value.updateIsSendingBlocked(isBlocked = true)
val attachments = buildList {
val currAttachments = chatBoxAttachments.value
@ -594,6 +598,7 @@ class ChatViewModel @Inject constructor(
)
}
val path = if (attachment.capturedByCamera) {
shouldClearChatTempFolder = true
withContext(dispatchers.io) {
copyFileToCacheDirectory.copy(attachment.uri)
}.orEmpty()
@ -610,6 +615,14 @@ class ChatViewModel @Inject constructor(
Block.Content.File.Type.IMAGE
)
).onSuccess { file ->
withContext(dispatchers.io) {
val isDeleted = copyFileToCacheDirectory.delete(path)
if (isDeleted) {
Timber.d("DROID-2966 Successfully deleted temp file: ${attachment.uri}")
} else {
Timber.w("DROID-2966 Error while deleting temp file: ${attachment.uri}")
}
}
add(
Chat.Message.Attachment(
target = file.id,
@ -688,7 +701,7 @@ class ChatViewModel @Inject constructor(
type = Block.Content.File.Type.NONE
)
).onSuccess { file ->
// TODO delete file.
copyFileToCacheDirectory.delete(path)
add(
Chat.Message.Attachment(
target = file.id,
@ -792,6 +805,12 @@ class ChatViewModel @Inject constructor(
// Do nothing.
}
}
if (shouldClearChatTempFolder) {
withContext(dispatchers.io) {
clearChatsTempFolder()
}
}
}
}

View file

@ -22,6 +22,7 @@ import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.notifications.NotificationBuilder
import com.anytypeio.anytype.domain.objects.CreateObjectFromUrl
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.feature_chats.tools.ClearChatsTempFolder
import com.anytypeio.anytype.presentation.notifications.NotificationPermissionManager
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.vault.ExitToVaultDelegate
@ -51,7 +52,8 @@ class ChatViewModelFactory @Inject constructor(
private val generateSpaceInviteLink: GenerateSpaceInviteLink,
private val makeSpaceShareable: MakeSpaceShareable,
private val getSpaceInviteLink: GetSpaceInviteLink,
private val revokeSpaceInviteLink: RevokeSpaceInviteLink
private val revokeSpaceInviteLink: RevokeSpaceInviteLink,
private val clearChatsTempFolder: ClearChatsTempFolder
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = ChatViewModel(
@ -78,6 +80,7 @@ class ChatViewModelFactory @Inject constructor(
generateSpaceInviteLink = generateSpaceInviteLink,
makeSpaceShareable = makeSpaceShareable,
getSpaceInviteLink = getSpaceInviteLink,
revokeSpaceInviteLink = revokeSpaceInviteLink
revokeSpaceInviteLink = revokeSpaceInviteLink,
clearChatsTempFolder = clearChatsTempFolder
) as T
}

View file

@ -5,6 +5,8 @@ import android.net.Uri
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.core.content.FileProvider
import java.io.File
import javax.inject.Inject
import timber.log.Timber
fun launchCamera(
@ -12,7 +14,13 @@ fun launchCamera(
launcher: ManagedActivityResultLauncher<Uri, Boolean>,
onUriReceived: (Uri) -> Unit
) {
val photoFile = File.createTempFile("IMG_", ".jpg", context.cacheDir).apply {
val tempDir = File(context.cacheDir, CHATS_TEMP_FOLDER_NAME)
if (!tempDir.exists()) {
val created = tempDir.mkdirs()
Timber.d("Created camera temp dir: $created at ${tempDir.absolutePath}")
}
val photoFile = File.createTempFile("IMG_", ".jpg", tempDir).apply {
createNewFile()
deleteOnExit()
}
@ -23,7 +31,29 @@ fun launchCamera(
photoFile
)
onUriReceived(uri)
Timber.d("Launching camera with URI: $uri (path: ${photoFile.absolutePath})")
onUriReceived(uri)
launcher.launch(uri)
}
const val CHATS_TEMP_FOLDER_NAME = "chats_temp_folder"
class ClearChatsTempFolder @Inject constructor(
private val context: Context
) {
companion object {
private const val CHATS_TEMP_FOLDER_NAME = "chats_temp_folder"
}
operator fun invoke(): Boolean {
val folder = File(context.cacheDir, CHATS_TEMP_FOLDER_NAME)
return if (folder.exists()) {
val deleted = folder.deleteRecursively()
// Optional: log if needed
deleted
} else {
false
}
}
}

View file

@ -4,6 +4,9 @@ import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import com.anytypeio.anytype.core_utils.ext.msg
import java.io.File
import java.io.FileOutputStream
import java.lang.ref.WeakReference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -11,9 +14,6 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.lang.ref.WeakReference
/**
* Interface defining the contract for copying files to a cache directory.
@ -42,6 +42,8 @@ interface CopyFileToCacheDirectory {
* @return `true` if the operation is active, `false` otherwise.
*/
fun isActive(): Boolean
fun delete(uri: String): Boolean
}
/**
@ -212,6 +214,37 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
}
return result
}
override fun delete(uri: String): Boolean {
val context = mContext?.get() ?: return false
return try {
val path = Uri.parse(uri).path ?: return false
val file = File(path)
// Optional: check if file is in cache or external files dir
val allowedRoots = listOfNotNull(
context.cacheDir?.absolutePath,
context.getExternalFilesDir(null)?.absolutePath
)
if (allowedRoots.any { file.absolutePath.startsWith(it) }) {
if (!file.exists()) {
Timber.w("File does not exist: $path")
return false
}
val deleted = file.delete()
Timber.d("Attempting to delete file: $path → deleted=$deleted")
deleted
} else {
Timber.w("Blocked delete attempt outside allowed folders: $path")
false
}
} catch (e: Exception) {
Timber.e(e, "Error deleting file at $uri")
false
}
}
}
/**
@ -330,6 +363,19 @@ class NetworkModeCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDir
return result
}
override fun delete(uri: String): Boolean {
val context = mContext?.get() ?: return false
return try {
val file = File(Uri.parse(uri).path ?: return false)
val deleted = file.delete()
Timber.d("Attempting to delete file by uri: $uri$deleted")
deleted
} catch (e: Exception) {
Timber.e(e, "Error deleting file by uri: $uri")
false
}
}
companion object {
const val CONFIG_FILE_NAME = "configCustom.txt"
}