mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-17 03:16:27 -04:00
#1676 Search is slow and laggy - add kotlin UDF implementation
This commit is contained in:
parent
08b4e20e2a
commit
e1f87b1a60
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* 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
|
||||
@ -27,7 +27,6 @@ import org.kiwix.kiwixmobile.di.KiwixScope;
|
||||
|
||||
@KiwixScope
|
||||
public class KiwixViewModelFactory extends ViewModelFactory {
|
||||
|
||||
@Inject
|
||||
public KiwixViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
|
||||
super(creators);
|
||||
|
@ -32,6 +32,7 @@ import kotlinx.android.synthetic.main.activity_language.language_recycler_view
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
|
||||
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
|
||||
import org.kiwix.kiwixmobile.kiwixActivityComponent
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageAdapter
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.HeaderDelegate
|
||||
@ -44,7 +45,6 @@ import org.kiwix.kiwixmobile.language.viewmodel.State
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Content
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Loading
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Saving
|
||||
import org.kiwix.kiwixmobile.zim_manager.SimpleTextListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class LanguageActivity : BaseActivity() {
|
||||
|
@ -51,14 +51,17 @@ class LanguageViewModel @Inject constructor(
|
||||
.subscribe(state::postValue, Throwable::printStackTrace),
|
||||
languageDao.languages().filter { it.isNotEmpty() }
|
||||
.subscribe(
|
||||
{
|
||||
actions.offer(UpdateLanguages(it))
|
||||
},
|
||||
{ actions.offer(UpdateLanguages(it)) },
|
||||
Throwable::printStackTrace
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
compositeDisposable.clear()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
action: Action,
|
||||
currentState: State
|
||||
|
@ -32,6 +32,7 @@ import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
|
||||
import org.kiwix.kiwixmobile.core.utils.LanguageUtils
|
||||
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
|
||||
import org.kiwix.kiwixmobile.kiwixActivityComponent
|
||||
import org.kiwix.kiwixmobile.language.LanguageActivity
|
||||
import org.kiwix.kiwixmobile.local_file_transfer.LocalFileTransferActivity
|
||||
|
@ -23,7 +23,10 @@
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="m">
|
||||
|
||||
<activity android:name=".search.SearchActivity" />
|
||||
<activity
|
||||
android:name=".search.SearchActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<activity android:name=".bookmark.BookmarksActivity" />
|
||||
|
||||
<provider
|
||||
|
@ -21,11 +21,15 @@ package org.kiwix.kiwixmobile.core;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ViewModelFactory implements ViewModelProvider.Factory {
|
||||
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
|
||||
|
||||
@Inject
|
||||
public ViewModelFactory(
|
||||
Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
|
||||
this.creators = creators;
|
||||
|
@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.rx.RxQuery
|
||||
import io.reactivex.BackpressureStrategy
|
||||
import io.reactivex.BackpressureStrategy.LATEST
|
||||
@ -40,5 +41,8 @@ class NewLanguagesDao @Inject constructor(private val box: Box<LanguageEntity>)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> Box<T>.asFlowable(backpressureStrategy: BackpressureStrategy = LATEST) =
|
||||
RxQuery.observable(query {}).toFlowable(backpressureStrategy)
|
||||
internal fun <T> Box<T>.asFlowable(
|
||||
query: Query<T> = query {},
|
||||
backpressureStrategy: BackpressureStrategy = LATEST
|
||||
) =
|
||||
RxQuery.observable(query).toFlowable(backpressureStrategy)
|
||||
|
@ -22,28 +22,25 @@ import io.objectbox.kotlin.query
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity_
|
||||
import org.kiwix.kiwixmobile.core.data.local.entity.RecentSearch
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewRecentSearchDao @Inject constructor(
|
||||
val box: Box<RecentSearchEntity>
|
||||
) {
|
||||
fun getRecentSearches(zimId: String?) = box
|
||||
.query {
|
||||
fun recentSearches(zimId: String?) = box.asFlowable(
|
||||
box.query {
|
||||
equal(RecentSearchEntity_.zimId, zimId ?: "")
|
||||
orderDesc(RecentSearchEntity_.id)
|
||||
}
|
||||
.find()
|
||||
.distinctBy(RecentSearchEntity::searchTerm)
|
||||
.take(NUM_RECENT_RESULTS)
|
||||
.map(RecentSearchEntity::searchTerm)
|
||||
).map { searchEntities ->
|
||||
searchEntities.distinct()
|
||||
.take(NUM_RECENT_RESULTS)
|
||||
.map { searchEntity -> RecentSearchListItem(searchEntity.searchTerm) }
|
||||
}
|
||||
|
||||
fun saveSearch(title: String, id: String) {
|
||||
box.put(
|
||||
RecentSearchEntity(
|
||||
searchTerm = title,
|
||||
zimId = id
|
||||
)
|
||||
)
|
||||
box.put(RecentSearchEntity(searchTerm = title, zimId = id))
|
||||
}
|
||||
|
||||
fun deleteSearchString(searchTerm: String) {
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.di.components
|
||||
|
||||
import android.app.Activity
|
||||
import dagger.BindsInstance
|
||||
import dagger.Subcomponent
|
||||
import org.kiwix.kiwixmobile.core.di.ActivityScope
|
||||
import org.kiwix.kiwixmobile.core.di.modules.ActivityModule
|
||||
import org.kiwix.kiwixmobile.core.search.SearchActivity
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog
|
||||
|
||||
@ActivityScope
|
||||
@Subcomponent(modules = [ActivityModule::class])
|
||||
interface CoreActivityComponent {
|
||||
fun inject(searchActivity: SearchActivity)
|
||||
fun inject(showDeleteSearchDialog: ShowDeleteSearchDialog)
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
@BindsInstance
|
||||
fun activity(activity: Activity): CoreActivityComponent.Builder
|
||||
|
||||
fun build(): CoreActivityComponent
|
||||
}
|
||||
}
|
@ -29,19 +29,21 @@ import org.kiwix.kiwixmobile.core.StorageObserver
|
||||
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.data.DataModule
|
||||
import org.kiwix.kiwixmobile.core.data.DataSource
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
|
||||
import org.kiwix.kiwixmobile.core.di.modules.ApplicationModule
|
||||
import org.kiwix.kiwixmobile.core.di.modules.CoreViewModelModule
|
||||
import org.kiwix.kiwixmobile.core.di.modules.JNIModule
|
||||
import org.kiwix.kiwixmobile.core.di.modules.NetworkModule
|
||||
import org.kiwix.kiwixmobile.core.di.modules.SearchModule
|
||||
import org.kiwix.kiwixmobile.core.downloader.Downloader
|
||||
import org.kiwix.kiwixmobile.core.main.AddNoteDialog
|
||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimContentProvider
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||
import org.kiwix.kiwixmobile.core.search.AutoCompleteAdapter
|
||||
import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment
|
||||
import org.kiwix.kiwixmobile.core.utils.BookUtils
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||
@ -53,7 +55,10 @@ import javax.inject.Singleton
|
||||
ApplicationModule::class,
|
||||
NetworkModule::class,
|
||||
JNIModule::class,
|
||||
DataModule::class]
|
||||
DataModule::class,
|
||||
CoreViewModelModule::class,
|
||||
SearchModule::class
|
||||
]
|
||||
)
|
||||
interface CoreComponent {
|
||||
|
||||
@ -65,6 +70,7 @@ interface CoreComponent {
|
||||
fun build(): CoreComponent
|
||||
}
|
||||
|
||||
fun activityComponentBuilder(): CoreActivityComponent.Builder
|
||||
fun zimReaderContainer(): ZimReaderContainer
|
||||
fun sharedPrefUtil(): SharedPreferenceUtil
|
||||
fun zimFileReaderFactory(): ZimFileReader.Factory
|
||||
@ -76,6 +82,7 @@ interface CoreComponent {
|
||||
fun fetchDownloadDao(): FetchDownloadDao
|
||||
fun newBookDao(): NewBookDao
|
||||
fun newLanguagesDao(): NewLanguagesDao
|
||||
fun recentSearchDao(): NewRecentSearchDao
|
||||
fun connectivityManager(): ConnectivityManager
|
||||
fun context(): Context
|
||||
fun downloader(): Downloader
|
||||
@ -85,7 +92,6 @@ interface CoreComponent {
|
||||
fun inject(zimContentProvider: ZimContentProvider)
|
||||
fun inject(kiwixWebView: KiwixWebView)
|
||||
fun inject(prefsFragment: CorePrefsFragment)
|
||||
fun inject(autoCompleteAdapter: AutoCompleteAdapter)
|
||||
fun inject(storageSelectDialog: StorageSelectDialog)
|
||||
fun inject(addNoteDialog: AddNoteDialog)
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import org.kiwix.kiwixmobile.core.error.ErrorActivity;
|
||||
import org.kiwix.kiwixmobile.core.help.HelpActivity;
|
||||
import org.kiwix.kiwixmobile.core.history.HistoryActivity;
|
||||
import org.kiwix.kiwixmobile.core.history.HistoryModule;
|
||||
import org.kiwix.kiwixmobile.core.search.SearchActivity;
|
||||
|
||||
/**
|
||||
* Dagger.Android annotation processor will create the sub-components. We also specify the modules
|
||||
@ -38,10 +37,6 @@ import org.kiwix.kiwixmobile.core.search.SearchActivity;
|
||||
@Module
|
||||
public abstract class ActivityBindingModule {
|
||||
|
||||
@ActivityScope
|
||||
@ContributesAndroidInjector
|
||||
public abstract SearchActivity provideSearchActivity();
|
||||
|
||||
@ActivityScope
|
||||
@ContributesAndroidInjector(modules = BookmarksModule.class)
|
||||
public abstract BookmarksActivity provideBookmarksActivity();
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.di.modules
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
import org.kiwix.kiwixmobile.core.ViewModelFactory
|
||||
import org.kiwix.kiwixmobile.core.di.ViewModelKey
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
abstract class CoreViewModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(SearchViewModel::class)
|
||||
abstract fun bindSearchViewModel(searchViewModel: SearchViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindViewModelFactory(factory: ViewModelFactory):
|
||||
ViewModelProvider.Factory
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.di.modules
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchResultGenerator
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.ZimSearchResultGenerator
|
||||
|
||||
@Module
|
||||
abstract class SearchModule {
|
||||
@Binds
|
||||
abstract fun bindsSearchResultGenerator(zimSearchResultGenerator: ZimSearchResultGenerator):
|
||||
SearchResultGenerator
|
||||
}
|
@ -28,6 +28,7 @@ import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import org.kiwix.kiwixmobile.core.CoreApp
|
||||
import org.kiwix.kiwixmobile.core.Intents
|
||||
|
||||
object ActivityExtensions {
|
||||
@ -87,4 +88,7 @@ object ActivityExtensions {
|
||||
) =
|
||||
ViewModelProviders.of(this, viewModelFactory)
|
||||
.get(T::class.java)
|
||||
|
||||
val Activity.coreActivityComponent
|
||||
get() = CoreApp.getCoreComponent().activityComponentBuilder().activity(this).build()
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION_CODES;
|
||||
import static org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection;
|
||||
import static org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener;
|
||||
import static org.kiwix.kiwixmobile.core.search.SearchActivity.EXTRA_SEARCH_IN_TEXT;
|
||||
import static org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchInPreviousScreen.EXTRA_SEARCH_IN_TEXT;
|
||||
import static org.kiwix.kiwixmobile.core.utils.AnimationUtils.rotate;
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.BOOKMARK_CHOSEN_REQUEST;
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.EXTRA_CHOSE_X_TITLE;
|
||||
|
@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 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.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.TextView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.kiwix.kiwixlib.JNIKiwix;
|
||||
import org.kiwix.kiwixlib.JNIKiwixSearcher;
|
||||
import org.kiwix.kiwixmobile.core.CoreApp;
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil;
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer;
|
||||
|
||||
public class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
|
||||
|
||||
@Inject JNIKiwix currentJNIReader;
|
||||
@Inject SharedPreferenceUtil sharedPreferenceUtil;
|
||||
@Inject ZimReaderContainer zimReaderContainer;
|
||||
private List<String> mData;
|
||||
private KiwixFilter mFilter;
|
||||
private Context context;
|
||||
|
||||
public AutoCompleteAdapter(Context context) {
|
||||
super(context, android.R.layout.simple_list_item_1);
|
||||
this.context = context;
|
||||
mData = new ArrayList<>();
|
||||
mFilter = new KiwixFilter();
|
||||
setupDagger();
|
||||
}
|
||||
|
||||
private void setupDagger() {
|
||||
CoreApp.getCoreComponent().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View row = super.getView(position, convertView, parent);
|
||||
|
||||
TextView tv = row.findViewById(android.R.id.text1);
|
||||
tv.setText(Html.fromHtml(getItem(position)));
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItem(int index) {
|
||||
String a = mData.get(index);
|
||||
if (a.endsWith(".html")) {
|
||||
String trim = a.substring(2);
|
||||
trim = trim.substring(0, trim.length() - 5);
|
||||
return trim.replace("_", " ");
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
class KiwixFilter extends Filter {
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
FilterResults filterResults = new FilterResults();
|
||||
ArrayList<String> data = new ArrayList<>();
|
||||
|
||||
if (constraint != null) {
|
||||
try {
|
||||
|
||||
/* Get search request */
|
||||
final String query = constraint.toString();
|
||||
|
||||
/* Fulltext search */
|
||||
if (sharedPreferenceUtil.getPrefFullTextSearch()) {
|
||||
zimReaderContainer.search(query, 200);
|
||||
JNIKiwixSearcher.Result result = zimReaderContainer.getNextResult();
|
||||
while (result != null) {
|
||||
if (!result.getTitle().trim().isEmpty()) {
|
||||
data.add(result.getTitle());
|
||||
}
|
||||
result = zimReaderContainer.getNextResult();
|
||||
}
|
||||
}
|
||||
|
||||
/* Suggestion search if no fulltext results */
|
||||
if (data.size() == 0) {
|
||||
zimReaderContainer.searchSuggestions(query, 200);
|
||||
String suggestion;
|
||||
String suggestionUrl;
|
||||
SearchSuggestion results;
|
||||
List<String> alreadyAdded = new ArrayList<>();
|
||||
while ((results = zimReaderContainer.getNextSuggestion()) != null) {
|
||||
suggestion = results.getTitle();
|
||||
suggestionUrl = results.getUrl();
|
||||
|
||||
if (!alreadyAdded.contains(suggestionUrl)) {
|
||||
alreadyAdded.add(suggestionUrl);
|
||||
data.add(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
/* Return results */
|
||||
filterResults.values = data;
|
||||
filterResults.count = data.size();
|
||||
}
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence contraint, FilterResults results) {
|
||||
mData = (ArrayList<String>) results.values;
|
||||
if (results.count > 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,353 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 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.search;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import org.kiwix.kiwixmobile.core.Intents;
|
||||
import org.kiwix.kiwixmobile.core.R;
|
||||
import org.kiwix.kiwixmobile.core.base.BaseActivity;
|
||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity;
|
||||
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.EXTRA_IS_WIDGET_VOICE;
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.EXTRA_SEARCH;
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.EXTRA_SEARCH_TEXT;
|
||||
import static org.kiwix.kiwixmobile.core.utils.Constants.TAG_FILE_SEARCHED;
|
||||
|
||||
public class SearchActivity extends BaseActivity
|
||||
implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
|
||||
SearchViewCallback {
|
||||
|
||||
public static final String EXTRA_SEARCH_IN_TEXT = "bool_searchintext";
|
||||
|
||||
private final int REQ_CODE_SPEECH_INPUT = 100;
|
||||
@Inject
|
||||
SearchPresenter searchPresenter;
|
||||
private ListView listView;
|
||||
private ArrayAdapter<String> currentAdapter;
|
||||
private AutoCompleteAdapter autoAdapter;
|
||||
private ArrayAdapter<String> defaultAdapter;
|
||||
private SearchView searchView;
|
||||
private String searchText;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.search);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
searchText = savedInstanceState.getString(EXTRA_SEARCH_TEXT);
|
||||
}
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
ViewCompat.setLayoutDirection(toolbar, ViewCompat.LAYOUT_DIRECTION_LOCALE);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_back);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
searchPresenter.attachView(this);
|
||||
listView = findViewById(R.id.search_list);
|
||||
defaultAdapter = getDefaultAdapter();
|
||||
searchPresenter.getRecentSearches();
|
||||
activateDefaultAdapter();
|
||||
|
||||
autoAdapter = new AutoCompleteAdapter(this);
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setOnItemLongClickListener(this);
|
||||
|
||||
boolean IS_VOICE_SEARCH_INTENT = getIntent().getBooleanExtra(EXTRA_IS_WIDGET_VOICE, false);
|
||||
if (IS_VOICE_SEARCH_INTENT) {
|
||||
promptSpeechInput();
|
||||
}
|
||||
}
|
||||
|
||||
public void activateDefaultAdapter() {
|
||||
currentAdapter = defaultAdapter;
|
||||
listView.setAdapter(currentAdapter);
|
||||
}
|
||||
|
||||
public void activateAutoAdapter() {
|
||||
currentAdapter = autoAdapter;
|
||||
listView.setAdapter(currentAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRecentSearches(List<String> recentSearches) {
|
||||
defaultAdapter.addAll(recentSearches);
|
||||
defaultAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
int value =
|
||||
Settings.System.getInt(getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0);
|
||||
if (value == 1) {
|
||||
Intent intent = Intents.internal(CoreMainActivity.class);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
super.finish();
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_search, menu);
|
||||
MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
|
||||
searchMenuItem.expandActionView();
|
||||
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
|
||||
searchView.setMaxWidth(Integer.MAX_VALUE);
|
||||
if (searchText != null) {
|
||||
searchView.setQuery(searchText, false);
|
||||
activateAutoAdapter();
|
||||
autoAdapter.getFilter().filter(searchText.toLowerCase());
|
||||
}
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String s) {
|
||||
if (s.equals("")) {
|
||||
View item = findViewById(R.id.menu_searchintext);
|
||||
item.setVisibility(View.VISIBLE);
|
||||
activateDefaultAdapter();
|
||||
} else {
|
||||
View item = findViewById(R.id.menu_searchintext);
|
||||
item.setVisibility(View.GONE);
|
||||
activateAutoAdapter();
|
||||
autoAdapter.getFilter().filter(s.toLowerCase());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (getIntent().hasExtra(Intent.EXTRA_PROCESS_TEXT)) {
|
||||
searchView.setQuery(getIntent().getStringExtra(Intent.EXTRA_PROCESS_TEXT), true);
|
||||
}
|
||||
|
||||
if (getIntent().hasExtra(EXTRA_SEARCH)) {
|
||||
searchView.setQuery(getIntent().getStringExtra(EXTRA_SEARCH), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_searchintext) {
|
||||
String queryText = "";
|
||||
if (searchView != null) {
|
||||
queryText = searchView.getQuery().toString();
|
||||
}
|
||||
Intent resultIntent = Intents.internal(CoreMainActivity.class);
|
||||
resultIntent.putExtra(EXTRA_SEARCH_IN_TEXT, true);
|
||||
resultIntent.putExtra(TAG_FILE_SEARCHED, queryText);
|
||||
if (shouldStartNewActivity() != 1) {
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
} else {
|
||||
startActivity(resultIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
String title = currentAdapter.getItem(position);
|
||||
searchPresenter.saveSearch(title);
|
||||
sendMessage(title);
|
||||
}
|
||||
|
||||
private void sendMessage(String uri) {
|
||||
int value = shouldStartNewActivity();
|
||||
if (value == 1) {
|
||||
Intent i = Intents.internal(CoreMainActivity.class);
|
||||
i.putExtra(TAG_FILE_SEARCHED, uri);
|
||||
startActivity(i);
|
||||
} else {
|
||||
Intent i = new Intent();
|
||||
i.putExtra(TAG_FILE_SEARCHED, uri);
|
||||
setResult(RESULT_OK, i);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the ActivityManager is set to aggressively reclaim Activities.
|
||||
*
|
||||
* @return 1 if the above setting is true.
|
||||
*/
|
||||
private int shouldStartNewActivity() {
|
||||
int value;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
//deprecated in API 17
|
||||
value =
|
||||
Settings.System.getInt(getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0);
|
||||
} else {
|
||||
value =
|
||||
Settings.System.getInt(getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (parent.getAdapter() == defaultAdapter) {
|
||||
String searched = listView.getItemAtPosition(position).toString();
|
||||
deleteSpecificSearchDialog(searched);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteSpecificSearchDialog(final String search) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.delete_recent_search_item))
|
||||
.setPositiveButton(getResources().getString(R.string.delete), (dialog, which) -> {
|
||||
deleteSpecificSearchItem(search);
|
||||
Toast.makeText(getBaseContext(),
|
||||
getResources().getString(R.string.delete_specific_search_toast), Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, (dialog, which) -> {
|
||||
// do nothing
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void deleteSpecificSearchItem(String search) {
|
||||
searchPresenter.deleteSearchString(search);
|
||||
resetAdapter();
|
||||
}
|
||||
|
||||
private void resetAdapter() {
|
||||
defaultAdapter = getDefaultAdapter();
|
||||
activateDefaultAdapter();
|
||||
searchPresenter.getRecentSearches();
|
||||
}
|
||||
|
||||
private ArrayAdapter<String> getDefaultAdapter() {
|
||||
return new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1) {
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View row;
|
||||
|
||||
if (convertView == null) {
|
||||
row = LayoutInflater.from(parent.getContext())
|
||||
.inflate(android.R.layout.simple_list_item_1, null);
|
||||
} else {
|
||||
row = convertView;
|
||||
}
|
||||
|
||||
((TextView) row).setText(Html.fromHtml(getItem(position)));
|
||||
return row;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void promptSpeechInput() {
|
||||
String appName = getResources().getString(R.string.app_name);
|
||||
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
|
||||
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,
|
||||
Locale.getDefault()); // TODO: choose selected lang on kiwix
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
|
||||
String.format(getString(R.string.speech_prompt_text), appName));
|
||||
try {
|
||||
startActivityForResult(intent, REQ_CODE_SPEECH_INPUT);
|
||||
} catch (ActivityNotFoundException a) {
|
||||
Toast.makeText(getApplicationContext(),
|
||||
getString(R.string.speech_not_supported),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
switch (requestCode) {
|
||||
|
||||
case REQ_CODE_SPEECH_INPUT: {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
ArrayList<String> result = data
|
||||
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
|
||||
searchViaVoice(result.get(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void searchViaVoice(String search) {
|
||||
searchView.setQuery(search, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (searchView != null && searchView.getQuery() != null) {
|
||||
outState.putString(EXTRA_SEARCH_TEXT, searchView.getQuery().toString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 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.search
|
||||
|
||||
import android.R.anim
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MenuItem.OnActionExpandListener
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.android.synthetic.main.activity_search.searchViewAnimator
|
||||
import kotlinx.android.synthetic.main.activity_search.search_list
|
||||
import kotlinx.android.synthetic.main.layout_toolbar.toolbar
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.R.id
|
||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.coreActivityComponent
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
|
||||
import org.kiwix.kiwixmobile.core.extensions.setDistinctDisplayedChild
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchAdapter
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchDelegate.RecentSearchDelegate
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchDelegate.ZimSearchResultDelegate
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ActivityResultReceived
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.CreatedWithIntent
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ExitedSearch
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Empty
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Initialising
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
||||
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchActivity : BaseActivity() {
|
||||
|
||||
val activityComponent by lazy { coreActivityComponent }
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
private lateinit var searchView: SearchView
|
||||
private lateinit var searchInTextMenuItem: MenuItem
|
||||
|
||||
private val searchViewModel by lazy { viewModel<SearchViewModel>(viewModelFactory) }
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val searchAdapter: SearchAdapter by lazy {
|
||||
SearchAdapter(
|
||||
RecentSearchDelegate(::onItemClick) {
|
||||
searchViewModel.actions.offer(OnItemLongClick(it))
|
||||
},
|
||||
ZimSearchResultDelegate(::onItemClick)
|
||||
)
|
||||
}
|
||||
|
||||
override fun injection() {
|
||||
activityComponent.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar!!.setHomeAsUpIndicator(R.drawable.ic_action_back)
|
||||
supportActionBar!!.setHomeButtonEnabled(true)
|
||||
search_list.run {
|
||||
adapter = searchAdapter
|
||||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
compositeDisposable.add(searchViewModel.effects.subscribe { it.invokeWith(this) })
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
compositeDisposable.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
super.finish()
|
||||
overridePendingTransition(anim.fade_in, anim.fade_out)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_search, menu)
|
||||
val searchMenuItem = menu.findItem(id.menu_search)
|
||||
searchMenuItem.expandActionView()
|
||||
searchView = searchMenuItem.actionView as SearchView
|
||||
searchView.setOnQueryTextListener(SimpleTextListener {
|
||||
searchViewModel.actions.offer(Filter(it))
|
||||
})
|
||||
searchMenuItem.setOnActionExpandListener(object : OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem) = false
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
searchViewModel.actions.offer(ExitedSearch)
|
||||
return false
|
||||
}
|
||||
})
|
||||
searchInTextMenuItem = menu.findItem(R.id.menu_searchintext)
|
||||
searchInTextMenuItem.setOnMenuItemClickListener {
|
||||
searchViewModel.actions.offer(ClickedSearchInText)
|
||||
true
|
||||
}
|
||||
|
||||
searchViewModel.state.observe(this, Observer(::render))
|
||||
searchViewModel.actions.offer(CreatedWithIntent(intent))
|
||||
return true
|
||||
}
|
||||
|
||||
private fun render(state: State) = when (state) {
|
||||
is Results -> {
|
||||
searchViewAnimator.setDistinctDisplayedChild(0)
|
||||
searchAdapter.items = state.values
|
||||
searchView.setQuery(state.searchString, false)
|
||||
searchInTextMenuItem.isVisible = state.searchString.isNotBlank()
|
||||
}
|
||||
Empty -> searchViewAnimator.setDistinctDisplayedChild(1)
|
||||
Initialising -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private fun onItemClick(it: SearchListItem) {
|
||||
searchViewModel.actions.offer(OnItemClick(it))
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
searchViewModel.actions.offer(ActivityResultReceived(requestCode, resultCode, data))
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 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.search;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import org.kiwix.kiwixmobile.core.base.BasePresenter;
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao;
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer;
|
||||
|
||||
/**
|
||||
* Created by srv_twry on 14/2/18.
|
||||
*/
|
||||
|
||||
public class SearchPresenter extends BasePresenter<SearchViewCallback> {
|
||||
|
||||
private final NewRecentSearchDao recentSearchDao;
|
||||
private final ZimReaderContainer zimReaderContainer;
|
||||
|
||||
@Inject SearchPresenter(NewRecentSearchDao recentSearchDao,
|
||||
ZimReaderContainer zimReaderContainer) {
|
||||
this.recentSearchDao = recentSearchDao;
|
||||
this.zimReaderContainer = zimReaderContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachView(SearchViewCallback searchViewCallback) {
|
||||
super.attachView(searchViewCallback);
|
||||
}
|
||||
|
||||
void getRecentSearches() {
|
||||
view.addRecentSearches(recentSearchDao.getRecentSearches(zimReaderContainer.getId()));
|
||||
}
|
||||
|
||||
void saveSearch(String title) {
|
||||
recentSearchDao.saveSearch(title, zimReaderContainer.getId());
|
||||
}
|
||||
|
||||
void deleteSearchString(String search) {
|
||||
recentSearchDao.deleteSearchString(search);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.search.adapter
|
||||
|
||||
import org.kiwix.kiwixmobile.core.base.adapter.AdapterDelegate
|
||||
import org.kiwix.kiwixmobile.core.base.adapter.BaseDelegateAdapter
|
||||
|
||||
class SearchAdapter(
|
||||
vararg delegates: AdapterDelegate<SearchListItem>
|
||||
) : BaseDelegateAdapter<SearchListItem>(*delegates) {
|
||||
override fun getIdFor(item: SearchListItem) = item.value.hashCode().toLong()
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.search.adapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter
|
||||
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.inflate
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchViewHolder.RecentSearchViewHolder
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchViewHolder.ZimSearchResultViewHolder
|
||||
|
||||
sealed class SearchDelegate<I : SearchListItem, out VH : SearchViewHolder<I>> :
|
||||
AbsDelegateAdapter<I, SearchListItem, VH> {
|
||||
|
||||
class RecentSearchDelegate(
|
||||
private val onClickListener: (SearchListItem) -> Unit,
|
||||
private val onLongClickListener: (SearchListItem) -> Unit
|
||||
) : SearchDelegate<RecentSearchListItem, RecentSearchViewHolder>() {
|
||||
override val itemClass = RecentSearchListItem::class.java
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
RecentSearchViewHolder(
|
||||
parent.inflate(android.R.layout.simple_list_item_1, false),
|
||||
onClickListener,
|
||||
onLongClickListener
|
||||
)
|
||||
}
|
||||
|
||||
class ZimSearchResultDelegate(
|
||||
private val onClickListener: (SearchListItem) -> Unit
|
||||
) : SearchDelegate<ZimSearchResultListItem, ZimSearchResultViewHolder>() {
|
||||
override val itemClass = ZimSearchResultListItem::class.java
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
ZimSearchResultViewHolder(
|
||||
parent.inflate(android.R.layout.simple_list_item_1, false),
|
||||
onClickListener
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.search.adapter
|
||||
|
||||
sealed class SearchListItem() {
|
||||
abstract val value: String
|
||||
|
||||
data class RecentSearchListItem(override val value: String) : SearchListItem()
|
||||
data class ZimSearchResultListItem constructor(override val value: String) : SearchListItem()
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.search.adapter
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem
|
||||
|
||||
sealed class SearchViewHolder<in T : SearchListItem>(containerView: View) :
|
||||
BaseViewHolder<T>(containerView) {
|
||||
|
||||
class RecentSearchViewHolder(
|
||||
override val containerView: View,
|
||||
private val onClickListener: (SearchListItem) -> Unit,
|
||||
private val onLongClickListener: (SearchListItem) -> Unit
|
||||
) : SearchViewHolder<RecentSearchListItem>(containerView) {
|
||||
override fun bind(item: RecentSearchListItem) {
|
||||
containerView.setOnClickListener { onClickListener(item) }
|
||||
containerView.setOnLongClickListener {
|
||||
onLongClickListener(item)
|
||||
true
|
||||
}
|
||||
(containerView as TextView).text = item.value
|
||||
}
|
||||
}
|
||||
|
||||
class ZimSearchResultViewHolder(
|
||||
override val containerView: View,
|
||||
private val onClickListener: (SearchListItem) -> Unit
|
||||
) : SearchViewHolder<ZimSearchResultListItem>(containerView) {
|
||||
override fun bind(item: ZimSearchResultListItem) {
|
||||
containerView.setOnClickListener { onClickListener(item) }
|
||||
(containerView as TextView).text = item.value
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.search.viewmodel
|
||||
|
||||
import android.content.Intent
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
|
||||
sealed class Action {
|
||||
object ExitedSearch : Action()
|
||||
object ClickedSearchInText : Action()
|
||||
object ReceivedPromptForSpeechInput : Action()
|
||||
object StartSpechInputFailed : Action()
|
||||
|
||||
data class OnItemClick(val searchListItem: SearchListItem) : Action()
|
||||
data class OnItemLongClick(val searchListItem: SearchListItem) : Action()
|
||||
data class Filter(val term: String) : Action()
|
||||
data class ConfirmedDelete(val searchListItem: SearchListItem) : Action()
|
||||
data class CreatedWithIntent(val intent: Intent?) : Action()
|
||||
data class ActivityResultReceived(val requestCode: Int, val resultCode: Int, val data: Intent?) :
|
||||
Action()
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.search.viewmodel
|
||||
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||
import javax.inject.Inject
|
||||
|
||||
interface SearchResultGenerator {
|
||||
fun generateSearchResults(searchTerm: String): List<SearchListItem>
|
||||
}
|
||||
|
||||
class ZimSearchResultGenerator @Inject constructor(
|
||||
val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||
val zimReaderContainer: ZimReaderContainer
|
||||
) : SearchResultGenerator {
|
||||
override fun generateSearchResults(searchTerm: String) =
|
||||
if (searchTerm.isNotEmpty()) readResultsFromZim(searchTerm)
|
||||
else emptyList()
|
||||
|
||||
private fun readResultsFromZim(it: String) =
|
||||
if (sharedPreferenceUtil.prefFullTextSearch)
|
||||
zimReaderContainer.search(it, 200).run { fullTextResults() }
|
||||
else
|
||||
zimReaderContainer.searchSuggestions(it, 200).run { suggestionResults() }
|
||||
|
||||
private fun suggestionResults() = generateSequence {
|
||||
zimReaderContainer.getNextSuggestion()?.let { ZimSearchResultListItem(it.title) }
|
||||
}.distinct()
|
||||
.toList()
|
||||
|
||||
private fun fullTextResults() = generateSequence {
|
||||
zimReaderContainer.getNextResult()?.let { ZimSearchResultListItem(it.title) }
|
||||
}.filter { it.value.isNotBlank() }
|
||||
.toList()
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.search.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Function3
|
||||
import io.reactivex.processors.BehaviorProcessor
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.kiwix.kiwixmobile.core.R.string
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ActivityResultReceived
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ConfirmedDelete
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.CreatedWithIntent
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ExitedSearch
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpechInputFailed
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Empty
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Initialising
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.DeleteRecentSearch
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.OpenSearchItem
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ProcessActivityResult
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SaveSearchToRecents
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchInPreviousScreen
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchIntentProcessing
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowDeleteSearchDialog
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.ShowToast
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.StartSpeechInput
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchViewModel @Inject constructor(
|
||||
val recentSearchDao: NewRecentSearchDao,
|
||||
val zimReaderContainer: ZimReaderContainer,
|
||||
private val searchResultGenerator: SearchResultGenerator
|
||||
) : ViewModel() {
|
||||
|
||||
val state = MutableLiveData<State>().apply { value = Initialising }
|
||||
val effects = PublishProcessor.create<SideEffect<*>>()
|
||||
val actions = PublishProcessor.create<Action>()
|
||||
private val filter = BehaviorProcessor.createDefault("")
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
init {
|
||||
compositeDisposable.addAll(
|
||||
viewStateReducer(),
|
||||
actionMapper()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
compositeDisposable.clear()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun actionMapper() = actions.map {
|
||||
when (it) {
|
||||
ExitedSearch -> effects.offer(Finish)
|
||||
is OnItemClick -> saveSearchAndOpenItem(it)
|
||||
is OnItemLongClick -> showDeleteDialog(it)
|
||||
is Filter -> filter.offer(it.term)
|
||||
ClickedSearchInText -> searchPreviousScreenhenStateIsValid()
|
||||
is ConfirmedDelete -> deleteItemAndShowToast(it)
|
||||
is CreatedWithIntent -> effects.offer(SearchIntentProcessing(it.intent, actions))
|
||||
ReceivedPromptForSpeechInput -> effects.offer(StartSpeechInput(actions))
|
||||
StartSpechInputFailed -> effects.offer(ShowToast(string.speech_not_supported))
|
||||
is ActivityResultReceived ->
|
||||
effects.offer(ProcessActivityResult(it.requestCode, it.resultCode, it.data, actions))
|
||||
}
|
||||
}.subscribe(
|
||||
{},
|
||||
Throwable::printStackTrace
|
||||
)
|
||||
|
||||
private fun deleteItemAndShowToast(it: ConfirmedDelete) {
|
||||
effects.offer(DeleteRecentSearch(it.searchListItem, recentSearchDao))
|
||||
effects.offer(ShowToast(string.delete_specific_search_toast))
|
||||
}
|
||||
|
||||
private fun searchPreviousScreenhenStateIsValid(): Any =
|
||||
when (val currentState = state.value) {
|
||||
is Results -> effects.offer(SearchInPreviousScreen(currentState.searchString))
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
private fun showDeleteDialog(longClick: OnItemLongClick) {
|
||||
effects.offer(ShowDeleteSearchDialog(longClick.searchListItem, actions))
|
||||
}
|
||||
|
||||
private fun saveSearchAndOpenItem(it: OnItemClick) {
|
||||
effects.offer(
|
||||
SaveSearchToRecents(recentSearchDao, it.searchListItem, zimReaderContainer.id)
|
||||
)
|
||||
effects.offer(
|
||||
OpenSearchItem(it.searchListItem)
|
||||
)
|
||||
}
|
||||
|
||||
private fun viewStateReducer() =
|
||||
Flowable.combineLatest(
|
||||
recentSearchDao.recentSearches(zimReaderContainer.id),
|
||||
searchResultsFromZimReader(),
|
||||
filter,
|
||||
Function3(this::reduce)
|
||||
).debounce(100, MILLISECONDS)
|
||||
.subscribe(state::postValue, Throwable::printStackTrace)
|
||||
|
||||
private fun reduce(
|
||||
recentSearchResults: List<SearchListItem>,
|
||||
zimSearchResults: List<SearchListItem>,
|
||||
searchString: String
|
||||
) = when {
|
||||
searchString.isNotEmpty() && zimSearchResults.isNotEmpty() ->
|
||||
Results(searchString, zimSearchResults)
|
||||
searchString.isEmpty() && recentSearchResults.isNotEmpty() ->
|
||||
Results(searchString, recentSearchResults)
|
||||
else -> Empty
|
||||
}
|
||||
|
||||
private fun searchResultsFromZimReader() = filter
|
||||
.distinctUntilChanged()
|
||||
.switchMap(::searchResults)
|
||||
|
||||
private fun searchResults(it: String) = Flowable.fromCallable {
|
||||
searchResultGenerator.generateSearchResults(it)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.search.viewmodel
|
||||
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
|
||||
sealed class State {
|
||||
data class Results(val searchString: String, val values: List<SearchListItem>) : State()
|
||||
object Empty : State()
|
||||
object Initialising : State()
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
|
||||
data class DeleteRecentSearch(
|
||||
val searchListItem: SearchListItem,
|
||||
val recentSearchDao: NewRecentSearchDao
|
||||
) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
recentSearchDao.deleteSearchString(searchListItem.value)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* 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
|
||||
@ -15,15 +15,14 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.search;
|
||||
|
||||
import java.util.List;
|
||||
import org.kiwix.kiwixmobile.core.base.BaseContract;
|
||||
package org.kiwix.kiwixmobile.core.search.viewmodel.effects
|
||||
|
||||
/**
|
||||
* Created by srv_twry on 14/2/18.
|
||||
*/
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
|
||||
public interface SearchViewCallback extends BaseContract.View {
|
||||
void addRecentSearches(List<String> recentSearches);
|
||||
object Finish : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
activity.finish()
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.utils.Constants
|
||||
|
||||
class OpenSearchItem(private val searchListItem: SearchListItem) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
activity.setResult(
|
||||
Activity.RESULT_OK,
|
||||
Intent().putExtra(Constants.TAG_FILE_SEARCHED, searchListItem.value)
|
||||
)
|
||||
activity.finish()
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.speech.RecognizerIntent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter
|
||||
|
||||
data class ProcessActivityResult(
|
||||
val requestCode: Int,
|
||||
val resultCode: Int,
|
||||
val data: Intent?,
|
||||
val actions: PublishProcessor<Action>
|
||||
) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
if (requestCode == StartSpeechInput.REQ_CODE_SPEECH_INPUT &&
|
||||
resultCode == Activity.RESULT_OK &&
|
||||
data != null
|
||||
) {
|
||||
actions.offer(Filter(data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)[0]))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
|
||||
class SaveSearchToRecents(
|
||||
private val recentSearchDao: NewRecentSearchDao,
|
||||
private val searchListItem: SearchListItem,
|
||||
private val id: String?
|
||||
) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
id?.let { recentSearchDao.saveSearch(searchListItem.value, it) }
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.utils.Constants
|
||||
|
||||
data class SearchInPreviousScreen(val searchString: String) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
activity.setResult(
|
||||
Activity.RESULT_OK,
|
||||
Intent().apply {
|
||||
putExtra(EXTRA_SEARCH_IN_TEXT, true)
|
||||
putExtra(Constants.TAG_FILE_SEARCHED, searchString)
|
||||
}
|
||||
)
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_SEARCH_IN_TEXT = "bool_searchintext"
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter
|
||||
import org.kiwix.kiwixmobile.core.utils.Constants
|
||||
|
||||
data class SearchIntentProcessing(
|
||||
val intent: Intent?,
|
||||
val actions: PublishProcessor<Action>
|
||||
) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
if (intent != null) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.M && intent.hasExtra(Intent.EXTRA_PROCESS_TEXT)) {
|
||||
actions.offer(Filter(intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT)))
|
||||
}
|
||||
if (intent.hasExtra(Constants.EXTRA_SEARCH)) {
|
||||
actions.offer(Filter(intent.getStringExtra(Constants.EXTRA_SEARCH)))
|
||||
}
|
||||
if (intent.getBooleanExtra(Constants.EXTRA_IS_WIDGET_VOICE, false)) {
|
||||
actions.offer(Action.ReceivedPromptForSpeechInput)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.search.SearchActivity
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action
|
||||
import org.kiwix.kiwixmobile.core.utils.DialogShower
|
||||
import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteSearch
|
||||
import javax.inject.Inject
|
||||
|
||||
data class ShowDeleteSearchDialog(
|
||||
val searchListItem: SearchListItem,
|
||||
val actions: PublishProcessor<Action>
|
||||
) : SideEffect<Unit> {
|
||||
|
||||
@Inject lateinit var dialogShower: DialogShower
|
||||
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
(activity as SearchActivity).activityComponent.inject(this)
|
||||
dialogShower.show(DeleteSearch, { actions.offer(Action.ConfirmedDelete(searchListItem)) })
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.extensions.toast
|
||||
|
||||
data class ShowToast(@StringRes val stringId: Int) : SideEffect<Unit> {
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
activity.toast(stringId, Toast.LENGTH_SHORT)
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.search.viewmodel.effects
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.speech.RecognizerIntent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action
|
||||
import java.util.Locale
|
||||
|
||||
data class StartSpeechInput(val actions: PublishProcessor<Action>) : SideEffect<Unit> {
|
||||
|
||||
override fun invokeWith(activity: AppCompatActivity) {
|
||||
try {
|
||||
activity.startActivityForResult(
|
||||
Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
|
||||
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
|
||||
putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
|
||||
putExtra(
|
||||
RecognizerIntent.EXTRA_PROMPT,
|
||||
activity.getString(R.string.speech_prompt_text, activity.getString(R.string.app_name))
|
||||
)
|
||||
},
|
||||
REQ_CODE_SPEECH_INPUT
|
||||
)
|
||||
} catch (a: ActivityNotFoundException) {
|
||||
actions.offer(Action.StartSpechInputFailed)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQ_CODE_SPEECH_INPUT = 100
|
||||
}
|
||||
}
|
@ -99,6 +99,10 @@ sealed class KiwixDialog(
|
||||
constructor(selectedPeerDeviceName: String) : this(listOf(selectedPeerDeviceName))
|
||||
}
|
||||
|
||||
object DeleteSearch : KiwixDialog(
|
||||
null, R.string.delete_recent_search_item, R.string.delete, R.string.no
|
||||
)
|
||||
|
||||
open class YesNoDialog(
|
||||
title: Int,
|
||||
message: Int
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* 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
|
||||
@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.zim_manager
|
||||
package org.kiwix.kiwixmobile.core.utils
|
||||
|
||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||
|
@ -1,9 +1,7 @@
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
@ -18,10 +16,25 @@
|
||||
<include layout="@layout/layout_toolbar" />
|
||||
</RelativeLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/search_list"
|
||||
<ViewAnimator
|
||||
android:id="@+id/searchViewAnimator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/search_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/no_results"
|
||||
android:textAppearance="?textAppearanceHeadline6" />
|
||||
|
||||
</ViewAnimator>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -9,5 +9,5 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:popupTheme="@style/KiwixTheme"
|
||||
app:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
||||
tools:showIn="@layout/search" />
|
||||
tools:showIn="@layout/activity_search" />
|
||||
</merge>
|
||||
|
@ -292,6 +292,7 @@
|
||||
<string name="storage_permission_denied">Storage Permission Denied</string>
|
||||
<string name="grant_read_storage_permission">This app requires the ability to read storage to function. Please grant the permission in your settings</string>
|
||||
<string name="go_to_permissions">Go to Settings</string>
|
||||
<string name="no_results">No Results</string>
|
||||
<string-array name="pref_night_modes_entries">
|
||||
<item>On</item>
|
||||
<item>Off</item>
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* 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
|
||||
@ -27,7 +27,6 @@ import org.kiwix.kiwixmobile.custom.di.CustomScope;
|
||||
|
||||
@CustomScope
|
||||
public class CustomViewModelFactory extends ViewModelFactory {
|
||||
|
||||
@Inject
|
||||
public CustomViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
|
||||
super(creators);
|
||||
|
Loading…
x
Reference in New Issue
Block a user