mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix: parse OpenedFilesCache manually to avoid ClassCastException in release builds (fixes #1102)
This commit is contained in:
Vendored
+19
-6
@@ -4,6 +4,10 @@
|
||||
-dontnote **
|
||||
-dontobfuscate
|
||||
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
-keepattributes *Annotation*
|
||||
|
||||
-keep class javax.** { *; }
|
||||
-keep class jdkx.** { *; }
|
||||
|
||||
@@ -35,7 +39,6 @@
|
||||
}
|
||||
|
||||
# EventBus
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class ** {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
@@ -91,19 +94,29 @@
|
||||
# Retrofit 2
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
# OkHttp3
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-keep class okhttp3.** { *; }
|
||||
-keep interface okhttp3.** { *; }
|
||||
-dontwarn okhttp3.**
|
||||
|
||||
# Stat uploader
|
||||
-keep class com.itsaky.androidide.stats.** { *; }
|
||||
-keep class com.itsaky.androidide.stats.** { *; }
|
||||
|
||||
# Gson
|
||||
-keep class * extends com.google.gson.TypeAdapter
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
-keepclassmembers,allowobfuscation class * {
|
||||
@com.google.gson.annotations.SerializedName <fields>;
|
||||
}
|
||||
|
||||
## Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
||||
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||
@@ -163,15 +163,13 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
|
||||
try {
|
||||
if (viewModel.openedFilesCache != null) {
|
||||
viewModel.openedFilesCache?.apply {
|
||||
this.allFiles.forEach { openFile(File(it.filePath), it.selection) }
|
||||
openFile(File(selectedFile))
|
||||
viewModel.openedFilesCache?.let { cache ->
|
||||
onReadOpenedFilesCache(cache)
|
||||
}
|
||||
} else {
|
||||
viewModel.readOpenedFiles {
|
||||
it ?: return@readOpenedFiles
|
||||
it.allFiles.forEach { file -> openFile(File(file.filePath), file.selection) }
|
||||
openFile(File(it.selectedFile))
|
||||
viewModel.readOpenedFiles { cache ->
|
||||
cache ?: return@readOpenedFiles
|
||||
onReadOpenedFilesCache(cache)
|
||||
}
|
||||
}
|
||||
viewModel.openedFilesCache = null
|
||||
@@ -180,6 +178,13 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onReadOpenedFilesCache(cache: OpenedFilesCache) {
|
||||
cache.allFiles.forEach { file ->
|
||||
openFile(File(file.filePath), file.selection)
|
||||
}
|
||||
openFile(File(cache.selectedFile))
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
ensureToolbarMenu(menu)
|
||||
return true
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
|
||||
package com.itsaky.androidide.models
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
|
||||
/**
|
||||
* A file that is opened in editor.
|
||||
@@ -25,6 +28,37 @@ import com.google.gson.annotations.SerializedName
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class OpenedFile(
|
||||
@SerializedName("file") val filePath: String,
|
||||
@SerializedName("selection") var selection: Range
|
||||
)
|
||||
@SerializedName(KEY_FILE) val filePath: String,
|
||||
@SerializedName(KEY_SELECTION) var selection: Range
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_FILE = "file"
|
||||
private const val KEY_SELECTION = "selection"
|
||||
private val log = ILogger.newInstance("OpenedFile")
|
||||
|
||||
fun readFrom(reader: JsonReader): OpenedFile? {
|
||||
return try {
|
||||
// reader.beginObject()
|
||||
// var path = ""
|
||||
// var selection = Range.NONE
|
||||
// while(reader.hasNext()) {
|
||||
// val name = reader.nextName()
|
||||
// if (name == KEY_FILE) {
|
||||
// path = reader.nextString()
|
||||
// } else if (name == KEY_SELECTION) {
|
||||
// selection = Gson().fromJson(reader, Range::class.java)
|
||||
// }
|
||||
// }
|
||||
// reader.endObject()
|
||||
//
|
||||
// OpenedFile(path, selection)
|
||||
Gson().fromJson(reader, OpenedFile::class.java)
|
||||
} catch (err: Exception) {
|
||||
log.error("Failed to read opened file", err)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,56 @@
|
||||
|
||||
package com.itsaky.androidide.models
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import java.io.Reader
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class OpenedFilesCache(val selectedFile: String, val allFiles: List<OpenedFile>)
|
||||
data class OpenedFilesCache(@SerializedName(KEY_SELECTED_FILE) val selectedFile: String,
|
||||
@SerializedName(KEY_ALL_FILES) val allFiles: List<OpenedFile>) {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_SELECTED_FILE = "selectedFile"
|
||||
private const val KEY_ALL_FILES = "allFiles"
|
||||
private val log = ILogger.newInstance("OpenedFilesCache")
|
||||
|
||||
@JvmStatic
|
||||
fun parse(contentReader: Reader): OpenedFilesCache? {
|
||||
return try {
|
||||
JsonReader(contentReader).use { reader ->
|
||||
if (!reader.hasNext()) {
|
||||
return@use null
|
||||
}
|
||||
|
||||
reader.beginObject()
|
||||
var selectedFile = ""
|
||||
var allFiles = emptyList<OpenedFile>()
|
||||
while (reader.hasNext()) {
|
||||
val name = reader.nextName()
|
||||
|
||||
if (name == KEY_SELECTED_FILE) {
|
||||
selectedFile = reader.nextString()
|
||||
} else if (name == KEY_ALL_FILES) {
|
||||
allFiles = Gson().fromJson(reader, object : TypeToken<List<OpenedFile>>() {})
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
|
||||
return@use OpenedFilesCache(selectedFile, allFiles)
|
||||
}
|
||||
} catch (err: Exception) {
|
||||
log.error("Failed to parse opened files cache", err)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun readOpenedFiles(reader: JsonReader, allFiles: MutableList<OpenedFile>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel
|
||||
import com.blankj.utilcode.util.FileUtils
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.itsaky.androidide.models.OpenedFilesCache
|
||||
import com.itsaky.androidide.projects.ProjectManager
|
||||
import com.itsaky.androidide.tasks.executeAsync
|
||||
@@ -199,11 +200,13 @@ class EditorViewModel : ViewModel() {
|
||||
|
||||
fun readOpenedFiles(result: (OpenedFilesCache?) -> Unit) {
|
||||
executeAsync({
|
||||
val file = getOpenedFilesCache()
|
||||
val file = getOpenedFilesCache(false)
|
||||
if (file.length() == 0L) {
|
||||
return@executeAsync null
|
||||
}
|
||||
return@executeAsync Gson().fromJson(file.readText(), OpenedFilesCache::class.java)
|
||||
return@executeAsync file.reader().buffered().use { reader ->
|
||||
OpenedFilesCache.parse(reader)
|
||||
}
|
||||
}) {
|
||||
result(it)
|
||||
}
|
||||
@@ -224,17 +227,17 @@ class EditorViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOpenedFilesCache(): File {
|
||||
private fun getOpenedFilesCache(forWrite: Boolean = false): File {
|
||||
var file = Environment.getProjectCacheDir(ProjectManager.projectPath)
|
||||
file = File(file, "editor/openedFiles.json")
|
||||
if (file.exists()) {
|
||||
if (file.exists() && forWrite) {
|
||||
FileUtils.rename(file, "${file.name}.bak")
|
||||
}
|
||||
|
||||
if (file.parentFile?.exists() == false) {
|
||||
file.parentFile?.mkdirs()
|
||||
}
|
||||
|
||||
|
||||
file.createNewFile()
|
||||
|
||||
return file
|
||||
|
||||
@@ -24,6 +24,7 @@ plugins {
|
||||
dependencies {
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.google.guava)
|
||||
implementation(libs.google.gson)
|
||||
implementation(projects.logger)
|
||||
|
||||
testImplementation(libs.tests.google.truth)
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
|
||||
package com.itsaky.androidide.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.nio.file.Path
|
||||
|
||||
data class Location(var file: Path, var range: Range)
|
||||
|
||||
data class Position @JvmOverloads constructor(var line: Int, var column: Int, var index: Int = -1) :
|
||||
Comparable<Position> {
|
||||
data class Position @JvmOverloads constructor(
|
||||
@SerializedName("line") var line: Int,
|
||||
@SerializedName("column") var column: Int,
|
||||
@SerializedName("index") var index: Int = -1
|
||||
) : Comparable<Position> {
|
||||
|
||||
fun requireIndex(): Int {
|
||||
if (index == -1) {
|
||||
@@ -43,7 +47,9 @@ data class Position @JvmOverloads constructor(var line: Int, var column: Int, va
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField val NONE = Position(-1, -1)
|
||||
|
||||
@JvmField
|
||||
val NONE = Position(-1, -1)
|
||||
}
|
||||
|
||||
override fun compareTo(other: Position): Int {
|
||||
@@ -85,21 +91,26 @@ data class Position @JvmOverloads constructor(var line: Int, var column: Int, va
|
||||
|
||||
open class Range
|
||||
@JvmOverloads
|
||||
constructor(var start: Position = Position(0, 0), var end: Position = Position(0, 0)) :
|
||||
Comparable<Range> {
|
||||
|
||||
constructor(src: Range) : this(Position(src.start.line, src.start.column), Position(src.end.line, src.end.column))
|
||||
constructor(
|
||||
@SerializedName("start") var start: Position = Position(0, 0),
|
||||
@SerializedName("end") var end: Position = Position(0, 0)
|
||||
) : Comparable<Range> {
|
||||
|
||||
constructor(src: Range) : this(Position(src.start.line, src.start.column),
|
||||
Position(src.end.line, src.end.column))
|
||||
|
||||
companion object {
|
||||
@JvmField val NONE = Range(Position.NONE, Position.NONE)
|
||||
|
||||
@JvmField
|
||||
val NONE = Range(Position.NONE, Position.NONE)
|
||||
|
||||
@JvmStatic
|
||||
fun pointRange(line: Int, column: Int) : Range {
|
||||
fun pointRange(line: Int, column: Int): Range {
|
||||
return pointRange(Position(line, column))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun pointRange(position: Position) : Range {
|
||||
fun pointRange(position: Position): Range {
|
||||
return Range(position, position)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user