Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save edited HTML on configuration changed #15

Merged
merged 7 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
LunarX marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.infomaniak.lib.richhtmleditor

import android.os.Looper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

/**
* A utility class to help easily reload the html content of a [RichHtmlEditorWebView] on configuration changes.
*
* This class is meant to be instantiated inside a ViewModel to be able to retain the HTML content through configuration changes.
*
* @param coroutineScope A coroutine scope where the exportation of the HTML will be processed. Preferably the viewModelScope of
* the ViewModel where this class has been instantiated.
*
* @see load
* @see save
*/
class EditorReloader(private val coroutineScope: CoroutineScope) {

private var needToReloadHtml: Boolean = false
private var savedHtml = MutableStateFlow<String?>(null)

/**
* An alternative for loading the HTML content of the [RichHtmlEditorWebView].
*
* This method can be called within the `onViewCreated()` method of the Fragment containing your [RichHtmlEditorWebView].
* On the initial call, it loads the default HTML content. For all subsequent calls, it reloads the previously loaded HTML
* content.
*
* @param editor The editor to load content into.
* @param defaultHtml The HTML to load on the initial call. On subsequent calls, this value won't be taken into account.
*
* Usage:
* ```
* override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
* super.onViewCreated(view, savedInstanceState)
* lifecycleScope.launch {
* viewModel.editorReloader.load(editor, "<p>Hello World</p>")
* }
* }
* ```
*
* @throws IllegalStateException If the method is not called on the main thread.
*/
suspend fun load(editor: RichHtmlEditorWebView, defaultHtml: String) {
if (Looper.myLooper() != Looper.getMainLooper()) error("The load method needs to be called on the main thread")

if (needToReloadHtml) {
LunarX marked this conversation as resolved.
Show resolved Hide resolved
savedHtml.collectLatest {
if (it == null) return@collectLatest

resetSavedHtml()
editor.setHtml(it)
}
} else {
editor.setHtml(defaultHtml)
}

enableHtmlReload()
}

/**
* Exports and saves the editor's HTML content for later reloading.
*
* This method should be called within the `onSaveInstanceState()` method of the Fragment.
*
* @param editor The editor whose content needs to be saved.
*
* Usage:
* ```
* override fun onSaveInstanceState(outState: Bundle) {
* super.onSaveInstanceState(outState)
* viewModel.editorReloader.save(editor)
* }
* ```
*/
fun save(editor: RichHtmlEditorWebView) {
editor.exportHtml {
coroutineScope.launch { savedHtml.emit(it) }
}
}

private suspend fun resetSavedHtml() {
savedHtml.emit(null)
}

private fun enableHtmlReload() {
needToReloadHtml = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.view.inputmethod.InputMethodManager
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.infomaniak.lib.richhtmleditor.sample.databinding.CreateLinkTextInputBinding
Expand All @@ -22,6 +23,8 @@ class EditorSampleFragment : Fragment() {
private var _binding: FragmentEditorSampleBinding? = null
private val binding get() = _binding!! // This property is only valid between onCreateView and onDestroyView

private val editorSampleViewModel: EditorSampleViewModel by activityViewModels()

private val createLinkDialog by lazy { CreateLinkDialog() }

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
Expand All @@ -31,25 +34,28 @@ class EditorSampleFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = with(binding) {
super.onViewCreated(view, savedInstanceState)

val html = readAsset("example1.html")

setToolbarEnabledStatus(false)

setEditorContent()
editor.apply {
// You can add custom scripts and css such as:
// addCss(readAsset("editor_custom_css.css"))
// addScript("document.body.style['background'] = '#00FFFF'")

setHtml(html)

isVisible = true

setOnFocusChangeListener { _, hasFocus -> setToolbarEnabledStatus(hasFocus) }
}

setEditorButtonClickListeners()
observeEditorStatusUpdates()
}

private fun setEditorContent() {
lifecycleScope.launch {
editorSampleViewModel.editorReloader.load(binding.editor, readAsset("example1.html"))
}
}

private fun setEditorButtonClickListeners() = with(binding) {
buttonBold.setOnClickListener { editor.toggleBold() }
buttonItalic.setOnClickListener { editor.toggleItalic() }
Expand Down Expand Up @@ -89,6 +95,11 @@ class EditorSampleFragment : Fragment() {
}
}

override fun onSaveInstanceState(outState: Bundle) {
editorSampleViewModel.editorReloader.save(binding.editor)
super.onSaveInstanceState(outState)
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.infomaniak.lib.richhtmleditor.sample

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.infomaniak.lib.richhtmleditor.EditorReloader

class EditorSampleViewModel : ViewModel() {
val editorReloader = EditorReloader(viewModelScope)
}
Loading