From 7cec8e3312345eb2098f2cda6ad75da92d7b118e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 10 Mar 2024 19:03:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=AA=8C=E8=AF=81=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E3=80=81=E9=80=80=E5=87=BA=E5=BA=94=E7=94=A8&?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=A3=81=E5=8A=9B=E9=93=BE=E6=8E=A5=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 42 ++-- .../github/zerorooot/nap511/MainActivity.kt | 103 +++++++--- .../nap511/activity/OfflineTaskActivity.kt | 184 ++++++++++++++++++ .../zerorooot/nap511/screen/AlertDialog.kt | 87 ++++++++- .../zerorooot/nap511/screen/FileScreen.kt | 6 - .../zerorooot/nap511/service/Sha1Service.kt | 13 +- app/src/main/res/drawable/android_exit.xml | 10 + .../drawable/baseline_add_moderator_24.xml | 10 + 9 files changed, 400 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/github/zerorooot/nap511/activity/OfflineTaskActivity.kt create mode 100644 app/src/main/res/drawable/android_exit.xml create mode 100644 app/src/main/res/drawable/baseline_add_moderator_24.xml diff --git a/app/build.gradle b/app/build.gradle index 3508520..ad335b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -104,6 +104,8 @@ dependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version") // LiveData implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version") + + implementation 'androidx.work:work-runtime-ktx:2.8.0' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fe6f350..09dfb8a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + - - - - - - - + android:theme="@android:style/Theme.Translucent.NoTitleBar" + tools:ignore="AppLinkUrlError"> @@ -45,11 +36,28 @@ - - - - + + + + + + + + + + + + + + ? = null + /* Context.APP_OPS_MANAGER */try { + appOpsClass = Class.forName(AppOpsManager::class.java.name) + val checkOpNoThrowMethod: Method = appOpsClass.getMethod( + CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, + String::class.java + ) + val opPostNotificationValue: Field = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION) + val value = opPostNotificationValue.get(Int::class.java) as Int + return checkOpNoThrowMethod.invoke( + mAppOps, + value, + uid, + pkg + ) as Int == AppOpsManager.MODE_ALLOWED + } catch (e: ClassNotFoundException) { + e.printStackTrace() + } catch (e: NoSuchMethodException) { + e.printStackTrace() + } catch (e: NoSuchFieldException) { + e.printStackTrace() + } catch (e: InvocationTargetException) { + e.printStackTrace() + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + return false + } + + /** + * 跳转到app的设置界面--开启通知 + * @param context + */ + private fun goToNotificationSetting(context: Context) { + val intent = Intent() + // android 8.0引导 + intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS") + intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } + @Composable private fun Init(cookie: String) { val fileViewModel by viewModels { @@ -71,31 +135,6 @@ class MainActivity : ComponentActivity() { cookie, this.application ) } - - - //处理输入链接 - if (intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_PROCESS_TEXT) { - val urlList = (intent.dataString ?: run { - intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT) - } ?: run { "" }).split("\n").filter { i -> - i.startsWith("http", true) || i.startsWith( - "ftp", - true - ) || i.startsWith("magnet", true) || i.startsWith("ed2k", true) - }.toList() - if (urlList.isNotEmpty()) { - offlineFileViewModel.addTask( - urlList, - DataStoreUtil.getData(ConfigUtil.defaultOfflineCid, ""), - true - ) - } else { - App.instance.toast("仅支持以http、ftp、magnet、ed2k开头的链接") - finishAndRemoveTask() - } - intent.action = "" - return - } //恢复因MyPhotoScreen而造成的isSystemBarsVisible为false的情况 var visible by remember { mutableStateOf(true) @@ -127,9 +166,11 @@ class MainActivity : ComponentActivity() { R.drawable.baseline_cloud_24 to "我的文件", R.drawable.baseline_cloud_download_24 to "离线下载", R.drawable.baseline_cloud_done_24 to "离线列表", + R.drawable.baseline_add_moderator_24 to "验证账号", R.drawable.baseline_web_24 to "网页版", R.drawable.ic_baseline_delete_24 to "回收站", R.drawable.baseline_settings_24 to "高级设置", + R.drawable.android_exit to "退出应用" ) ModalNavigationDrawer(gesturesEnabled = App.gesturesEnabled, @@ -160,6 +201,13 @@ class MainActivity : ComponentActivity() { "网页版" -> WebViewScreen() "回收站" -> RecycleScreen(recycleViewModel) "高级设置" -> SettingScreen() + "验证账号" -> { + App.captchaUrl = + "https://captchaapi.115.com/?ac=security_code&type=web&cb=Close911_" + System.currentTimeMillis() + CaptchaWebViewScreen() + } + + "退出应用" -> ExitApp() "captchaWebView" -> CaptchaWebViewScreen() "loginWebView" -> LoginWebViewScreen() "photo" -> { @@ -169,6 +217,7 @@ class MainActivity : ComponentActivity() { }) } + @SuppressLint("UnrememberedMutableState") @Composable private fun Login() { diff --git a/app/src/main/java/github/zerorooot/nap511/activity/OfflineTaskActivity.kt b/app/src/main/java/github/zerorooot/nap511/activity/OfflineTaskActivity.kt new file mode 100644 index 0000000..47c0653 --- /dev/null +++ b/app/src/main/java/github/zerorooot/nap511/activity/OfflineTaskActivity.kt @@ -0,0 +1,184 @@ +package github.zerorooot.nap511.activity + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.Data +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import github.zerorooot.nap511.R +import github.zerorooot.nap511.bean.BaseReturnMessage +import github.zerorooot.nap511.bean.SignBean +import github.zerorooot.nap511.util.App +import github.zerorooot.nap511.util.ConfigUtil +import github.zerorooot.nap511.util.DataStoreUtil +import okhttp3.FormBody +import okhttp3.OkHttpClient +import okhttp3.Request +import java.util.StringJoiner + + +class OfflineTaskActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_PROCESS_TEXT) { + val urlList = (intent.dataString ?: run { + intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT) + } ?: run { "" }).split("\n").filter { i -> + i.startsWith("http", true) || i.startsWith( + "ftp", true + ) || i.startsWith("magnet", true) || i.startsWith("ed2k", true) + }.toList() +// App.instance.toast(urlList.toString()) + val listType = object : TypeToken?>() {}.type + val list = Gson().toJson(urlList, listType) + println(list) + if (urlList.isNotEmpty()) { + val data: Data = + Data.Builder().putString("cookie", App.cookie).putString("list", list).build() + val request: OneTimeWorkRequest = + OneTimeWorkRequest.Builder(OfflineTaskWorker::class.java).setInputData(data) + .build() + WorkManager.getInstance(applicationContext).enqueue(request) + } else { + App.instance.toast("仅支持以http、ftp、magnet、ed2k开头的链接") + } + } + finishAndRemoveTask() + } +} + + +class OfflineTaskWorker( + appContext: Context, workerParams: WorkerParameters +) : Worker(appContext, workerParams) { + private val okHttpClient = OkHttpClient() + override fun doWork(): Result { + val cookie: String = inputData.getString("cookie").toString() + val listType = object : TypeToken?>() {}.type + val a: List = Gson().fromJson(inputData.getString("list").toString(), listType) + val addTaskData = addTask(a, cookie) + val message = addTaskData.getString("return").toString() + println(message) + toast(message) + return Result.success(addTaskData); + } + + + @SuppressLint("WrongConstant") + private fun toast(message: String) { + //渠道Id + val channelId = "toast" + //渠道名 + val channelName = "离线下载结果" + //渠道重要级 + val importance = NotificationManagerCompat.IMPORTANCE_MAX + //通知Id + val notificationId = 1 + + val notificationManager = + applicationContext.getSystemService(AppCompatActivity.NOTIFICATION_SERVICE) as NotificationManager + //创建通知渠道 + notificationManager.createNotificationChannel( + NotificationChannel( + channelId, + channelName, + importance + ) + ) + val notification = NotificationCompat.Builder(applicationContext, channelId) + .apply { + setSmallIcon(R.mipmap.ic_launcher) + setContentTitle("离线下载结果")//标题 + setAutoCancel(true) + setContentText(message) + setStyle(NotificationCompat.BigTextStyle().bigText(message)) + }.build() + notificationManager.notify(notificationId, notification) + } + + private fun addTask(urlList: List, cookie: String): Data { + val resultMessage = StringJoiner("\n") + val cid = DataStoreUtil.getData(ConfigUtil.defaultOfflineCid, "") + val downloadPath = setDownloadPath(cid, cookie) + if (!downloadPath.state) { + resultMessage.add("设置离线位置失败,默认保存到\"云下载\"目录") + } + val map = HashMap() + map["savepath"] = "" + map["wp_path_id"] = cid + map["uid"] = DataStoreUtil.getData(ConfigUtil.uid, "") + map["sign"] = getSign(cookie).sign + map["time"] = (System.currentTimeMillis() / 1000).toString() + urlList.forEachIndexed { index, s -> + map["url[$index]"] = s + } + val addTask = addTask(cookie, map) + val message = if (addTask.state) { + "任务添加成功" + } else { + "任务添加失败,${addTask.errorMsg}" + } + resultMessage.add(message) + return Data.Builder().putString("return", resultMessage.toString()).build() + } + + private fun addTask(cookie: String, map: HashMap): BaseReturnMessage { + val builder = FormBody.Builder() + map.forEach { (t, u) -> builder.add(t, u) } + val formBody = builder.build() + + val request: Request = + Request.Builder().url("https://115.com/web/lixian/?ct=lixian&ac=add_task_urls") + .addHeader("cookie", cookie) + .addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6" + ).post(formBody).build() + val response = okHttpClient.newCall(request).execute() + return Gson().fromJson( + response.body?.string(), BaseReturnMessage::class.java + ) + } + + private fun getSign(cookie: String): SignBean { + val request: Request = Request.Builder() + .url("https://115.com?ct=offline&ac=space&_=${System.currentTimeMillis() / 1000}") + .addHeader("cookie", cookie) +// .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6" + ).get().build() + val response = okHttpClient.newCall(request).execute() + return Gson().fromJson( + response.body?.string(), SignBean::class.java + ) + } + + private fun setDownloadPath(cid: String, cookie: String): BaseReturnMessage { + val formBody = FormBody.Builder().add("file_id", cid).build() + val request: Request = Request.Builder().url("https://webapi.115.com/offine/downpath") + .addHeader("cookie", cookie) + .addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6" + ).post(formBody).build() + val response = okHttpClient.newCall(request).execute() + return Gson().fromJson( + response.body?.string(), BaseReturnMessage::class.java + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/github/zerorooot/nap511/screen/AlertDialog.kt b/app/src/main/java/github/zerorooot/nap511/screen/AlertDialog.kt index 34ebe84..4299adc 100644 --- a/app/src/main/java/github/zerorooot/nap511/screen/AlertDialog.kt +++ b/app/src/main/java/github/zerorooot/nap511/screen/AlertDialog.kt @@ -1,5 +1,7 @@ package github.zerorooot.nap511.screen +import android.app.Activity +import android.app.Application import androidx.compose.animation.* import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -16,6 +18,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.TextRange @@ -28,11 +31,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import com.google.gson.JsonObject import github.zerorooot.nap511.R +import github.zerorooot.nap511.util.App import github.zerorooot.nap511.util.ConfigUtil import github.zerorooot.nap511.viewmodel.FileViewModel import github.zerorooot.nap511.viewmodel.OfflineFileViewModel import github.zerorooot.nap511.viewmodel.RecycleViewModel import kotlinx.coroutines.delay +import kotlin.system.exitProcess @Composable fun CreateFolderDialog(fileViewModel: FileViewModel, enter: (String) -> Unit) { @@ -105,6 +110,32 @@ fun RecyclePasswordDialog( enter = enter ) } +} + +@Composable +fun ExitApp() { + var isOpen by remember { + mutableStateOf(true) + } + + val activity = LocalContext.current as Activity + if (isOpen) { + InfoDialog( + onDismissRequest = { + isOpen = false + App.selectedItem = "我的文件" + }, + onConfirmation = { + activity.finish() + }, + dialogTitle = "是否离开Nap511?", + ) + } + + + + + } @Composable @@ -114,7 +145,11 @@ fun CookieDialog(enter: (String) -> Unit) { } if (isOpen) { - BaseDialog(title = "设置Cookie", label = "请输入Cookie", dismissButtonText = "通过网页登陆") { + BaseDialog( + title = "设置Cookie", + label = "请输入Cookie", + dismissButtonText = "通过网页登陆" + ) { enter.invoke(it) isOpen = false } @@ -283,6 +318,39 @@ private fun RadioButtonDialog( } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun InfoDialog( + onDismissRequest: () -> Unit, + onConfirmation: () -> Unit, + dialogTitle: String, +) { + AlertDialog( + title = { + Text(text = dialogTitle) + }, + onDismissRequest = { + onDismissRequest() + }, + confirmButton = { + Button(onClick = { + onConfirmation() + }) { + Text("确定") + } + }, + dismissButton = { + TextButton( + onClick = { + onDismissRequest() + } + ) { + Text("取消") + } + } + ) +} + @Composable private fun BaseDialog( title: String, @@ -370,11 +438,26 @@ private fun BaseDialog( @Preview fun aa() { // Aria2Dialog() - BaseDialog("title", "lable", "context") { + BaseDialog("title", "", "context") { } } +@Composable +@Preview +fun ab() { +// Aria2Dialog() + InfoDialog( + onDismissRequest = { }, + onConfirmation = { + println("Confirmation registered") // Add logic here to handle confirmation. + }, + dialogTitle = "Alert dialog example" + ) + +} + + @ExperimentalMaterial3Api @Composable @Preview diff --git a/app/src/main/java/github/zerorooot/nap511/screen/FileScreen.kt b/app/src/main/java/github/zerorooot/nap511/screen/FileScreen.kt index f27c476..0b7852e 100644 --- a/app/src/main/java/github/zerorooot/nap511/screen/FileScreen.kt +++ b/app/src/main/java/github/zerorooot/nap511/screen/FileScreen.kt @@ -14,9 +14,7 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.with import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -37,16 +35,13 @@ import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext @@ -65,7 +60,6 @@ import github.zerorooot.nap511.util.App import github.zerorooot.nap511.util.ConfigUtil import github.zerorooot.nap511.util.DataStoreUtil import github.zerorooot.nap511.viewmodel.FileViewModel -import kotlinx.coroutines.DelicateCoroutinesApi import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request diff --git a/app/src/main/java/github/zerorooot/nap511/service/Sha1Service.kt b/app/src/main/java/github/zerorooot/nap511/service/Sha1Service.kt index ebb4916..ddb3625 100644 --- a/app/src/main/java/github/zerorooot/nap511/service/Sha1Service.kt +++ b/app/src/main/java/github/zerorooot/nap511/service/Sha1Service.kt @@ -1,7 +1,6 @@ package github.zerorooot.nap511.service - import android.app.Service import android.content.Intent import android.os.Build @@ -39,13 +38,17 @@ class Sha1Service : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val command = intent!!.getStringExtra(ConfigUtil.command) val cookie = intent.getStringExtra("cookie")!! - val fileBeanList = - arrayListOf(Gson().fromJson(intent.getStringExtra("list"), FileBean::class.java)) - thread { - val downloadUrl = getDownloadUrl(fileBeanList, cookie) when (command) { ConfigUtil.sentToAria2 -> { + val fileBeanList = + arrayListOf( + Gson().fromJson( + intent.getStringExtra("list"), + FileBean::class.java + ) + ) + val downloadUrl = getDownloadUrl(fileBeanList, cookie) sendToAria2(downloadUrl) } } diff --git a/app/src/main/res/drawable/android_exit.xml b/app/src/main/res/drawable/android_exit.xml new file mode 100644 index 0000000..b65f789 --- /dev/null +++ b/app/src/main/res/drawable/android_exit.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/baseline_add_moderator_24.xml b/app/src/main/res/drawable/baseline_add_moderator_24.xml new file mode 100644 index 0000000..b2cdc2c --- /dev/null +++ b/app/src/main/res/drawable/baseline_add_moderator_24.xml @@ -0,0 +1,10 @@ + + +