Skip to content

Commit

Permalink
[feature|optimize] Support #96; support opening OPML/XML backup files…
Browse files Browse the repository at this point in the history
…; optimize article list image display logic; introduce test code
  • Loading branch information
SkyD666 committed Dec 7, 2024
1 parent c58caa8 commit 2c4ced7
Show file tree
Hide file tree
Showing 19 changed files with 378 additions and 54 deletions.
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
minSdk = 24
targetSdk = 35
versionCode = 24
versionName = "2.1-beta13"
versionName = "2.1-beta14"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down Expand Up @@ -92,6 +92,12 @@ android {
"proguard-rules.pro"
)
}
create("benchmark") {
initWith(buildTypes.getByName("release"))
matchingFallbacks += listOf("release")
isDebuggable = false
applicationIdSuffix = ".benchmark"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
Expand Down
54 changes: 50 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,23 @@
android:supportsRtl="true"
android:theme="@style/Theme.AniVu.Pink"
tools:targetApi="31">
<profileable
android:shell="true"
tools:targetApi="29" />

<receiver
android:name="androidx.profileinstaller.ProfileInstallReceiver"
android:exported="true"
android:permission="android.permission.DUMP">
<intent-filter>
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION" />
</intent-filter>
</receiver>

<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge">
</service>
tools:node="merge" />

<activity
android:name=".ui.activity.MainActivity"
Expand All @@ -57,6 +69,7 @@
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
Expand Down Expand Up @@ -89,17 +102,51 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:pathPattern=".*\\.torrent" />
<data android:pathPattern=".*\\.xml" />
<data android:pathPattern=".*\\.opml" />
</intent-filter>
</activity>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:mimeType="application/x-bittorrent" />
<data android:mimeType="applications/x-bittorrent" />
<data android:mimeType="application/xml" />
<data android:mimeType="text/xml" />
<data android:mimeType="text/x-opml" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />

<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="application/x-bittorrent" />
<data android:mimeType="applications/x-bittorrent" />
<data android:mimeType="application/xml" />
<data android:mimeType="text/xml" />
<data android:mimeType="text/x-opml" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
</activity>
<activity
android:name=".ui.activity.PlayActivity"
android:autoRemoveFromRecents="true"
Expand All @@ -122,7 +169,6 @@
<data android:mimeType="audio/*" />
</intent-filter>
</activity>

<activity android:name=".ui.activity.CrashActivity" />

<provider
Expand Down
14 changes: 9 additions & 5 deletions app/src/main/java/com/skyd/anivu/ext/IOExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import java.security.NoSuchAlgorithmException
import java.util.LinkedList


fun Uri.copyTo(target: File): File {
fun Uri.copyTo(target: File): Long {
return appContext.contentResolver.openInputStream(this)!!.use { it.saveTo(target) }
}

Expand All @@ -52,6 +52,9 @@ fun Uri.fileName(): String? {
return name ?: path?.substringAfterLast("/")?.toDecodedUrl()
}

val Uri.type: String?
get() = appContext.contentResolver.getType(this)

fun String.openBrowser(context: Context) {
Uri.parse(this).openBrowser(context)
}
Expand Down Expand Up @@ -200,20 +203,21 @@ fun Uri.findFile(contentResolver: ContentResolver, name: String): Uri? {
return null
}

fun Uri.isLocal(): Boolean = URLUtil.isFileUrl(toString()) || URLUtil.isContentUrl(toString())
fun Uri.isLocal(): Boolean = toString().startsWith("/") ||
URLUtil.isFileUrl(toString()) ||
URLUtil.isContentUrl(toString())

fun Uri.isNetwork(): Boolean = URLUtil.isNetworkUrl(toString())

fun InputStream.saveTo(target: File): File {
fun InputStream.saveTo(target: File): Long {
val parentFile = target.parentFile
if (parentFile?.exists() == false) {
parentFile.mkdirs()
}
if (!target.exists()) {
target.createNewFile()
}
FileOutputStream(target).use { copyTo(it) }
return target
return FileOutputStream(target).use { copyTo(it) }
}

fun File.md5(): String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import android.content.Context
import android.os.Build
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.skyd.anivu.R
import com.skyd.anivu.config.Const
import com.skyd.anivu.ext.copyTo
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.fileName
import com.skyd.anivu.ext.getOrDefault
import com.skyd.anivu.ext.isLocal
import com.skyd.anivu.ext.validateFileName
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
import com.skyd.anivu.model.repository.download.bt.BtDownloadManager
import com.skyd.anivu.model.worker.download.isTorrentMimetype
Expand All @@ -27,9 +34,25 @@ object DownloadStarter {
}
val isMagnetOrTorrent =
url.startsWith("magnet:") || isTorrentMimetype(type) ||
Regex("^(http|https)://.*\\.torrent$").matches(url)
Regex("^(((http|https|file|content)://)|/).*\\.torrent$").matches(url)
if (isMagnetOrTorrent) {
BtDownloadManager.download(context, url, requestId = null)
var uri = url.toUri()
if (url.startsWith("/")) {
uri = uri.buildUpon().scheme("file").build()
}
if (uri.isLocal()) {
val newUrl = File(Const.TEMP_TORRENT_DIR, uri.fileName() ?: url.validateFileName())
if (uri.scheme == "content") {
if (uri.copyTo(newUrl) > 0) {
BtDownloadManager.download(context, newUrl.path, requestId = null)
}
} else {
File(url).copyTo(newUrl)
BtDownloadManager.download(context, newUrl.path, requestId = null)
}
} else {
BtDownloadManager.download(context, url, requestId = null)
}
} else {
DownloadManager.getInstance(context).download(
url = url,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,7 @@ class BtDownloadWorker(context: Context, parameters: WorkerParameters) :

private fun howToDownload(saveDir: File) = runBlocking {
sessionManager.apply {
val lastSessionParams = BtDownloadManager
.getSessionParams(link = torrentLink)
val lastSessionParams = BtDownloadManager.getSessionParams(link = torrentLink)
val sessionParams = if (lastSessionParams == null) SessionParams()
else SessionParams(lastSessionParams.data)

Expand Down Expand Up @@ -238,14 +237,19 @@ class BtDownloadWorker(context: Context, parameters: WorkerParameters) :
sessionManager.download(link, saveDir, flags)
},
onUnsupported = {
val tempTorrentFile = File(
Const.TEMP_TORRENT_DIR,
link.substringAfterLast('/').toDecodedUrl().validateFileName()
)
// May throw exceptions
hiltEntryPoint.retrofit.create(HttpService::class.java)
.requestGetResponseBody(link).execute().body()!!.byteStream()
.use { it.saveTo(tempTorrentFile) }
val tempTorrentFile = if (link.startsWith("file:") || link.startsWith("/")) {
File(link)
} else {
File(
Const.TEMP_TORRENT_DIR,
link.substringAfterLast('/').toDecodedUrl().validateFileName()
).apply {
// May throw exceptions
hiltEntryPoint.retrofit.create(HttpService::class.java)
.requestGetResponseBody(link).execute().body()!!.byteStream()
.use { it.saveTo(this) }
}
}
sessionManager.download(
TorrentInfo(tempTorrentFile), saveDir,
null, null, null,
Expand Down
54 changes: 28 additions & 26 deletions app/src/main/java/com/skyd/anivu/ui/activity/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.skyd.anivu.ui.activity

import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
Expand Down Expand Up @@ -40,7 +39,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
Expand All @@ -58,6 +56,10 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.skyd.anivu.R
import com.skyd.anivu.base.BaseComposeActivity
import com.skyd.anivu.ext.toDecodedUrl
import com.skyd.anivu.ext.type
import com.skyd.anivu.ui.activity.intenthandler.ImportOpmlIntentHandler
import com.skyd.anivu.ui.activity.intenthandler.OpenDownloadIntentHandler
import com.skyd.anivu.ui.activity.intenthandler.UrlDownloadIntentHandler
import com.skyd.anivu.ui.local.LocalNavController
import com.skyd.anivu.ui.screen.MAIN_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.MainScreen
Expand Down Expand Up @@ -125,6 +127,8 @@ import com.skyd.anivu.ui.screen.settings.data.importexport.exportopml.EXPORT_OPM
import com.skyd.anivu.ui.screen.settings.data.importexport.exportopml.ExportOpmlScreen
import com.skyd.anivu.ui.screen.settings.data.importexport.importopml.IMPORT_OPML_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.settings.data.importexport.importopml.ImportOpmlScreen
import com.skyd.anivu.ui.screen.settings.data.importexport.importopml.OPML_URL_KEY
import com.skyd.anivu.ui.screen.settings.data.importexport.importopml.openImportOpmlScreen
import com.skyd.anivu.ui.screen.settings.playerconfig.PLAYER_CONFIG_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.settings.playerconfig.PlayerConfigScreen
import com.skyd.anivu.ui.screen.settings.playerconfig.advanced.PLAYER_CONFIG_ADVANCED_SCREEN_ROUTE
Expand All @@ -137,7 +141,6 @@ import com.skyd.anivu.ui.screen.settings.transmission.TRANSMISSION_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.settings.transmission.TransmissionScreen
import com.skyd.anivu.ui.screen.settings.transmission.proxy.PROXY_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.settings.transmission.proxy.ProxyScreen
import com.skyd.downloader.notification.NotificationConst
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.serialization.json.Json

Expand Down Expand Up @@ -180,27 +183,25 @@ class MainActivity : BaseComposeActivity() {
private fun handleIntent(intent: Intent?, navController: NavController) {
intent ?: return
val data = intent.data
if (intent.extras?.containsKey(NotificationConst.KEY_DOWNLOAD_REQUEST_ID) == true) {
openDownloadScreen(navController = navController)
} else if (Intent.ACTION_VIEW == intent.action && data != null) {
val scheme = data.scheme
var url: String? = null
when (scheme) {
"magnet" -> url = data.toString()
"http", "https" -> {
val path = data.path
if (path != null && path.endsWith(".torrent")) {
url = data.toString()
}
}
}
if (url != null) {

listOf(
OpenDownloadIntentHandler {
openDownloadScreen(navController = navController)
},
UrlDownloadIntentHandler { downloadUrl ->
openDownloadScreen(
navController = navController,
downloadLink = url,
downloadLink = downloadUrl,
mimetype = data?.type,
)
},
ImportOpmlIntentHandler { opmlUrl ->
openImportOpmlScreen(
navController = navController,
opmlUrl = opmlUrl,
)
}
}
).forEach { it.handle(intent) }
}
}

Expand Down Expand Up @@ -277,7 +278,12 @@ private fun MainNavHost() {
composable(route = BEHAVIOR_SCREEN_ROUTE) { BehaviorScreen() }
composable(route = AUTO_DELETE_SCREEN_ROUTE) { AutoDeleteScreen() }
composable(route = EXPORT_OPML_SCREEN_ROUTE) { ExportOpmlScreen() }
composable(route = IMPORT_OPML_SCREEN_ROUTE) { ImportOpmlScreen() }
composable(
route = IMPORT_OPML_SCREEN_ROUTE,
arguments = listOf(navArgument(OPML_URL_KEY) { nullable = true }),
) {
ImportOpmlScreen(opmlUrl = it.arguments?.getString(OPML_URL_KEY))
}
composable(route = IMPORT_EXPORT_SCREEN_ROUTE) { ImportExportScreen() }
composable(route = DATA_SCREEN_ROUTE) { DataScreen() }
composable(route = PLAYER_CONFIG_SCREEN_ROUTE) { PlayerConfigScreen() }
Expand Down Expand Up @@ -372,17 +378,13 @@ private fun MainContent(onHandleIntent: @Composable () -> Unit) {
MainNavHost()
onHandleIntent()
} else {
val context = LocalContext.current
RequestStoragePermissionScreen(
shouldShowRationale = false,
onPermissionRequest = {
permissionGranted = Environment.isExternalStorageManager()
if (!permissionGranted) {
permissionRequester.launch(
Intent(
ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION,
Uri.parse("package:${context.packageName}"),
)
Intent(ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
)
}
},
Expand Down
Loading

0 comments on commit 2c4ced7

Please sign in to comment.