From a3c55d4f5fcfd676704a0821b1f78fcc03866c80 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Sun, 9 Feb 2020 15:34:28 -0500 Subject: [PATCH 01/20] Introduce new classes to manage the searching functionality --- .../ui/pages/PageParentSearchAdapter.kt | 57 ++++++++++ .../ui/pages/PageParentSearchFragment.kt | 104 ++++++++++++++++++ .../ui/pages/PageParentSearchViewModel.kt | 65 +++++++++++ 3 files changed, 226 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchViewModel.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt new file mode 100644 index 000000000000..24d53c297dfc --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt @@ -0,0 +1,57 @@ +package org.wordpress.android.ui.pages + +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView.Adapter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.wordpress.android.R.layout +import org.wordpress.android.ui.pages.PageItem.ParentPage +import org.wordpress.android.ui.pages.PageItemViewHolder.EmptyViewHolder +import org.wordpress.android.ui.pages.PageItemViewHolder.PageDividerViewHolder +import org.wordpress.android.ui.pages.PageItemViewHolder.PageParentViewHolder + +class PageParentSearchAdapter( + private val onParentSelected: (ParentPage) -> Unit, + private val uiScope: CoroutineScope +) : Adapter() { + private val items = mutableListOf() + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageItemViewHolder { + return when (viewType) { + PageItem.Type.PARENT.viewType -> PageParentViewHolder(parent, + this::selectParent, + layout.page_parent_list_item) + PageItem.Type.TOP_LEVEL_PARENT.viewType -> PageParentViewHolder(parent, + this::selectParent, + layout.page_parent_top_level_item) + PageItem.Type.DIVIDER.viewType -> PageDividerViewHolder(parent) + PageItem.Type.EMPTY.viewType -> EmptyViewHolder(parent) { } + else -> throw Throwable("Unexpected view type") + } + } + + private fun selectParent(parent: ParentPage) { + onParentSelected(parent) + uiScope.launch { + delay(200) // let the selection animation play out before refreshing the list + notifyDataSetChanged() + } + } + override fun getItemCount(): Int = items.size + + override fun getItemViewType(position: Int): Int { + return items[position].type.viewType + } + + override fun onBindViewHolder(holder: PageItemViewHolder, position: Int) { + holder.onBind(items[position]) + } + + fun update(result: List) { + val diffResult = DiffUtil.calculateDiff(PageItemDiffUtil(items.toList(), result)) + items.clear() + items.addAll(result) + diffResult.dispatchUpdatesTo(this) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt new file mode 100644 index 000000000000..8493d3896f34 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt @@ -0,0 +1,104 @@ +package org.wordpress.android.ui.pages + +import android.os.Bundle +import android.os.Parcelable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.pages_list_fragment.* +import kotlinx.coroutines.CoroutineScope +import org.wordpress.android.R +import org.wordpress.android.WordPress +import org.wordpress.android.modules.UI_SCOPE +import org.wordpress.android.util.DisplayUtils +import org.wordpress.android.viewmodel.pages.PageParentSearchViewModel +import org.wordpress.android.viewmodel.pages.PageParentViewModel +import org.wordpress.android.widgets.RecyclerItemDecoration +import javax.inject.Inject +import javax.inject.Named + +class PageParentSearchFragment : Fragment() { + @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var viewModel: PageParentSearchViewModel + @field:[Inject Named(UI_SCOPE)] lateinit var uiScope: CoroutineScope + private var linearLayoutManager: LinearLayoutManager? = null + + private val listStateKey = "list_state" + + companion object { + fun newInstance(): PageParentSearchFragment { + return PageParentSearchFragment() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.pages_list_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val nonNullActivity = checkNotNull(activity) + (nonNullActivity.application as? WordPress)?.component()?.inject(this) + + initializeViews(savedInstanceState) + initializeViewModels(nonNullActivity) + } + + override fun onSaveInstanceState(outState: Bundle) { + linearLayoutManager?.let { + outState.putParcelable(listStateKey, it.onSaveInstanceState()) + } + super.onSaveInstanceState(outState) + } + + private fun initializeViewModels(activity: FragmentActivity) { + val pageParentViewModel = ViewModelProviders.of(activity, viewModelFactory) + .get(PageParentViewModel::class.java) + + viewModel = ViewModelProviders.of(this, viewModelFactory) + .get(PageParentSearchViewModel::class.java) + viewModel.start(pageParentViewModel) + + setupObservers() + } + + private fun initializeViews(savedInstanceState: Bundle?) { + val layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false) + savedInstanceState?.getParcelable(listStateKey)?.let { + layoutManager.onRestoreInstanceState(it) + } + + linearLayoutManager = layoutManager + recyclerView.layoutManager = linearLayoutManager + recyclerView.addItemDecoration(RecyclerItemDecoration(0, DisplayUtils.dpToPx(activity, 1))) + } + + private fun setupObservers() { + viewModel.searchResult.observe(this, Observer { data -> + data?.let { setSearchResult(data) } + }) + } + + private fun setSearchResult(pages: List) { + val adapter: PageParentSearchAdapter + if (recyclerView.adapter == null) { + adapter = PageParentSearchAdapter( + { page -> viewModel.onParentSelected(page) }, uiScope ) + recyclerView.adapter = adapter + } else { + adapter = recyclerView.adapter as PageParentSearchAdapter + } + adapter.update(pages) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchViewModel.kt new file mode 100644 index 000000000000..efdbfb447301 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchViewModel.kt @@ -0,0 +1,65 @@ +package org.wordpress.android.viewmodel.pages + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.wordpress.android.R +import org.wordpress.android.modules.UI_SCOPE +import org.wordpress.android.ui.pages.PageItem +import org.wordpress.android.ui.pages.PageItem.Empty +import org.wordpress.android.ui.pages.PageItem.ParentPage +import javax.inject.Inject +import javax.inject.Named + +class PageParentSearchViewModel +@Inject constructor( + @Named(UI_SCOPE) private val uiScope: CoroutineScope +) : ViewModel() { + private val _searchResult: MutableLiveData> = MutableLiveData() + val searchResult: LiveData> = _searchResult + + private var isStarted: Boolean = false + private lateinit var pageParentViewModel: PageParentViewModel + + fun start(pageParentViewModel: PageParentViewModel) { + this.pageParentViewModel = pageParentViewModel + + if (!isStarted) { + isStarted = true + + /** TODO Implement the method in pageParentView Model + pageParentViewModel.searchPages.observeForever(searchObserver) + */ + } + } + + override fun onCleared() { + /** TODO Implement the method in pageParentView Model + pageParentViewModel.searchPages.removeObserver(searchObserver) + */ + } + + private val searchObserver = Observer> { pageItems -> + if (pageItems != null) { + loadFoundPages(pageItems) + } else { + _searchResult.value = listOf(Empty(R.string.pages_search_suggestion, true)) + } + } + + + fun onParentSelected(page: ParentPage) { + pageParentViewModel.onParentSelected(page) + } + + private fun loadFoundPages(pageItems: List) = uiScope.launch { + if (pageItems.isNotEmpty()) { + _searchResult.value = pageItems + } else { + _searchResult.value = listOf(Empty(R.string.pages_empty_search_result, true)) + } + } +} From 57cfc44c41af630375bb676f27ab546dec4187c4 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Sun, 9 Feb 2020 15:36:06 -0500 Subject: [PATCH 02/20] Update AppComponent to inject PageParentSearchFragment --- .../main/java/org/wordpress/android/modules/AppComponent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java index 6c3498117247..8899bbbf8ac3 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java @@ -65,6 +65,7 @@ import org.wordpress.android.ui.notifications.receivers.NotificationsPendingDraftsReceiver; import org.wordpress.android.ui.pages.PageListFragment; import org.wordpress.android.ui.pages.PageParentFragment; +import org.wordpress.android.ui.pages.PageParentSearchFragment; import org.wordpress.android.ui.pages.PagesFragment; import org.wordpress.android.ui.pages.SearchListFragment; import org.wordpress.android.ui.people.PeopleInviteFragment; @@ -490,6 +491,8 @@ public interface AppComponent extends AndroidInjector { void inject(AddContentAdapter object); + void inject(PageParentSearchFragment object); + // Allows us to inject the application without having to instantiate any modules, and provides the Application // in the app graph @Component.Builder From 163de5425c14a1010d9fc7df9ad685fa0351489a Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Sun, 9 Feb 2020 15:37:32 -0500 Subject: [PATCH 03/20] Update ViewModelModule to for PageParentSearchViewModel --- .../java/org/wordpress/android/modules/ViewModelModule.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java index 31ffa0bb456d..0827e28d0e90 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java @@ -42,6 +42,7 @@ import org.wordpress.android.viewmodel.history.HistoryViewModel; import org.wordpress.android.viewmodel.main.WPMainActivityViewModel; import org.wordpress.android.viewmodel.pages.PageListViewModel; +import org.wordpress.android.viewmodel.pages.PageParentSearchViewModel; import org.wordpress.android.viewmodel.pages.PageParentViewModel; import org.wordpress.android.viewmodel.pages.PagesViewModel; import org.wordpress.android.viewmodel.pages.SearchListViewModel; @@ -276,6 +277,11 @@ abstract class ViewModelModule { @ViewModelKey(PostSignupInterstitialViewModel.class) abstract ViewModel postSignupInterstitialViewModel(PostSignupInterstitialViewModel viewModel); + @Binds + @IntoMap + @ViewModelKey(PageParentSearchViewModel.class) + abstract ViewModel pageParentSearchViewModel(PageParentSearchViewModel viewModel); + @Binds abstract ViewModelProvider.Factory provideViewModelFactory(ViewModelFactory viewModelFactory); } From 00f594b57c765162b37392858a54e71bebf6b2bc Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Sun, 9 Feb 2020 15:43:37 -0500 Subject: [PATCH 04/20] Set up the search on the menu --- .idea/codeStyles/Project.xml | 67 ++++--------------- .../src/main/res/menu/page_parent_menu.xml | 7 ++ 2 files changed, 20 insertions(+), 54 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 3bf04816d9bb..f50f4769139a 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,8 @@ + +