Benoit Marty
1 year ago
committed by
Benoit Marty
12 changed files with 282 additions and 11 deletions
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* 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 io.element.android.features.preferences.impl.tasks |
||||
|
||||
import android.content.Context |
||||
import com.squareup.anvil.annotations.ContributesBinding |
||||
import io.element.android.libraries.androidutils.file.getSizeOfFiles |
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers |
||||
import io.element.android.libraries.di.ApplicationContext |
||||
import io.element.android.libraries.di.SessionScope |
||||
import io.element.android.libraries.matrix.api.MatrixClient |
||||
import kotlinx.coroutines.withContext |
||||
import javax.inject.Inject |
||||
|
||||
interface ComputeCacheSizeUseCase { |
||||
suspend fun execute(): Long |
||||
} |
||||
|
||||
@ContributesBinding(SessionScope::class) |
||||
class DefaultComputeCacheSizeUseCase @Inject constructor( |
||||
@ApplicationContext private val context: Context, |
||||
private val matrixClient: MatrixClient, |
||||
private val coroutineDispatchers: CoroutineDispatchers, |
||||
) : ComputeCacheSizeUseCase { |
||||
override suspend fun execute(): Long = withContext(coroutineDispatchers.io) { |
||||
var cumulativeSize = 0L |
||||
cumulativeSize += matrixClient.getCacheSize() |
||||
cumulativeSize += context.cacheDir.getSizeOfFiles() |
||||
cumulativeSize |
||||
} |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* 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 io.element.android.features.preferences.impl.tasks |
||||
|
||||
import io.element.android.tests.testutils.simulateLongTask |
||||
|
||||
class FakeComputeCacheSizeUseCase : ComputeCacheSizeUseCase { |
||||
override suspend fun execute() = simulateLongTask { |
||||
0L |
||||
} |
||||
} |
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* 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 io.element.android.libraries.androidutils.file |
||||
|
||||
import android.content.Context |
||||
import androidx.annotation.WorkerThread |
||||
import timber.log.Timber |
||||
import java.io.File |
||||
import java.util.Locale |
||||
|
||||
// Implementation should return true in case of success |
||||
typealias ActionOnFile = (file: File) -> Boolean |
||||
|
||||
/* ========================================================================================== |
||||
* Delete |
||||
* ========================================================================================== */ |
||||
|
||||
fun deleteAllFiles(root: File) { |
||||
Timber.v("Delete ${root.absolutePath}") |
||||
recursiveActionOnFile(root, ::deleteAction) |
||||
} |
||||
|
||||
private fun deleteAction(file: File): Boolean { |
||||
if (file.exists()) { |
||||
Timber.v("deleteFile: $file") |
||||
return file.delete() |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
/* ========================================================================================== |
||||
* Log |
||||
* ========================================================================================== */ |
||||
|
||||
fun lsFiles(context: Context) { |
||||
Timber.v("Content of cache dir:") |
||||
recursiveActionOnFile(context.cacheDir, ::logAction) |
||||
|
||||
Timber.v("Content of files dir:") |
||||
recursiveActionOnFile(context.filesDir, ::logAction) |
||||
} |
||||
|
||||
private fun logAction(file: File): Boolean { |
||||
if (file.isDirectory) { |
||||
Timber.v(file.toString()) |
||||
} else { |
||||
Timber.v("$file ${file.length()} bytes") |
||||
} |
||||
return true |
||||
} |
||||
|
||||
/* ========================================================================================== |
||||
* Private |
||||
* ========================================================================================== */ |
||||
|
||||
/** |
||||
* Return true in case of success. |
||||
*/ |
||||
private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean { |
||||
if (file.isDirectory) { |
||||
file.list()?.forEach { |
||||
val result = recursiveActionOnFile(File(file, it), action) |
||||
|
||||
if (!result) { |
||||
// Break the loop |
||||
return false |
||||
} |
||||
} |
||||
} |
||||
|
||||
return action.invoke(file) |
||||
} |
||||
|
||||
/** |
||||
* Get the file extension of a fileUri or a filename. |
||||
* |
||||
* @param fileUri the fileUri (can be a simple filename) |
||||
* @return the file extension, in lower case, or null is extension is not available or empty |
||||
*/ |
||||
fun getFileExtension(fileUri: String): String? { |
||||
var reducedStr = fileUri |
||||
|
||||
if (reducedStr.isNotEmpty()) { |
||||
// Remove fragment |
||||
reducedStr = reducedStr.substringBeforeLast('#') |
||||
|
||||
// Remove query |
||||
reducedStr = reducedStr.substringBeforeLast('?') |
||||
|
||||
// Remove path |
||||
val filename = reducedStr.substringAfterLast('/') |
||||
|
||||
// Contrary to method MimeTypeMap.getFileExtensionFromUrl, we do not check the pattern |
||||
// See https://stackoverflow.com/questions/14320527/android-should-i-use-mimetypemap-getfileextensionfromurl-bugs |
||||
if (filename.isNotEmpty()) { |
||||
val dotPos = filename.lastIndexOf('.') |
||||
if (0 <= dotPos) { |
||||
val ext = filename.substring(dotPos + 1) |
||||
|
||||
if (ext.isNotBlank()) { |
||||
return ext.lowercase(Locale.ROOT) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return null |
||||
} |
||||
|
||||
/* ========================================================================================== |
||||
* Size |
||||
* ========================================================================================== */ |
||||
|
||||
@WorkerThread |
||||
fun File.getSizeOfFiles(): Long { |
||||
return walkTopDown() |
||||
.onEnter { |
||||
Timber.v("Get size of ${it.absolutePath}") |
||||
true |
||||
} |
||||
.sumOf { it.length() } |
||||
} |
Loading…
Reference in new issue