Skip to content

Commit 73ff502

Browse files
authored
Merge branch 'stage' into fix/ADFA-3489-overlay-permissions
2 parents b4bff6d + 82c6e60 commit 73ff502

File tree

4 files changed

+78
-18
lines changed

4 files changed

+78
-18
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
android:theme="@style/Theme.AndroidIDE" />
9494
<activity
9595
android:name=".activities.editor.EditorActivityKt"
96-
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
96+
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|fontScale"
9797
android:launchMode="singleTask"
9898
android:windowSoftInputMode="adjustResize" />
9999
<activity

app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ open class EditorHandlerActivity :
232232
loadPluginTabs()
233233
}
234234

235+
/**
236+
* Persists which tabs are open (preferences only). Does **not** write project file buffers to disk;
237+
* saving is explicit or prompted (e.g. close project).
238+
*/
235239
override fun onPause() {
236240
super.onPause()
237241
// Record timestamps for all currently open files before saving the cache
@@ -246,7 +250,6 @@ open class EditorHandlerActivity :
246250
if (!isOpenedFilesSaved.get()) {
247251
saveOpenedFiles()
248252
saveOpenedPluginTabs()
249-
saveAllAsync(notify = false)
250253
}
251254
}
252255

@@ -271,26 +274,29 @@ open class EditorHandlerActivity :
271274
invalidateOptionsMenu()
272275
}
273276

277+
/**
278+
* Reloads disk content into an open editor only when the file changed on disk since the last
279+
* [onPause] snapshot **and** the in-memory buffer is still clean ([CodeEditorView.isModified] is
280+
* false). A clean buffer may still have undo history after [IDEEditor.markUnmodified] / save; we
281+
* reload anyway so external edits are not ignored. Never replaces buffers with unsaved edits.
282+
*/
274283
private fun checkForExternalFileChanges() {
275-
// Get the list of files currently managed by the ViewModel
276284
val openFiles = editorViewModel.getOpenedFiles()
277285
if (openFiles.isEmpty() || fileTimestamps.isEmpty()) return
278286

279287
lifecycleScope.launch(Dispatchers.IO) {
280-
// Check each open file
281288
openFiles.forEach { file ->
282289
val lastKnownTimestamp = fileTimestamps[file.absolutePath] ?: return@forEach
283290
val currentTimestamp = file.lastModified()
284291

285-
// If the file on disk is newer.
286292
if (currentTimestamp > lastKnownTimestamp) {
287293
val newContent = runCatching { file.readText() }.getOrNull() ?: return@forEach
288294
withContext(Dispatchers.Main) {
289-
// If the editor for the new file exists AND has no unsaved changes...
290295
val editorView = getEditorForFile(file) ?: return@withContext
291296
if (editorView.isModified) return@withContext
297+
val ideEditor = editorView.editor ?: return@withContext
292298

293-
editorView.editor?.setText(newContent)
299+
ideEditor.setText(newContent)
294300
editorView.markAsSaved()
295301
updateTabs()
296302
}
@@ -341,12 +347,19 @@ open class EditorHandlerActivity :
341347
prefs.getString(PREF_KEY_OPEN_FILES_CACHE, null)
342348
} ?: return@launch
343349

350+
if (editorViewModel.getOpenedFileCount() > 0) {
351+
// Returning to an in-memory session (e.g. after onPause/onStop). Replaying the
352+
// snapshot would be redundant and could interfere with dirty buffers and undo.
353+
withContext(Dispatchers.IO) { prefs.putString(PREF_KEY_OPEN_FILES_CACHE, null) }
354+
return@launch
355+
}
356+
344357
val cache = withContext(Dispatchers.Default) {
345358
Gson().fromJson(jsonCache, OpenedFilesCache::class.java)
346359
}
347360
onReadOpenedFilesCache(cache)
348361

349-
// Clear the preference so it's only loaded once on startup
362+
// Clear the preference so it's only loaded once per cold restore
350363
withContext(Dispatchers.IO) { prefs.putString(PREF_KEY_OPEN_FILES_CACHE, null) }
351364
} catch (err: Throwable) {
352365
log.error("Failed to reopen recently opened files", err)
@@ -747,6 +760,11 @@ open class EditorHandlerActivity :
747760
override fun onConfigurationChanged(newConfig: Configuration) {
748761
super.onConfigurationChanged(newConfig)
749762

763+
val safeContent = contentOrNull ?: return
764+
for (i in 0 until safeContent.editorContainer.childCount) {
765+
(safeContent.editorContainer.getChildAt(i) as? CodeEditorView)?.reapplyEditorDisplayPreferences()
766+
}
767+
750768
getCurrentEditor()?.editor?.apply {
751769
doOnNextLayout {
752770
cursor?.let { c -> ensurePositionVisible(c.leftLine, c.leftColumn, true) }
@@ -1069,17 +1087,20 @@ open class EditorHandlerActivity :
10691087
nameBuilder.addPath(it, it.path)
10701088
}
10711089

1072-
for (index in 0 until content.tabs.tabCount) {
1073-
val file = files.getOrNull(index) ?: continue
1090+
for (tabPos in 0 until content.tabs.tabCount) {
1091+
if (isPluginTab(tabPos)) continue
1092+
val fileIndex = getFileIndexForTabPosition(tabPos)
1093+
if (fileIndex < 0) continue
1094+
val file = files.getOrNull(fileIndex) ?: continue
10741095
val count = dupliCount[file.name] ?: 0
10751096

1076-
val isModified = getEditorAtIndex(index)?.isModified ?: false
1097+
val isModified = getEditorAtIndex(fileIndex)?.isModified ?: false
10771098
var name = if (count > 1) nameBuilder.getShortPath(file) else file.name
10781099
if (isModified) {
10791100
name = "*$name"
10801101
}
10811102

1082-
names[index] = name to FileExtension.Factory.forFile(file, file.isDirectory).icon
1103+
names[tabPos] = name to FileExtension.Factory.forFile(file, file.isDirectory).icon
10831104
}
10841105

10851106
withContext(Dispatchers.Main) {

app/src/main/java/com/itsaky/androidide/fragments/RecyclerViewFragment.kt

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ package com.itsaky.androidide.fragments
2020
import android.annotation.SuppressLint
2121
import android.os.Bundle
2222
import android.view.GestureDetector
23+
import android.view.LayoutInflater
2324
import android.view.MotionEvent
2425
import android.view.View
26+
import android.view.ViewGroup
2527
import androidx.recyclerview.widget.LinearLayoutManager
2628
import androidx.recyclerview.widget.RecyclerView
2729
import androidx.recyclerview.widget.RecyclerView.LayoutManager
28-
import com.itsaky.androidide.databinding.FragmentRecyclerviewBinding
30+
import androidx.viewbinding.ViewBinding
31+
import com.itsaky.androidide.R
2932
import com.itsaky.androidide.idetooltips.TooltipManager
3033

3134
/**
@@ -34,7 +37,7 @@ import com.itsaky.androidide.idetooltips.TooltipManager
3437
* @author Akash Yadav
3538
*/
3639
abstract class RecyclerViewFragment<A : RecyclerView.Adapter<*>> :
37-
EmptyStateFragment<FragmentRecyclerviewBinding>(FragmentRecyclerviewBinding::inflate) {
40+
EmptyStateFragment<FragmentRecyclerviewManualBinding>(FragmentRecyclerviewManualBinding::inflate) {
3841
protected abstract val fragmentTooltipTag: String?
3942

4043
private var unsavedAdapter: A? = null
@@ -86,7 +89,7 @@ abstract class RecyclerViewFragment<A : RecyclerView.Adapter<*>> :
8689
* Sets up the recycler view in the fragment.
8790
*/
8891
protected open fun onSetupRecyclerView() {
89-
binding.root.apply {
92+
binding.list.apply {
9093
layoutManager = onCreateLayoutManager()
9194
adapter = unsavedAdapter ?: onCreateAdapter()
9295
}
@@ -107,7 +110,7 @@ abstract class RecyclerViewFragment<A : RecyclerView.Adapter<*>> :
107110

108111
onSetupRecyclerView()
109112

110-
binding.root.addOnItemTouchListener(touchListener)
113+
binding.list.addOnItemTouchListener(touchListener)
111114

112115
unsavedAdapter = null
113116

@@ -123,7 +126,7 @@ abstract class RecyclerViewFragment<A : RecyclerView.Adapter<*>> :
123126
* Set the adapter for the [RecyclerView].
124127
*/
125128
fun setAdapter(adapter: A) {
126-
_binding?.root?.let { list -> list.adapter = adapter } ?: run { unsavedAdapter = adapter }
129+
_binding?.list?.let { list -> list.adapter = adapter } ?: run { unsavedAdapter = adapter }
127130
if (isAdded && view != null) {
128131
checkIsEmpty()
129132
}
@@ -142,6 +145,33 @@ abstract class RecyclerViewFragment<A : RecyclerView.Adapter<*>> :
142145

143146
private fun checkIsEmpty() {
144147
if (!isAdded || isDetached) return
145-
isEmpty = _binding?.root?.adapter?.itemCount == 0
148+
isEmpty = _binding?.list?.adapter?.itemCount == 0
149+
}
150+
}
151+
152+
/**
153+
* Manual [ViewBinding] for [R.layout.fragment_recyclerview] so annotation processors (kapt) do not
154+
* depend on generated `FragmentRecyclerviewBinding` during stub analysis.
155+
*
156+
* Public (not internal/file-private): [RecyclerViewFragment] is public and Kotlin forbids a public
157+
* class from using a non-public type as a [EmptyStateFragment] type argument.
158+
*
159+
* [getRoot] returns [RecyclerView] (covariant override), matching generated view binding so
160+
* subclasses can use `binding.root.adapter` and other [RecyclerView] APIs.
161+
*/
162+
class FragmentRecyclerviewManualBinding(
163+
val list: RecyclerView,
164+
) : ViewBinding {
165+
override fun getRoot(): RecyclerView = list
166+
167+
companion object {
168+
fun inflate(
169+
inflater: LayoutInflater,
170+
parent: ViewGroup?,
171+
attachToParent: Boolean,
172+
): FragmentRecyclerviewManualBinding {
173+
val root = inflater.inflate(R.layout.fragment_recyclerview, parent, false) as RecyclerView
174+
return FragmentRecyclerviewManualBinding(root)
175+
}
146176
}
147177
}

app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,15 @@ class CodeEditorView(
567567
onPinLineNumbersPrefChanged()
568568
}
569569

570+
/**
571+
* Re-applies display-related preferences (font size, typeface, flags) after a configuration change
572+
* such as system font scale, so the editor activity can handle `fontScale` without being recreated.
573+
*/
574+
fun reapplyEditorDisplayPreferences() {
575+
if (_binding == null) return
576+
configureEditorIfNeeded()
577+
}
578+
570579
private fun onMagnifierPrefChanged() {
571580
binding.editor.getComponent(Magnifier::class.java).isEnabled =
572581
EditorPreferences.useMagnifier

0 commit comments

Comments
 (0)