Skip to content

Commit

Permalink
Download files iin new file manager (#994)
Browse files Browse the repository at this point in the history
**Background**

Add file downloading into new file manager

**Changes**

- Fix bug when size of file during download could not calculate
- Fixed coroutine cancellation with channel.receive
- Add downloading for file

**Test plan**

- Open some folder, try download file and share it
- Open some folder, start download file and cancel it

---------

Co-authored-by: Nikita Kulikov <[email protected]>
  • Loading branch information
makeevrserg and LionZXY authored Dec 2, 2024
1 parent 7af93e0 commit 8b325fd
Show file tree
Hide file tree
Showing 38 changed files with 739 additions and 48 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Changelog

# 1.8.1 - In Progress
# 1.8.2 - In Progress


# 1.8.1

- [Feature] Add count subfolders for new file manager
- [Feature] Add file downloading for new file manager
- [FIX] Migrate url host from metric.flipperdevices.com to metric.flipp.dev
- [FIX] Fix empty response in faphub category
- [FIX] New file manager uploading progress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.flipperdevices.bridge.connection.feature.storage.api.fm

import com.flipperdevices.bridge.connection.feature.storage.api.model.StorageRequestPriority
import com.flipperdevices.core.progress.FixedProgressListener
import kotlinx.coroutines.CoroutineScope
import okio.Path
import okio.Source

Expand All @@ -15,6 +16,7 @@ interface FFileDownloadApi {

fun source(
pathOnFlipper: String,
scope: CoroutineScope,
priority: StorageRequestPriority = StorageRequestPriority.DEFAULT
): Source
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class FFileStorageApiFactoryImpl @Inject constructor() : FDeviceFeatureApi.Facto
md5Api = FFileStorageMD5ApiImpl(rpcApi),
fListingStorageApi = FListingStorageApiImpl(listingDelegate),
fileUploadApi = FFileUploadApiImpl(rpcApi, scope = scope),
fileDownloadApi = FFileDownloadApiImpl(rpcApi, scope = scope),
fileDownloadApi = FFileDownloadApiImpl(rpcApi),
deleteApi = FFileDeleteApiImpl(rpcApi),
timestampApi = if (versionApi.isSupported(API_SUPPORTED_TIMESTAMP)) {
FFileTimestampApiImpl(rpcApi)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import okio.use

class FFileDownloadApiImpl(
private val rpcFeatureApi: FRpcFeatureApi,
private val scope: CoroutineScope,
private val fileSystem: FileSystem = FileSystem.SYSTEM
) : FFileDownloadApi {
override suspend fun download(
Expand All @@ -35,19 +34,24 @@ class FFileDownloadApiImpl(
info { "Start download file $pathOnFlipper to $fileOnAndroid" }

runCatching {
val sourceLength = getTotalSize(pathOnFlipper)
fileSystem.sink(fileOnAndroid).buffer().use { sink ->
source(pathOnFlipper, priority).use { source ->
source(pathOnFlipper, this, priority).use { source ->
source.copyWithProgress(
sink,
progressListener,
sourceLength = { getTotalSize(pathOnFlipper) }
sink = sink,
progressListener = progressListener,
sourceLength = { sourceLength }
)
}
}
}
}

override fun source(pathOnFlipper: String, priority: StorageRequestPriority): Source {
override fun source(
pathOnFlipper: String,
scope: CoroutineScope,
priority: StorageRequestPriority
): Source {
return FFlipperSource(
readerLoop = ReaderRequestLooper(
rpcFeatureApi = rpcFeatureApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import com.flipperdevices.bridge.connection.feature.rpc.model.wrapToRequest
import com.flipperdevices.core.ktx.jre.FlipperDispatchers
import com.flipperdevices.protobuf.Main
import com.flipperdevices.protobuf.storage.ReadRequest
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okio.Closeable
Expand All @@ -17,7 +19,7 @@ class ReaderRequestLooper(
private val rpcFeatureApi: FRpcFeatureApi,
private val pathOnFlipper: String,
private val priority: FlipperRequestPriority,
scope: CoroutineScope
private val scope: CoroutineScope
) : Closeable {
private val queue = Channel<Main>(Channel.UNLIMITED)
private var isFinished = false
Expand All @@ -41,8 +43,16 @@ class ReaderRequestLooper(
}
}

/**
* Implementation like this is required because after coroutine
* is cancelled, the queue.receive() will lasts forever
*/
suspend fun getNextBytePack(): Main {
return queue.receive()
while (scope.isActive) {
val value = queue.tryReceive().getOrNull()
if (value != null) return value
}
throw CancellationException("Scope got cancelled during getting next byte pack")
}

override fun close() {
Expand Down
3 changes: 3 additions & 0 deletions components/bridge/connection/sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
implementation(projects.components.core.log)
implementation(projects.components.core.preference)
implementation(projects.components.core.storage)
implementation(projects.components.core.share)

implementation(projects.components.bridge.connection.transport.ble.api)
implementation(projects.components.bridge.connection.transport.ble.impl)
Expand Down Expand Up @@ -86,6 +87,8 @@ dependencies {
implementation(projects.components.filemngr.search.impl)
implementation(projects.components.filemngr.editor.api)
implementation(projects.components.filemngr.editor.impl)
implementation(projects.components.filemngr.download.api)
implementation(projects.components.filemngr.download.impl)

implementation(projects.components.newfilemanager.api)
implementation(projects.components.newfilemanager.impl)
Expand Down
1 change: 1 addition & 0 deletions components/core/progress/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ android.namespace = "com.flipperdevices.core.progress"
commonDependencies {
implementation(projects.components.core.buildKonfig)
implementation(libs.okio)
implementation(libs.kotlin.coroutines)
}

commonTestDependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.flipperdevices.core.progress

import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
import okio.Buffer
import okio.Sink
import okio.Source
Expand All @@ -20,7 +22,7 @@ suspend fun Source.copyWithProgress(

var totalBytesRead = 0L
val buffer = Buffer()
while (true) {
while (currentCoroutineContext().isActive) {
val readCount: Long = read(buffer, chunkSize)
if (readCount == -1L) break
sink.write(buffer, readCount)
Expand Down
11 changes: 9 additions & 2 deletions components/core/share/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
plugins {
id("flipper.android-lib")
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
id("flipper.anvil-multiplatform")
}

android.namespace = "com.flipperdevices.core.share"
Expand All @@ -13,7 +15,12 @@ android {
}
}

dependencies {
commonDependencies {
implementation(projects.components.core.di)
implementation(libs.okio)
}

androidDependencies {
implementation(projects.components.core.ktx)
implementation(libs.annotations)
implementation(libs.appcompat)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.flipperdevices.core.share

import android.content.Context
import com.flipperdevices.core.di.AppGraph
import com.squareup.anvil.annotations.ContributesBinding
import okio.Path.Companion.toOkioPath
import javax.inject.Inject

@ContributesBinding(AppGraph::class, PlatformShareHelper::class)
class AndroidShareHelper @Inject constructor(
private val context: Context
) : PlatformShareHelper {

override fun provideSharableFile(fileName: String): PlatformSharableFile {
val path = SharableFile(context, fileName).toOkioPath()
return PlatformSharableFile(path)
}

override fun shareFile(file: PlatformSharableFile, title: String) {
ShareHelper.shareFile(
context = context,
file = file,
text = title
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import androidx.core.content.FileProvider
import com.flipperdevices.core.ktx.jre.createNewFileWithMkDirs
import okio.Path.Companion.toOkioPath

object ShareHelper {
fun shareRawFile(context: Context, data: ByteArray, resId: Int, name: String) {
Expand All @@ -22,26 +23,35 @@ object ShareHelper {
resId = resId
)
}
fun shareFile(context: Context, file: SharableFile, resId: Int) {

fun shareFile(context: Context, file: PlatformSharableFile, text: String) {
val uri = FileProvider.getUriForFile(
context,
BuildConfig.SHARE_FILE_AUTHORITIES,
file,
file.name
file.path.toFile(),
file.path.name
)
val intent = Intent(Intent.ACTION_SEND, uri).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
putExtra(Intent.EXTRA_STREAM, uri)
}
val activityIntent = Intent.createChooser(
intent,
context.getString(resId, file.name)
text
).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(activityIntent)
}

fun shareFile(context: Context, file: SharableFile, resId: Int) {
shareFile(
context = context,
file = PlatformSharableFile(file.toOkioPath()),
text = context.getString(resId, file.name)
)
}

fun shareText(
context: Context,
title: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.flipperdevices.core.share

import okio.Path

/**
* This class should be only created via [PlatformShareHelper] due
* to permission restrictions. On android we can share files only in
* specified folder specified in AndroidManifest
*/
class PlatformSharableFile internal constructor(val path: Path)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.flipperdevices.core.share

interface PlatformShareHelper {
/**
* Provide file which can be shared later via [shareFile]
*
* If file with same name [fileName] exists, it will be deleted
*/
fun provideSharableFile(fileName: String): PlatformSharableFile

/**
* @param file file to share
* @param title text displayed as hint for user
*/
fun shareFile(file: PlatformSharableFile, title: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.flipperdevices.core.share

import com.flipperdevices.core.di.AppGraph
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject

@ContributesBinding(AppGraph::class, PlatformShareHelper::class)
class DesktopShareHelper @Inject constructor() : PlatformShareHelper {

override fun provideSharableFile(fileName: String): PlatformSharableFile {
error("The desktop feature is not yet implemented!")
}

override fun shareFile(file: PlatformSharableFile, title: String) {
error("The desktop feature is not yet implemented!")
}
}
15 changes: 15 additions & 0 deletions components/filemngr/download/api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
}

android.namespace = "com.flipperdevices.filemanager.download.api"

commonDependencies {
implementation(projects.components.core.ui.decompose)

implementation(libs.compose.ui)
implementation(libs.decompose)

implementation(libs.okio)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.flipperdevices.filemanager.download.api

import com.arkivanov.decompose.ComponentContext
import com.flipperdevices.ui.decompose.ScreenDecomposeComponent
import kotlinx.coroutines.flow.StateFlow
import okio.Path

abstract class DownloadDecomposeComponent(
componentContext: ComponentContext
) : ScreenDecomposeComponent(componentContext) {
abstract val isInProgress: StateFlow<Boolean>

abstract fun onCancel()

abstract fun download(
fullPath: Path,
size: Long
)

fun interface Factory {
operator fun invoke(
componentContext: ComponentContext,
): DownloadDecomposeComponent
}
}
48 changes: 48 additions & 0 deletions components/filemngr/download/impl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
plugins {
id("flipper.multiplatform-compose")
id("flipper.multiplatform-dependencies")
id("flipper.anvil-multiplatform")
id("kotlinx-serialization")
}
android.namespace = "com.flipperdevices.filemanager.download.impl"

commonDependencies {
implementation(projects.components.core.di)
implementation(projects.components.core.ktx)
implementation(projects.components.core.log)

implementation(projects.components.core.ui.lifecycle)
implementation(projects.components.core.ui.theme)
implementation(projects.components.core.ui.decompose)
implementation(projects.components.core.ui.ktx)
implementation(projects.components.core.ui.res)
implementation(projects.components.core.share)
implementation(projects.components.core.storage)
implementation(projects.components.core.progress)

implementation(projects.components.bridge.connection.feature.common.api)
implementation(projects.components.bridge.connection.transport.common.api)
implementation(projects.components.bridge.connection.feature.provider.api)
implementation(projects.components.bridge.connection.feature.storage.api)
implementation(projects.components.bridge.connection.feature.storageinfo.api)
implementation(projects.components.bridge.connection.feature.serialspeed.api)
implementation(projects.components.bridge.connection.feature.rpcinfo.api)
implementation(projects.components.bridge.dao.api)

implementation(projects.components.filemngr.download.api)

// Compose
implementation(libs.compose.ui)
implementation(libs.compose.tooling)
implementation(libs.compose.foundation)
implementation(libs.compose.material)

implementation(libs.decompose)
implementation(libs.kotlin.coroutines)
implementation(libs.essenty.lifecycle)
implementation(libs.essenty.lifecycle.coroutines)

implementation(libs.bundles.decompose)
implementation(libs.okio)
implementation(libs.kotlin.immutable.collections)
}
Loading

0 comments on commit 8b325fd

Please sign in to comment.