diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.java b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.java index a33193121..4acde9bd5 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.java +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.java @@ -151,6 +151,7 @@ import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_CURRENT_FILE; import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_CURRENT_POSITIONS; import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_CURRENT_TAB; import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_FILE_SEARCHED; +import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_FILE_SEARCHED_NEW_TAB; import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_KIWIX; import static org.kiwix.kiwixmobile.core.utils.LanguageUtils.getResourceString; import static org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_KIWIX_MOBILE; @@ -430,8 +431,10 @@ public abstract class CoreReaderFragment extends BaseFragment private void handleIntentExtras(Intent intent) { if (intent.hasExtra(TAG_FILE_SEARCHED)) { + boolean openInNewTab = isInTabSwitcher() + || intent.getBooleanExtra(TAG_FILE_SEARCHED_NEW_TAB, false); searchForTitle(intent.getStringExtra(TAG_FILE_SEARCHED), - isInTabSwitcher()); + openInNewTab); selectTab(webViewList.size() - 1); } if (intent.hasExtra(EXTRA_CHOSE_X_URL)) { @@ -1510,7 +1513,9 @@ public abstract class CoreReaderFragment extends BaseFragment compatCallback.findAll(); compatCallback.showSoftInput(); } else { - searchForTitle(title, wasFromTabSwitcher); + boolean openInNewTab = wasFromTabSwitcher || + data.getBooleanExtra(TAG_FILE_SEARCHED_NEW_TAB, false); + searchForTitle(title, openInNewTab); } } else if (resultCode == RESULT_CANCELED) { Log.w(TAG_KIWIX, "Search cancelled or exited"); diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchActivity.kt index 4f1d4cd4e..a50e13b50 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchActivity.kt @@ -49,6 +49,7 @@ 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.OnOpenInNewTabClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick import org.kiwix.kiwixmobile.core.search.viewmodel.SearchOrigin.FromWebView import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel @@ -70,10 +71,10 @@ class SearchActivity : BaseActivity() { private val compositeDisposable = CompositeDisposable() private val searchAdapter: SearchAdapter by lazy { SearchAdapter( - RecentSearchDelegate(::onItemClick) { + RecentSearchDelegate(::onItemClick, ::onItemClickNewTab) { searchViewModel.actions.offer(OnItemLongClick(it)) }, - ZimSearchResultDelegate(::onItemClick) + ZimSearchResultDelegate(::onItemClick, ::onItemClickNewTab) ) } @@ -155,6 +156,10 @@ class SearchActivity : BaseActivity() { searchViewModel.actions.offer(OnItemClick(it)) } + private fun onItemClickNewTab(it: SearchListItem) { + searchViewModel.actions.offer(OnOpenInNewTabClick(it)) + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) searchViewModel.actions.offer(ActivityResultReceived(requestCode, resultCode, data)) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchDelegate.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchDelegate.kt index e535a3648..30f43ce8a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchDelegate.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchDelegate.kt @@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.core.search.adapter import android.view.ViewGroup +import org.kiwix.kiwixmobile.core.R 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 @@ -31,27 +32,31 @@ sealed class SearchDelegate> : class RecentSearchDelegate( private val onClickListener: (SearchListItem) -> Unit, + private val onClickListenerNewTab: (SearchListItem) -> Unit, private val onLongClickListener: (SearchListItem) -> Unit ) : SearchDelegate() { override val itemClass = RecentSearchListItem::class.java override fun createViewHolder(parent: ViewGroup) = RecentSearchViewHolder( - parent.inflate(android.R.layout.simple_selectable_list_item, false), + parent.inflate(R.layout.list_item_search, false), onClickListener, + onClickListenerNewTab, onLongClickListener ) } class ZimSearchResultDelegate( - private val onClickListener: (SearchListItem) -> Unit + private val onClickListener: (SearchListItem) -> Unit, + private val onClickListenerNewTab: (SearchListItem) -> Unit ) : SearchDelegate() { override val itemClass = ZimSearchResultListItem::class.java override fun createViewHolder(parent: ViewGroup) = ZimSearchResultViewHolder( - parent.inflate(android.R.layout.simple_selectable_list_item, false), - onClickListener + parent.inflate(R.layout.list_item_search, false), + onClickListener, + onClickListenerNewTab ) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchViewHolder.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchViewHolder.kt index 6640892a2..12f2408c9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchViewHolder.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/adapter/SearchViewHolder.kt @@ -19,7 +19,8 @@ package org.kiwix.kiwixmobile.core.search.adapter import android.view.View -import android.widget.TextView +import kotlinx.android.synthetic.main.list_item_search.list_item_search_new_tab_button +import kotlinx.android.synthetic.main.list_item_search.list_item_search_text 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 @@ -30,6 +31,7 @@ sealed class SearchViewHolder(containerView: View) : class RecentSearchViewHolder( override val containerView: View, private val onClickListener: (SearchListItem) -> Unit, + private val onClickListenerNewTab: (SearchListItem) -> Unit, private val onLongClickListener: (SearchListItem) -> Unit ) : SearchViewHolder(containerView) { override fun bind(item: RecentSearchListItem) { @@ -38,17 +40,20 @@ sealed class SearchViewHolder(containerView: View) : onLongClickListener(item) true } - (containerView as TextView).text = item.value + list_item_search_new_tab_button.setOnClickListener { onClickListenerNewTab(item) } + list_item_search_text.text = item.value } } class ZimSearchResultViewHolder( override val containerView: View, - private val onClickListener: (SearchListItem) -> Unit + private val onClickListener: (SearchListItem) -> Unit, + private val onClickListenerNewTab: (SearchListItem) -> Unit ) : SearchViewHolder(containerView) { override fun bind(item: ZimSearchResultListItem) { containerView.setOnClickListener { onClickListener(item) } - (containerView as TextView).text = item.value + list_item_search_new_tab_button.setOnClickListener { onClickListenerNewTab(item) } + list_item_search_text.text = item.value } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/Action.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/Action.kt index f223d9a01..ca8e29fab 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/Action.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/Action.kt @@ -28,6 +28,8 @@ sealed class Action { object StartSpeechInputFailed : Action() data class OnItemClick(val searchListItem: SearchListItem) : Action() + data class OnOpenInNewTabClick(val searchListItem: SearchListItem) : Action() + data class OnItemLongClick(val searchListItem: SearchListItem) : Action() data class Filter(val term: String) : Action() data class ScreenWasStartedFrom(val searchOrigin: SearchOrigin) : Action() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt index b22dcbc23..a23c6c954 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt @@ -38,6 +38,7 @@ 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.OnOpenInNewTabClick 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.ScreenWasStartedFrom @@ -89,7 +90,8 @@ class SearchViewModel @Inject constructor( private fun actionMapper() = actions.map { when (it) { ExitedSearch -> effects.offer(Finish) - is OnItemClick -> saveSearchAndOpenItem(it) + is OnItemClick -> saveSearchAndOpenItem(it.searchListItem, false) + is OnOpenInNewTabClick -> saveSearchAndOpenItem(it.searchListItem, true) is OnItemLongClick -> showDeleteDialog(it) is Filter -> filter.offer(it.term) ClickedSearchInText -> searchPreviousScreenWhenStateIsValid() @@ -118,12 +120,12 @@ class SearchViewModel @Inject constructor( effects.offer(ShowDeleteSearchDialog(longClick.searchListItem, actions)) } - private fun saveSearchAndOpenItem(it: OnItemClick) { + private fun saveSearchAndOpenItem(searchListItem: SearchListItem, openInNewTab: Boolean) { effects.offer( - SaveSearchToRecents(recentSearchDao, it.searchListItem, zimReaderContainer.id) + SaveSearchToRecents(recentSearchDao, searchListItem, zimReaderContainer.id) ) effects.offer( - OpenSearchItem(it.searchListItem) + OpenSearchItem(searchListItem, openInNewTab) ) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt index 62fd130b9..67264d27c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt @@ -24,12 +24,17 @@ 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.TAG_FILE_SEARCHED +import org.kiwix.kiwixmobile.core.utils.TAG_FILE_SEARCHED_NEW_TAB -data class OpenSearchItem(private val searchListItem: SearchListItem) : SideEffect { +data class OpenSearchItem( + private val searchListItem: SearchListItem, + private val openInNewTab: Boolean = false +) : SideEffect { override fun invokeWith(activity: AppCompatActivity) { activity.setResult( Activity.RESULT_OK, Intent().putExtra(TAG_FILE_SEARCHED, searchListItem.value) + .putExtra(TAG_FILE_SEARCHED_NEW_TAB, openInNewTab) ) activity.finish() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt index 83ca174c0..c1f9f3ab1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt @@ -34,6 +34,7 @@ const val RESULT_HISTORY_CLEARED = 1239 // Tags const val TAG_FILE_SEARCHED = "searchedarticle" +const val TAG_FILE_SEARCHED_NEW_TAB = "searchedarticlenewtab" const val TAG_CURRENT_FILE = "currentzimfile" const val TAG_CURRENT_ARTICLES = "currentarticles" const val TAG_CURRENT_POSITIONS = "currentpositions" diff --git a/core/src/main/res/drawable/ic_open_in_new_24dp.xml b/core/src/main/res/drawable/ic_open_in_new_24dp.xml new file mode 100644 index 000000000..034831af2 --- /dev/null +++ b/core/src/main/res/drawable/ic_open_in_new_24dp.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/core/src/main/res/layout/list_item_search.xml b/core/src/main/res/layout/list_item_search.xml new file mode 100644 index 000000000..910a9fe5f --- /dev/null +++ b/core/src/main/res/layout/list_item_search.xml @@ -0,0 +1,51 @@ + + + + + + + + + + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 5c1a6cd21..5118f0d27 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -278,6 +278,7 @@ Please send all the following details so we can diagnose the problem %d%% Text Zoom + Open in new tab Experimental Navigation @string/on diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt index e6ff3cd31..51dc22c6c 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt @@ -45,6 +45,7 @@ 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.OnOpenInNewTabClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ScreenWasStartedFrom import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed @@ -226,7 +227,17 @@ internal class SearchViewModelTest { actionResultsInEffects( OnItemClick(searchListItem), SaveSearchToRecents(recentSearchDao, searchListItem, "id"), - OpenSearchItem(searchListItem) + OpenSearchItem(searchListItem, false) + ) + } + + @Test + fun `OnOpenInNewTabClick offers Saves and Opens in new tab`() { + val searchListItem = RecentSearchListItem("") + actionResultsInEffects( + OnOpenInNewTabClick(searchListItem), + SaveSearchToRecents(recentSearchDao, searchListItem, "id"), + OpenSearchItem(searchListItem, true) ) } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItemTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItemTest.kt index 22997ebb2..41d2a318d 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItemTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItemTest.kt @@ -28,6 +28,7 @@ import io.mockk.verify import org.junit.jupiter.api.Test import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem import org.kiwix.kiwixmobile.core.utils.TAG_FILE_SEARCHED +import org.kiwix.kiwixmobile.core.utils.TAG_FILE_SEARCHED_NEW_TAB internal class OpenSearchItemTest { @@ -39,8 +40,26 @@ internal class OpenSearchItemTest { val intent = mockk() every { anyConstructed().putExtra(TAG_FILE_SEARCHED, searchListItem.value) + .putExtra(TAG_FILE_SEARCHED_NEW_TAB, false) } returns intent - OpenSearchItem(searchListItem).invokeWith(activity) + OpenSearchItem(searchListItem, false).invokeWith(activity) + verify { + activity.setResult(Activity.RESULT_OK, intent) + activity.finish() + } + } + + @Test + fun `invoke with returns an Ok Result with list item value for new tab`() { + val searchListItem = RecentSearchListItem("") + val activity: AppCompatActivity = mockk() + mockkConstructor(Intent::class) + val intent = mockk() + every { + anyConstructed().putExtra(TAG_FILE_SEARCHED, searchListItem.value) + .putExtra(TAG_FILE_SEARCHED_NEW_TAB, true) + } returns intent + OpenSearchItem(searchListItem, true).invokeWith(activity) verify { activity.setResult(Activity.RESULT_OK, intent) activity.finish()