diff --git a/android/app/src/main/cpp/log.c b/android/app/src/main/cpp/log.c
index e9e9690..7ffc568 100644
--- a/android/app/src/main/cpp/log.c
+++ b/android/app/src/main/cpp/log.c
@@ -29,114 +29,118 @@
void log_cb_android(ChiakiLogLevel level, const char *msg, void *user)
{
- int prio;
- switch(level)
- {
- case CHIAKI_LOG_DEBUG:
- prio = ANDROID_LOG_DEBUG;
- break;
- case CHIAKI_LOG_VERBOSE:
- prio = ANDROID_LOG_VERBOSE;
- break;
- case CHIAKI_LOG_INFO:
- prio = ANDROID_LOG_INFO;
- break;
- case CHIAKI_LOG_WARNING:
- prio = ANDROID_LOG_ERROR;
- break;
- case CHIAKI_LOG_ERROR:
- prio = ANDROID_LOG_ERROR;
- break;
- default:
- prio = ANDROID_LOG_INFO;
- break;
- }
- __android_log_write(prio, LOG_TAG, msg);
+ int prio;
+ switch(level)
+ {
+ case CHIAKI_LOG_DEBUG:
+ prio = ANDROID_LOG_DEBUG;
+ break;
+ case CHIAKI_LOG_VERBOSE:
+ prio = ANDROID_LOG_VERBOSE;
+ break;
+ case CHIAKI_LOG_INFO:
+ prio = ANDROID_LOG_INFO;
+ break;
+ case CHIAKI_LOG_WARNING:
+ prio = ANDROID_LOG_ERROR;
+ break;
+ case CHIAKI_LOG_ERROR:
+ prio = ANDROID_LOG_ERROR;
+ break;
+ default:
+ prio = ANDROID_LOG_INFO;
+ break;
+ }
+ __android_log_write(prio, LOG_TAG, msg);
}
static void log_cb_android_file(ChiakiLogLevel level, const char *msg, void *user)
{
- log_cb_android(level, msg, user);
- FILE *f = user;
- if(!f)
- return;
- switch(level)
- {
- case CHIAKI_LOG_DEBUG:
- fwrite("[D] ", 4, 1, f);
- break;
- case CHIAKI_LOG_VERBOSE:
- fwrite("[V] ", 4, 1, f);
- break;
- case CHIAKI_LOG_INFO:
- fwrite("[I] ", 4, 1, f);
- break;
- case CHIAKI_LOG_WARNING:
- fwrite("[W] ", 4, 1, f);
- break;
- case CHIAKI_LOG_ERROR:
- fwrite("[E] ", 4, 1, f);
- break;
- default:
- fwrite("[?] ", 4, 1, f);
- break;
- }
- fwrite(msg, strlen(msg), 1, f);
- fwrite("\n", 1, 1, f);
+ log_cb_android(level, msg, user);
+ FILE *f = user;
+ if(!f)
+ return;
+ switch(level)
+ {
+ case CHIAKI_LOG_DEBUG:
+ fwrite("[D] ", 4, 1, f);
+ break;
+ case CHIAKI_LOG_VERBOSE:
+ fwrite("[V] ", 4, 1, f);
+ break;
+ case CHIAKI_LOG_INFO:
+ fwrite("[I] ", 4, 1, f);
+ break;
+ case CHIAKI_LOG_WARNING:
+ fwrite("[W] ", 4, 1, f);
+ break;
+ case CHIAKI_LOG_ERROR:
+ fwrite("[E] ", 4, 1, f);
+ break;
+ default:
+ fwrite("[?] ", 4, 1, f);
+ break;
+ }
+ fwrite(msg, strlen(msg), 1, f);
+ fwrite("\n", 1, 1, f);
}
ChiakiErrorCode android_chiaki_file_log_init(ChiakiLog *log, uint32_t level, const char *file)
{
- chiaki_log_init(log, level, log_cb_android, NULL);
- if(file)
- {
- FILE *f = fopen(file, "w+");
- if(!f)
- {
- CHIAKI_LOGE(log, "Failed to open log file %s for writing: %s", file, strerror(errno));
- return CHIAKI_ERR_UNKNOWN;
- }
- log->user = f;
- log->cb = log_cb_android_file;
- }
- return CHIAKI_ERR_SUCCESS;
+ chiaki_log_init(log, level, log_cb_android, NULL);
+ if(file)
+ {
+ FILE *f = fopen(file, "w+");
+ if(!f)
+ {
+ CHIAKI_LOGE(log, "Failed to open log file %s for writing: %s", file, strerror(errno));
+ return CHIAKI_ERR_UNKNOWN;
+ }
+ else
+ {
+ log->user = f;
+ log->cb = log_cb_android_file;
+ CHIAKI_LOGI(log, "Logging to file %s", file);
+ }
+ }
+ return CHIAKI_ERR_SUCCESS;
}
void android_chiaki_file_log_fini(ChiakiLog *log)
{
- if(log->user)
- {
- FILE *f = log->user;
- fclose(f);
- log->user = NULL;
- }
+ if(log->user)
+ {
+ FILE *f = log->user;
+ fclose(f);
+ log->user = NULL;
+ }
}
static void android_chiaki_log_cb(ChiakiLogLevel level, const char *msg, void *user)
{
- log_cb_android(level, msg, NULL);
+ log_cb_android(level, msg, NULL);
- AndroidChiakiJNILog *log = user;
- JNIEnv *env = attach_thread_jni();
- if(!env)
- return;
- E->CallVoidMethod(env, log->java_log, log->java_log_meth, (jint)level, jnistr_from_ascii(env, msg));
- (*global_vm)->DetachCurrentThread(global_vm);
+ AndroidChiakiJNILog *log = user;
+ JNIEnv *env = attach_thread_jni();
+ if(!env)
+ return;
+ E->CallVoidMethod(env, log->java_log, log->java_log_meth, (jint)level, jnistr_from_ascii(env, msg));
+ (*global_vm)->DetachCurrentThread(global_vm);
}
void android_chiaki_jni_log_init(AndroidChiakiJNILog *log, JNIEnv *env, jobject java_log)
{
- log->java_log = E->NewGlobalRef(env, java_log);
- jclass log_class = E->GetObjectClass(env, log->java_log);
- log->java_log_meth = E->GetMethodID(env, log_class, "log", "(ILjava/lang/String;)V");
- log->log.level_mask = (uint32_t)E->GetIntField(env, log->java_log, E->GetFieldID(env, log_class, "levelMask", "I"));
- log->log.cb = android_chiaki_log_cb;
- log->log.user = log;
+ log->java_log = E->NewGlobalRef(env, java_log);
+ jclass log_class = E->GetObjectClass(env, log->java_log);
+ log->java_log_meth = E->GetMethodID(env, log_class, "log", "(ILjava/lang/String;)V");
+ log->log.level_mask = (uint32_t)E->GetIntField(env, log->java_log, E->GetFieldID(env, log_class, "levelMask", "I"));
+ log->log.cb = android_chiaki_log_cb;
+ log->log.user = log;
}
void android_chiaki_jni_log_fini(AndroidChiakiJNILog *log, JNIEnv *env)
{
- E->DeleteGlobalRef(env, log->java_log);
+ E->DeleteGlobalRef(env, log->java_log);
}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/metallic/chiaki/common/LogManager.kt b/android/app/src/main/java/com/metallic/chiaki/common/LogManager.kt
new file mode 100644
index 0000000..26fbfb5
--- /dev/null
+++ b/android/app/src/main/java/com/metallic/chiaki/common/LogManager.kt
@@ -0,0 +1,74 @@
+/*
+ * This file is part of Chiaki.
+ *
+ * Chiaki is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chiaki is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chiaki. If not, see .
+ */
+
+package com.metallic.chiaki.common
+
+import android.content.Context
+import java.io.File
+import java.io.FilenameFilter
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.regex.Pattern
+
+private val dateFormat = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSSSSS", Locale.US)
+private val filePrefix = "chiaki_session_"
+private val filePostfix = ".log"
+private val fileRegex = Regex("$filePrefix(.*)$filePostfix")
+private val keepLogFilesCount = 5
+
+class LogFile private constructor(val logManager: LogManager, val filename: String)
+{
+ val date = fileRegex.matchEntire(filename)?.groupValues?.get(1)?.let {
+ dateFormat.parse(it)
+ } ?: throw IllegalArgumentException()
+
+ val file get() = File(logManager.baseDir, filename)
+
+ companion object
+ {
+ fun fromFilename(logManager: LogManager, filename: String) = try { LogFile(logManager, filename) } catch(e: IllegalArgumentException) { null }
+ }
+}
+
+class LogManager(context: Context)
+{
+ val baseDir = File(context.filesDir, "session_logs").also {
+ it.mkdirs()
+ }
+
+ val files: List get() =
+ (baseDir.list { _, s -> s.matches(fileRegex) }?.toList() ?: listOf()).mapNotNull {
+ LogFile.fromFilename(this, it)
+ }.sortedByDescending {
+ it.date
+ }
+
+ fun createNewFile(): LogFile
+ {
+ val currentFiles = files
+ if(currentFiles.size > keepLogFilesCount)
+ {
+ currentFiles.subList(keepLogFilesCount, currentFiles.size).forEach {
+ it.file.delete()
+ }
+ }
+
+ val date = Date()
+ val filename = "$filePrefix${dateFormat.format(date)}$filePostfix"
+ return LogFile.fromFilename(this, filename)!!
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt b/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt
index 10a07c4..aaf70aa 100644
--- a/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt
@@ -22,6 +22,7 @@ import android.util.Log
import android.view.*
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.lib.*
sealed class StreamState
@@ -32,7 +33,7 @@ data class StreamStateCreateError(val error: CreateError): StreamState()
data class StreamStateQuit(val reason: QuitReason, val reasonString: String?): StreamState()
data class StreamStateLoginPinRequest(val pinIncorrect: Boolean): StreamState()
-class StreamSession(val connectInfo: ConnectInfo, val logVerbose: Boolean, val input: StreamInput)
+class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, val logVerbose: Boolean, val input: StreamInput)
{
var session: Session? = null
private set
@@ -69,7 +70,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logVerbose: Boolean, val i
return
try
{
- val session = Session(connectInfo, null, logVerbose) // TODO: log file
+ val session = Session(connectInfo, logManager.createNewFile().file.absolutePath, logVerbose)
_state.value = StreamStateConnecting
session.eventCallback = this::eventCallback
session.start()
diff --git a/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt b/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
index 8ba742b..43dee24 100644
--- a/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
@@ -34,6 +34,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.metallic.chiaki.R
+import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.lib.ConnectInfo
@@ -69,7 +70,7 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
}
viewModel = ViewModelProviders.of(this, viewModelFactory {
- StreamViewModel(Preferences(this), connectInfo)
+ StreamViewModel(Preferences(this), LogManager(this), connectInfo)
})[StreamViewModel::class.java]
setContentView(R.layout.activity_stream)
diff --git a/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt b/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
index d8eb35e..8f59f72 100644
--- a/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
@@ -20,16 +20,17 @@ package com.metallic.chiaki.stream
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
+import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.session.StreamSession
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.lib.*
import com.metallic.chiaki.session.StreamInput
-class StreamViewModel(val preferences: Preferences, val connectInfo: ConnectInfo): ViewModel()
+class StreamViewModel(val preferences: Preferences, val logManager: LogManager, val connectInfo: ConnectInfo): ViewModel()
{
private var _session: StreamSession? = null
val input = StreamInput(preferences)
- val session = StreamSession(connectInfo, preferences.logVerbose, input)
+ val session = StreamSession(connectInfo, logManager, preferences.logVerbose, input)
private var _onScreenControlsEnabled = MutableLiveData(preferences.onScreenControlsEnabled)
val onScreenControlsEnabled: LiveData get() = _onScreenControlsEnabled