commit
3fbdfe682f
@ -1,5 +1,5 @@ |
||||
{ |
||||
"uploadUrl": "http://sy.mgz6.cc/shuyuan,{\"method\":\"POST\",\"body\": {\"file\": \"fileRequest\"},\"type\": \"multipart/form-data\"}", |
||||
"uploadUrl": "https://sy.mgz6.cc/shuyuan,{\"method\":\"POST\",\"body\": {\"file\": \"fileRequest\"},\"type\": \"multipart/form-data\"}", |
||||
"downloadUrlRule": "$.data@js:if (result == '') \n '' \n else \n 'https://shuyuan.mgz6.cc/shuyuan/' + result", |
||||
"summary": "有效期2天" |
||||
} |
@ -1,225 +0,0 @@ |
||||
package io.legado.app.utils |
||||
|
||||
import android.content.Context |
||||
import android.database.Cursor |
||||
import android.net.Uri |
||||
import android.provider.DocumentsContract |
||||
import androidx.documentfile.provider.DocumentFile |
||||
import io.legado.app.exception.NoStackTraceException |
||||
import splitties.init.appCtx |
||||
import java.io.File |
||||
import java.nio.charset.Charset |
||||
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate") |
||||
object DocumentUtils { |
||||
|
||||
fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean { |
||||
val parent = getDirDocument(root, *subDirs) ?: return false |
||||
return parent.findFile(fileName)?.exists() ?: false |
||||
} |
||||
|
||||
fun delete(root: DocumentFile, fileName: String, vararg subDirs: String) { |
||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs) |
||||
parent?.findFile(fileName)?.delete() |
||||
} |
||||
|
||||
fun createFileIfNotExist( |
||||
root: DocumentFile, |
||||
fileName: String, |
||||
mimeType: String = "", |
||||
vararg subDirs: String |
||||
): DocumentFile? { |
||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs) |
||||
return parent?.findFile(fileName) ?: parent?.createFile(mimeType, fileName) |
||||
} |
||||
|
||||
fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? { |
||||
var parent: DocumentFile? = root |
||||
for (subDirName in subDirs) { |
||||
val subDir = parent?.findFile(subDirName) |
||||
?: parent?.createDirectory(subDirName) |
||||
parent = subDir |
||||
} |
||||
return parent |
||||
} |
||||
|
||||
fun getDirDocument(root: DocumentFile, vararg subDirs: String): DocumentFile? { |
||||
var parent = root |
||||
for (subDirName in subDirs) { |
||||
val subDir = parent.findFile(subDirName) |
||||
parent = subDir ?: return null |
||||
} |
||||
return parent |
||||
} |
||||
|
||||
@JvmStatic |
||||
@Throws(Exception::class) |
||||
fun writeText( |
||||
context: Context, |
||||
data: String, |
||||
fileUri: Uri, |
||||
charset: Charset = Charsets.UTF_8 |
||||
): Boolean { |
||||
return writeBytes(context, data.toByteArray(charset), fileUri) |
||||
} |
||||
|
||||
@JvmStatic |
||||
@Throws(Exception::class) |
||||
fun writeBytes(context: Context, data: ByteArray, fileUri: Uri): Boolean { |
||||
context.contentResolver.openOutputStream(fileUri)?.let { |
||||
it.write(data) |
||||
it.close() |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
@JvmStatic |
||||
@Throws(Exception::class) |
||||
fun readText(context: Context, uri: Uri): String { |
||||
return String(readBytes(context, uri)) |
||||
} |
||||
|
||||
@JvmStatic |
||||
@Throws(Exception::class) |
||||
fun readBytes(context: Context, uri: Uri): ByteArray { |
||||
context.contentResolver.openInputStream(uri)?.let { |
||||
val len: Int = it.available() |
||||
val buffer = ByteArray(len) |
||||
it.read(buffer) |
||||
it.close() |
||||
return buffer |
||||
} ?: throw NoStackTraceException("打开文件失败\n${uri}") |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun listFiles(uri: Uri, filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc> { |
||||
if (!uri.isContentScheme()) { |
||||
return listFiles(uri.path!!, filter) |
||||
} |
||||
val childrenUri = DocumentsContract |
||||
.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getDocumentId(uri)) |
||||
val docList = arrayListOf<FileDoc>() |
||||
var cursor: Cursor? = null |
||||
try { |
||||
cursor = appCtx.contentResolver.query( |
||||
childrenUri, arrayOf( |
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID, |
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME, |
||||
DocumentsContract.Document.COLUMN_LAST_MODIFIED, |
||||
DocumentsContract.Document.COLUMN_SIZE, |
||||
DocumentsContract.Document.COLUMN_MIME_TYPE |
||||
), null, null, DocumentsContract.Document.COLUMN_DISPLAY_NAME |
||||
) |
||||
cursor?.let { |
||||
val ici = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID) |
||||
val nci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME) |
||||
val sci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE) |
||||
val mci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE) |
||||
val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED) |
||||
if (cursor.moveToFirst()) { |
||||
do { |
||||
val item = FileDoc( |
||||
name = cursor.getString(nci), |
||||
isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR, |
||||
size = cursor.getLong(sci), |
||||
lastModified = cursor.getLong(dci), |
||||
uri = DocumentsContract |
||||
.buildDocumentUriUsingTree(uri, cursor.getString(ici)) |
||||
) |
||||
if (filter == null || filter.invoke(item)) { |
||||
docList.add(item) |
||||
} |
||||
} while (cursor.moveToNext()) |
||||
} |
||||
} |
||||
} finally { |
||||
cursor?.close() |
||||
} |
||||
return docList |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun listFiles(path: String, filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc> { |
||||
val docList = arrayListOf<FileDoc>() |
||||
val file = File(path) |
||||
file.listFiles()?.forEach { |
||||
val item = FileDoc( |
||||
it.name, |
||||
it.isDirectory, |
||||
it.length(), |
||||
it.lastModified(), |
||||
Uri.fromFile(it) |
||||
) |
||||
if (filter == null || filter.invoke(item)) { |
||||
docList.add(item) |
||||
} |
||||
} |
||||
return docList |
||||
} |
||||
|
||||
} |
||||
|
||||
data class FileDoc( |
||||
val name: String, |
||||
val isDir: Boolean, |
||||
val size: Long, |
||||
val lastModified: Long, |
||||
val uri: Uri |
||||
) { |
||||
|
||||
override fun toString(): String { |
||||
return if (uri.isContentScheme()) uri.toString() else uri.path!! |
||||
} |
||||
|
||||
val isContentScheme get() = uri.isContentScheme() |
||||
|
||||
fun readBytes(): ByteArray { |
||||
return uri.readBytes(appCtx) |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
fun fromDocumentFile(doc: DocumentFile): FileDoc { |
||||
return FileDoc( |
||||
name = doc.name ?: "", |
||||
isDir = doc.isDirectory, |
||||
size = doc.length(), |
||||
lastModified = doc.lastModified(), |
||||
uri = doc.uri |
||||
) |
||||
} |
||||
|
||||
fun fromFile(file: File): FileDoc { |
||||
return FileDoc( |
||||
name = file.name, |
||||
isDir = file.isDirectory, |
||||
size = file.length(), |
||||
lastModified = file.lastModified(), |
||||
uri = Uri.fromFile(file) |
||||
) |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.writeText(context: Context, data: String, charset: Charset = Charsets.UTF_8) { |
||||
DocumentUtils.writeText(context, data, this.uri, charset) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.writeBytes(context: Context, data: ByteArray) { |
||||
DocumentUtils.writeBytes(context, data, this.uri) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.readText(context: Context): String { |
||||
return DocumentUtils.readText(context, this.uri) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.readBytes(context: Context): ByteArray { |
||||
return DocumentUtils.readBytes(context, this.uri) |
||||
} |
@ -0,0 +1,47 @@ |
||||
package io.legado.app.utils |
||||
|
||||
import androidx.documentfile.provider.DocumentFile |
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate") |
||||
object DocumentUtils { |
||||
|
||||
fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean { |
||||
val parent = getDirDocument(root, *subDirs) ?: return false |
||||
return parent.findFile(fileName)?.exists() ?: false |
||||
} |
||||
|
||||
fun delete(root: DocumentFile, fileName: String, vararg subDirs: String) { |
||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs) |
||||
parent?.findFile(fileName)?.delete() |
||||
} |
||||
|
||||
fun createFileIfNotExist( |
||||
root: DocumentFile, |
||||
fileName: String, |
||||
mimeType: String = "", |
||||
vararg subDirs: String |
||||
): DocumentFile? { |
||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs) |
||||
return parent?.findFile(fileName) ?: parent?.createFile(mimeType, fileName) |
||||
} |
||||
|
||||
fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? { |
||||
var parent: DocumentFile? = root |
||||
for (subDirName in subDirs) { |
||||
val subDir = parent?.findFile(subDirName) |
||||
?: parent?.createDirectory(subDirName) |
||||
parent = subDir |
||||
} |
||||
return parent |
||||
} |
||||
|
||||
fun getDirDocument(root: DocumentFile, vararg subDirs: String): DocumentFile? { |
||||
var parent = root |
||||
for (subDirName in subDirs) { |
||||
val subDir = parent.findFile(subDirName) |
||||
parent = subDir ?: return null |
||||
} |
||||
return parent |
||||
} |
||||
|
||||
} |
@ -0,0 +1,255 @@ |
||||
@file:Suppress("unused") |
||||
|
||||
package io.legado.app.utils |
||||
|
||||
import android.app.DownloadManager |
||||
import android.content.Context |
||||
import android.database.Cursor |
||||
import android.net.Uri |
||||
import android.provider.DocumentsContract |
||||
import androidx.documentfile.provider.DocumentFile |
||||
import io.legado.app.exception.NoStackTraceException |
||||
import splitties.init.appCtx |
||||
import splitties.systemservices.downloadManager |
||||
import java.io.File |
||||
import java.nio.charset.Charset |
||||
|
||||
|
||||
data class FileDoc( |
||||
val name: String, |
||||
val isDir: Boolean, |
||||
val size: Long, |
||||
val lastModified: Long, |
||||
val uri: Uri |
||||
) { |
||||
|
||||
override fun toString(): String { |
||||
return if (uri.isContentScheme()) uri.toString() else uri.path!! |
||||
} |
||||
|
||||
val isContentScheme get() = uri.isContentScheme() |
||||
|
||||
fun readBytes(): ByteArray { |
||||
return uri.readBytes(appCtx) |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
fun fromUri(uri: Uri, isDir: Boolean): FileDoc { |
||||
if (uri.isContentScheme()) { |
||||
val doc = if (isDir) { |
||||
DocumentFile.fromTreeUri(appCtx, uri)!! |
||||
} else if (uri.host == "downloads") { |
||||
val query = DownloadManager.Query() |
||||
query.setFilterById(uri.lastPathSegment!!.toLong()) |
||||
downloadManager.query(query).use { |
||||
if (it.moveToFirst()) { |
||||
val lUriColum = it.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI) |
||||
val lUri = it.getString(lUriColum) |
||||
DocumentFile.fromSingleUri(appCtx, Uri.parse(lUri))!! |
||||
} else { |
||||
DocumentFile.fromSingleUri(appCtx, uri)!! |
||||
} |
||||
} |
||||
} else { |
||||
DocumentFile.fromSingleUri(appCtx, uri)!! |
||||
} |
||||
return FileDoc(doc.name ?: "", isDir, doc.length(), doc.lastModified(), doc.uri) |
||||
} |
||||
val file = File(uri.path!!) |
||||
return FileDoc(file.name, isDir, file.length(), file.lastModified(), uri) |
||||
} |
||||
|
||||
fun fromDocumentFile(doc: DocumentFile): FileDoc { |
||||
return FileDoc( |
||||
name = doc.name ?: "", |
||||
isDir = doc.isDirectory, |
||||
size = doc.length(), |
||||
lastModified = doc.lastModified(), |
||||
uri = doc.uri |
||||
) |
||||
} |
||||
|
||||
fun fromFile(file: File): FileDoc { |
||||
return FileDoc( |
||||
name = file.name, |
||||
isDir = file.isDirectory, |
||||
size = file.length(), |
||||
lastModified = file.lastModified(), |
||||
uri = Uri.fromFile(file) |
||||
) |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 过滤器 |
||||
*/ |
||||
typealias FileDocFilter = (file: FileDoc) -> Boolean |
||||
|
||||
private val projection by lazy { |
||||
arrayOf( |
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID, |
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME, |
||||
DocumentsContract.Document.COLUMN_LAST_MODIFIED, |
||||
DocumentsContract.Document.COLUMN_SIZE, |
||||
DocumentsContract.Document.COLUMN_MIME_TYPE |
||||
) |
||||
} |
||||
|
||||
/** |
||||
* 返回子文件列表,如果不是文件夹则返回null |
||||
*/ |
||||
fun FileDoc.list(filter: FileDocFilter? = null): ArrayList<FileDoc>? { |
||||
if (isDir) { |
||||
if (uri.isContentScheme()) { |
||||
/** |
||||
* DocumentFile 的 listFiles() 非常的慢,所以这里直接从数据库查询 |
||||
*/ |
||||
val childrenUri = DocumentsContract |
||||
.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getDocumentId(uri)) |
||||
val docList = arrayListOf<FileDoc>() |
||||
var cursor: Cursor? = null |
||||
try { |
||||
cursor = appCtx.contentResolver.query( |
||||
childrenUri, |
||||
projection, |
||||
null, |
||||
null, |
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME |
||||
) |
||||
cursor?.let { |
||||
val ici = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID) |
||||
val nci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME) |
||||
val sci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE) |
||||
val mci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE) |
||||
val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED) |
||||
if (cursor.moveToFirst()) { |
||||
do { |
||||
val item = FileDoc( |
||||
name = cursor.getString(nci), |
||||
isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR, |
||||
size = cursor.getLong(sci), |
||||
lastModified = cursor.getLong(dci), |
||||
uri = DocumentsContract.buildDocumentUriUsingTree( |
||||
uri, |
||||
cursor.getString(ici) |
||||
) |
||||
) |
||||
if (filter == null || filter.invoke(item)) { |
||||
docList.add(item) |
||||
} |
||||
} while (cursor.moveToNext()) |
||||
} |
||||
} |
||||
} finally { |
||||
cursor?.close() |
||||
} |
||||
return docList |
||||
} else { |
||||
return File(uri.path!!).listFileDocs(filter) |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
|
||||
/** |
||||
* 查找文档, 如果存在则返回文档,如果不存在返回空 |
||||
* @param name 文件名 |
||||
* @param depth 查找文件夹深度 |
||||
*/ |
||||
fun FileDoc.find(name: String, depth: Int = 0): FileDoc? { |
||||
val list = list() |
||||
list?.forEach { |
||||
if (it.name == name) { |
||||
return it |
||||
} |
||||
} |
||||
if (depth > 0) { |
||||
list?.forEach { |
||||
if (it.isDir) { |
||||
val fileDoc = it.find(name, depth - 1) |
||||
if (fileDoc != null) { |
||||
return fileDoc |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
|
||||
fun FileDoc.createFileIfNotExist( |
||||
fileName: String, |
||||
mimeType: String = "", |
||||
vararg subDirs: String |
||||
): FileDoc { |
||||
return if (uri.isContentScheme()) { |
||||
val documentFile = DocumentFile.fromTreeUri(appCtx, uri)!! |
||||
val tmp = DocumentUtils.createFileIfNotExist(documentFile, fileName, mimeType, *subDirs)!! |
||||
FileDoc.fromDocumentFile(tmp) |
||||
} else { |
||||
val path = FileUtils.getPath(uri.path!!, *subDirs) + File.separator + fileName |
||||
val tmp = FileUtils.createFileIfNotExist(path) |
||||
FileDoc.fromFile(tmp) |
||||
} |
||||
} |
||||
|
||||
fun FileDoc.createFolderIfNotExist( |
||||
vararg subDirs: String |
||||
): FileDoc { |
||||
return if (uri.isContentScheme()) { |
||||
val documentFile = DocumentFile.fromTreeUri(appCtx, uri)!! |
||||
val tmp = DocumentUtils.createFolderIfNotExist(documentFile, *subDirs)!! |
||||
FileDoc.fromDocumentFile(tmp) |
||||
} else { |
||||
val path = FileUtils.getPath(uri.path!!, *subDirs) |
||||
val tmp = FileUtils.createFolderIfNotExist(path) |
||||
FileDoc.fromFile(tmp) |
||||
} |
||||
} |
||||
|
||||
fun FileDoc.exists( |
||||
fileName: String, |
||||
vararg subDirs: String |
||||
): Boolean { |
||||
return if (uri.isContentScheme()) { |
||||
DocumentUtils.exists(DocumentFile.fromTreeUri(appCtx, uri)!!, fileName, *subDirs) |
||||
} else { |
||||
val path = FileUtils.getPath(uri.path!!, *subDirs) + File.separator + fileName |
||||
FileUtils.exist(path) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* DocumentFile 的 listFiles() 非常的慢,尽量不要使用 |
||||
*/ |
||||
fun DocumentFile.listFileDocs(filter: FileDocFilter? = null): ArrayList<FileDoc>? { |
||||
return FileDoc.fromDocumentFile(this).list(filter) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.writeText(context: Context, data: String, charset: Charset = Charsets.UTF_8) { |
||||
uri.writeText(context, data, charset) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.writeBytes(context: Context, data: ByteArray) { |
||||
uri.writeBytes(context, data) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.readText(context: Context): String { |
||||
return String(readBytes(context)) |
||||
} |
||||
|
||||
@Throws(Exception::class) |
||||
fun DocumentFile.readBytes(context: Context): ByteArray { |
||||
return context.contentResolver.openInputStream(uri)?.let { |
||||
val len: Int = it.available() |
||||
val buffer = ByteArray(len) |
||||
it.read(buffer) |
||||
it.close() |
||||
return buffer |
||||
} ?: throw NoStackTraceException("打开文件失败\n${uri}") |
||||
} |
@ -1,35 +1,64 @@ |
||||
package io.legado.app.utils |
||||
|
||||
import android.util.Log |
||||
import androidx.core.os.postDelayed |
||||
import com.script.SimpleBindings |
||||
import io.legado.app.constant.AppConst |
||||
import io.legado.app.exception.RegexTimeoutException |
||||
import io.legado.app.help.CrashHandler |
||||
import io.legado.app.help.coroutine.Coroutine |
||||
import kotlinx.coroutines.suspendCancellableCoroutine |
||||
import splitties.init.appCtx |
||||
import kotlin.coroutines.resume |
||||
import kotlin.coroutines.resumeWithException |
||||
|
||||
private val handler by lazy { buildMainHandler() } |
||||
|
||||
/** |
||||
* 带有超时检测的正则替换 |
||||
*/ |
||||
fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): String { |
||||
val startTime = System.currentTimeMillis() |
||||
val charSequence = this |
||||
suspend fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): String { |
||||
val charSequence = this@replace |
||||
val isJs = replacement.startsWith("@js:") |
||||
val replacement1 = if (isJs) replacement.substring(4) else replacement |
||||
val pattern = regex.toPattern() |
||||
val matcher = pattern.matcher(charSequence) |
||||
val stringBuffer = StringBuffer() |
||||
while (matcher.find()) { |
||||
if (System.currentTimeMillis() - startTime > timeout) { |
||||
val timeoutMsg = "替换超时,将禁用替换规则" |
||||
throw RegexTimeoutException(timeoutMsg) |
||||
return suspendCancellableCoroutine { block -> |
||||
val coroutine = Coroutine.async { |
||||
try { |
||||
val pattern = regex.toPattern() |
||||
val matcher = pattern.matcher(charSequence) |
||||
val stringBuffer = StringBuffer() |
||||
while (matcher.find()) { |
||||
if (isJs) { |
||||
val bindings = SimpleBindings() |
||||
bindings["result"] = matcher.group() |
||||
val jsResult = |
||||
AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() |
||||
matcher.appendReplacement(stringBuffer, jsResult) |
||||
} else { |
||||
matcher.appendReplacement(stringBuffer, replacement1) |
||||
} |
||||
} |
||||
matcher.appendTail(stringBuffer) |
||||
Log.e("regex", "end") |
||||
block.resume(stringBuffer.toString()) |
||||
} catch (e: Exception) { |
||||
block.resumeWithException(e) |
||||
} |
||||
} |
||||
if (isJs) { |
||||
val bindings = SimpleBindings() |
||||
bindings["result"] = matcher.group() |
||||
val jsResult = AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() |
||||
matcher.appendReplacement(stringBuffer, jsResult) |
||||
} else { |
||||
matcher.appendReplacement(stringBuffer, replacement1) |
||||
handler.postDelayed(timeout) { |
||||
if (coroutine.isActive) { |
||||
val timeoutMsg = "替换超时,3秒后还未结束将重启应用\n替换规则$regex\n替换内容:${this}" |
||||
val exception = RegexTimeoutException(timeoutMsg) |
||||
block.cancel(exception) |
||||
appCtx.longToastOnUi(timeoutMsg) |
||||
CrashHandler.saveCrashInfo2File(exception) |
||||
handler.postDelayed(3000) { |
||||
if (coroutine.isActive) { |
||||
appCtx.restart() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
matcher.appendTail(stringBuffer) |
||||
return stringBuffer.toString() |
||||
} |
||||
|
||||
|
Loading…
Reference in new issue