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

Saving and Restoring the Web View Navigation History Across Sessions #3996

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import org.kiwix.kiwixmobile.core.main.RestoreOrigin
import org.kiwix.kiwixmobile.core.main.RestoreOrigin.FromExternalLaunch
import org.kiwix.kiwixmobile.core.main.RestoreOrigin.FromSearchScreen
import org.kiwix.kiwixmobile.core.main.ToolbarScrollingKiwixWebView
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseValue
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
Expand Down Expand Up @@ -225,30 +226,29 @@ class KiwixReaderFragment : CoreReaderFragment() {
}
}

override fun restoreViewStateOnInvalidJSON() {
override fun restoreViewStateOnInvalidWebViewHistory() {
Log.d(TAG_KIWIX, "Kiwix normal start, no zimFile loaded last time -> display home page")
exitBook()
}

/**
* Restores the view state based on the provided JSON data and restore origin.
* Restores the view state based on the provided webViewHistoryItemList data and restore origin.
*
* Depending on the `restoreOrigin`, this method either restores the last opened ZIM file
* (if the launch is external) or skips re-opening the ZIM file when coming from the search screen,
* as the ZIM file is already set in the reader. The method handles setting up the ZIM file and bookmarks,
* and restores the tabs and positions from the provided data.
*
* @param zimArticles JSON string representing the list of articles to be restored.
* @param zimPositions JSON string representing the positions of the restored articles.
* @param currentTab Index of the tab to be restored as the currently active one.
* @param webViewHistoryItemList WebViewHistoryItem list representing the list of articles to be restored.
* @param currentTab Index of the tab to be restored as the currently active one.
* @param restoreOrigin Indicates whether the restoration is triggered from an external launch or the search screen.
* @param onComplete Callback to be invoked upon completion of the restoration process.
*/

override fun restoreViewStateOnValidJSON(
zimArticles: String?,
zimPositions: String?,
override fun restoreViewStateOnValidWebViewHistory(
webViewHistoryItemList: List<WebViewHistoryItem>,
currentTab: Int,
restoreOrigin: RestoreOrigin
restoreOrigin: RestoreOrigin,
onComplete: () -> Unit
) {
when (restoreOrigin) {
FromExternalLaunch -> {
Expand All @@ -258,15 +258,15 @@ class KiwixReaderFragment : CoreReaderFragment() {
val zimReaderSource = fromDatabaseValue(settings.getString(TAG_CURRENT_FILE, null))
if (zimReaderSource?.canOpenInLibkiwix() == true) {
if (zimReaderContainer?.zimReaderSource == null) {
openZimFile(zimReaderSource)
openZimFile(zimReaderSource, isFromManageExternalLaunch = true)
Log.d(
TAG_KIWIX,
"Kiwix normal start, Opened last used zimFile: -> ${zimReaderSource.toDatabase()}"
)
} else {
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
}
restoreTabs(zimArticles, zimPositions, currentTab)
restoreTabs(webViewHistoryItemList, currentTab, onComplete)
} else {
getCurrentWebView()?.snack(string.zim_not_opened)
exitBook() // hide the options for zim file to avoid unexpected UI behavior
Expand All @@ -275,7 +275,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
}

FromSearchScreen -> {
restoreTabs(zimArticles, zimPositions, currentTab)
restoreTabs(webViewHistoryItemList, currentTab, onComplete)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/detekt_baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
<ID>LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" )</ID>
<ID>LongParameterList:Repository.kt$Repository$( @param:IO private val ioThread: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val notesRoomDao: NotesRoomDao, private val languageDao: NewLanguagesDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer )</ID>
<ID>LongParameterList:Repository.kt$Repository$( @param:IO private val ioThread: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao, private val notesRoomDao: NotesRoomDao, private val languageDao: NewLanguagesDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer )</ID>
<ID>LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup, videoView: ViewGroup, webViewClient: CoreWebViewClient, private val toolbarView: View, private val bottomBarView: View, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null )</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ abstract class HistoryRoomDao : PageDao {
historyItem.dateString
)?.let {
it.apply {
// update the exiting entity
// update the existing entity
historyUrl = historyItem.historyUrl
historyTitle = historyItem.title
timeStamp = historyItem.timeStamp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Kiwix Android
* Copyright (c) 2024 Kiwix <android.kiwix.org>
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package org.kiwix.kiwixmobile.core.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import io.reactivex.Flowable
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity

@Dao
abstract class WebViewHistoryRoomDao {

fun insertWebViewPageHistoryItem(webViewHistoryEntity: WebViewHistoryEntity) {
insertWebViewPageHistoryItems(listOf(webViewHistoryEntity))
}

@Insert
abstract fun insertWebViewPageHistoryItems(webViewHistoryEntityList: List<WebViewHistoryEntity>)

@Query("SELECT * FROM WebViewHistoryEntity ORDER BY webViewIndex ASC")
abstract fun getAllWebViewPagesHistory(): Flowable<List<WebViewHistoryEntity>>

@Query("Delete from WebViewHistoryEntity")
abstract fun clearWebViewPagesHistory()

fun clearPageHistoryWithPrimaryKey() {
clearWebViewPagesHistory()
}

@Query("DELETE FROM sqlite_sequence WHERE name='PageHistoryRoomEntity'")
abstract fun resetPrimaryKey()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Kiwix Android
* Copyright (c) 2024 Kiwix <android.kiwix.org>
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package org.kiwix.kiwixmobile.core.dao.entities

import android.os.Bundle
import android.os.Parcel
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem

@Entity
data class WebViewHistoryEntity(
@PrimaryKey(autoGenerate = true) var id: Long = 0L,
val zimId: String,
val webViewIndex: Int,
val webViewCurrentPosition: Int,
@TypeConverters(BundleRoomConverter::class)
val webViewBackForwardListBundle: Bundle?
) {
constructor(webViewHistoryItem: WebViewHistoryItem) : this(
webViewHistoryItem.databaseId,
webViewHistoryItem.zimId,
webViewHistoryItem.webViewIndex,
webViewHistoryItem.webViewCurrentPosition,
webViewHistoryItem.webViewBackForwardListBundle,
)
}

class BundleRoomConverter {
@TypeConverter
fun convertToDatabaseValue(bundle: Bundle?): ByteArray? {
if (bundle == null) return null
val parcel = Parcel.obtain()
parcel.writeBundle(bundle)
val bytes = parcel.marshall()
parcel.recycle()
return bytes
}

@TypeConverter
fun convertToEntityProperty(byteArray: ByteArray?): Bundle? {
if (byteArray == null) return null
val parcel = Parcel.obtain()
parcel.unmarshall(byteArray, 0, byteArray.size)
parcel.setDataPosition(0)
val bundle = parcel.readBundle(Bundle::class.java.classLoader)
parcel.recycle()
return bundle
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.core.data
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Single
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
Expand Down Expand Up @@ -53,4 +54,8 @@ interface DataSource {
fun saveNote(noteListItem: NoteListItem): Completable
fun deleteNote(noteTitle: String): Completable
fun deleteNotes(noteList: List<NoteListItem>): Completable

suspend fun insertWebViewPageHistoryItems(webViewHistoryEntityList: List<WebViewHistoryEntity>)
fun getAllWebViewPagesHistory(): Single<List<WebViewHistoryEntity>>
suspend fun clearWebViewPagesHistory()
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.HistoryRoomDaoCoverts
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.entities.BundleRoomConverter
import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter

@Suppress("UnnecessaryAbstractClass")
Expand All @@ -42,17 +45,23 @@ import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter
RecentSearchRoomEntity::class,
HistoryRoomEntity::class,
NotesRoomEntity::class,
DownloadRoomEntity::class
DownloadRoomEntity::class,
WebViewHistoryEntity::class
],
version = 5,
version = 6,
exportSchema = false
)
@TypeConverters(HistoryRoomDaoCoverts::class, ZimSourceRoomConverter::class)
@TypeConverters(
HistoryRoomDaoCoverts::class,
ZimSourceRoomConverter::class,
BundleRoomConverter::class
)
abstract class KiwixRoomDatabase : RoomDatabase() {
abstract fun recentSearchRoomDao(): RecentSearchRoomDao
abstract fun historyRoomDao(): HistoryRoomDao
abstract fun notesRoomDao(): NotesRoomDao
abstract fun downloadRoomDao(): DownloadRoomDao
abstract fun webViewHistoryRoomDao(): WebViewHistoryRoomDao

companion object {
private var db: KiwixRoomDatabase? = null
Expand All @@ -62,7 +71,13 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
?: Room.databaseBuilder(context, KiwixRoomDatabase::class.java, "KiwixRoom.db")
// We have already database name called kiwix.db in order to avoid complexity we named
// as kiwixRoom.db
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
.addMigrations(
MIGRATION_1_2,
MIGRATION_2_3,
MIGRATION_3_4,
MIGRATION_4_5,
MIGRATION_5_6
)
.build().also { db = it }
}
}
Expand Down Expand Up @@ -202,6 +217,23 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
}
}

@Suppress("MagicNumber")
private val MIGRATION_5_6 = object : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `WebViewHistoryEntity` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`zimId` TEXT NOT NULL,
`webViewIndex` INTEGER NOT NULL,
`webViewCurrentPosition` INTEGER NOT NULL,
`webViewBackForwardListBundle` BLOB NULL
)
"""
)
}
}

fun destroyInstance() {
db = null
}
Expand Down
19 changes: 19 additions & 0 deletions core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
import org.kiwix.kiwixmobile.core.di.qualifiers.IO
import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread
import org.kiwix.kiwixmobile.core.extensions.HeaderizableList
Expand All @@ -55,6 +57,7 @@ class Repository @Inject internal constructor(
private val bookDao: NewBookDao,
private val libkiwixBookmarks: LibkiwixBookmarks,
private val historyRoomDao: HistoryRoomDao,
private val webViewHistoryRoomDao: WebViewHistoryRoomDao,
private val notesRoomDao: NotesRoomDao,
private val languageDao: NewLanguagesDao,
private val recentSearchRoomDao: RecentSearchRoomDao,
Expand Down Expand Up @@ -144,6 +147,22 @@ class Repository @Inject internal constructor(
Completable.fromAction { notesRoomDao.deleteNotes(noteList) }
.subscribeOn(ioThread)

override suspend fun insertWebViewPageHistoryItems(
webViewHistoryEntityList: List<WebViewHistoryEntity>
) {
webViewHistoryRoomDao.insertWebViewPageHistoryItems(webViewHistoryEntityList)
}

override fun getAllWebViewPagesHistory() =
webViewHistoryRoomDao.getAllWebViewPagesHistory()
.first(emptyList())
.subscribeOn(ioThread)
.observeOn(mainThread)

override suspend fun clearWebViewPagesHistory() {
webViewHistoryRoomDao.clearWebViewPagesHistory()
}

override fun deleteNote(noteTitle: String): Completable =
Completable.fromAction { notesRoomDao.deleteNote(noteTitle) }
.subscribeOn(ioThread)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
import org.kiwix.kiwixmobile.core.data.DataModule
import org.kiwix.kiwixmobile.core.data.DataSource
Expand Down Expand Up @@ -107,6 +108,7 @@ interface CoreComponent {
fun libkiwixBookmarks(): LibkiwixBookmarks
fun recentSearchRoomDao(): RecentSearchRoomDao
fun historyRoomDao(): HistoryRoomDao
fun webViewHistoryRoomDao(): WebViewHistoryRoomDao
fun noteRoomDao(): NotesRoomDao
fun objectBoxToRoomMigrator(): ObjectBoxToRoomMigrator
fun context(): Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ open class DatabaseModule {
@Singleton
fun provideHistoryDao(db: KiwixRoomDatabase) = db.historyRoomDao()

@Provides
@Singleton
fun provideWebViewHistoryRoomDao(db: KiwixRoomDatabase) = db.webViewHistoryRoomDao()

@Singleton
@Provides
fun provideNoteRoomDao(db: KiwixRoomDatabase) = db.notesRoomDao()
Expand Down
Loading
Loading