Skip to content

Commit

Permalink
sync: smoother download (fixes #4589) (#4594)
Browse files Browse the repository at this point in the history
Co-authored-by: dogi <[email protected]>
  • Loading branch information
Okuro3499 and dogi committed Oct 10, 2024
1 parent 8f03b4f commit 5c0eb82
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 83 deletions.
1 change: 1 addition & 0 deletions .github/workflows/android-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ jobs:

- name: publish AAB to playstore fallback
if: github.event_name != 'workflow_dispatch' && matrix.build == 'lite' && steps.playstore.outcome == 'failure'
continue-on-error: true
uses: dogi/[email protected]
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
applicationId "org.ole.planet.myplanet"
minSdkVersion 26
targetSdkVersion 34
versionCode 2011
versionName "0.20.11"
versionCode 2012
versionName "0.20.12"
ndkVersion '21.3.6528147'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<uses-feature
android:name="android.hardware.camera"
Expand Down Expand Up @@ -124,6 +125,7 @@
android:resource="@xml/file_paths" />
</provider>
<service android:name=".datamanager.MyDownloadService"
android:exported="false" />
android:exported="false"
android:foregroundServiceType="dataSync" />
</application>
</manifest>
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml.lite
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<uses-feature
android:name="android.hardware.camera"
Expand Down Expand Up @@ -124,6 +125,7 @@
android:resource="@xml/file_paths" />
</provider>
<service android:name=".datamanager.MyDownloadService"
android:exported="false" />
android:exported="false"
android:foregroundServiceType="dataSync" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
package org.ole.planet.myplanet.datamanager

import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.ResponseBody
import org.ole.planet.myplanet.R
import org.ole.planet.myplanet.model.Download
import org.ole.planet.myplanet.model.RealmMyLibrary
import org.ole.planet.myplanet.ui.dashboard.DashboardActivity
import org.ole.planet.myplanet.utilities.Constants.PREFS_NAME
import org.ole.planet.myplanet.utilities.FileUtils
import org.ole.planet.myplanet.ui.dashboard.DashboardActivity.Companion.MESSAGE_PROGRESS
import org.ole.planet.myplanet.utilities.FileUtils.availableExternalMemorySize
import org.ole.planet.myplanet.utilities.FileUtils.externalMemoryAvailable
import org.ole.planet.myplanet.utilities.FileUtils.getFileNameFromUrl
import org.ole.planet.myplanet.utilities.FileUtils.getSDPathFromUrl
import org.ole.planet.myplanet.utilities.NotificationUtil
import org.ole.planet.myplanet.utilities.Utilities
import org.ole.planet.myplanet.utilities.Utilities.header
import retrofit2.Call
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.*
import kotlin.math.pow
import kotlin.math.roundToInt

class MyDownloadService(context: Context, params: WorkerParameters) : Worker(context, params) {
class MyDownloadService : Service() {
private var count = 0
private var data = ByteArray(1024 * 4)
private var outputFile: File? = null
Expand All @@ -42,69 +42,89 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
private var request: Call<ResponseBody>? = null
private var completeAll = false
private var fromSync = false

private val databaseService: DatabaseService by lazy {
DatabaseService(applicationContext)
DatabaseService(this)
}

private val mRealm: Realm by lazy {
databaseService.realmInstance
}

override fun doWork(): Result {
preferences = applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
override fun onBind(intent: Intent?): IBinder? {
return null
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

val urlsKey = inputData.getString("urls_key")
val settings = applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
urls = (settings.getStringSet(urlsKey, emptySet())?.toList() ?: return Result.failure()).toTypedArray()
val urlsKey = intent?.getStringExtra("urls_key") ?: "url_list_key"

fromSync = inputData.getBoolean("fromSync", false)
val urlSet = preferences?.getStringSet(urlsKey, emptySet())

if (urls.isEmpty()) {
return Result.failure()
if (urlSet.isNullOrEmpty()) {
stopSelf()
return START_NOT_STICKY
}

notificationBuilder = NotificationCompat.Builder(applicationContext, "11")
NotificationUtil.setChannel(notificationManager)
val notification = notificationBuilder?.setSmallIcon(R.mipmap.ic_launcher)
?.setContentTitle("OLE Download")?.setContentText("Downloading File...")
?.setAutoCancel(true)?.build()
notificationManager?.notify(0, notification)

for (i in urls.indices) {
url = urls[i]
currentIndex = i
initDownload(fromSync)
urls = urlSet.toTypedArray()
fromSync = intent?.getBooleanExtra("fromSync", false) == true

initNotification()

CoroutineScope(Dispatchers.IO).launch {
for (i in urls.indices) {
url = urls[i]
currentIndex = i
initDownload(fromSync)
}
}

return Result.success()
return START_STICKY
}

private fun initNotification() {
notificationBuilder = NotificationCompat.Builder(this, "11")
NotificationUtil.setChannel(notificationManager)
val notification = notificationBuilder?.setSmallIcon(R.mipmap.ic_launcher)
?.setContentTitle("OLE Download")
?.setContentText("Downloading File...")
?.setAutoCancel(true)
?.build()
startForeground(1, notification)
}

private fun initDownload(fromSync: Boolean) {
val retrofitInterface = ApiClient.client?.create(ApiInterface::class.java)
if (retrofitInterface != null) {
request = retrofitInterface.downloadFile(Utilities.header, url)
try {
val r = request?.execute()
if (r != null) {
if (r.code() == 200) {
val responseBody = r.body()
request = retrofitInterface.downloadFile(header, url)
val response = request?.execute()

when {
response == null -> {
downloadFailed("Null response from server", fromSync)
}
response.isSuccessful -> {
val responseBody = response.body()
if (!checkStorage(responseBody?.contentLength() ?: 0L)) {
responseBody?.let { downloadFile(it) }
}
} else {
downloadFiled(if (r.code() == 404) "File Not found " else "Connection failed", fromSync)
}
else -> {
downloadFailed(if (response.code() == 404) "File Not found" else "Connection failed (${response.code()})", fromSync)
}
}
} catch (e: IOException) {
e.printStackTrace()
e.localizedMessage?.let { downloadFiled(it, fromSync) }
e.printStackTrace()
e.localizedMessage?.let { downloadFailed(it, fromSync) }
}
} else {
downloadFailed("Network client initialization failed", fromSync)
}
}

private fun downloadFiled(message: String, fromSync: Boolean) {
private fun downloadFailed(message: String, fromSync: Boolean) {
notificationBuilder?.setContentText(message)
notificationManager?.notify(0, notificationBuilder?.build())
val d = Download()
Expand All @@ -118,7 +138,7 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
private fun downloadFile(body: ResponseBody?) {
val fileSize = body?.contentLength()
val bis: InputStream = BufferedInputStream(body?.byteStream(), 1024 * 8)
outputFile = FileUtils.getSDPathFromUrl(url)
outputFile = getSDPathFromUrl(url)
val output: OutputStream = FileOutputStream(outputFile)
var total: Long = 0
val startTime = System.currentTimeMillis()
Expand All @@ -135,7 +155,7 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
val progress = fileSize?.let { (total * 100 / it).toInt() } ?: 0
val currentTime = System.currentTimeMillis() - startTime
val download = Download()
download.fileName = FileUtils.getFileNameFromUrl(url)
download.fileName = getFileNameFromUrl(url)
download.totalFileSize = totalFileSize
if (currentTime > 1000 * timeCount) {
download.currentFileSize = current.toInt()
Expand All @@ -149,11 +169,11 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
}

private fun checkStorage(fileSize: Long): Boolean {
if (!FileUtils.externalMemoryAvailable()) {
downloadFiled("Download Failed : SD card Not available", fromSync)
if (!externalMemoryAvailable()) {
downloadFailed("Download Failed : SD card Not available", fromSync)
return true
} else if (fileSize > FileUtils.availableExternalMemorySize) {
downloadFiled("Download Failed : Not enough storage in SD card", fromSync)
} else if (fileSize > availableExternalMemorySize) {
downloadFailed("Download Failed : Not enough storage in SD card", fromSync)
return true
}
return false
Expand All @@ -168,31 +188,32 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
}

private fun sendNotification(download: Download) {
download.fileName = "Downloading : " + FileUtils.getFileNameFromUrl(url)
download.fileName = "Downloading : ${getFileNameFromUrl(url)}"
sendIntent(download, fromSync)
notificationBuilder?.setProgress(100, download.progress, false)
notificationBuilder?.setContentText("Downloading file " + download.currentFileSize + "/" + totalFileSize + " KB")
notificationBuilder?.setContentText("Downloading file ${download.currentFileSize}/$totalFileSize KB")
notificationManager?.notify(0, notificationBuilder?.build())
}

private fun sendIntent(download: Download, fromSync: Boolean) {
val intent = Intent(DashboardActivity.MESSAGE_PROGRESS)
val intent = Intent(MESSAGE_PROGRESS)
intent.putExtra("download", download)
intent.putExtra("fromSync", fromSync)
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}

private fun onDownloadComplete() {
if ((outputFile?.length() ?: 0) > 0) {
changeOfflineStatus()
}
val download = Download()
download.fileName = FileUtils.getFileNameFromUrl(url)
download.fileName = getFileNameFromUrl(url)
download.fileUrl = url
download.progress = 100
if (currentIndex == urls.size - 1) {
completeAll = true
download.completeAll = true
stopSelf()
}
sendIntent(download, fromSync)
notificationManager?.cancel(0)
Expand All @@ -202,7 +223,7 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
}

private fun changeOfflineStatus() {
val currentFileName = FileUtils.getFileNameFromUrl(url)
val currentFileName = getFileNameFromUrl(url)
mRealm.executeTransaction { realm: Realm ->
val matchingItems = realm.where(RealmMyLibrary::class.java)
.equalTo("resourceLocalAddress", currentFileName)
Expand All @@ -215,4 +236,16 @@ class MyDownloadService(context: Context, params: WorkerParameters) : Worker(con
}
}
}

companion object {
const val PREFS_NAME = "MyPrefsFile"

fun startService(context: Context, urlsKey: String, fromSync: Boolean) {
val intent = Intent(context, MyDownloadService::class.java).apply {
putExtra("urls_key", urlsKey)
putExtra("fromSync", fromSync)
}
context.startService(intent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ object DialogUtils {
}

override fun onFail() {
val url = arrayListOf(path)
val urls = arrayListOf(path)
if (progressDialog != null) {
progressDialog.setText(context.getString(R.string.downloading_file))
progressDialog.setCancelable(false)
progressDialog.show()
}
Utilities.openDownloadService(context, url, false)
MyDownloadService.startService(context, "$urls", false)
}
}, path)
}
Expand Down Expand Up @@ -247,7 +247,7 @@ object DialogUtils {
progressTitle.text = text
}
fun isShowing(): Boolean {
return dialog?.isShowing ?: false
return dialog?.isShowing == true
}

fun disableNegativeButton() {
Expand Down
23 changes: 5 additions & 18 deletions app/src/main/java/org/ole/planet/myplanet/utilities/Utilities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import android.util.Patterns
import android.webkit.MimeTypeMap
import android.widget.ImageView
import android.widget.Toast
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.bumptech.glide.Glide
import fisk.chipcloud.ChipCloudConfig
import org.ole.planet.myplanet.MainApplication.Companion.context
Expand All @@ -28,7 +25,6 @@ import java.math.BigInteger

object Utilities {
private var contextRef: WeakReference<Context>? = null
private var settings: SharedPreferences? = null

fun setContext(ctx: Context) {
contextRef = WeakReference(ctx.applicationContext)
Expand Down Expand Up @@ -60,19 +56,10 @@ object Utilities {
}

fun openDownloadService(context: Context?, urls: ArrayList<String>, fromSync: Boolean) {
settings = context?.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
settings?.edit()?.putStringSet("url_list", urls.toSet())?.apply()
val inputData = Data.Builder()
.putString("urls_key", "url_list")
.putBoolean("fromSync", fromSync)
.build()

val downloadWorkRequest = OneTimeWorkRequest.Builder(MyDownloadService::class.java)
.setInputData(inputData)
.build()

context?.let {
WorkManager.getInstance(it).enqueue(downloadWorkRequest)
context?.let { ctx ->
val preferences = ctx.getSharedPreferences(MyDownloadService.PREFS_NAME, Context.MODE_PRIVATE)
preferences.edit()?.putStringSet("url_list_key", urls.toSet())?.apply()
MyDownloadService.startService(ctx, "url_list_key", fromSync)
}
}

Expand Down Expand Up @@ -128,7 +115,7 @@ object Utilities {
val header: String
get() {
val settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
return "Basic " + Base64.encodeToString((settings.getString("url_user", "") + ":" + settings.getString("url_pwd", "")).toByteArray(), Base64.NO_WRAP)
return "Basic ${Base64.encodeToString(("${settings.getString("url_user", "")}:${ settings.getString("url_pwd", "") }").toByteArray(), Base64.NO_WRAP)}"
}

fun getUrl(): String {
Expand Down

0 comments on commit 5c0eb82

Please sign in to comment.