Merge pull request #2695 from 4shutosh/all_notes_page

Accessing all notes inside App
This commit is contained in:
Kelson 2022-07-16 18:46:59 +02:00 committed by GitHub
commit e44987bcdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 878 additions and 23 deletions

View File

@ -55,6 +55,7 @@ class KiwixMainActivity : CoreMainActivity() {
override val bookmarksFragmentResId: Int = R.id.bookmarksFragment override val bookmarksFragmentResId: Int = R.id.bookmarksFragment
override val settingsFragmentResId: Int = R.id.kiwixSettingsFragment override val settingsFragmentResId: Int = R.id.kiwixSettingsFragment
override val historyFragmentResId: Int = R.id.historyFragment override val historyFragmentResId: Int = R.id.historyFragment
override val notesFragmentResId: Int = R.id.notesFragment
override val readerFragmentResId: Int = R.id.readerFragment override val readerFragmentResId: Int = R.id.readerFragment
override val helpFragmentResId: Int = R.id.helpFragment override val helpFragmentResId: Int = R.id.helpFragment
override val topLevelDestinations = override val topLevelDestinations =

View File

@ -86,6 +86,11 @@
android:name="org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment" android:name="org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment"
android:label="BookmarksFragment" android:label="BookmarksFragment"
tools:layout="@layout/fragment_page" /> tools:layout="@layout/fragment_page" />
<fragment
android:id="@+id/notesFragment"
android:name="org.kiwix.kiwixmobile.core.page.notes.NotesFragment"
android:label="NotesFragment"
tools:layout="@layout/fragment_page" />
<fragment <fragment
android:id="@+id/introFragment" android:id="@+id/introFragment"
android:name="org.kiwix.kiwixmobile.intro.IntroFragment" android:name="org.kiwix.kiwixmobile.intro.IntroFragment"

View File

@ -376,9 +376,53 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "10:3205842982118792800",
"lastPropertyId": "9:5286545520416917562",
"name": "NotesEntity",
"properties": [
{
"id": "1:7685155280711004546",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4525352813855835976",
"name": "zimId",
"type": 9
},
{
"id": "4:4488566778591049174",
"name": "zimFilePath",
"type": 9
},
{
"id": "5:7998345745727500384",
"name": "noteTitle",
"type": 9
},
{
"id": "7:2160052450778801841",
"name": "favicon",
"type": 9
},
{
"id": "8:4304230668306976620",
"name": "noteFilePath",
"type": 9
},
{
"id": "9:5286545520416917562",
"name": "zimUrl",
"type": 9
}
],
"relations": []
} }
], ],
"lastEntityId": "8:8093454424037540087", "lastEntityId": "10:3205842982118792800",
"lastIndexId": "4:4868787482832538530", "lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
@ -386,7 +430,8 @@
"modelVersionParserMinimum": 5, "modelVersionParserMinimum": 5,
"retiredEntityUids": [ "retiredEntityUids": [
349148274283701276, 349148274283701276,
7257718270326155947 7257718270326155947,
7394649290555378565
], ],
"retiredIndexUids": [ "retiredIndexUids": [
1293695782925933448, 1293695782925933448,
@ -428,7 +473,16 @@
3550975911715416030, 3550975911715416030,
8949996430663588693, 8949996430663588693,
7554483297276446029, 7554483297276446029,
8085320504542486236 8085320504542486236,
2542527583182293894,
3541826612160879182,
1761651285457480725,
8027872860151528568,
3582932035787057158,
8819082642546094709,
7233601933599801875,
4335394620556092321,
1899740026144478138
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -376,9 +376,53 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "10:3205842982118792800",
"lastPropertyId": "8:4304230668306976620",
"name": "NotesEntity",
"properties": [
{
"id": "1:7685155280711004546",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4525352813855835976",
"name": "zimId",
"type": 9
},
{
"id": "4:4488566778591049174",
"name": "zimFilePath",
"type": 9
},
{
"id": "5:7998345745727500384",
"name": "noteTitle",
"type": 9
},
{
"id": "6:1899740026144478138",
"name": "noteBody",
"type": 9
},
{
"id": "7:2160052450778801841",
"name": "favicon",
"type": 9
},
{
"id": "8:4304230668306976620",
"name": "noteFilePath",
"type": 9
}
],
"relations": []
} }
], ],
"lastEntityId": "8:8093454424037540087", "lastEntityId": "10:3205842982118792800",
"lastIndexId": "4:4868787482832538530", "lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
@ -386,7 +430,8 @@
"modelVersionParserMinimum": 5, "modelVersionParserMinimum": 5,
"retiredEntityUids": [ "retiredEntityUids": [
349148274283701276, 349148274283701276,
7257718270326155947 7257718270326155947,
7394649290555378565
], ],
"retiredIndexUids": [ "retiredIndexUids": [
1293695782925933448, 1293695782925933448,
@ -428,7 +473,15 @@
3550975911715416030, 3550975911715416030,
8949996430663588693, 8949996430663588693,
7554483297276446029, 7554483297276446029,
8085320504542486236 8085320504542486236,
2542527583182293894,
3541826612160879182,
1761651285457480725,
8027872860151528568,
3582932035787057158,
8819082642546094709,
7233601933599801875,
4335394620556092321
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -0,0 +1,55 @@
/*
* Kiwix Android
* Copyright (c) 2020 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 io.objectbox.Box
import io.objectbox.kotlin.query
import io.reactivex.Flowable
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import javax.inject.Inject
class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
fun notes(): Flowable<List<Page>> = box.asFlowable(
box.query {
order(NotesEntity_.noteTitle)
}
).map { it.map(::NoteListItem) }
override fun pages(): Flowable<List<Page>> = notes()
override fun deletePages(pagesToDelete: List<Page>) =
deleteNotes(pagesToDelete as List<NoteListItem>)
fun saveNote(noteItem: NoteListItem) {
box.put(NotesEntity(noteItem))
}
fun deleteNotes(noteList: List<NoteListItem>) {
box.remove(noteList.map(::NotesEntity))
}
fun deleteNote(noteUniqueKey: String) {
box.query {
equal(NotesEntity_.noteTitle, noteUniqueKey)
}.remove()
}
}

View File

@ -0,0 +1,46 @@
/*
* Kiwix Android
* Copyright (c) 2022 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 io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.Unique
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
@Entity
data class NotesEntity(
@Id var id: Long = 0L,
val zimId: String,
var zimFilePath: String?,
val zimUrl: String,
@Unique
var noteTitle: String,
var noteFilePath: String,
var favicon: String?
) {
constructor(item: NoteListItem) : this(
id = item.databaseId,
zimId = item.zimId,
zimFilePath = item.zimFilePath,
zimUrl = item.zimUrl,
noteTitle = item.title,
noteFilePath = item.noteFilePath,
favicon = item.favicon
)
}

View File

@ -23,6 +23,7 @@ import io.reactivex.Single
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
@ -46,4 +47,8 @@ interface DataSource {
fun deleteBookmarks(bookmarks: List<BookmarkItem>): Completable fun deleteBookmarks(bookmarks: List<BookmarkItem>): Completable
fun deleteBookmark(bookmarkUrl: String): Completable? fun deleteBookmark(bookmarkUrl: String): Completable?
fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>> fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>>
fun saveNote(noteListItem: NoteListItem): Completable
fun deleteNote(noteUniqueKey: String): Completable
fun deleteNotes(noteList: List<NoteListItem>): Completable
} }

View File

@ -26,6 +26,7 @@ import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao 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.NewRecentSearchDao
import org.kiwix.kiwixmobile.core.di.qualifiers.IO import org.kiwix.kiwixmobile.core.di.qualifiers.IO
import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread
@ -33,6 +34,7 @@ import org.kiwix.kiwixmobile.core.extensions.HeaderizableList
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
@ -45,6 +47,7 @@ import javax.inject.Singleton
* A central repository of data which should provide the presenters with the required data. * A central repository of data which should provide the presenters with the required data.
*/ */
@Suppress("LongParameterList")
@Singleton @Singleton
class Repository @Inject internal constructor( class Repository @Inject internal constructor(
@param:IO private val io: Scheduler, @param:IO private val io: Scheduler,
@ -52,6 +55,7 @@ class Repository @Inject internal constructor(
private val bookDao: NewBookDao, private val bookDao: NewBookDao,
private val bookmarksDao: NewBookmarksDao, private val bookmarksDao: NewBookmarksDao,
private val historyDao: HistoryDao, private val historyDao: HistoryDao,
private val notesDao: NewNoteDao,
private val languageDao: NewLanguagesDao, private val languageDao: NewLanguagesDao,
private val recentSearchDao: NewRecentSearchDao, private val recentSearchDao: NewRecentSearchDao,
private val zimReaderContainer: ZimReaderContainer private val zimReaderContainer: ZimReaderContainer
@ -71,7 +75,7 @@ class Repository @Inject internal constructor(
{ current, next -> current.locale.displayName != next.locale.displayName } { current, next -> current.locale.displayName != next.locale.displayName }
) )
} }
.map { it.toList() } .map(MutableList<BooksOnDiskListItem>::toList)
override fun saveBooks(books: List<BookOnDisk>) = override fun saveBooks(books: List<BookOnDisk>) =
Completable.fromAction { bookDao.insert(books) } Completable.fromAction { bookDao.insert(books) }
@ -118,4 +122,16 @@ class Repository @Inject internal constructor(
override fun deleteBookmark(bookmarkUrl: String): Completable? = override fun deleteBookmark(bookmarkUrl: String): Completable? =
Completable.fromAction { bookmarksDao.deleteBookmark(bookmarkUrl) } Completable.fromAction { bookmarksDao.deleteBookmark(bookmarkUrl) }
.subscribeOn(io) .subscribeOn(io)
override fun saveNote(noteListItem: NoteListItem): Completable =
Completable.fromAction { notesDao.saveNote(noteListItem) }
.subscribeOn(io)
override fun deleteNotes(noteList: List<NoteListItem>) =
Completable.fromAction { notesDao.deleteNotes(noteList) }
.subscribeOn(io)
override fun deleteNote(noteUniqueKey: String): Completable =
Completable.fromAction { notesDao.deleteNote(noteUniqueKey) }
.subscribeOn(io)
} }

View File

@ -179,7 +179,7 @@ public class KiwixDatabase extends SquidDatabase {
public void migrateBookmarksVersion6() { public void migrateBookmarksVersion6() {
String[] ids = context.fileList(); String[] ids = context.fileList();
for (String id : ids) { for (String id : ids) {
if (id.length() == 40 && id.substring(id.length() - 4).equals(".txt")) { if (id.length() == 40 && id.endsWith(".txt")) {
try { try {
String idName = id.substring(0, id.length() - 4); String idName = id.substring(0, id.length() - 4);
InputStream stream = context.openFileInput(id); InputStream stream = context.openFileInput(id);

View File

@ -29,6 +29,9 @@ import org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment
import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.ShowDeleteBookmarksDialog import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.ShowDeleteBookmarksDialog
import org.kiwix.kiwixmobile.core.page.history.HistoryFragment import org.kiwix.kiwixmobile.core.page.history.HistoryFragment
import org.kiwix.kiwixmobile.core.page.history.viewmodel.effects.ShowDeleteHistoryDialog import org.kiwix.kiwixmobile.core.page.history.viewmodel.effects.ShowDeleteHistoryDialog
import org.kiwix.kiwixmobile.core.page.notes.NotesFragment
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects.ShowDeleteNotesDialog
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects.ShowOpenNoteDialog
import org.kiwix.kiwixmobile.core.search.SearchFragment import org.kiwix.kiwixmobile.core.search.SearchFragment
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog
import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment
@ -40,11 +43,14 @@ interface CoreActivityComponent {
fun inject(showDeleteSearchDialog: ShowDeleteSearchDialog) fun inject(showDeleteSearchDialog: ShowDeleteSearchDialog)
fun inject(showDeleteBookmarksDialog: ShowDeleteBookmarksDialog) fun inject(showDeleteBookmarksDialog: ShowDeleteBookmarksDialog)
fun inject(showDeleteHistoryDialog: ShowDeleteHistoryDialog) fun inject(showDeleteHistoryDialog: ShowDeleteHistoryDialog)
fun inject(showOpenNoteDialog: ShowOpenNoteDialog)
fun inject(showDeleteNotesDialog: ShowDeleteNotesDialog)
fun inject(corePrefsFragment: CorePrefsFragment) fun inject(corePrefsFragment: CorePrefsFragment)
fun inject(historyFragment: HistoryFragment) fun inject(historyFragment: HistoryFragment)
fun inject(bookmarksFragment: BookmarksFragment) fun inject(bookmarksFragment: BookmarksFragment)
fun inject(addNoteDialog: AddNoteDialog) fun inject(addNoteDialog: AddNoteDialog)
fun inject(helpFragment: HelpFragment) fun inject(helpFragment: HelpFragment)
fun inject(notesFragment: NotesFragment)
@Subcomponent.Builder @Subcomponent.Builder
interface Builder { interface Builder {

View File

@ -31,6 +31,7 @@ import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao 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.NewRecentSearchDao
import org.kiwix.kiwixmobile.core.data.DataModule import org.kiwix.kiwixmobile.core.data.DataModule
import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.DataSource
@ -87,6 +88,7 @@ interface CoreComponent {
fun fetchDownloadDao(): FetchDownloadDao fun fetchDownloadDao(): FetchDownloadDao
fun newBookDao(): NewBookDao fun newBookDao(): NewBookDao
fun historyDao(): HistoryDao fun historyDao(): HistoryDao
fun noteDao(): NewNoteDao
fun newLanguagesDao(): NewLanguagesDao fun newLanguagesDao(): NewLanguagesDao
fun recentSearchDao(): NewRecentSearchDao fun recentSearchDao(): NewRecentSearchDao
fun newBookmarksDao(): NewBookmarksDao fun newBookmarksDao(): NewBookmarksDao

View File

@ -27,6 +27,7 @@ import org.kiwix.kiwixmobile.core.ViewModelFactory
import org.kiwix.kiwixmobile.core.di.ViewModelKey import org.kiwix.kiwixmobile.core.di.ViewModelKey
import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.BookmarkViewModel import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.BookmarkViewModel
import org.kiwix.kiwixmobile.core.page.history.viewmodel.HistoryViewModel import org.kiwix.kiwixmobile.core.page.history.viewmodel.HistoryViewModel
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.NotesViewModel
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel
@Module @Module
@ -45,6 +46,11 @@ abstract class CoreViewModelModule {
@ViewModelKey(HistoryViewModel::class) @ViewModelKey(HistoryViewModel::class)
abstract fun bindHistoryViewModel(historyViewModel: HistoryViewModel): ViewModel abstract fun bindHistoryViewModel(historyViewModel: HistoryViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(NotesViewModel::class)
abstract fun bindNotesViewModel(notesViewModel: NotesViewModel): ViewModel
@Binds @Binds
@IntoMap @IntoMap
@ViewModelKey(BookmarkViewModel::class) @ViewModelKey(BookmarkViewModel::class)

View File

@ -28,6 +28,7 @@ import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao 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.NewRecentSearchDao
import org.kiwix.kiwixmobile.core.dao.entities.MyObjectBox import org.kiwix.kiwixmobile.core.dao.entities.MyObjectBox
import javax.inject.Singleton import javax.inject.Singleton
@ -58,6 +59,9 @@ open class DatabaseModule {
@Provides @Singleton fun providesNewBookmarksDao(boxStore: BoxStore): NewBookmarksDao = @Provides @Singleton fun providesNewBookmarksDao(boxStore: BoxStore): NewBookmarksDao =
NewBookmarksDao(boxStore.boxFor()) NewBookmarksDao(boxStore.boxFor())
@Provides @Singleton fun providesNewNoteDao(boxStore: BoxStore): NewNoteDao =
NewNoteDao(boxStore.boxFor())
@Provides @Singleton fun providesNewRecentSearchDao( @Provides @Singleton fun providesNewRecentSearchDao(
boxStore: BoxStore, boxStore: BoxStore,
flowBuilder: FlowBuilder flowBuilder: FlowBuilder

View File

@ -43,6 +43,7 @@ import org.kiwix.kiwixmobile.core.CoreApp.Companion.instance
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.extensions.closeKeyboard import org.kiwix.kiwixmobile.core.extensions.closeKeyboard
import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.SimpleTextWatcher import org.kiwix.kiwixmobile.core.utils.SimpleTextWatcher
@ -66,8 +67,10 @@ const val DISABLE_ICON_ITEM_ALPHA = 130
const val ENABLE_ICON_ITEM_ALPHA = 255 const val ENABLE_ICON_ITEM_ALPHA = 255
class AddNoteDialog : DialogFragment() { class AddNoteDialog : DialogFragment() {
private lateinit var zimId: String
private var zimFileName: String? = null private var zimFileName: String? = null
private var zimFileTitle: String? = null private var zimFileTitle: String? = null
private lateinit var zimFileUrl: String
private var articleTitle: String? = null private var articleTitle: String? = null
// Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt" // Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
@ -90,6 +93,9 @@ class AddNoteDialog : DialogFragment() {
@Inject @Inject
lateinit var alertDialogShower: AlertDialogShower lateinit var alertDialogShower: AlertDialogShower
@Inject
lateinit var mainRepositoryActions: MainRepositoryActions
private val saveItem by lazy { toolbar.menu.findItem(R.id.save_note) } private val saveItem by lazy { toolbar.menu.findItem(R.id.save_note) }
private val shareItem by lazy { toolbar.menu.findItem(R.id.share_note) } private val shareItem by lazy { toolbar.menu.findItem(R.id.share_note) }
@ -108,13 +114,20 @@ class AddNoteDialog : DialogFragment() {
zimFileName = zimReaderContainer.zimCanonicalPath zimFileName = zimReaderContainer.zimCanonicalPath
if (zimFileName != null) { // No zim file currently opened if (zimFileName != null) { // No zim file currently opened
zimFileTitle = zimReaderContainer.zimFileTitle zimFileTitle = zimReaderContainer.zimFileTitle
articleTitle = (activity as WebViewProvider?)?.getCurrentWebView()?.title zimId = zimReaderContainer.id.orEmpty()
if (arguments != null) {
articleTitle = arguments?.getString(NOTES_TITLE)
zimFileUrl = arguments?.getString(ARTICLE_URL).orEmpty()
} else {
val webView = (activity as WebViewProvider?)?.getCurrentWebView()
articleTitle = webView?.title
zimFileUrl = webView?.url.orEmpty()
}
// Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt" // Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
articleNoteFileName = getArticleNotefileName() articleNoteFileName = getArticleNoteFileName()
zimNotesDirectory = "$NOTES_DIRECTORY$zimNoteDirectoryName/" zimNotesDirectory = "$NOTES_DIRECTORY$zimNoteDirectoryName/"
} else {
onFailureToCreateAddNoteDialog()
} }
} }
@ -140,8 +153,12 @@ class AddNoteDialog : DialogFragment() {
return (if (noteDirectoryName.isNotEmpty()) noteDirectoryName else zimFileTitle) ?: "" return (if (noteDirectoryName.isNotEmpty()) noteDirectoryName else zimFileTitle) ?: ""
} }
private fun getArticleNotefileName(): String { private fun getArticleNoteFileName(): String {
// Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html" // Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html"
arguments?.getString(NOTE_FILE_PATH)?.let {
return@getArticleNoteFileName getTextAfterLastSlashWithoutExtension(it)
}
val articleUrl = (activity as WebViewProvider?)?.getCurrentWebView()?.url val articleUrl = (activity as WebViewProvider?)?.getCurrentWebView()?.url
var noteFileName = "" var noteFileName = ""
if (articleUrl == null) { if (articleUrl == null) {
@ -149,7 +166,7 @@ class AddNoteDialog : DialogFragment() {
} else { } else {
noteFileName = getTextAfterLastSlashWithoutExtension(articleUrl) noteFileName = getTextAfterLastSlashWithoutExtension(articleUrl)
} }
return (if (noteFileName.isNotEmpty()) noteFileName else articleTitle) ?: "" return noteFileName.ifEmpty { articleTitle } ?: ""
} }
/* From ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim", returns "granbluefantasy_en_all_all_nopic_2018-10" /* From ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim", returns "granbluefantasy_en_all_all_nopic_2018-10"
@ -264,7 +281,6 @@ class AddNoteDialog : DialogFragment() {
} }
private fun saveNote(noteText: String) { private fun saveNote(noteText: String) {
/* String content of the EditText, given by noteText, is saved into the text file given by: /* String content of the EditText, given by noteText, is saved into the text file given by:
* "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt" * "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt"
* */ * */
@ -298,12 +314,14 @@ class AddNoteDialog : DialogFragment() {
context.toast(R.string.note_save_successful, Toast.LENGTH_SHORT) context.toast(R.string.note_save_successful, Toast.LENGTH_SHORT)
noteEdited = false // As no unsaved changes remain noteEdited = false // As no unsaved changes remain
enableDeleteNoteMenuItem() enableDeleteNoteMenuItem()
// adding only if saving file is success
addNoteToDao(noteFile.canonicalPath, "${zimFileTitle.orEmpty()}: $articleTitle")
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
.also { context.toast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG) } .also { context.toast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG) }
} }
} else { } else {
context.toast(R.string.note_save_successful, Toast.LENGTH_LONG) context.toast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG)
Log.d(TAG, "Required folder doesn't exist") Log.d(TAG, "Required folder doesn't exist")
} }
} else { } else {
@ -311,6 +329,27 @@ class AddNoteDialog : DialogFragment() {
} }
} }
private fun addNoteToDao(noteFilePath: String?, title: String) {
noteFilePath?.let { filePath ->
if (filePath.isNotEmpty() && zimFileUrl.isNotEmpty()) {
val zimReader = zimReaderContainer.zimFileReader
if (zimReader != null) {
val noteToSave = NoteListItem(
title = title,
url = zimFileUrl,
noteFilePath = noteFilePath,
zimFileReader = zimReader
)
mainRepositoryActions.saveNote(noteToSave)
} else {
Log.d(TAG, "zim reader found null")
}
} else {
Log.d(TAG, "Cannot process with empty zim url or noteFilePath")
}
}
}
private fun deleteNote() { private fun deleteNote() {
val notesFolder = File(zimNotesDirectory) val notesFolder = File(zimNotesDirectory)
val noteFile = val noteFile =
@ -318,6 +357,7 @@ class AddNoteDialog : DialogFragment() {
val noteDeleted = noteFile.delete() val noteDeleted = noteFile.delete()
if (noteDeleted) { if (noteDeleted) {
add_note_edit_text.text.clear() add_note_edit_text.text.clear()
mainRepositoryActions.deleteNote(articleNoteFileName)
disableMenuItems() disableMenuItems()
context.toast(R.string.note_delete_successful, Toast.LENGTH_LONG) context.toast(R.string.note_delete_successful, Toast.LENGTH_LONG)
} else { } else {
@ -330,7 +370,6 @@ class AddNoteDialog : DialogFragment() {
* is displayed in the EditText field (note content area) * is displayed in the EditText field (note content area)
*/ */
private fun displayNote() { private fun displayNote() {
val noteFile = File("$zimNotesDirectory$articleNoteFileName.txt") val noteFile = File("$zimNotesDirectory$articleNoteFileName.txt")
if (noteFile.exists()) { if (noteFile.exists()) {
readNoteFromFile(noteFile) readNoteFromFile(noteFile)
@ -403,5 +442,8 @@ class AddNoteDialog : DialogFragment() {
@JvmField val NOTES_DIRECTORY = @JvmField val NOTES_DIRECTORY =
instance.getExternalFilesDir("").toString() + "/Kiwix/Notes/" instance.getExternalFilesDir("").toString() + "/Kiwix/Notes/"
const val TAG = "AddNoteDialog" const val TAG = "AddNoteDialog"
const val NOTE_FILE_PATH = "NoteFilePath"
const val ARTICLE_URL = "ArticleUrl"
const val NOTES_TITLE = "NotesTitle"
} }
} }

View File

@ -72,6 +72,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
abstract val bookmarksFragmentResId: Int abstract val bookmarksFragmentResId: Int
abstract val settingsFragmentResId: Int abstract val settingsFragmentResId: Int
abstract val historyFragmentResId: Int abstract val historyFragmentResId: Int
abstract val notesFragmentResId: Int
abstract val helpFragmentResId: Int abstract val helpFragmentResId: Int
abstract val cachedComponent: CoreActivityComponent abstract val cachedComponent: CoreActivityComponent
abstract val topLevelDestinations: Set<Int> abstract val topLevelDestinations: Set<Int>
@ -194,6 +195,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
R.id.menu_support_kiwix -> openSupportKiwixExternalLink() R.id.menu_support_kiwix -> openSupportKiwixExternalLink()
R.id.menu_settings -> openSettings() R.id.menu_settings -> openSettings()
R.id.menu_help -> openHelpFragment() R.id.menu_help -> openHelpFragment()
R.id.menu_notes -> openNotes()
R.id.menu_history -> openHistory() R.id.menu_history -> openHistory()
R.id.menu_bookmarks_list -> openBookmarks() R.id.menu_bookmarks_list -> openBookmarks()
else -> return false else -> return false
@ -303,6 +305,10 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
handleDrawerOnNavigation() handleDrawerOnNavigation()
} }
private fun openNotes() {
navigate(notesFragmentResId)
}
protected fun handleDrawerOnNavigation() { protected fun handleDrawerOnNavigation() {
closeNavigationDrawer() closeNavigationDrawer()
disableDrawer() disableDrawer()

View File

@ -22,6 +22,7 @@ import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.di.ActivityScope import org.kiwix.kiwixmobile.core.di.ActivityScope
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import javax.inject.Inject import javax.inject.Inject
private const val TAG = "MainPresenter" private const val TAG = "MainPresenter"
@ -44,4 +45,14 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour
?.subscribe({}, { e -> Log.e(TAG, "Unable to delete bookmark", e) }) ?.subscribe({}, { e -> Log.e(TAG, "Unable to delete bookmark", e) })
?: Log.e(TAG, "Unable to delete bookmark") ?: Log.e(TAG, "Unable to delete bookmark")
} }
fun saveNote(note: NoteListItem) {
dataSource.saveNote(note)
.subscribe({}, { e -> Log.e(TAG, "Unable to save note", e) })
}
fun deleteNote(noteUniqueKey: String) {
dataSource.deleteNote(noteUniqueKey)
.subscribe({}, { e -> Log.e(TAG, "Unable to delete note", e) })
}
} }

View File

@ -0,0 +1,49 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.page.notes
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.page.PageFragment
import org.kiwix.kiwixmobile.core.page.adapter.PageAdapter
import org.kiwix.kiwixmobile.core.page.adapter.PageDelegate
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.NotesViewModel
class NotesFragment : PageFragment() {
override val pageViewModel by lazy { viewModel<NotesViewModel>(viewModelFactory) }
override val screenTitle: String
get() = getString(R.string.pref_notes)
override val pageAdapter: PageAdapter by lazy {
PageAdapter(PageDelegate.PageItemDelegate(this))
}
override val noItemsString: String by lazy { getString(R.string.no_notes) }
override val switchString: String by lazy { getString(R.string.notes_from_all_books) }
override val switchIsChecked: Boolean = true
override fun inject(baseActivity: BaseActivity) {
baseActivity.cachedComponent.inject(this)
}
override val searchQueryHint: String by lazy { getString(R.string.search_notes) }
}

View File

@ -0,0 +1,43 @@
package org.kiwix.kiwixmobile.core.page.notes.adapter
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
data class NoteListItem(
val databaseId: Long = 0L,
override val zimId: String,
override val title: String,
override val zimFilePath: String?,
val zimUrl: String,
val noteFilePath: String,
override val favicon: String?,
override var isSelected: Boolean = false,
override val url: String = zimUrl,
override val id: Long = databaseId
) : Page {
constructor(notesEntity: NotesEntity) : this(
notesEntity.id,
notesEntity.zimId,
notesEntity.noteTitle,
notesEntity.zimFilePath,
notesEntity.zimUrl,
notesEntity.noteFilePath,
notesEntity.favicon
)
constructor(
title: String,
url: String,
noteFilePath: String,
zimFileReader: ZimFileReader
) : this(
zimId = zimFileReader.id,
title = title,
zimFilePath = zimFileReader.zimFile.canonicalPath,
zimUrl = url,
favicon = zimFileReader.favicon,
noteFilePath = noteFilePath
)
}

View File

@ -0,0 +1,35 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.page.notes.viewmodel
import org.kiwix.kiwixmobile.core.page.adapter.PageRelated
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.page.viewmodel.PageState
data class NotesState(
override val pageItems: List<NoteListItem>,
override val showAll: Boolean,
override val currentZimId: String?,
override val searchTerm: String = ""
) : PageState<NoteListItem>() {
override val visiblePageItems: List<PageRelated> = filteredPageItems
override fun copyWithNewItems(newItems: List<NoteListItem>): PageState<NoteListItem> =
copy(pageItems = newItems)
}

View File

@ -0,0 +1,73 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.page.notes.viewmodel
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects.ShowDeleteNotesDialog
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects.ShowOpenNoteDialog
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects.UpdateAllNotesPreference
import org.kiwix.kiwixmobile.core.page.viewmodel.Action
import org.kiwix.kiwixmobile.core.page.viewmodel.PageViewModel
import org.kiwix.kiwixmobile.core.page.viewmodel.PageViewModelClickListener
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import javax.inject.Inject
class NotesViewModel @Inject constructor(
notesDao: NewNoteDao,
zimReaderContainer: ZimReaderContainer,
sharedPrefs: SharedPreferenceUtil
) : PageViewModel<NoteListItem, NotesState>(notesDao, sharedPrefs, zimReaderContainer),
PageViewModelClickListener {
init {
setOnItemClickListener(this)
}
override fun initialState(): NotesState =
NotesState(emptyList(), sharedPreferenceUtil.showNotesAllBooks, zimReaderContainer.id)
override fun updatePagesBasedOnFilter(state: NotesState, action: Action.Filter): NotesState =
state.copy(searchTerm = action.searchTerm)
override fun updatePages(state: NotesState, action: Action.UpdatePages): NotesState =
state.copy(pageItems = action.pages.filterIsInstance<NoteListItem>())
override fun offerUpdateToShowAllToggle(
action: Action.UserClickedShowAllToggle,
state: NotesState
): NotesState {
effects.offer(UpdateAllNotesPreference(sharedPreferenceUtil, action.isChecked))
return state.copy(showAll = action.isChecked)
}
override fun copyWithNewItems(state: NotesState, newItems: List<NoteListItem>): NotesState =
state.copy(pageItems = newItems)
override fun deselectAllPages(state: NotesState): NotesState =
state.copy(pageItems = state.pageItems.map { it.copy(isSelected = false) })
override fun createDeletePageDialogEffect(state: NotesState) =
ShowDeleteNotesDialog(effects, state, pageDao)
override fun onItemClick(page: Page) =
ShowOpenNoteDialog(effects, page, zimReaderContainer)
}

View File

@ -0,0 +1,50 @@
package org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects
/*
* Kiwix Android
* Copyright (c) 2020 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/>.
*
*/
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.PageDao
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.NotesState
import org.kiwix.kiwixmobile.core.page.viewmodel.effects.DeletePageItems
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteAllNotes
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedNotes
import javax.inject.Inject
data class ShowDeleteNotesDialog(
private val effects: PublishProcessor<SideEffect<*>>,
private val state: NotesState,
private val pageDao: PageDao
) : SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower
override fun invokeWith(activity: AppCompatActivity) {
activity.cachedComponent.inject(this)
Log.d("invoke", "invokeWith: invoked")
dialogShower.show(
if (state.isInSelectionState) DeleteSelectedNotes else DeleteAllNotes,
{
effects.offer(DeletePageItems(state, pageDao))
}
)
}
}

View File

@ -0,0 +1,54 @@
/*
* Kiwix Android
* Copyright (c) 2021 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.page.notes.viewmodel.effects
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.page.viewmodel.effects.OpenNote
import org.kiwix.kiwixmobile.core.page.viewmodel.effects.OpenPage
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.ShowNoteDialog
import java.io.File
import javax.inject.Inject
data class ShowOpenNoteDialog(
private val effects: PublishProcessor<SideEffect<*>>,
private val page: Page,
private val zimReaderContainer: ZimReaderContainer
) : SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower
override fun invokeWith(activity: AppCompatActivity) {
activity.cachedComponent.inject(this)
dialogShower.show(
ShowNoteDialog,
{ effects.offer(OpenPage(page, zimReaderContainer)) },
{
val item = page as NoteListItem
val file = File(item.zimFilePath.orEmpty())
zimReaderContainer.setZimFile(file)
effects.offer(OpenNote(item.noteFilePath, item.zimUrl, item.title))
}
)
}
}

View File

@ -0,0 +1,32 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.page.notes.viewmodel.effects
import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
data class UpdateAllNotesPreference(
private val sharedPreferenceUtil: SharedPreferenceUtil,
private val isChecked: Boolean
) : SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) {
sharedPreferenceUtil.showNotesAllBooks = isChecked
}
}

View File

@ -49,6 +49,8 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
abstract fun initialState(): S abstract fun initialState(): S
private lateinit var pageViewModelClickListener: PageViewModelClickListener
val state: MutableLiveData<S> by lazy { val state: MutableLiveData<S> by lazy {
MutableLiveData<S>().apply { MutableLiveData<S>().apply {
value = initialState() value = initialState()
@ -106,13 +108,21 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
abstract fun copyWithNewItems(state: S, newItems: List<T>): S abstract fun copyWithNewItems(state: S, newItems: List<T>): S
private fun handleItemClick(state: S, action: Action.OnItemClick): S { private fun handleItemClick(state: S, action: Action.OnItemClick): S {
if (state.isInSelectionState) { if (::pageViewModelClickListener.isInitialized) {
return copyWithNewItems(state, state.getItemsAfterToggleSelectionOfItem(action.page)) effects.offer(pageViewModelClickListener.onItemClick(action.page))
} else {
if (state.isInSelectionState) {
return copyWithNewItems(state, state.getItemsAfterToggleSelectionOfItem(action.page))
}
effects.offer(OpenPage(action.page, zimReaderContainer))
} }
effects.offer(OpenPage(action.page, zimReaderContainer))
return state return state
} }
fun setOnItemClickListener(clickListener: PageViewModelClickListener) {
pageViewModelClickListener = clickListener
}
abstract fun deselectAllPages(state: S): S abstract fun deselectAllPages(state: S): S
private fun exitFragment(state: S): S { private fun exitFragment(state: S): S {

View File

@ -0,0 +1,26 @@
/*
* Kiwix Android
* Copyright (c) 2021 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.page.viewmodel
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.page.adapter.Page
interface PageViewModelClickListener {
fun onItemClick(page: Page): SideEffect<*>
}

View File

@ -0,0 +1,60 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.page.viewmodel.effects
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.main.AddNoteDialog
import org.kiwix.kiwixmobile.core.main.AddNoteDialog.Companion.ARTICLE_URL
import org.kiwix.kiwixmobile.core.main.AddNoteDialog.Companion.NOTES_TITLE
import org.kiwix.kiwixmobile.core.main.AddNoteDialog.Companion.NOTE_FILE_PATH
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
class OpenNote(
private val noteFilePath: String,
private val zimFileUrl: String,
private val title: String,
) : SideEffect<Unit> {
override fun invokeWith(activity: AppCompatActivity) {
activity as CoreMainActivity
showAddNoteDialog(activity)
}
private fun showAddNoteDialog(activity: AppCompatActivity) {
val coreMainActivity: CoreMainActivity = activity as CoreMainActivity
val fragmentTransaction: FragmentTransaction =
coreMainActivity.supportFragmentManager.beginTransaction()
val previousInstance: Fragment? =
coreMainActivity.supportFragmentManager.findFragmentByTag(AddNoteDialog.TAG)
if (previousInstance == null) {
val dialogFragment = AddNoteDialog()
val bundle = Bundle().apply {
putString(NOTE_FILE_PATH, noteFilePath)
putString(ARTICLE_URL, zimFileUrl)
putString(NOTES_TITLE, title)
}
dialogFragment.arguments = bundle
dialogFragment.show(fragmentTransaction, AddNoteDialog.TAG)
}
}
}

View File

@ -151,6 +151,12 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
putBoolean(PREF_SHOW_BOOKMARKS_ALL_BOOKS, prefShowBookmarksFromCurrentBook) putBoolean(PREF_SHOW_BOOKMARKS_ALL_BOOKS, prefShowBookmarksFromCurrentBook)
} }
var showNotesAllBooks: Boolean
get() = sharedPreferences.getBoolean(PREF_SHOW_NOTES_ALL_BOOKS, true)
set(prefShowBookmarksFromCurrentBook) = sharedPreferences.edit {
putBoolean(PREF_SHOW_NOTES_ALL_BOOKS, prefShowBookmarksFromCurrentBook)
}
val nightMode: NightModeConfig.Mode val nightMode: NightModeConfig.Mode
get() = from( get() = from(
sharedPreferences.getString(PREF_NIGHT_MODE, null)?.toInt() sharedPreferences.getString(PREF_NIGHT_MODE, null)?.toInt()
@ -209,6 +215,7 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
private const val PREF_IS_FIRST_RUN = "isFirstRun" private const val PREF_IS_FIRST_RUN = "isFirstRun"
private const val PREF_SHOW_BOOKMARKS_ALL_BOOKS = "show_bookmarks_current_book" private const val PREF_SHOW_BOOKMARKS_ALL_BOOKS = "show_bookmarks_current_book"
private const val PREF_SHOW_HISTORY_ALL_BOOKS = "show_history_current_book" private const val PREF_SHOW_HISTORY_ALL_BOOKS = "show_history_current_book"
private const val PREF_SHOW_NOTES_ALL_BOOKS = "show_notes_current_book"
private const val PREF_HOSTED_BOOKS = "hosted_books" private const val PREF_HOSTED_BOOKS = "hosted_books"
const val PREF_NIGHT_MODE = "pref_night_mode" const val PREF_NIGHT_MODE = "pref_night_mode"
private const val TEXT_ZOOM = "true_text_zoom" private const val TEXT_ZOOM = "true_text_zoom"

View File

@ -257,6 +257,27 @@ sealed class KiwixDialog(
positiveMessage = R.string.delete, positiveMessage = R.string.delete,
negativeMessage = R.string.cancel negativeMessage = R.string.cancel
) )
object DeleteAllNotes : KiwixDialog(
R.string.delete_notes_confirmation_msg,
R.string.delete_note_dialog_message,
positiveMessage = R.string.delete,
negativeMessage = R.string.cancel
)
object DeleteSelectedNotes : KiwixDialog(
R.string.delete_selected_notes,
R.string.delete_note_dialog_message,
positiveMessage = R.string.delete,
negativeMessage = R.string.cancel
)
object ShowNoteDialog : KiwixDialog(
R.string.choose_your_action,
null,
positiveMessage = R.string.open_article,
negativeMessage = R.string.open_note
)
} }
interface HasBodyFormatArgs { interface HasBodyFormatArgs {

View File

@ -13,7 +13,6 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:showIn="@layout/fragment_help"> tools:showIn="@layout/fragment_help">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -40,7 +39,6 @@
app:layout_constraintTop_toBottomOf="@id/app_bar" /> app:layout_constraintTop_toBottomOf="@id/app_bar" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
tools:listitem="@layout/item_bookmark_history"
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@ -48,6 +46,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/app_bar" /> app:layout_constraintTop_toBottomOf="@id/app_bar"
tools:listitem="@layout/item_bookmark_history" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -17,6 +17,12 @@
android:title="@string/history" android:title="@string/history"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_notes"
android:icon="@drawable/ic_add_note"
android:title="@string/pref_notes"
app:showAsAction="never" />
<item <item
android:id="@+id/menu_host_books" android:id="@+id/menu_host_books"
android:icon="@drawable/ic_mobile_screen_share_24px" android:icon="@drawable/ic_mobile_screen_share_24px"

View File

@ -313,4 +313,12 @@
<item>1</item> <item>1</item>
<item>-1</item> <item>-1</item>
</string-array> </string-array>
<string name="no_notes">No Notes</string>
<string name="notes_from_all_books">View Notes From All Books</string>
<string name="search_notes">Search Notes</string>
<string name="choose_your_action">Choose your Action</string>
<string name="open_note">Open Note</string>
<string name="open_article">Open Article</string>
<string name="delete_note_dialog_message">Note: Notes are not deleted from your storage</string>
<string name="delete_selected_notes">Delete Selected Notes?</string>
</resources> </resources>

View File

@ -0,0 +1,65 @@
/*
* Kiwix Android
* Copyright (c) 2022 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 io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.objectbox.Box
import io.objectbox.query.Query
import io.objectbox.query.QueryBuilder
import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
internal class NewNoteDaoTest {
private val notesBox: Box<NotesEntity> = mockk(relaxed = true)
private val newNotesDao = NewNoteDao(notesBox)
@Test
fun deletePages() {
val notesItem: NoteListItem = mockk(relaxed = true)
val notesItemList: List<NoteListItem> = listOf(notesItem)
val pagesToDelete: List<Page> = notesItemList
newNotesDao.deletePages(pagesToDelete)
verify { newNotesDao.deleteNotes(notesItemList) }
}
@Test
fun deleteNotePage() {
val noteTitle = "abNotesTitle"
val queryBuilder: QueryBuilder<NotesEntity> = mockk(relaxed = true)
every { notesBox.query() } returns queryBuilder
every { queryBuilder.equal(NotesEntity_.noteTitle, noteTitle) } returns queryBuilder
val query: Query<NotesEntity> = mockk(relaxed = true)
every { queryBuilder.build() } returns query
newNotesDao.deleteNote(noteTitle)
verify { query.remove() }
}
@Test
fun saveNotePage() {
val newNote: NoteListItem = mockk(relaxed = true)
newNotesDao.saveNote(newNote)
verify { notesBox.put(NotesEntity(newNote)) }
}
}

View File

@ -47,6 +47,7 @@ class CustomMainActivity : CoreMainActivity() {
override val settingsFragmentResId: Int = R.id.customSettingsFragment override val settingsFragmentResId: Int = R.id.customSettingsFragment
override val readerFragmentResId: Int = R.id.customReaderFragment override val readerFragmentResId: Int = R.id.customReaderFragment
override val historyFragmentResId: Int = R.id.historyFragment override val historyFragmentResId: Int = R.id.historyFragment
override val notesFragmentResId: Int = R.id.notesFragment
override val helpFragmentResId: Int = R.id.helpFragment override val helpFragmentResId: Int = R.id.helpFragment
override val cachedComponent by lazy { customActivityComponent } override val cachedComponent by lazy { customActivityComponent }
override val topLevelDestinations = override val topLevelDestinations =

View File

@ -47,6 +47,10 @@
android:id="@+id/historyFragment" android:id="@+id/historyFragment"
android:name="org.kiwix.kiwixmobile.core.page.history.HistoryFragment" android:name="org.kiwix.kiwixmobile.core.page.history.HistoryFragment"
android:label="HistoryFragment" /> android:label="HistoryFragment" />
<fragment
android:id="@+id/notesFragment"
android:name="org.kiwix.kiwixmobile.core.page.notes.NotesFragment"
android:label="NotesFragment" />
<fragment <fragment
android:id="@+id/bookmarksFragment" android:id="@+id/bookmarksFragment"
android:name="org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment" android:name="org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment"