Skip to content

Commit c94d074

Browse files
authored
Merge pull request #645 from wordpress-mobile/issue/561-second-attempt-no-big-instance-data
Make sure big instance data is not stored to the default state/bundle
2 parents c94a8b4 + eb6eaa6 commit c94d074

File tree

3 files changed

+113
-78
lines changed

3 files changed

+113
-78
lines changed

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 17 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import org.wordpress.aztec.spans.UnknownClickableSpan
8585
import org.wordpress.aztec.spans.UnknownHtmlSpan
8686
import org.wordpress.aztec.toolbar.AztecToolbar
8787
import org.wordpress.aztec.util.AztecLog
88+
import org.wordpress.aztec.util.InstanceStateUtils
8889
import org.wordpress.aztec.util.SpanWrapper
8990
import org.wordpress.aztec.util.coerceToHtmlText
9091
import org.wordpress.aztec.watchers.BlockElementWatcher
@@ -108,12 +109,6 @@ import org.wordpress.aztec.watchers.event.text.BeforeTextChangedEventData
108109
import org.wordpress.aztec.watchers.event.text.OnTextChangedEventData
109110
import org.wordpress.aztec.watchers.event.text.TextWatcherEvent
110111
import org.xml.sax.Attributes
111-
import java.io.File
112-
import java.io.FileInputStream
113-
import java.io.FileOutputStream
114-
import java.io.IOException
115-
import java.io.ObjectInputStream
116-
import java.io.ObjectOutputStream
117112
import java.util.ArrayList
118113
import java.util.Arrays
119114
import java.util.LinkedList
@@ -521,17 +516,17 @@ class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknownHtmlT
521516
val savedState = state as SavedState
522517
super.onRestoreInstanceState(savedState.superState)
523518
val customState = savedState.state
524-
val array = readAndPurgeTempInstance<ArrayList<String>>(HISTORY_LIST_KEY, ArrayList<String>(), savedState.state)
519+
val array = InstanceStateUtils.readAndPurgeTempInstance<ArrayList<String>>(HISTORY_LIST_KEY, ArrayList<String>(), savedState.state)
525520
val list = LinkedList<String>()
526521

527522
list += array
528523

529524
history.historyList = list
530525
history.historyCursor = customState.getInt(HISTORY_CURSOR_KEY)
531-
history.inputLast = readAndPurgeTempInstance<String>(INPUT_LAST_KEY, "", savedState.state)
526+
history.inputLast = InstanceStateUtils.readAndPurgeTempInstance<String>(INPUT_LAST_KEY, "", savedState.state)
532527
visibility = customState.getInt(VISIBILITY_KEY)
533528

534-
val retainedHtml = readAndPurgeTempInstance<String>(RETAINED_HTML_KEY, "", savedState.state)
529+
val retainedHtml = InstanceStateUtils.readAndPurgeTempInstance<String>(RETAINED_HTML_KEY, "", savedState.state)
535530
fromHtml(retainedHtml)
536531

537532
val retainedSelectionStart = customState.getInt(SELECTION_START_KEY)
@@ -555,7 +550,7 @@ class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknownHtmlT
555550
if (retainedBlockHtmlIndex != -1) {
556551
val unknownSpan = text.getSpans(retainedBlockHtmlIndex, retainedBlockHtmlIndex + 1, UnknownHtmlSpan::class.java).firstOrNull()
557552
if (unknownSpan != null) {
558-
val retainedBlockHtml = readAndPurgeTempInstance<String>(BLOCK_EDITOR_HTML_KEY, "",
553+
val retainedBlockHtml = InstanceStateUtils.readAndPurgeTempInstance<String>(BLOCK_EDITOR_HTML_KEY, "",
559554
savedState.state)
560555
showBlockEditorDialog(unknownSpan, retainedBlockHtml)
561556
}
@@ -567,15 +562,23 @@ class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknownHtmlT
567562
enableTextChangedListener()
568563
}
569564

565+
// Do not include the content of the editor when saving state to bundle.
566+
// EditText has it `true` by default, and then the content was saved in bundle making the app crashing
567+
// due to the TransactionTooLargeException Exception.
568+
// The content is saved in tmp files in `onSaveInstanceState`. See: https://github.com/wordpress-mobile/AztecEditor-Android/pull/641
569+
override fun getFreezesText(): Boolean {
570+
return false
571+
}
572+
570573
override fun onSaveInstanceState(): Parcelable {
571574
val superState = super.onSaveInstanceState()
572575
val savedState = SavedState(superState)
573576
val bundle = Bundle()
574-
writeTempInstance(HISTORY_LIST_KEY, ArrayList<String>(history.historyList), bundle)
577+
InstanceStateUtils.writeTempInstance(context, externalLogger, HISTORY_LIST_KEY, ArrayList<String>(history.historyList), bundle)
575578
bundle.putInt(HISTORY_CURSOR_KEY, history.historyCursor)
576-
writeTempInstance(INPUT_LAST_KEY, history.inputLast, bundle)
579+
InstanceStateUtils.writeTempInstance(context, externalLogger, INPUT_LAST_KEY, history.inputLast, bundle)
577580
bundle.putInt(VISIBILITY_KEY, visibility)
578-
writeTempInstance(RETAINED_HTML_KEY, toHtml(false), bundle)
581+
InstanceStateUtils.writeTempInstance(context, externalLogger, RETAINED_HTML_KEY, toHtml(false), bundle)
579582
bundle.putInt(SELECTION_START_KEY, selectionStart)
580583
bundle.putInt(SELECTION_END_KEY, selectionEnd)
581584

@@ -594,7 +597,7 @@ class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknownHtmlT
594597

595598
bundle.putBoolean(BLOCK_DIALOG_VISIBLE_KEY, true)
596599
bundle.putInt(BLOCK_EDITOR_START_INDEX_KEY, unknownBlockSpanStart)
597-
writeTempInstance(BLOCK_EDITOR_HTML_KEY, source?.getPureHtml(false), bundle)
600+
InstanceStateUtils.writeTempInstance(context, externalLogger, BLOCK_EDITOR_HTML_KEY, source?.getPureHtml(false), bundle)
598601
}
599602

600603
bundle.putBoolean(IS_MEDIA_ADDED_KEY, isMediaAdded)
@@ -603,69 +606,6 @@ class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknownHtmlT
603606
return savedState
604607
}
605608

606-
private fun cacheFilenameKey(varName: String): String {
607-
return "CACHEFILENAMEKEY_$varName"
608-
}
609-
610-
private fun logCacheWriteException(varName: String, e: Exception) {
611-
AppLog.w(AppLog.T.EDITOR, "Error trying to write cache for $varName. Exception: ${e.message}")
612-
externalLogger?.logException(e, "Error trying to write cache for $varName.")
613-
}
614-
615-
private fun writeTempInstance(varName: String, obj: Any?, bundle: Bundle) {
616-
try {
617-
with(File.createTempFile(varName, ".inst", context.getCacheDir())) {
618-
deleteOnExit() // just make sure if we miss deleting this cache file the VM will eventually do it
619-
620-
FileOutputStream(this).use { output ->
621-
ObjectOutputStream(output).use { objectOutput ->
622-
objectOutput.writeObject(obj)
623-
624-
// keep the filename in the bundle to use it to read the object back
625-
bundle.putString(cacheFilenameKey(varName), this.path)
626-
}
627-
}
628-
}
629-
} catch (e: IOException) {
630-
logCacheWriteException(varName, e)
631-
} catch (e: SecurityException) {
632-
logCacheWriteException(varName, e)
633-
} catch (e: NullPointerException) {
634-
logCacheWriteException(varName, e)
635-
}
636-
}
637-
638-
private fun <T> readAndPurgeTempInstance(varName: String, defaultValue: T, bundle: Bundle): T {
639-
// the full path is kept in the bundle so, get it from there
640-
val filename = bundle.getString(cacheFilenameKey(varName))
641-
642-
if (TextUtils.isEmpty(filename)) {
643-
return defaultValue
644-
}
645-
646-
val file = File(filename)
647-
648-
if (!file.exists()) {
649-
return defaultValue
650-
}
651-
652-
var obj: T = defaultValue
653-
654-
with(file) {
655-
FileInputStream(this).use { input ->
656-
ObjectInputStream(input).use { objectInput ->
657-
val r: Any? = objectInput.readObject()
658-
659-
@Suppress("UNCHECKED_CAST")
660-
obj = (r ?: defaultValue) as T
661-
}
662-
}
663-
delete() // eagerly delete the cache file. If any is missed the VM will delete it on reboot.
664-
}
665-
666-
return obj
667-
}
668-
669609
internal class SavedState : BaseSavedState {
670610
var state: Bundle = Bundle()
671611

aztec/src/main/kotlin/org/wordpress/aztec/source/SourceViewEditText.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import org.wordpress.aztec.AztecText
1717
import org.wordpress.aztec.History
1818
import org.wordpress.aztec.R
1919
import org.wordpress.aztec.spans.AztecCursorSpan
20+
import org.wordpress.aztec.util.InstanceStateUtils
2021

2122
@SuppressLint("SupportAnnotationUsage")
2223
class SourceViewEditText : android.support.v7.widget.AppCompatEditText, TextWatcher {
24+
companion object {
25+
val RETAINED_CONTENT_KEY = "RETAINED_CONTENT_KEY"
26+
}
2327

2428
@ColorInt var tagColor = ContextCompat.getColor(context, R.color.html_tag)
2529
internal set
@@ -84,12 +88,23 @@ class SourceViewEditText : android.support.v7.widget.AppCompatEditText, TextWatc
8488
super.onRestoreInstanceState(savedState.superState)
8589
val customState = savedState.state
8690
visibility = customState.getInt("visibility")
91+
val retainedContent = InstanceStateUtils.readAndPurgeTempInstance<String>(RETAINED_CONTENT_KEY, "", savedState.state)
92+
setText(retainedContent)
93+
}
94+
95+
// Do not include the content of the editor when saving state to bundle.
96+
// EditText has it `true` by default, and then the content was saved in bundle making the app crashing
97+
// due to the TransactionTooLargeException Exception.
98+
// The content is saved in tmp files in `onSaveInstanceState`. See: https://github.com/wordpress-mobile/AztecEditor-Android/pull/641
99+
override fun getFreezesText(): Boolean {
100+
return false
87101
}
88102

89103
override fun onSaveInstanceState(): Parcelable {
104+
val bundle = Bundle()
105+
InstanceStateUtils.writeTempInstance(context, null, RETAINED_CONTENT_KEY, text.toString(), bundle)
90106
val superState = super.onSaveInstanceState()
91107
val savedState = SavedState(superState)
92-
val bundle = Bundle()
93108
bundle.putInt("visibility", visibility)
94109
savedState.state = bundle
95110
return savedState
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.wordpress.aztec.util
2+
3+
import android.content.Context
4+
import android.os.Bundle
5+
import android.text.TextUtils
6+
import org.wordpress.android.util.AppLog
7+
import java.io.File
8+
import java.io.FileInputStream
9+
import java.io.FileOutputStream
10+
import java.io.IOException
11+
import java.io.ObjectInputStream
12+
import java.io.ObjectOutputStream
13+
14+
class InstanceStateUtils {
15+
companion object {
16+
17+
private fun cacheFilenameKey(varName: String): String {
18+
return "CACHEFILENAMEKEY_$varName"
19+
}
20+
21+
private fun logCacheWriteException(externalLogger: AztecLog.ExternalLogger?, varName: String, e: Exception) {
22+
AppLog.w(AppLog.T.EDITOR, "Error trying to write cache for $varName. Exception: ${e.message}")
23+
externalLogger?.logException(e, "Error trying to write cache for $varName.")
24+
}
25+
26+
open fun writeTempInstance(context: Context, externalLogger: AztecLog.ExternalLogger?, varName: String, obj: Any?, bundle: Bundle) {
27+
try {
28+
with(File.createTempFile(varName, ".inst", context.getCacheDir())) {
29+
deleteOnExit() // just make sure if we miss deleting this cache file the VM will eventually do it
30+
31+
FileOutputStream(this).use { output ->
32+
ObjectOutputStream(output).use { objectOutput ->
33+
objectOutput.writeObject(obj)
34+
35+
// keep the filename in the bundle to use it to read the object back
36+
bundle.putString(cacheFilenameKey(varName), this.path)
37+
}
38+
}
39+
}
40+
} catch (e: IOException) {
41+
logCacheWriteException(externalLogger, varName, e)
42+
} catch (e: SecurityException) {
43+
logCacheWriteException(externalLogger, varName, e)
44+
} catch (e: NullPointerException) {
45+
logCacheWriteException(externalLogger, varName, e)
46+
}
47+
}
48+
49+
open fun <T> readAndPurgeTempInstance(varName: String, defaultValue: T, bundle: Bundle): T {
50+
// the full path is kept in the bundle so, get it from there
51+
val filename = bundle.getString(cacheFilenameKey(varName))
52+
53+
if (TextUtils.isEmpty(filename)) {
54+
return defaultValue
55+
}
56+
57+
val file = File(filename)
58+
59+
if (!file.exists()) {
60+
return defaultValue
61+
}
62+
63+
var obj: T = defaultValue
64+
65+
with(file) {
66+
FileInputStream(this).use { input ->
67+
ObjectInputStream(input).use { objectInput ->
68+
val r: Any? = objectInput.readObject()
69+
70+
@Suppress("UNCHECKED_CAST")
71+
obj = (r ?: defaultValue) as T
72+
}
73+
}
74+
delete() // eagerly delete the cache file. If any is missed the VM will delete it on reboot.
75+
}
76+
77+
return obj
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)