diff --git a/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt b/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt index 99ce3a8d..87080a82 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt @@ -18,6 +18,7 @@ import com.audiobookshelf.app.plugins.AbsAudioPlayer import com.audiobookshelf.app.plugins.AbsDatabase import com.audiobookshelf.app.plugins.AbsDownloader import com.audiobookshelf.app.plugins.AbsFileSystem +import com.audiobookshelf.app.plugins.AbsLogger import com.getcapacitor.BridgeActivity @@ -57,6 +58,7 @@ class MainActivity : BridgeActivity() { registerPlugin(AbsDownloader::class.java) registerPlugin(AbsFileSystem::class.java) registerPlugin(AbsDatabase::class.java) + registerPlugin(AbsLogger::class.java) super.onCreate(savedInstanceState) Log.d(tag, "onCreate") diff --git a/android/app/src/main/java/com/audiobookshelf/app/managers/DbManager.kt b/android/app/src/main/java/com/audiobookshelf/app/managers/DbManager.kt index 4b5da5af..fa840c2d 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/managers/DbManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/managers/DbManager.kt @@ -4,6 +4,7 @@ import android.content.Context import android.util.Log import com.audiobookshelf.app.data.* import com.audiobookshelf.app.models.DownloadItem +import com.audiobookshelf.app.plugins.AbsLog import io.paperdb.Paper import java.io.File @@ -287,4 +288,17 @@ class DbManager { } return sessions } + + fun saveLog(log:AbsLog) { + Paper.book("log").write(log.id, log) + } + fun getAllLogs() : List { + val logs:MutableList = mutableListOf() + Paper.book("log").allKeys.forEach { logId -> + Paper.book("log").read(logId)?.let { + logs.add(it) + } + } + return logs + } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDatabase.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDatabase.kt index 304cbdd6..955f3d69 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDatabase.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDatabase.kt @@ -219,6 +219,7 @@ class AbsDatabase : Plugin() { @PluginMethod fun syncLocalSessionsWithServer(call:PluginCall) { + AbsLogger.info("[AbsDatabase] syncLocalSessionsWithServer") if (DeviceManager.serverConnectionConfig == null) { Log.e(tag, "syncLocalSessionsWithServer not connected to server") return call.resolve() @@ -226,6 +227,7 @@ class AbsDatabase : Plugin() { apiHandler.syncLocalMediaProgressForUser { Log.d(tag, "Finished syncing local media progress for user") + AbsLogger.info("[AbsDatabase] Finished syncing local media progress for user") val savedSessions = DeviceManager.dbManager.getPlaybackSessions().filter { it.serverConnectionConfigId == DeviceManager.serverConnectionConfigId } if (savedSessions.isNotEmpty()) { diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt new file mode 100644 index 00000000..1da1ab11 --- /dev/null +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsLogger.kt @@ -0,0 +1,61 @@ +package com.audiobookshelf.app.plugins + +import android.util.Log +import com.audiobookshelf.app.device.DeviceManager +import com.fasterxml.jackson.core.json.JsonReadFeature +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.getcapacitor.JSObject +import com.getcapacitor.Plugin +import com.getcapacitor.PluginCall +import com.getcapacitor.PluginMethod +import com.getcapacitor.annotation.CapacitorPlugin +import java.util.UUID + +data class AbsLog( + var id:String, + var level:String, + var message:String, + var timestamp:Long +) + +data class AbsLogList(val value:List) + +@CapacitorPlugin(name = "AbsLogger") +class AbsLogger : Plugin() { + private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature()) + + override fun load() { + Log.i("AbsLogger", "Initialize AbsLogger plugin") + } + + companion object { + fun info(message:String) { + Log.i("AbsLogger", message) + DeviceManager.dbManager.saveLog(AbsLog(id = UUID.randomUUID().toString(), level = "info", message, timestamp = System.currentTimeMillis())) + } + fun error(message:String) { + Log.e("AbsLogger", message) + DeviceManager.dbManager.saveLog(AbsLog(id = UUID.randomUUID().toString(), level = "error", message, timestamp = System.currentTimeMillis())) + } + } + + @PluginMethod + fun info(call: PluginCall) { + val msg = call.getString("message") ?: return call.reject("No message") + info(msg) + call.resolve() + } + + @PluginMethod + fun error(call: PluginCall) { + val msg = call.getString("message") ?: return call.reject("No message") + error(msg) + call.resolve() + } + + @PluginMethod + fun getAllLogs(call: PluginCall) { + val absLogs = DeviceManager.dbManager.getAllLogs() + call.resolve(JSObject(jacksonMapper.writeValueAsString(AbsLogList(absLogs)))) + } +} diff --git a/components/app/SideDrawer.vue b/components/app/SideDrawer.vue index 3f8624d2..ee788317 100644 --- a/components/app/SideDrawer.vue +++ b/components/app/SideDrawer.vue @@ -134,6 +134,15 @@ export default { to: '/settings' }) + if (this.$platform !== 'ios') { + items.push({ + icon: 'bug_report', + iconOutlined: true, + text: this.$strings.ButtonLogs, + to: '/logs' + }) + } + if (this.serverConnectionConfig) { items.push({ icon: 'language', diff --git a/layouts/default.vue b/layouts/default.vue index 135df4ce..67912ca1 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -16,6 +16,7 @@ diff --git a/plugins/capacitor/AbsLogger.js b/plugins/capacitor/AbsLogger.js new file mode 100644 index 00000000..eb1f7606 --- /dev/null +++ b/plugins/capacitor/AbsLogger.js @@ -0,0 +1,42 @@ +import { registerPlugin, WebPlugin } from '@capacitor/core' + +class AbsLoggerWeb extends WebPlugin { + constructor() { + super() + + this.logs = [] + } + + saveLog(level, message) { + this.logs.push({ + id: Math.random().toString(36).substring(2, 15), + timestamp: Date.now(), + level: level, + message: message + }) + } + + async info(data) { + if (data?.message) { + this.saveLog('info', data.message) + console.log('AbsLogger: info', data.message) + } + } + + async error(data) { + if (data?.message) { + this.saveLog('error', data.message) + console.error('AbsLogger: error', data.message) + } + } + + async getAllLogs() { + return this.logs + } +} + +const AbsLogger = registerPlugin('AbsLogger', { + web: () => new AbsLoggerWeb() +}) + +export { AbsLogger } diff --git a/plugins/capacitor/index.js b/plugins/capacitor/index.js index 6a2db783..29b4ae87 100644 --- a/plugins/capacitor/index.js +++ b/plugins/capacitor/index.js @@ -2,12 +2,9 @@ import Vue from 'vue' import { AbsAudioPlayer } from './AbsAudioPlayer' import { AbsDownloader } from './AbsDownloader' import { AbsFileSystem } from './AbsFileSystem' +import { AbsLogger } from './AbsLogger' import { Capacitor } from '@capacitor/core' Vue.prototype.$platform = Capacitor.getPlatform() -export { - AbsAudioPlayer, - AbsDownloader, - AbsFileSystem -} \ No newline at end of file +export { AbsAudioPlayer, AbsDownloader, AbsFileSystem, AbsLogger } diff --git a/strings/en-us.json b/strings/en-us.json index ae6e72b4..ea710c5b 100644 --- a/strings/en-us.json +++ b/strings/en-us.json @@ -26,6 +26,7 @@ "ButtonLatest": "Latest", "ButtonLibrary": "Library", "ButtonLocalMedia": "Local Media", + "ButtonLogs": "Logs", "ButtonManageLocalFiles": "Manage Local Files", "ButtonNewFolder": "New Folder", "ButtonNextEpisode": "Next Episode",