feat: 对齐常驻商城与七鱼客服交互
- 同步商城 H5 登录态与原生 token - 完善商城/原生 tab 切换、动画及返回交互 - 修复七鱼客服键盘避让和状态栏恢复 - 调整开屏跳过按钮与登录入口交互
This commit is contained in:
1 parent
b4343ea528
commit
3b089e2205
37 files changed
+2970
-375
No files matched your search
@@ -3,6 +3,9 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<application
|
||||
@@ -55,6 +58,16 @@
|
||||
android:name="UMENG_MESSAGE_SECRET"
|
||||
android:value="${UMENG_MESSAGE_SECRET}" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/web_capture_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
package com.jinzhi.app
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.content.FileProvider
|
||||
import com.umeng.commonsdk.UMConfigure
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
companion object {
|
||||
private const val WEB_FILE_PICKER_CHANNEL = "com.jinzhi.app/web_file_picker"
|
||||
private const val WEB_FILE_PICKER_REQUEST_CODE = 9814
|
||||
}
|
||||
|
||||
private var pendingFilePickerResult: MethodChannel.Result? = null
|
||||
private var pendingCapturedFile: File? = null
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "jinzhi/privacy")
|
||||
@@ -17,5 +34,140 @@ class MainActivity : FlutterActivity() {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, WEB_FILE_PICKER_CHANNEL)
|
||||
.setMethodCallHandler { call, result ->
|
||||
if (call.method != "pickFiles") {
|
||||
result.notImplemented()
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
openWebFilePicker(call, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openWebFilePicker(call: MethodCall, result: MethodChannel.Result) {
|
||||
if (pendingFilePickerResult != null) {
|
||||
result.error("picker_in_progress", "A file picker is already open.", null)
|
||||
return
|
||||
}
|
||||
val acceptTypes = call.argument<List<String>>("acceptTypes")
|
||||
.orEmpty().map(String::trim).filter(String::isNotEmpty).distinct()
|
||||
val allowMultiple = call.argument<Boolean>("allowMultiple") ?: false
|
||||
val captureEnabled = call.argument<Boolean>("captureEnabled") ?: false
|
||||
val isQiyuSession = call.argument<Boolean>("isQiyuSession") ?: false
|
||||
|
||||
if (captureEnabled && !allowMultiple) {
|
||||
captureIntentFor(acceptTypes)?.let { intent ->
|
||||
pendingFilePickerResult = result
|
||||
launchWebFilePicker(intent, result)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (isQiyuSession && captureEnabled && isImageOrVideoRequest(acceptTypes)) {
|
||||
val captureIntent = captureIntentFor(acceptTypes)
|
||||
if (captureIntent != null) {
|
||||
pendingFilePickerResult = result
|
||||
val chooser = Intent.createChooser(
|
||||
fileContentIntent(acceptTypes, allowMultiple),
|
||||
"选择操作",
|
||||
).putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(captureIntent))
|
||||
launchWebFilePicker(chooser, result)
|
||||
return
|
||||
}
|
||||
}
|
||||
pendingFilePickerResult = result
|
||||
launchWebFilePicker(
|
||||
Intent.createChooser(fileContentIntent(acceptTypes, allowMultiple), "选择文件"),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
private fun fileContentIntent(acceptTypes: List<String>, allowMultiple: Boolean) =
|
||||
Intent(Intent.ACTION_GET_CONTENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple)
|
||||
.apply {
|
||||
if (acceptTypes.size == 1) type = acceptTypes.first()
|
||||
else {
|
||||
type = "*/*"
|
||||
if (acceptTypes.isNotEmpty()) {
|
||||
putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isImageOrVideoRequest(acceptTypes: List<String>): Boolean {
|
||||
if (acceptTypes.isEmpty()) return false
|
||||
val onlyImages = acceptTypes.all {
|
||||
it.equals("image/*", ignoreCase = true) || it.startsWith("image/", ignoreCase = true)
|
||||
}
|
||||
val onlyVideos = acceptTypes.all {
|
||||
it.equals("video/*", ignoreCase = true) || it.startsWith("video/", ignoreCase = true)
|
||||
}
|
||||
return onlyImages || onlyVideos
|
||||
}
|
||||
|
||||
private fun captureIntentFor(acceptTypes: List<String>): Intent? {
|
||||
val acceptsVideo = acceptTypes.any {
|
||||
it.equals("video/*", ignoreCase = true) || it.startsWith("video/", ignoreCase = true)
|
||||
}
|
||||
val acceptsImage = acceptTypes.isEmpty() || acceptTypes.any {
|
||||
it.equals("image/*", ignoreCase = true) || it.startsWith("image/", ignoreCase = true)
|
||||
}
|
||||
val action = when {
|
||||
acceptsVideo && !acceptsImage -> MediaStore.ACTION_VIDEO_CAPTURE
|
||||
acceptsImage -> MediaStore.ACTION_IMAGE_CAPTURE
|
||||
else -> return null
|
||||
}
|
||||
val directory = File(cacheDir, "web_captures").apply { mkdirs() }
|
||||
val suffix = if (action == MediaStore.ACTION_VIDEO_CAPTURE) ".mp4" else ".jpg"
|
||||
val output = File.createTempFile("web_", suffix, directory)
|
||||
val uri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", output)
|
||||
pendingCapturedFile = output
|
||||
return Intent(action)
|
||||
.putExtra(MediaStore.EXTRA_OUTPUT, uri)
|
||||
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.apply { clipData = ClipData.newRawUri("web_capture", uri) }
|
||||
}
|
||||
|
||||
private fun launchWebFilePicker(intent: Intent, result: MethodChannel.Result) {
|
||||
try {
|
||||
startActivityForResult(intent, WEB_FILE_PICKER_REQUEST_CODE)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
pendingFilePickerResult = null
|
||||
pendingCapturedFile?.delete()
|
||||
pendingCapturedFile = null
|
||||
result.success(emptyList<String>())
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Android, retained for FlutterActivity compatibility")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode != WEB_FILE_PICKER_REQUEST_CODE) return
|
||||
val result = pendingFilePickerResult ?: return
|
||||
pendingFilePickerResult = null
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
pendingCapturedFile?.delete()
|
||||
pendingCapturedFile = null
|
||||
result.success(emptyList<String>())
|
||||
return
|
||||
}
|
||||
val capturedFile = pendingCapturedFile
|
||||
val hasCapturedFile = capturedFile?.length()?.let { it > 0L } == true
|
||||
val uris = buildList {
|
||||
if (hasCapturedFile) {
|
||||
add(FileProvider.getUriForFile(
|
||||
this@MainActivity, "$packageName.fileprovider", capturedFile!!,
|
||||
).toString())
|
||||
} else {
|
||||
data?.data?.let { add(it.toString()) }
|
||||
data?.clipData?.let { clip ->
|
||||
for (index in 0 until clip.itemCount) add(clip.getItemAt(index).uri.toString())
|
||||
}
|
||||
}
|
||||
}.distinct()
|
||||
if (!hasCapturedFile) capturedFile?.delete()
|
||||
pendingCapturedFile = null
|
||||
result.success(uris)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<cache-path name="web_captures" path="web_captures/" />
|
||||
</paths>
|
||||
Reference in new issue
Block a user