Skip to content

Commit dd88660

Browse files
committed
add request install packages for github flavor only
1 parent c3a5b9e commit dd88660

8 files changed

Lines changed: 128 additions & 176 deletions

File tree

app/src/github/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<!-- Install YTDLnis packages permission for github releases only, as its an anti feature for F-droid builds. You have to manually install the downloaded apks and then restart the app to apply the changes-->
3+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
4+
</manifest>

app/src/main/java/com/deniscerri/ytdl/core/packages/PackageBase.kt

Lines changed: 29 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package com.deniscerri.ytdl.core.packages
22

33
import android.annotation.SuppressLint
4-
import android.app.DownloadManager
5-
import android.content.BroadcastReceiver
64
import android.content.Context
7-
import android.content.Intent
8-
import android.content.IntentFilter
95
import android.content.pm.PackageManager
106
import android.os.Build
117
import android.os.Environment
12-
import android.util.Log
13-
import androidx.activity.result.ActivityResultLauncher
148
import androidx.core.content.edit
15-
import androidx.core.net.toUri
16-
import androidx.documentfile.provider.DocumentFile
179
import androidx.preference.PreferenceManager
18-
import com.deniscerri.ytdl.R
1910
import com.deniscerri.ytdl.core.RuntimeManager
2011
import com.deniscerri.ytdl.core.ZipUtils
2112
import com.deniscerri.ytdl.database.models.GithubReleaseAsset
2213
import com.deniscerri.ytdl.util.FileUtil
2314
import com.google.gson.annotations.SerializedName
2415
import kotlinx.coroutines.Dispatchers
25-
import kotlinx.coroutines.delay
2616
import kotlinx.coroutines.isActive
2717
import kotlinx.coroutines.withContext
2818
import kotlinx.serialization.SerialName
@@ -197,86 +187,44 @@ abstract class PackageBase {
197187
)
198188
}
199189

200-
private var downloadReleaseId = -1L
201-
private val onDownloadReleaseComplete = object : BroadcastReceiver() {
202-
@SuppressLint("Range")
203-
override fun onReceive(context: Context?, intent: Intent) {
204-
if (context == null) return;
205-
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
206-
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
207-
208-
if (id == downloadReleaseId) {
209-
context.unregisterReceiver(this)
210-
211-
val query = DownloadManager.Query().setFilterById(id)
212-
val cursor = downloadManager.query(query)
213-
214-
if (cursor.moveToFirst()) {
215-
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
216-
if (status == DownloadManager.STATUS_SUCCESSFUL) {
217-
val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
218-
FileUtil.openFileIntent(context, localUri)
219-
}
220-
}
221-
cursor.close()
222-
}
223-
}
224-
}
225-
226190
@SuppressLint("UnspecifiedRegisterReceiverFlag", "Range")
227-
suspend fun downloadReleaseApk(context: Context, release: PackageRelease, onProgress: (Long) -> Unit) : Result<Unit> {
191+
suspend fun downloadReleaseApk(release: PackageRelease, onProgress: (Long) -> Unit) : Result<File> {
228192
return withContext(Dispatchers.IO) {
229193
try {
230-
val releaseVersion = release.assets.first()
231-
232-
val uri = releaseVersion.browser_download_url.toUri()
233-
Environment
234-
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
235-
.mkdirs()
236-
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
237-
downloadReleaseId = downloadManager.enqueue(
238-
DownloadManager.Request(uri)
239-
.setAllowedNetworkTypes(
240-
DownloadManager.Request.NETWORK_WIFI or
241-
DownloadManager.Request.NETWORK_MOBILE
242-
)
243-
.setAllowedOverRoaming(true)
244-
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
245-
.setTitle(releaseVersion.name)
246-
.setDescription(context.getString(R.string.downloading_update))
247-
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, releaseVersion.name)
248-
)
249-
250-
var downloading = true
251-
while (downloading) {
252-
val query = DownloadManager.Query().setFilterById(downloadReleaseId)
253-
val cursor = downloadManager.query(query)
254-
255-
if (cursor.moveToFirst()) {
256-
val bytesDownloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
257-
val totalBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
258-
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
259-
260-
if (status == DownloadManager.STATUS_SUCCESSFUL) {
261-
downloading = false
262-
}
194+
File(FileUtil.getDefaultApksPath()).mkdirs()
195+
val tempApk = File(FileUtil.getDefaultApksPath(), "${packageFolderName}_${release.version.replace(".", "")}.apk")
196+
197+
val request = Request.Builder()
198+
.url(release.assets.first().browser_download_url)
199+
.build()
263200

264-
if (totalBytes > 0) {
265-
val progress = (bytesDownloaded * 100L / totalBytes)
201+
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdirs()
202+
val response = sharedClient.newCall(request).execute()
203+
if (!response.isSuccessful) {
204+
return@withContext Result.failure(Throwable(response.body.string()))
205+
}
206+
207+
val body = response.body
208+
val totalBytes = body.contentLength()
209+
var bytesDownloaded = 0L
210+
211+
body.byteStream().use { inputStream ->
212+
tempApk.outputStream().use { outputStream ->
213+
val buffer = ByteArray(8192)
214+
var bytesRead: Int
215+
216+
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
217+
if (!isActive) throw CancellationException("Download cancelled by user")
218+
219+
outputStream.write(buffer, 0, bytesRead)
220+
bytesDownloaded += bytesRead
221+
val progress = ((bytesDownloaded * 100) / totalBytes)
266222
onProgress(progress)
267223
}
268224
}
269-
cursor.close()
270-
delay(500)
271225
}
272226

273-
val intentFilter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
274-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
275-
context.registerReceiver(onDownloadReleaseComplete, intentFilter, Context.RECEIVER_EXPORTED)
276-
} else {
277-
context.registerReceiver(onDownloadReleaseComplete, intentFilter)
278-
}
279-
Result.success(Unit)
227+
Result.success(tempApk)
280228
} catch (e: Exception) {
281229
Result.failure(e)
282230
}

app/src/main/java/com/deniscerri/ytdl/ui/more/settings/downloading/DownloadSettingsModule.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ object DownloadSettingsModule : SettingModule {
116116
if (!scheduler.canSchedule() && Build.VERSION.SDK_INT >= 31){
117117
Intent().also { intent ->
118118
intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
119-
context.startActivity(intent)
119+
host.getHostContext().startActivity(intent)
120120
}
121121
allowChange = false
122122
}
@@ -135,7 +135,7 @@ object DownloadSettingsModule : SettingModule {
135135
if (!scheduler.canSchedule() && Build.VERSION.SDK_INT >= 31){
136136
Intent().also { intent ->
137137
intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
138-
context.startActivity(intent)
138+
host.getHostContext().startActivity(intent)
139139
}
140140
allowChange = false
141141
}else{

app/src/main/java/com/deniscerri/ytdl/ui/more/settings/updating/PackagesFragment.kt

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.deniscerri.ytdl.ui.more.settings.updating
22

33
import android.annotation.SuppressLint
4-
import android.app.Activity
54
import android.app.AlertDialog
65
import android.content.DialogInterface
76
import android.content.Intent
87
import android.content.SharedPreferences
98
import android.net.Uri
9+
import android.os.Build
1010
import android.os.Bundle
1111
import android.text.method.LinkMovementMethod
1212
import android.util.DisplayMetrics
@@ -20,7 +20,6 @@ import android.widget.Toast
2020
import androidx.activity.result.contract.ActivityResultContracts
2121
import androidx.core.content.FileProvider
2222
import androidx.core.view.isVisible
23-
import androidx.documentfile.provider.DocumentFile
2423
import androidx.fragment.app.Fragment
2524
import androidx.lifecycle.lifecycleScope
2625
import androidx.preference.PreferenceManager
@@ -51,10 +50,8 @@ import kotlinx.coroutines.Dispatchers
5150
import kotlinx.coroutines.Job
5251
import kotlinx.coroutines.launch
5352
import kotlinx.coroutines.withContext
54-
import androidx.core.net.toUri
55-
import com.anggrayudi.storage.file.toRawFile
5653
import com.deniscerri.ytdl.core.packages.Deno
57-
import java.io.File
54+
import com.deniscerri.ytdl.util.Extensions.hasPermission
5855

5956

6057
class PackagesFragment : Fragment(), PackagesAdapter.OnItemClickListener, PackageReleaseAdapter.OnItemClickListener {
@@ -231,7 +228,7 @@ class PackagesFragment : Fragment(), PackagesAdapter.OnItemClickListener, Packag
231228

232229
tmpDownloadJob = lifecycleScope.launch {
233230
val instance = tmpItem!!.getInstance()
234-
val fileResp = instance.downloadReleaseApk(requireContext(), item) { progress ->
231+
val fileResp = instance.downloadReleaseApk(item) { progress ->
235232
lifecycleScope.launch {
236233
withContext(Dispatchers.Main) {
237234
positiveButton.text = "$progress%"
@@ -248,11 +245,27 @@ class PackagesFragment : Fragment(), PackagesAdapter.OnItemClickListener, Packag
248245
}
249246
}
250247

251-
fileResp.onSuccess {
248+
fileResp.onSuccess { file ->
252249
lifecycleScope.launch {
253250
withContext(Dispatchers.Main) {
254251
view.dismiss()
255-
Snackbar.make(requireView(), getString(R.string.install_downloaded_file), Snackbar.LENGTH_LONG).show()
252+
253+
val canRequestPackageInstalls = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
254+
android.Manifest.permission.REQUEST_INSTALL_PACKAGES.hasPermission(requireContext())
255+
} else {
256+
true
257+
}
258+
259+
if (canRequestPackageInstalls) {
260+
val contentUri = FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".fileprovider", file)
261+
val intent = Intent(Intent.ACTION_VIEW).apply {
262+
setDataAndType(contentUri, "application/vnd.android.package-archive")
263+
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
264+
}
265+
installLauncher.launch(intent)
266+
} else {
267+
Snackbar.make(requireView(), getString(R.string.install_downloaded_file), Snackbar.LENGTH_LONG).show()
268+
}
256269
}
257270
}
258271
}

app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.animation.ValueAnimator
44
import android.annotation.SuppressLint
55
import android.app.Dialog
66
import android.content.Context
7+
import android.content.pm.PackageManager
78
import android.content.res.Resources
89
import android.graphics.Bitmap
910
import android.graphics.Canvas
@@ -16,6 +17,7 @@ import android.graphics.drawable.shapes.OvalShape
1617
import android.media.MediaMetadataRetriever
1718
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
1819
import android.net.Uri
20+
import android.os.Build
1921
import android.text.Editable
2022
import android.text.Spanned
2123
import android.text.TextWatcher
@@ -697,4 +699,21 @@ object Extensions {
697699
null
698700
}
699701
}
702+
703+
fun String.hasPermission(context: Context) : Boolean {
704+
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
705+
context.packageManager.getPackageInfo(
706+
context.packageName,
707+
PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong())
708+
)
709+
} else {
710+
@Suppress("DEPRECATION")
711+
context.packageManager.getPackageInfo(
712+
context.packageName,
713+
PackageManager.GET_PERMISSIONS
714+
)
715+
}
716+
717+
return packageInfo.requestedPermissions?.contains(this) ?: false
718+
}
700719
}

app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ object FileUtil {
363363
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + File.separator + "YTDLnis/Command"
364364
}
365365

366+
fun getDefaultApksPath() : String {
367+
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + File.separator + "YTDLnis/Apks"
368+
}
369+
366370
fun getDefaultApplicationPath() : String {
367371
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + File.separator + "YTDLnis"
368372
}

app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,14 @@ import android.animation.ObjectAnimator
55
import android.animation.ValueAnimator
66
import android.annotation.SuppressLint
77
import android.app.Activity
8-
import android.app.DownloadManager
9-
import android.content.BroadcastReceiver
108
import android.content.ClipData
119
import android.content.ClipboardManager
1210
import android.content.Context
1311
import android.content.DialogInterface
1412
import android.content.Intent
15-
import android.content.IntentFilter
1613
import android.content.SharedPreferences
1714
import android.net.Uri
1815
import android.os.Build
19-
import android.os.Environment
2016
import android.text.Editable
2117
import android.text.TextWatcher
2218
import android.text.format.DateFormat
@@ -51,7 +47,6 @@ import androidx.core.view.isVisible
5147
import androidx.core.widget.doAfterTextChanged
5248
import androidx.core.widget.doOnTextChanged
5349
import androidx.fragment.app.FragmentManager
54-
import androidx.lifecycle.LifecycleCoroutineScope
5550
import androidx.lifecycle.LifecycleOwner
5651
import androidx.lifecycle.lifecycleScope
5752
import androidx.preference.PreferenceManager
@@ -72,6 +67,7 @@ import com.deniscerri.ytdl.ui.downloadcard.VideoCutListener
7267
import com.deniscerri.ytdl.util.Extensions.createBadge
7368
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
7469
import com.deniscerri.ytdl.util.Extensions.getMediaDuration
70+
import com.deniscerri.ytdl.util.Extensions.hasPermission
7571
import com.deniscerri.ytdl.util.Extensions.toStringDuration
7672
import com.google.android.material.badge.BadgeDrawable
7773
import com.google.android.material.badge.BadgeUtils
@@ -2745,7 +2741,7 @@ object UiUtil {
27452741
val lifecycleScope = lifecycleOwner.lifecycleScope
27462742

27472743
tmpDownloadJob = lifecycleScope.launch {
2748-
val fileResp = updateUtil.downloadReleaseApk(context, v) { progress ->
2744+
val fileResp = updateUtil.downloadReleaseApk(v) { progress ->
27492745
lifecycleScope.launch {
27502746
withContext(Dispatchers.Main) {
27512747
positiveButton.text = "$progress%"
@@ -2766,15 +2762,27 @@ object UiUtil {
27662762
}
27672763
}
27682764

2769-
fileResp.onSuccess {
2765+
fileResp.onSuccess { file ->
27702766
lifecycleScope.launch {
27712767
withContext(Dispatchers.Main) {
2768+
val canRequestPackageInstalls = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
2769+
android.Manifest.permission.REQUEST_INSTALL_PACKAGES.hasPermission(context)
2770+
} else {
2771+
true
2772+
}
2773+
2774+
if (canRequestPackageInstalls) {
2775+
val contentUri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", file)
2776+
val intent = Intent(Intent.ACTION_VIEW).apply {
2777+
setDataAndType(contentUri, "application/vnd.android.package-archive")
2778+
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
2779+
}
2780+
context.startActivity(intent)
2781+
} else {
2782+
Snackbar.make(context.findViewById(R.id.frame_layout), context.getString(R.string.install_downloaded_file), Snackbar.LENGTH_LONG).show()
2783+
}
2784+
27722785
view.dismiss()
2773-
Snackbar.make(
2774-
context.findViewById(R.id.frame_layout),
2775-
context.getString(R.string.install_downloaded_file),
2776-
Snackbar.LENGTH_LONG
2777-
).show()
27782786
}
27792787
}
27802788
}

0 commit comments

Comments
 (0)