mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
style: format code
This commit is contained in:
@@ -3,5 +3,5 @@
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:name=".MyApplication"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<provider
|
||||
android:authorities="com.lifegpc.ehf.ClipboardImageProvider"
|
||||
android:name=".provider.ClipboardImageProvider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
android:authorities="com.lifegpc.ehf.ClipboardImageProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_config"/>
|
||||
android:resource="@xml/file_provider_config" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -3,8 +3,6 @@ package com.lifegpc.ehf
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.lifegpc.ehf.annotation.ChannelMethod
|
||||
import com.lifegpc.ehf.data.mmkv.SAFSettings
|
||||
@@ -13,18 +11,24 @@ import com.lifegpc.ehf.util.MethodChannelUtils
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.FileOutputStream
|
||||
import java.lang.Exception
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
private val safAuthorizationCode = 0x10086
|
||||
private var safAuthorizationResult: MethodChannel.Result? = null
|
||||
private var afterAuthSuccess:(()->Unit)?=null
|
||||
private var afterAuthSuccess: (() -> Unit)? = null
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
MethodChannelUtils.registerMethodChannel("lifegpc.eh_downloader_flutter/saf", flutterEngine, this)
|
||||
MethodChannelUtils.registerMethodChannel("lifegpc.eh_downloader_flutter/clipboard",flutterEngine,ClipboardUtils)
|
||||
MethodChannelUtils.registerMethodChannel(
|
||||
"lifegpc.eh_downloader_flutter/saf",
|
||||
flutterEngine,
|
||||
this
|
||||
)
|
||||
MethodChannelUtils.registerMethodChannel(
|
||||
"lifegpc.eh_downloader_flutter/clipboard",
|
||||
flutterEngine,
|
||||
ClipboardUtils
|
||||
)
|
||||
}
|
||||
|
||||
@ChannelMethod(responseManually = true)
|
||||
@@ -36,43 +40,43 @@ class MainActivity : FlutterActivity() {
|
||||
content: ByteArray
|
||||
) {
|
||||
this.safAuthorizationResult = channelResult
|
||||
if (!checkSafPermission()) {
|
||||
authSAF(channelResult) {
|
||||
doWriteFile(filename, dir, mimeType, content)
|
||||
channelResult.success(null)
|
||||
}
|
||||
} else {
|
||||
doWriteFile(filename, dir, mimeType,content)
|
||||
if (!checkSafPermission()) {
|
||||
authSAF(channelResult) {
|
||||
doWriteFile(filename, dir, mimeType, content)
|
||||
channelResult.success(null)
|
||||
}
|
||||
} else {
|
||||
doWriteFile(filename, dir, mimeType, content)
|
||||
channelResult.success(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doWriteFile(filename: String, dir: String, mimeType: String, content: ByteArray) {
|
||||
var documentDir = DocumentFile.fromTreeUri(this, Uri.parse(SAFSettings.authorizedUri))!!
|
||||
val pathPart=dir.split('/','\\')
|
||||
val pathPart = dir.split('/', '\\')
|
||||
pathPart.forEach {
|
||||
if (it.isNotEmpty()){
|
||||
if (it.isNotEmpty()) {
|
||||
documentDir = documentDir.createDirectory(it)!!
|
||||
}
|
||||
}
|
||||
|
||||
val filenameWithoutExtension=if (filename.indexOf('.')!=-1){
|
||||
filename.substring(0,filename.lastIndexOf('.'))
|
||||
}else{
|
||||
val filenameWithoutExtension = if (filename.indexOf('.') != -1) {
|
||||
filename.substring(0, filename.lastIndexOf('.'))
|
||||
} else {
|
||||
filename
|
||||
}
|
||||
val file=documentDir.createFile(mimeType,filenameWithoutExtension)!!
|
||||
val uri=file.uri
|
||||
val file = documentDir.createFile(mimeType, filenameWithoutExtension)!!
|
||||
val uri = file.uri
|
||||
contentResolver.openOutputStream(uri)!!.use {
|
||||
it.write(content)
|
||||
}
|
||||
}
|
||||
|
||||
private fun authSAF(result: MethodChannel.Result,onSuccess:(()->Unit)?=null) {
|
||||
private fun authSAF(result: MethodChannel.Result, onSuccess: (() -> Unit)? = null) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
startActivityForResult(intent, safAuthorizationCode)
|
||||
safAuthorizationResult = result
|
||||
this.afterAuthSuccess=onSuccess
|
||||
this.afterAuthSuccess = onSuccess
|
||||
}
|
||||
|
||||
private fun onSafAuthSuccess(data: Intent) {
|
||||
@@ -115,7 +119,7 @@ class MainActivity : FlutterActivity() {
|
||||
safAuthorizationResult?.error("Permission denied", null, null)
|
||||
safAuthorizationResult = null
|
||||
}
|
||||
afterAuthSuccess=null
|
||||
afterAuthSuccess = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@ package com.lifegpc.ehf
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
|
||||
class MyApplication:Application() {
|
||||
companion object{
|
||||
class MyApplication : Application() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private var mApplicationContext:Context?=null
|
||||
private var mApplicationContext: Context? = null
|
||||
|
||||
@JvmStatic
|
||||
@get:JvmName("_applicationContext")
|
||||
val applicationContext:Context
|
||||
val applicationContext: Context
|
||||
get() = mApplicationContext!!
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
mApplicationContext=applicationContext
|
||||
mApplicationContext = applicationContext
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,6 @@ package com.lifegpc.ehf.annotation
|
||||
* 否则,则会将实现方法的返回值返回给 [io.flutter.plugin.common.MethodChannel.Result]
|
||||
*/
|
||||
annotation class ChannelMethod(
|
||||
val methodName:String="",
|
||||
val responseManually:Boolean=false
|
||||
val methodName: String = "",
|
||||
val responseManually: Boolean = false
|
||||
)
|
||||
|
||||
@@ -3,6 +3,6 @@ package com.lifegpc.ehf.data.mmkv
|
||||
import com.dylanc.mmkv.MMKVOwner
|
||||
import com.dylanc.mmkv.mmkvString
|
||||
|
||||
object SAFSettings:MMKVOwner(mmapID = "SAFSettings") {
|
||||
object SAFSettings : MMKVOwner(mmapID = "SAFSettings") {
|
||||
var authorizedUri by mmkvString("")
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.lifegpc.ehf.provider
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.content.FileProvider
|
||||
|
||||
class ClipboardImageProvider:FileProvider()
|
||||
class ClipboardImageProvider : FileProvider()
|
||||
@@ -10,25 +10,27 @@ import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
object ClipboardUtils {
|
||||
private const val AUTHORITY="com.lifegpc.ehf.ClipboardImageProvider"
|
||||
@ChannelMethod
|
||||
fun copyImageToClipboard(mimeType:String,byteArray: ByteArray){
|
||||
val file= saveToImageCache(mimeType, byteArray)
|
||||
val uri=FileProvider.getUriForFile(MyApplication.applicationContext, AUTHORITY,file)
|
||||
private const val AUTHORITY = "com.lifegpc.ehf.ClipboardImageProvider"
|
||||
|
||||
val cbm=MyApplication.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipData=ClipData("image", arrayOf(mimeType),ClipData.Item(uri))
|
||||
@ChannelMethod
|
||||
fun copyImageToClipboard(mimeType: String, byteArray: ByteArray) {
|
||||
val file = saveToImageCache(mimeType, byteArray)
|
||||
val uri = FileProvider.getUriForFile(MyApplication.applicationContext, AUTHORITY, file)
|
||||
|
||||
val cbm =
|
||||
MyApplication.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipData = ClipData("image", arrayOf(mimeType), ClipData.Item(uri))
|
||||
cbm.setPrimaryClip(clipData)
|
||||
}
|
||||
|
||||
private fun saveToImageCache(mimeType:String,byteArray: ByteArray):File{
|
||||
val dir=File(MyApplication.applicationContext.filesDir,"images")
|
||||
if (!dir.exists()){
|
||||
private fun saveToImageCache(mimeType: String, byteArray: ByteArray): File {
|
||||
val dir = File(MyApplication.applicationContext.filesDir, "images")
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
val name=UUID.randomUUID().toString()
|
||||
val name = UUID.randomUUID().toString()
|
||||
|
||||
val file=File(dir,"$name.${mimeTypeToExtName(mimeType)}")
|
||||
val file = File(dir, "$name.${mimeTypeToExtName(mimeType)}")
|
||||
|
||||
file.outputStream().use {
|
||||
it.write(byteArray)
|
||||
@@ -37,10 +39,10 @@ object ClipboardUtils {
|
||||
return file
|
||||
}
|
||||
|
||||
private fun mimeTypeToExtName(mimeType: String)=when(mimeType){
|
||||
"image/png"->"png"
|
||||
"image/jpeg"->"png"
|
||||
"image/gif"->"gif"
|
||||
else->throw IllegalArgumentException("$mimeType is not supported")
|
||||
private fun mimeTypeToExtName(mimeType: String) = when (mimeType) {
|
||||
"image/png" -> "png"
|
||||
"image/jpeg" -> "png"
|
||||
"image/gif" -> "gif"
|
||||
else -> throw IllegalArgumentException("$mimeType is not supported")
|
||||
}
|
||||
}
|
||||
@@ -4,69 +4,84 @@ import android.util.Log
|
||||
import com.lifegpc.ehf.annotation.ChannelMethod
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.lang.Exception
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
object MethodChannelUtils {
|
||||
@JvmStatic
|
||||
fun registerMethodChannel(channelName: String, flutterEngine: FlutterEngine, obj: Any,ignoreNotImplemented:Boolean=true) {
|
||||
fun registerMethodChannel(
|
||||
channelName: String,
|
||||
flutterEngine: FlutterEngine,
|
||||
obj: Any,
|
||||
ignoreNotImplemented: Boolean = true
|
||||
) {
|
||||
val methodsMapping = mutableMapOf<String, Method>()
|
||||
val methods = obj::class.java.declaredMethods
|
||||
methods.forEach {
|
||||
val annotation = it.getAnnotation(ChannelMethod::class.java) ?: return@forEach
|
||||
val methodName = annotation.methodName.takeIf(String::isNotBlank) ?: it.name
|
||||
methodsMapping[methodName]=it
|
||||
methodsMapping[methodName] = it
|
||||
}
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName).setMethodCallHandler { call, result ->
|
||||
val invokeMethodName=call.method
|
||||
val targetMethod=methodsMapping[invokeMethodName]
|
||||
if (targetMethod==null){
|
||||
Log.w("MethodChannel","$channelName/$invokeMethodName not implemented.")
|
||||
if (!ignoreNotImplemented){
|
||||
MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
channelName
|
||||
).setMethodCallHandler { call, result ->
|
||||
val invokeMethodName = call.method
|
||||
val targetMethod = methodsMapping[invokeMethodName]
|
||||
if (targetMethod == null) {
|
||||
Log.w("MethodChannel", "$channelName/$invokeMethodName not implemented.")
|
||||
if (!ignoreNotImplemented) {
|
||||
result.notImplemented()
|
||||
}else{
|
||||
result.error("Not implemented",null,null)
|
||||
} else {
|
||||
result.error("Not implemented", null, null)
|
||||
}
|
||||
return@setMethodCallHandler
|
||||
}else{
|
||||
val argv=call.arguments?.takeIf { it is List<*> } as List<*>? ?:return@setMethodCallHandler result.error("Argument must be List",null,null)
|
||||
val argTypes=targetMethod.parameterTypes
|
||||
val targetArgc=argTypes.size
|
||||
} else {
|
||||
val argv = call.arguments?.takeIf { it is List<*> } as List<*>?
|
||||
?: return@setMethodCallHandler result.error("Argument must be List", null, null)
|
||||
val argTypes = targetMethod.parameterTypes
|
||||
val targetArgc = argTypes.size
|
||||
|
||||
val invokeTargetObject=if (Modifier.isStatic(targetMethod.modifiers)) null else obj
|
||||
targetMethod.isAccessible=true
|
||||
val invokeTargetObject =
|
||||
if (Modifier.isStatic(targetMethod.modifiers)) null else obj
|
||||
targetMethod.isAccessible = true
|
||||
|
||||
if (targetArgc==argv.size){
|
||||
if (targetArgc == argv.size) {
|
||||
try {
|
||||
val res=targetMethod.invoke(invokeTargetObject,*(argv.toTypedArray()))
|
||||
if (res is Unit){
|
||||
val res = targetMethod.invoke(invokeTargetObject, *(argv.toTypedArray()))
|
||||
if (res is Unit) {
|
||||
result.success(null)
|
||||
}else{
|
||||
} else {
|
||||
result.success(res)
|
||||
}
|
||||
}catch (e:Exception){
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
result.error("Error",e.toString(),e.stackTraceToString())
|
||||
result.error("Error", e.toString(), e.stackTraceToString())
|
||||
}
|
||||
}else if (targetArgc==argv.size+1&&argTypes[0]==MethodChannel.Result::class.java){
|
||||
} else if (targetArgc == argv.size + 1 && argTypes[0] == MethodChannel.Result::class.java) {
|
||||
try {
|
||||
val responseManually=targetMethod.getAnnotation(ChannelMethod::class.java)!!.responseManually
|
||||
val res=targetMethod.invoke(invokeTargetObject,result,*(argv.toTypedArray()))
|
||||
if (!responseManually){
|
||||
if (res is Unit){
|
||||
val responseManually =
|
||||
targetMethod.getAnnotation(ChannelMethod::class.java)!!.responseManually
|
||||
val res =
|
||||
targetMethod.invoke(invokeTargetObject, result, *(argv.toTypedArray()))
|
||||
if (!responseManually) {
|
||||
if (res is Unit) {
|
||||
result.success(null)
|
||||
}else{
|
||||
} else {
|
||||
result.success(res)
|
||||
}
|
||||
}
|
||||
}catch (e:Exception){
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
result.error("Error",e.toString(),e.stackTraceToString())
|
||||
result.error("Error", e.toString(), e.stackTraceToString())
|
||||
}
|
||||
}else{
|
||||
result.error("Error","Parameter count error, required: $targetArgc, found: $argv",null)
|
||||
} else {
|
||||
result.error(
|
||||
"Error",
|
||||
"Parameter count error, required: $targetArgc, found: $argv",
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<paths>
|
||||
<files-path
|
||||
name="images"
|
||||
path="images"/>
|
||||
path="images" />
|
||||
</paths>
|
||||
@@ -3,5 +3,5 @@
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
|
||||
Reference in New Issue
Block a user