Skip to content

Commit 85704cc

Browse files
committed
feat(debug): Add debug logging and dev profiles support
1 parent 2889e3e commit 85704cc

File tree

14 files changed

+670
-0
lines changed

14 files changed

+670
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,9 @@ Open-AutoGLM/
5151
*.keystore
5252
*.jks
5353
keystore_base64.txt
54+
55+
# Logcat dumps
56+
logcat*
57+
58+
# Dev profiles config
59+
dev_profiles.json

app/build.gradle.kts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ android {
3434
debug {
3535
applicationIdSuffix = ".debug"
3636
versionNameSuffix = "-debug"
37+
resValue("string", "app_name", "AutoGLM Dev")
3738
}
3839
release {
3940
isMinifyEnabled = false
@@ -69,6 +70,31 @@ android {
6970
}
7071
}
7172

73+
// Copy dev_profiles.json to assets for debug builds
74+
android.applicationVariants.all {
75+
val variant = this
76+
if (variant.buildType.name == "debug") {
77+
val copyDevProfiles = tasks.register("copyDevProfiles${variant.name.replaceFirstChar { it.uppercase() }}") {
78+
val devProfilesFile = rootProject.file("dev_profiles.json")
79+
val assetsDir = file("src/main/assets")
80+
81+
doLast {
82+
if (devProfilesFile.exists()) {
83+
assetsDir.mkdirs()
84+
devProfilesFile.copyTo(File(assetsDir, "dev_profiles.json"), overwrite = true)
85+
println("Copied dev_profiles.json to assets")
86+
} else {
87+
println("dev_profiles.json not found, skipping")
88+
}
89+
}
90+
}
91+
92+
tasks.named("merge${variant.name.replaceFirstChar { it.uppercase() }}Assets") {
93+
dependsOn(copyDevProfiles)
94+
}
95+
}
96+
}
97+
7298
dependencies {
7399
implementation(libs.androidx.core.ktx)
74100
implementation(libs.androidx.appcompat)

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
android:fullBackupContent="@xml/backup_rules"
2727
android:icon="@mipmap/ic_launcher"
2828
android:label="@string/app_name"
29+
android:networkSecurityConfig="@xml/network_security_config"
2930
android:roundIcon="@mipmap/ic_launcher_round"
3031
android:supportsRtl="true"
3132
android:theme="@style/Theme.AutoGLM">

app/src/main/java/com/kevinluo/autoglm/app/AutoGLMApplication.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.os.Bundle
66
import com.kevinluo.autoglm.config.SystemPrompts
77
import com.kevinluo.autoglm.settings.SettingsManager
88
import com.kevinluo.autoglm.ui.FloatingWindowService
9+
import com.kevinluo.autoglm.util.LogFileManager
910
import com.kevinluo.autoglm.util.Logger
1011

1112
/**
@@ -32,6 +33,12 @@ class AutoGLMApplication : Application() {
3233
override fun onCreate() {
3334
super.onCreate()
3435

36+
// Initialize log file manager for file-based logging
37+
LogFileManager.init(this)
38+
39+
// Import dev profiles if available (debug builds only)
40+
importDevProfilesIfNeeded()
41+
3542
// Load custom system prompts if set
3643
loadCustomSystemPrompts()
3744

@@ -100,6 +107,34 @@ class AutoGLMApplication : Application() {
100107
}
101108
}
102109

110+
/**
111+
* Imports dev profiles from assets if available and not already imported.
112+
*
113+
* This is used for debug builds to pre-populate model profiles for testing.
114+
* The dev_profiles.json file is only included in debug builds.
115+
*/
116+
private fun importDevProfilesIfNeeded() {
117+
val settingsManager = SettingsManager(this)
118+
119+
// Skip if already imported
120+
if (settingsManager.hasImportedDevProfiles()) {
121+
return
122+
}
123+
124+
try {
125+
val json = assets.open("dev_profiles.json").bufferedReader().readText()
126+
val count = settingsManager.importDevProfiles(json)
127+
if (count > 0) {
128+
Logger.i(TAG, "Imported $count dev profiles from assets")
129+
}
130+
} catch (e: java.io.FileNotFoundException) {
131+
// File not found - this is expected for release builds
132+
Logger.d(TAG, "dev_profiles.json not found in assets (expected for release builds)")
133+
} catch (e: Exception) {
134+
Logger.e(TAG, "Failed to import dev profiles", e)
135+
}
136+
}
137+
103138
companion object {
104139
private const val TAG = "AutoGLMApplication"
105140
}

app/src/main/java/com/kevinluo/autoglm/settings/SettingsActivity.kt

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.kevinluo.autoglm.settings
22

3+
import android.content.Intent
34
import android.os.Bundle
45
import android.view.LayoutInflater
56
import android.view.MenuItem
@@ -23,6 +24,7 @@ import com.kevinluo.autoglm.R
2324
import com.kevinluo.autoglm.agent.AgentConfig
2425
import com.kevinluo.autoglm.model.ModelClient
2526
import com.kevinluo.autoglm.model.ModelConfig
27+
import com.kevinluo.autoglm.util.LogFileManager
2628
import com.kevinluo.autoglm.util.Logger
2729
import com.google.android.material.textfield.TextInputEditText
2830
import com.google.android.material.textfield.TextInputLayout
@@ -82,6 +84,11 @@ class SettingsActivity : AppCompatActivity() {
8284
private lateinit var btnEditPromptCn: Button
8385
private lateinit var btnEditPromptEn: Button
8486

87+
// Debug logs views
88+
private lateinit var logSizeText: TextView
89+
private lateinit var btnExportLogs: Button
90+
private lateinit var btnClearLogs: Button
91+
8592
// Profile data
8693
private var savedProfiles: List<SavedModelProfile> = emptyList()
8794
private var currentProfileId: String? = null
@@ -156,6 +163,11 @@ class SettingsActivity : AppCompatActivity() {
156163
promptEnStatus = findViewById(R.id.promptEnStatus)
157164
btnEditPromptCn = findViewById(R.id.btnEditPromptCn)
158165
btnEditPromptEn = findViewById(R.id.btnEditPromptEn)
166+
167+
// Debug logs
168+
logSizeText = findViewById(R.id.logSizeText)
169+
btnExportLogs = findViewById(R.id.btnExportLogs)
170+
btnClearLogs = findViewById(R.id.btnClearLogs)
159171
}
160172

161173
/**
@@ -177,6 +189,9 @@ class SettingsActivity : AppCompatActivity() {
177189
// Update system prompt status
178190
updatePromptStatus()
179191

192+
// Update log size display
193+
updateLogSizeDisplay()
194+
180195
// Populate model settings
181196
baseUrlInput.setText(modelConfig.baseUrl)
182197
modelNameInput.setText(modelConfig.modelName)
@@ -290,6 +305,14 @@ class SettingsActivity : AppCompatActivity() {
290305
showEditPromptDialog("en")
291306
}
292307

308+
// Debug logs buttons
309+
btnExportLogs.setOnClickListener {
310+
exportDebugLogs()
311+
}
312+
btnClearLogs.setOnClickListener {
313+
showClearLogsDialog()
314+
}
315+
293316
// Clear errors on text change
294317
baseUrlInput.setOnFocusChangeListener { _, _ -> baseUrlLayout.error = null }
295318
modelNameInput.setOnFocusChangeListener { _, _ -> modelNameLayout.error = null }
@@ -911,4 +934,51 @@ class SettingsActivity : AppCompatActivity() {
911934
companion object {
912935
private const val TAG = "SettingsActivity"
913936
}
937+
938+
// ==================== Debug Logs ====================
939+
940+
/**
941+
* Updates the log size display text.
942+
*/
943+
private fun updateLogSizeDisplay() {
944+
val totalSize = LogFileManager.getTotalLogSize()
945+
val formattedSize = LogFileManager.formatSize(totalSize)
946+
logSizeText.text = getString(R.string.settings_debug_logs_size, formattedSize)
947+
}
948+
949+
/**
950+
* Exports debug logs and opens share dialog.
951+
*/
952+
private fun exportDebugLogs() {
953+
Logger.i(TAG, "Exporting debug logs")
954+
955+
val logFiles = LogFileManager.getLogFiles()
956+
if (logFiles.isEmpty()) {
957+
Toast.makeText(this, R.string.settings_logs_empty, Toast.LENGTH_SHORT).show()
958+
return
959+
}
960+
961+
val shareIntent = LogFileManager.exportLogs(this)
962+
if (shareIntent != null) {
963+
startActivity(Intent.createChooser(shareIntent, getString(R.string.settings_export_logs)))
964+
} else {
965+
Toast.makeText(this, R.string.settings_logs_export_failed, Toast.LENGTH_SHORT).show()
966+
}
967+
}
968+
969+
/**
970+
* Shows confirmation dialog to clear all logs.
971+
*/
972+
private fun showClearLogsDialog() {
973+
AlertDialog.Builder(this)
974+
.setTitle(R.string.settings_clear_logs)
975+
.setMessage(R.string.settings_clear_logs_confirm)
976+
.setPositiveButton(R.string.dialog_confirm) { _, _ ->
977+
LogFileManager.clearAllLogs()
978+
updateLogSizeDisplay()
979+
Toast.makeText(this, R.string.settings_logs_cleared, Toast.LENGTH_SHORT).show()
980+
}
981+
.setNegativeButton(R.string.dialog_cancel, null)
982+
.show()
983+
}
914984
}

app/src/main/java/com/kevinluo/autoglm/settings/SettingsManager.kt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class SettingsManager(private val context: Context) {
9696
private const val KEY_CUSTOM_SYSTEM_PROMPT_CN = "custom_system_prompt_cn"
9797
private const val KEY_CUSTOM_SYSTEM_PROMPT_EN = "custom_system_prompt_en"
9898

99+
// Dev profiles import key
100+
private const val KEY_DEV_PROFILES_IMPORTED = "dev_profiles_imported"
101+
99102
// Default values
100103
private val DEFAULT_MODEL_CONFIG = ModelConfig()
101104
private val DEFAULT_AGENT_CONFIG = AgentConfig()
@@ -613,4 +616,85 @@ class SettingsManager(private val context: Context) {
613616
fun hasCustomSystemPrompt(language: String): Boolean {
614617
return getCustomSystemPrompt(language) != null
615618
}
619+
620+
// ==================== Dev Profiles Import ====================
621+
622+
/**
623+
* Checks if dev profiles have already been imported.
624+
*
625+
* @return true if dev profiles have been imported, false otherwise
626+
*/
627+
fun hasImportedDevProfiles(): Boolean {
628+
return prefs.getBoolean(KEY_DEV_PROFILES_IMPORTED, false)
629+
}
630+
631+
/**
632+
* Marks dev profiles as imported.
633+
*/
634+
fun markDevProfilesImported() {
635+
prefs.edit().putBoolean(KEY_DEV_PROFILES_IMPORTED, true).apply()
636+
}
637+
638+
/**
639+
* Imports dev profiles from JSON string.
640+
*
641+
* @param json JSON string containing profiles configuration
642+
* @return Number of profiles imported, or -1 if parsing failed
643+
*/
644+
fun importDevProfiles(json: String): Int {
645+
return try {
646+
val root = JSONObject(json)
647+
val profilesArray = root.getJSONArray("profiles")
648+
val defaultProfileName = root.optString("defaultProfile", "")
649+
650+
var importedCount = 0
651+
var defaultProfileId: String? = null
652+
653+
for (i in 0 until profilesArray.length()) {
654+
val obj = profilesArray.getJSONObject(i)
655+
val name = obj.getString("name")
656+
val profileId = generateProfileId()
657+
658+
val profile = SavedModelProfile(
659+
id = profileId,
660+
displayName = name,
661+
config = ModelConfig(
662+
baseUrl = obj.getString("baseUrl"),
663+
apiKey = obj.optString("apiKey", "EMPTY").ifEmpty { "EMPTY" },
664+
modelName = obj.getString("modelName")
665+
)
666+
)
667+
668+
saveProfile(profile)
669+
importedCount++
670+
671+
if (name == defaultProfileName) {
672+
defaultProfileId = profileId
673+
}
674+
675+
Logger.d(TAG, "Imported dev profile: $name")
676+
}
677+
678+
// Set default profile and apply its config
679+
if (defaultProfileId != null) {
680+
setCurrentProfileId(defaultProfileId)
681+
getProfileById(defaultProfileId)?.let { profile ->
682+
saveModelConfig(profile.config)
683+
}
684+
} else if (importedCount > 0) {
685+
// Use first profile as default
686+
getSavedProfiles().firstOrNull()?.let { profile ->
687+
setCurrentProfileId(profile.id)
688+
saveModelConfig(profile.config)
689+
}
690+
}
691+
692+
markDevProfilesImported()
693+
Logger.i(TAG, "Imported $importedCount dev profiles")
694+
importedCount
695+
} catch (e: Exception) {
696+
Logger.e(TAG, "Failed to import dev profiles", e)
697+
-1
698+
}
699+
}
616700
}

0 commit comments

Comments
 (0)