diff --git a/viewer/src/androidMain/kotlin/org/uooc/document/DocumentPreviewer.android.kt b/viewer/src/androidMain/kotlin/org/uooc/document/DocumentPreviewer.android.kt index 9e65aef..0181230 100644 --- a/viewer/src/androidMain/kotlin/org/uooc/document/DocumentPreviewer.android.kt +++ b/viewer/src/androidMain/kotlin/org/uooc/document/DocumentPreviewer.android.kt @@ -1,9 +1,7 @@ package org.uooc.document import android.content.Context -import android.content.Intent -import android.os.Build -import android.provider.Settings +import android.util.Log import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -21,13 +19,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.viewinterop.AndroidView -import coil3.Uri import com.github.jing332.filepicker.base.FileImpl import com.tencent.tbs.reader.TbsFileInterfaceImpl import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch +private const val TAG = "DocumentPreviewer" internal actual fun DocumentPreviewer.setupLicense( license: String, @@ -36,22 +34,27 @@ internal actual fun DocumentPreviewer.setupLicense( val ctx = applicationContext.applicationContext as Context TbsFileInterfaceImpl.setLicenseKey(license) TbsFileInterfaceImpl.fileEnginePreCheck(ctx) - //初始化Engine - val isInit = if(TbsFileInterfaceImpl.isEngineLoaded().not()){ + + val initCode = if (!TbsFileInterfaceImpl.isEngineLoaded()) { TbsFileInterfaceImpl.initEngine(ctx) - }else { + } else { DocumentPreviewer.TMResult.SUCCESS.code } -// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ -// if(!Settings.System.canWrite(ctx)){ -// val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS) -// intent.setData(android.net.Uri.parse("package:" + ctx.packageName)) -// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) -// ctx.startActivity(intent) -// } -// } - this.currentState = DocumentPreviewer.TMResult.fromCode(isInit) - println("TbsFileInterfaceImpl.initEngine: ${this.currentState.message}") + + this.currentStateCode = initCode + this.currentState = DocumentPreviewer.TMResult.fromCode(initCode) + + val message = if (initCode == DocumentPreviewer.TMResult.SUCCESS.code) { + "TbsFile initEngine success(package=${ctx.packageName})" + } else { + "${DocumentPreviewer.TMResult.diagnosticMessage(initCode, this.currentState)}, package=${ctx.packageName}" + } + + if (initCode == DocumentPreviewer.TMResult.SUCCESS.code) { + Log.i(TAG, message) + } else { + Log.e(TAG, message) + } } @@ -71,7 +74,7 @@ internal actual fun DocumentPreviewer.documentView( val documentView = remember { mutableStateOf(null) } - with(LocalDensity.current){ + with(LocalDensity.current) { Column(modifier = Modifier.fillMaxSize()) { AndroidView( factory = { context -> @@ -82,28 +85,32 @@ internal actual fun DocumentPreviewer.documentView( } }, update = { - }, modifier = Modifier.fillMaxWidth() .wrapContentHeight() - ) } } LaunchedEffect(documentView.value) { - if(documentView.value==null){ + if (documentView.value == null) { return@LaunchedEffect } scope.launch { - documentView.value?.setDocument(scope, file.value, density,this@documentView.currentState) { success, message -> + documentView.value?.setDocument( + scope, + file.value, + density, + this@documentView.currentState, + this@documentView.currentStateCode + ) { success, message -> loadState.value = success to message } } } DisposableEffect(documentView.value) { - if(documentView.value==null){ - return@DisposableEffect onDispose { } + if (documentView.value == null) { + return@DisposableEffect onDispose { } } onDispose { documentView.value?.dispose() @@ -111,12 +118,12 @@ internal actual fun DocumentPreviewer.documentView( } } - if (loadState.value.first.not()) { + if (!loadState.value.first) { Text( text = loadState.value.second.let { - if(it.contains("未设置 licenseKey")){ + if (it.contains("未设置 licenseKey")) { "tbs未充值,请联系管理员" - }else{ + } else { it } }, @@ -127,10 +134,9 @@ internal actual fun DocumentPreviewer.documentView( snapshotFlow { loadState.value } .drop(1) .distinctUntilChanged() - .collect{ - callback(it.first,it.second) + .collect { + callback(it.first, it.second) } } - } -} \ No newline at end of file +} diff --git a/viewer/src/androidMain/kotlin/org/uooc/document/DocumentView.kt b/viewer/src/androidMain/kotlin/org/uooc/document/DocumentView.kt index d72d387..87c78fa 100644 --- a/viewer/src/androidMain/kotlin/org/uooc/document/DocumentView.kt +++ b/viewer/src/androidMain/kotlin/org/uooc/document/DocumentView.kt @@ -20,24 +20,28 @@ import kotlinx.coroutines.withContext import java.io.File import kotlin.math.roundToInt -private val TAG = "DocumentPreviewer" +private const val TAG = "DocumentPreviewer" class DocumentView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : FrameLayout(context, attrs) { private lateinit var currentState: DocumentPreviewer.TMResult + private var currentStateCode: Int = DocumentPreviewer.TMResult.UNKNOWN.code + suspend fun setDocument( scope: CoroutineScope, file: FileImpl, density: Density, currentState: DocumentPreviewer.TMResult, + currentStateCode: Int, callback: (Boolean, String) -> Unit ) { this.currentState = currentState + this.currentStateCode = currentStateCode val completer = CompletableDeferred>() scope.launch { withContext(Dispatchers.IO) { - //增加下面一句解决没有TbsReaderTemp文件夹存在导致加载文件失败 + // 增加下面一句解决没有 TbsReaderTemp 文件夹存在导致加载文件失败 val bsReaderTemp = FileUtils.getDir(context).toString() + File.separator + "TbsReaderTemp" val bsReaderTempFile = File(bsReaderTemp) @@ -49,20 +53,26 @@ class DocumentView @JvmOverloads constructor( return@withContext } } - if (this@DocumentView.currentState.code != 0) { - completer.complete(false to "TbsFile Engine初始化失败") + + if (this@DocumentView.currentStateCode != DocumentPreviewer.TMResult.SUCCESS.code) { + val diagnostic = DocumentPreviewer.TMResult.diagnosticMessage( + this@DocumentView.currentStateCode, + this@DocumentView.currentState + ) + Log.e(TAG, "$diagnostic, package=${context.packageName}") + completer.complete(false to diagnostic) return@withContext } - //文件格式 - val fileExt = FileUtils.getFileType(file.toString()) - println("文件格式:$fileExt") + // 文件格式 + val fileExt = FileUtils.getFileType(file.toString()) + Log.d(TAG, "文件格式:$fileExt") withContext(Dispatchers.Main) { val bool = TbsFileInterfaceImpl.canOpenFileExt(fileExt) Log.d(TAG, "文件是否支持$bool 文件路径:$file $bsReaderTemp $fileExt") if (bool) { - //加载文件 + // 加载文件 val localBundle = Bundle() localBundle.putString("filePath", file.absolutePath.toString()) localBundle.putString("tempPath", bsReaderTemp) @@ -70,55 +80,49 @@ class DocumentView @JvmOverloads constructor( localBundle.putInt( "set_content_view_width", - with(density) { measuredWidth.toFloat().dp.value.roundToInt() }) + with(density) { measuredWidth.toFloat().dp.value.roundToInt() } + ) // localBundle.putBoolean("file_reader_stream_mode", false)//设置为文件流打开模式 localBundle.putInt( "set_content_view_height", with(density) { measuredHeight.toFloat().dp.value.roundToInt().coerceAtLeast(200) - }) + } + ) this@DocumentView.post { val ret = TbsFileInterfaceImpl.getInstance().openFileReader( context, localBundle, { code, args, msg -> - Log.e(TAG, "文件打开回调 $code $args $msg") + Log.e(TAG, "文件打开回调 code=$code args=$args msg=$msg") when (code) { ITbsReader.OPEN_FILEREADER_STATUS_UI_CALLBACK -> { if (args is Bundle) { val id = args.getInt("typeId", 0) - val typeDes = - args.getString("typeDes", "fileReaderOpened") - if (ITbsReader.TBS_READER_TYPE_STATUS_UI_OPENED == id) { - //加密文档弹框取消需关闭activity -// Navigation.findNavController(getView()).popBackStack() - - } else if (ITbsReader.TBS_READER_TYPE_STATUS_UI_SHUTDOWN == id) { - //加密文档弹框取消需关闭activity -// Navigation.findNavController(getView()).popBackStack() - if (completer.isCompleted.not()) { - completer.complete(false to "文件打开2失败:${msg}") + if (ITbsReader.TBS_READER_TYPE_STATUS_UI_SHUTDOWN == id) { + if (!completer.isCompleted) { + completer.complete(false to "文件打开失败(code=$code): $msg") } } - } else { - if (completer.isCompleted.not()) { - completer.complete(false to "文件打开3失败:${msg}") - } + } else if (!completer.isCompleted) { + completer.complete(false to "文件打开失败(code=$code): $msg") } } ITbsReader.NOTIFY_CANDISPLAY -> { - //文件即将显示 - Log.wtf("NOTIFY_CANDISPLAY", "文件即将显示") - completer.complete(true to "") + Log.d(TAG, "文件即将显示") + if (!completer.isCompleted) { + completer.complete(true to "") + } } else -> Unit } }, this@DocumentView ) - if (ret == 0) { - } else { - completer.complete(false to "error:$ret") + if (ret != 0 && !completer.isCompleted) { + val message = "openFileReader失败(code=$ret)" + Log.e(TAG, "$message, file=$file") + completer.complete(false to message) } } } else { @@ -134,10 +138,10 @@ class DocumentView @JvmOverloads constructor( override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) - this.getViewTreeObserver().addOnGlobalLayoutListener(object : + this.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { - this@DocumentView.getViewTreeObserver().removeOnGlobalLayoutListener(this) + this@DocumentView.viewTreeObserver.removeOnGlobalLayoutListener(this) val w: Int = this@DocumentView.width val h: Int = this@DocumentView.height TbsFileInterfaceImpl.getInstance().onSizeChanged(w, h) @@ -153,4 +157,4 @@ class DocumentView @JvmOverloads constructor( } catch (ignore: Exception) { } } -} \ No newline at end of file +} diff --git a/viewer/src/commonMain/kotlin/org/uooc/document/DocumentPreviewer.kt b/viewer/src/commonMain/kotlin/org/uooc/document/DocumentPreviewer.kt index 64f2689..1e8b2b7 100644 --- a/viewer/src/commonMain/kotlin/org/uooc/document/DocumentPreviewer.kt +++ b/viewer/src/commonMain/kotlin/org/uooc/document/DocumentPreviewer.kt @@ -6,44 +6,44 @@ import com.github.jing332.filepicker.base.FileImpl object DocumentPreviewer { var currentState = TMResult.UNKNOWN internal set + + /** + * Raw return value from TbsFileInterfaceImpl.initEngine(). + * Keep this separately because UNKNOWN must not hide an unrecognised SDK error code. + */ + var currentStateCode: Int = TMResult.UNKNOWN.code + internal set + @Composable - fun previewDocument(document: FileImpl,callback: (Boolean, String) -> Unit) { + fun previewDocument(document: FileImpl, callback: (Boolean, String) -> Unit) { println("Previewing document at ${document.getAbsolutePath()}") - documentView(document,callback) + documentView(document, callback) } fun setup(license: String, applicationContext: coil3.PlatformContext) { - println("Setting up document previewer with license $license") + // Do not print the license key. It is a credential and may end up in CI/logcat logs. + println("Setting up document previewer (licenseConfigured=${license.isNotBlank()})") setupLicense(license, applicationContext) } /** * 初始化接口错误码 - * intEngine 接口错误码为方法返回值。 + * initEngine 接口错误码为方法返回值。 * initEngineAsync 接口错误码为回调 actionType == ITbsReader.OPEN_FILEREADER_ASYNC_LOAD_READER_ENTRY_CALLBACK 时 args 的值。 - * 错误码 - * 说明 - * 102 - * 未设置 licenseKey。 - * 202 - * 请检查调用接口是否正确,应调用 setLicenseKey 接口而不是 setLicense 接口。 - * 103 、305 - * 1. 请检查设备网络是否连通。 - * 2. 尝试切换网络。 - * 212、322 - * 调用量包次数用完。 - * 4001 - * licenseKey 不存在,请检查设置的 licenseKey 是否正确。 - * 4002 - * 客户端包名和 licenseKey 不匹配。 - * - * * + * 102: 未设置 licenseKey。 + * 202: 请检查调用接口是否正确,应调用 setLicenseKey 接口而不是 setLicense 接口。 + * 209: licenseKey 不匹配,需要检查 licenseKey 是否正确。 + * 103、305: 请检查设备网络是否连通,并尝试切换网络。 + * 212、322: 调用量包次数用完。 + * 4001: licenseKey 不存在,请检查设置的 licenseKey 是否正确。 + * 4002: 客户端包名和 licenseKey 不匹配。 */ enum class TMResult(val code: Int = 0, val message: String = "") { SUCCESS(0, "Success"), - MISMATCH(4002, "License key mismatch"), + // Keep the existing enum name for source compatibility; the documented code is 209. + MISMATCH(209, "License key mismatch"), UNSET(102, "Unset license key"), CHECK(202, "Check if the interface is called correctly, should call setLicenseKey instead of setLicense"), NETWORK_MAYBE1(103, "Check if the device network is connected, try switching networks"), @@ -58,11 +58,15 @@ object DocumentPreviewer { fun fromCode(code: Int): TMResult { return values().find { it.code == code }?.apply { println("Found code $code ${this.message}") - } ?: run{ + } ?: run { println("Unknown code $code") UNKNOWN } } + + fun diagnosticMessage(code: Int, state: TMResult = fromCode(code)): String { + return "TbsFile Engine初始化失败(code=$code, state=${state.name}): ${state.message}" + } } } } @@ -77,4 +81,4 @@ internal expect fun DocumentPreviewer.documentView( internal expect fun DocumentPreviewer.setupLicense( license: String, applicationContext: coil3.PlatformContext -) \ No newline at end of file +)