jenkins-bot has submitted this change and it was merged. Change subject: Integrate Search fragment into overhaul. ......................................................................
Integrate Search fragment into overhaul. Remaining things to do: - Fill in all the callback functions to have the proper effect. - Implement correct Back behavior. Change-Id: I37aef4b3274d6b6ae019ac73d12d4db76ed9dd4a --- M app/src/main/java/org/wikipedia/overhaul/OverhaulFragment.java A app/src/main/java/org/wikipedia/search/OverhaulSearchFragment.java M app/src/main/java/org/wikipedia/search/RecentSearchesFragment.java M app/src/main/java/org/wikipedia/search/SearchArticlesFragment.java M app/src/main/java/org/wikipedia/search/SearchResultsFragment.java M app/src/main/java/org/wikipedia/views/ViewUtil.java M app/src/main/res/layout/fragment_overhaul.xml A app/src/main/res/layout/fragment_search_overhaul.xml 8 files changed, 790 insertions(+), 60 deletions(-) Approvals: BearND: Looks good to me, approved jenkins-bot: Verified diff --git a/app/src/main/java/org/wikipedia/overhaul/OverhaulFragment.java b/app/src/main/java/org/wikipedia/overhaul/OverhaulFragment.java index c2ebdc4..e4656f6 100644 --- a/app/src/main/java/org/wikipedia/overhaul/OverhaulFragment.java +++ b/app/src/main/java/org/wikipedia/overhaul/OverhaulFragment.java @@ -23,18 +23,23 @@ import org.wikipedia.overhaul.navtab.NavTab; import org.wikipedia.overhaul.navtab.NavTabViewPagerAdapter; import org.wikipedia.page.PageTitle; +import org.wikipedia.readinglist.AddToReadingListDialog; import org.wikipedia.readinglist.ReadingListsFragment; +import org.wikipedia.search.OverhaulSearchFragment; +import org.wikipedia.search.SearchResultsFragment; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; public class OverhaulFragment extends Fragment implements FeedFragment.Callback, - NearbyFragment.Callback, HistoryFragment.Callback, ReadingListsFragment.Callback { + NearbyFragment.Callback, HistoryFragment.Callback, ReadingListsFragment.Callback, + OverhaulSearchFragment.Callback, SearchResultsFragment.Callback { @BindView(R.id.fragment_overhaul_view_pager) ViewPager viewPager; @BindView(R.id.view_nav_view_pager_tab_layout) TabLayout tabLayout; private Unbinder unbinder; private NavTabChangeListener pagerChangeListener = new NavTabChangeListener(); + private OverhaulSearchFragment searchFragment; public interface Callback { void onTabChanged(@NonNull NavTab tab, @NonNull Fragment fragment); @@ -57,6 +62,7 @@ viewPager.setAdapter(new NavTabViewPagerAdapter(getChildFragmentManager())); tabLayout.setupWithViewPager(viewPager); + searchFragment = (OverhaulSearchFragment) getChildFragmentManager().findFragmentById(R.id.search_fragment); return view; } @@ -68,7 +74,8 @@ } @Override public void onFeedSearchRequested() { - // todo: [overhaul] search. + searchFragment.setInvokeSource(OverhaulSearchFragment.InvokeSource.FEED_BAR); + searchFragment.openSearch(); } @Override public void onFeedVoiceSearchRequested() { @@ -145,4 +152,39 @@ ((NavTabViewPagerAdapter) viewPager.getAdapter()).getCurrentFragment()); } } + + @Override + public void onSearchResultCopyLink(@NonNull PageTitle title) { + // TODO: implement + } + + @Override + public void onSearchResultAddToList(@NonNull PageTitle title, @NonNull AddToReadingListDialog.InvokeSource source) { + // TODO: implement + } + + @Override + public void onSearchResultShareLink(@NonNull PageTitle title) { + // TODO: implement + } + + @Override + public void onSearchProgressBar(boolean enabled) { + // TODO: implement + } + + @Override + public void onSearchSelectPage(@NonNull HistoryEntry entry, boolean inNewTab) { + // TODO: implement + } + + @Override + public void onSearchOpen() { + // TODO: implement + } + + @Override + public void onSearchClose() { + // TODO: implement + } } \ No newline at end of file diff --git a/app/src/main/java/org/wikipedia/search/OverhaulSearchFragment.java b/app/src/main/java/org/wikipedia/search/OverhaulSearchFragment.java new file mode 100644 index 0000000..a39c647 --- /dev/null +++ b/app/src/main/java/org/wikipedia/search/OverhaulSearchFragment.java @@ -0,0 +1,504 @@ +package org.wikipedia.search; + +import android.content.DialogInterface; +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v7.widget.SearchView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.wikipedia.BackPressedHandler; +import org.wikipedia.R; +import org.wikipedia.WikipediaApp; +import org.wikipedia.activity.FragmentUtil; +import org.wikipedia.analytics.SearchFunnel; +import org.wikipedia.concurrency.SaneAsyncTask; +import org.wikipedia.database.contract.SearchHistoryContract; +import org.wikipedia.events.WikipediaZeroStateChangeEvent; +import org.wikipedia.history.HistoryEntry; +import org.wikipedia.model.EnumCode; +import org.wikipedia.model.EnumCodeMap; +import org.wikipedia.page.PageTitle; +import org.wikipedia.settings.LanguagePreferenceDialog; +import org.wikipedia.util.FeedbackUtil; +import org.wikipedia.views.ViewUtil; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + +public class OverhaulSearchFragment extends Fragment implements BackPressedHandler, + SearchResultsFragment.Parent, RecentSearchesFragment.Parent { + public enum InvokeSource implements EnumCode { + TOOLBAR(0), + WIDGET(1), + INTENT_SHARE(2), + INTENT_PROCESS_TEXT(3), + FEED_BAR(4), + VOICE(5); + + private static final EnumCodeMap<InvokeSource> MAP = new EnumCodeMap<>(InvokeSource.class); + + private final int code; + + public static InvokeSource of(int code) { + return MAP.get(code); + } + + @Override public int code() { + return code; + } + + InvokeSource(int code) { + this.code = code; + } + + public boolean fromIntent() { + return code == WIDGET.code() || code == INTENT_SHARE.code() + || code == INTENT_PROCESS_TEXT.code(); + } + } + + public interface Callback { + void onSearchSelectPage(@NonNull HistoryEntry entry, boolean inNewTab); + void onSearchOpen(); + void onSearchClose(); + } + + private static final String ARG_LAST_SEARCHED_TEXT = "lastSearchedText"; + private static final String ARG_SEARCH_CURRENT_PANEL = "searchCurrentPanel"; + private static final String ARG_INVOKE_SOURCE = "invokeSource"; + + private static final int PANEL_RECENT_SEARCHES = 0; + private static final int PANEL_SEARCH_RESULTS = 1; + + @BindView(R.id.search_container) View searchContainer; + @BindView(R.id.search_toolbar) Toolbar toolbar; + @BindView(R.id.search_cab_view) SearchView searchView; + @BindView(R.id.search_progress_bar) ProgressBar progressBar; + @BindView(R.id.search_lang_button_container) View langButtonContainer; + @BindView(R.id.search_lang_button) TextView langButton; + private Unbinder unbinder; + + private WikipediaApp app; + private EditText searchEditText; + private SearchFunnel funnel; + private InvokeSource invokeSource = InvokeSource.TOOLBAR; + + /** + * Whether the Search fragment is currently showing. + */ + private boolean isSearchActive = false; + + /** + * The last search term that the user entered. This will be passed into + * the TitleSearch and FullSearch sub-fragments. + */ + private String lastSearchedText; + + private RecentSearchesFragment recentSearchesFragment; + private SearchResultsFragment searchResultsFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + app = WikipediaApp.getInstance(); + funnel = new SearchFunnel(WikipediaApp.getInstance(), SearchArticlesFragment.InvokeSource.of(invokeSource.code())); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + app = WikipediaApp.getInstance(); + View view = inflater.inflate(R.layout.fragment_search_overhaul, container, false); + unbinder = ButterKnife.bind(this, view); + + searchContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Give the root container view an empty click handler, so that click events won't + // get passed down to any underlying views (e.g. a PageFragment on top of which + // this fragment is shown) + } + }); + + FragmentManager childFragmentManager = getChildFragmentManager(); + recentSearchesFragment = (RecentSearchesFragment)childFragmentManager.findFragmentById( + R.id.search_panel_recent); + searchResultsFragment = (SearchResultsFragment)childFragmentManager.findFragmentById( + R.id.fragment_search_results); + + // make sure we're hidden by default + searchContainer.setVisibility(View.GONE); + + if (savedInstanceState != null) { + lastSearchedText = savedInstanceState.getString(ARG_LAST_SEARCHED_TEXT); + invokeSource = InvokeSource.of(savedInstanceState.getInt(ARG_INVOKE_SOURCE)); + showPanel(savedInstanceState.getInt(ARG_SEARCH_CURRENT_PANEL)); + } + + initSearchView(); + initLangButton(); + return view; + } + + @Override + public void onDestroyView() { + unbinder.unbind(); + unbinder = null; + super.onDestroyView(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(ARG_LAST_SEARCHED_TEXT, lastSearchedText); + outState.putInt(ARG_SEARCH_CURRENT_PANEL, getActivePanel()); + outState.putInt(ARG_INVOKE_SOURCE, invokeSource.code()); + } + + @Override + @NonNull + public SearchFunnel getFunnel() { + return funnel; + } + + public void setInvokeSource(InvokeSource source) { + invokeSource = source; + } + + public boolean isLaunchedFromIntent() { + return invokeSource.fromIntent(); + } + + @Override + public void switchToSearch(@NonNull String queryText) { + startSearch(queryText, true); + searchView.setQuery(queryText, false); + } + + /** + * Changes the search text box to contain a different string. + * @param text The text you want to make the search box display. + */ + @Override + public void setSearchText(@NonNull CharSequence text) { + searchView.setQuery(text, false); + } + + /** + * Show a particular panel, which can be one of: + * - PANEL_RECENT_SEARCHES + * - PANEL_SEARCH_RESULTS + * Automatically hides the previous panel. + * @param panel Which panel to show. + */ + private void showPanel(int panel) { + switch (panel) { + case PANEL_RECENT_SEARCHES: + searchResultsFragment.hide(); + recentSearchesFragment.show(); + break; + case PANEL_SEARCH_RESULTS: + recentSearchesFragment.hide(); + searchResultsFragment.show(); + break; + default: + break; + } + } + + private int getActivePanel() { + if (searchResultsFragment.isShowing()) { + return PANEL_SEARCH_RESULTS; + } else { + //otherwise, the recent searches must be showing: + return PANEL_RECENT_SEARCHES; + } + } + + @Subscribe + public void onWikipediaZeroStateChangeEvent(WikipediaZeroStateChangeEvent event) { + updateZeroChrome(); + } + + /** + * Kick off a search, based on a given search term. Will automatically pass the search to + * Title search or Full search, based on which one is currently displayed. + * If the search term is empty, the "recent searches" view will be shown. + * @param term Phrase to search for. + * @param force Whether to "force" starting this search. If the search is not forced, the + * search may be delayed by a small time, so that network requests are not sent + * too often. If the search is forced, the network request is sent immediately. + */ + public void startSearch(String term, boolean force) { + if (!isSearchActive) { + openSearch(); + } + + if (TextUtils.isEmpty(term)) { + showPanel(PANEL_RECENT_SEARCHES); + } else if (getActivePanel() == PANEL_RECENT_SEARCHES) { + //start with title search... + showPanel(PANEL_SEARCH_RESULTS); + } + + lastSearchedText = term; + + searchResultsFragment.startSearch(term, force); + } + + /** + * Activate the Search fragment. + */ + public void openSearch() { + // create a new funnel every time Search is opened, to get a new session ID + funnel = new SearchFunnel(WikipediaApp.getInstance(), SearchArticlesFragment.InvokeSource.of(invokeSource.code())); + funnel.searchStart(); + isSearchActive = true; + Callback callback = callback(); + if (callback != null) { + callback.onSearchOpen(); + } + // show ourselves + ViewUtil.fadeIn(searchContainer); + + // if the current search string is empty, then it's a fresh start, so we'll show + // recent searches by default. Otherwise, the currently-selected panel should already + // be visible, so we don't need to do anything. + if (TextUtils.isEmpty(lastSearchedText)) { + showPanel(PANEL_RECENT_SEARCHES); + } + + searchView.setIconified(false); + searchView.requestFocusFromTouch(); + // if we already have a previous search query, then put it into the SearchView, and it will + // automatically trigger the showing of the corresponding search results. + if (isValidQuery(lastSearchedText)) { + searchView.setQuery(lastSearchedText, false); + searchEditText.selectAll(); + } + } + + public void closeSearch() { + isSearchActive = false; + Callback callback = callback(); + if (callback != null) { + callback.onSearchClose(); + } + // hide ourselves + ViewUtil.fadeOut(searchContainer); + addRecentSearch(lastSearchedText); + } + + /** + * Determine whether the Search fragment is currently active. + * @return Whether the Search fragment is active. + */ + public boolean isSearchActive() { + return isSearchActive; + } + + @Override + public boolean onBackPressed() { + if (isSearchActive) { + closeSearch(); + funnel.searchCancel(); + return true; + } + return false; + } + + @Override + public void setProgressBarEnabled(boolean enabled) { + progressBar.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + + private void initSearchView() { + searchView.setOnQueryTextListener(searchQueryListener); + searchView.setOnCloseListener(searchCloseListener); + + searchEditText = (EditText) searchView + .findViewById(android.support.v7.appcompat.R.id.search_src_text); + // reset its background + searchEditText.setBackgroundColor(Color.TRANSPARENT); + // make the search frame match_parent + View searchEditFrame = searchView + .findViewById(android.support.v7.appcompat.R.id.search_edit_frame); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + searchEditFrame.setLayoutParams(params); + // center the search text in it + searchEditText.setGravity(Gravity.CENTER_VERTICAL); + // remove focus line from search plate + View searchEditPlate = searchView + .findViewById(android.support.v7.appcompat.R.id.search_plate); + searchEditPlate.setBackgroundColor(Color.TRANSPARENT); + + ImageView searchClose = (ImageView) searchView.findViewById( + android.support.v7.appcompat.R.id.search_close_btn); + FeedbackUtil.setToolbarButtonLongPressToast(searchClose); + } + + private void initLangButton() { + langButton.setText(app.getAppOrSystemLanguageCode().toUpperCase()); + formatLangButtonText(); + langButtonContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showLangPreferenceDialog(); + } + }); + FeedbackUtil.setToolbarButtonLongPressToast(langButtonContainer); + } + + /* + Update any UI elements related to WP Zero + */ + private void updateZeroChrome() { + if (searchEditText != null) { + // setting the hint directly on the search EditText (instead of the SearchView) + // gets rid of the magnify icon, which we don't want. + searchEditText.setHint(app.getWikipediaZeroHandler().isZeroEnabled() ? getString( + R.string.zero_search_hint) : getString(R.string.search_hint)); + } + } + + private boolean isValidQuery(String queryText) { + return queryText != null && TextUtils.getTrimmedLength(queryText) > 0; + } + + private final SearchView.OnQueryTextListener searchQueryListener = new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String queryText) { + PageTitle firstResult = null; + if (getActivePanel() == PANEL_SEARCH_RESULTS) { + firstResult = searchResultsFragment.getFirstResult(); + } + if (firstResult != null) { + navigateToTitle(firstResult, false, 0); + closeSearch(); + } + return true; + } + + @Override + public boolean onQueryTextChange(String queryText) { + startSearch(queryText.trim(), false); + return true; + } + }; + + private final SearchView.OnCloseListener searchCloseListener = new SearchView.OnCloseListener() { + @Override + public boolean onClose() { + closeSearch(); + funnel.searchCancel(); + return false; + } + }; + + @Override + public void navigateToTitle(@NonNull PageTitle title, boolean inNewTab, int position) { + if (!isAdded()) { + return; + } + funnel.searchClick(position); + HistoryEntry historyEntry = new HistoryEntry(title, HistoryEntry.SOURCE_SEARCH); + closeSearch(); + Callback callback = callback(); + if (callback != null) { + callback.onSearchSelectPage(historyEntry, inNewTab); + } + } + + private void addRecentSearch(String title) { + if (isValidQuery(title)) { + new SaveRecentSearchTask(new RecentSearch(title)).execute(); + } + } + + private final class SaveRecentSearchTask extends SaneAsyncTask<Void> { + private final RecentSearch entry; + SaveRecentSearchTask(RecentSearch entry) { + this.entry = entry; + } + + @Override + public Void performTask() throws Throwable { + app.getDatabaseClient(RecentSearch.class).upsert(entry, SearchHistoryContract.Query.SELECTION); + return null; + } + + @Override + public void onFinish(Void result) { + super.onFinish(result); + recentSearchesFragment.updateList(); + } + + @Override + public void onCatch(Throwable caught) { + Log.w("SaveRecentSearchTask", "Caught " + caught.getMessage(), caught); + } + } + + private void formatLangButtonText() { + final int langCodeStandardLength = 3; + final int langButtonTextMaxLength = 7; + + // These values represent scaled pixels (sp) + final int langButtonTextSizeSmaller = 10; + final int langButtonTextSizeLarger = 13; + + String langCode = app.getAppOrSystemLanguageCode(); + if (langCode.length() > langCodeStandardLength) { + langButton.setTextSize(langButtonTextSizeSmaller); + if (langCode.length() > langButtonTextMaxLength) { + langButton.setText(langCode.substring(0, langButtonTextMaxLength).toUpperCase()); + } + return; + } + langButton.setTextSize(langButtonTextSizeLarger); + } + + @Nullable + private Callback callback() { + return FragmentUtil.getCallback(this, Callback.class); + } + + public void showLangPreferenceDialog() { + LanguagePreferenceDialog langPrefDialog = new LanguagePreferenceDialog(getContext(), true); + langPrefDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + langButton.setText(app.getAppOrSystemLanguageCode().toUpperCase()); + formatLangButtonText(); + if (!TextUtils.isEmpty(lastSearchedText)) { + startSearch(lastSearchedText, true); + } + } + }); + langPrefDialog.show(); + } +} diff --git a/app/src/main/java/org/wikipedia/search/RecentSearchesFragment.java b/app/src/main/java/org/wikipedia/search/RecentSearchesFragment.java index 57eddc2..23c3b77 100644 --- a/app/src/main/java/org/wikipedia/search/RecentSearchesFragment.java +++ b/app/src/main/java/org/wikipedia/search/RecentSearchesFragment.java @@ -1,14 +1,17 @@ package org.wikipedia.search; import android.content.Context; +import android.content.DialogInterface; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; +import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -18,13 +21,19 @@ import android.widget.TextView; import org.wikipedia.R; +import org.wikipedia.WikipediaApp; import org.wikipedia.database.contract.SearchHistoryContract; +import org.wikipedia.util.FeedbackUtil; import static org.wikipedia.Constants.RECENT_SEARCHES_FRAGMENT_LOADER_ID; /** Displays a list of recent searches */ public class RecentSearchesFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { - private SearchArticlesFragment searchFragment; + public interface Parent { + void switchToSearch(@NonNull String text); + } + + private Parent parentFragment; private View container; private ListView recentSearchesList; private RecentSearchesAdapter adapter; @@ -33,10 +42,30 @@ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_search_recent, container, false); - searchFragment = (SearchArticlesFragment) getParentFragment(); + parentFragment = (Parent) getParentFragment(); this.container = rootView.findViewById(R.id.recent_searches_container); recentSearchesList = (ListView) rootView.findViewById(R.id.recent_searches_list); + deleteButton = (ImageView) rootView.findViewById(R.id.recent_searches_delete_button); + deleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + new AlertDialog.Builder(getContext()) + .setMessage(getString(R.string.clear_recent_searches_confirm)) + .setPositiveButton( + getString(R.string.yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + new DeleteAllRecentSearchesTask(WikipediaApp.getInstance()).execute(); + } + }) + .setNegativeButton(getString(R.string.no), null) + .create().show(); + } + }); + FeedbackUtil.setToolbarButtonLongPressToast(deleteButton); + return rootView; } @@ -58,7 +87,7 @@ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { RecentSearch entry = (RecentSearch) view.getTag(); - searchFragment.switchToSearch(entry.getText()); + parentFragment.switchToSearch(entry.getText()); } }); diff --git a/app/src/main/java/org/wikipedia/search/SearchArticlesFragment.java b/app/src/main/java/org/wikipedia/search/SearchArticlesFragment.java index d7711a2..398166a 100644 --- a/app/src/main/java/org/wikipedia/search/SearchArticlesFragment.java +++ b/app/src/main/java/org/wikipedia/search/SearchArticlesFragment.java @@ -7,7 +7,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v7.app.AlertDialog; import android.support.v7.widget.SearchView; import android.text.TextUtils; import android.util.Log; @@ -41,7 +40,8 @@ import static org.wikipedia.util.DimenUtil.getContentTopOffsetPx; -public class SearchArticlesFragment extends Fragment implements BackPressedHandler { +public class SearchArticlesFragment extends Fragment implements BackPressedHandler, + SearchResultsFragment.Parent, RecentSearchesFragment.Parent { public enum InvokeSource implements EnumCode { TOOLBAR(0), WIDGET(1), @@ -143,26 +143,6 @@ } }); - final View deleteButton = parentLayout.findViewById(R.id.recent_searches_delete_button); - deleteButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - new AlertDialog.Builder(getContext()) - .setMessage(getString(R.string.clear_recent_searches_confirm)) - .setPositiveButton( - getString(R.string.yes), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - new DeleteAllRecentSearchesTask(app).execute(); - } - }) - .setNegativeButton(getString(R.string.no), null) - .create().show(); - } - }); - FeedbackUtil.setToolbarButtonLongPressToast(deleteButton); - FragmentManager childFragmentManager = getChildFragmentManager(); recentSearchesFragment = (RecentSearchesFragment)childFragmentManager.findFragmentById( R.id.search_panel_recent); @@ -194,6 +174,8 @@ outState.putInt(ARG_INVOKE_SOURCE, invokeSource.code()); } + @Override + @NonNull public SearchFunnel getFunnel() { return funnel; } @@ -206,7 +188,8 @@ return invokeSource.fromIntent(); } - public void switchToSearch(String queryText) { + @Override + public void switchToSearch(@NonNull String queryText) { startSearch(queryText, true); searchView.setQuery(queryText, false); } @@ -215,7 +198,8 @@ * Changes the search text box to contain a different string. * @param text The text you want to make the search box display. */ - public void setSearchText(CharSequence text) { + @Override + public void setSearchText(@NonNull CharSequence text) { searchView.setQuery(text, false); } @@ -333,6 +317,10 @@ return true; } return false; + } + + @Override + public void setProgressBarEnabled(boolean enabled) { } private void setSearchViewEnabled(boolean enabled) { @@ -460,7 +448,8 @@ } }; - public void navigateToTitle(PageTitle title, boolean inNewTab, int position) { + @Override + public void navigateToTitle(@NonNull PageTitle title, boolean inNewTab, int position) { if (!isAdded()) { return; } diff --git a/app/src/main/java/org/wikipedia/search/SearchResultsFragment.java b/app/src/main/java/org/wikipedia/search/SearchResultsFragment.java index 743a449..b2907e6 100644 --- a/app/src/main/java/org/wikipedia/search/SearchResultsFragment.java +++ b/app/src/main/java/org/wikipedia/search/SearchResultsFragment.java @@ -3,6 +3,7 @@ import org.wikipedia.LongPressHandler; import org.wikipedia.ParcelableLruCache; import org.wikipedia.activity.FragmentUtil; +import org.wikipedia.analytics.SearchFunnel; import org.wikipedia.history.HistoryEntry; import org.wikipedia.page.PageTitle; import org.wikipedia.R; @@ -44,6 +45,13 @@ void onSearchProgressBar(boolean enabled); } + public interface Parent { + void navigateToTitle(@NonNull PageTitle item, boolean inNewTab, int position); + void setSearchText(@NonNull CharSequence text); + @NonNull SearchFunnel getFunnel(); + void setProgressBarEnabled(boolean enabled); + } + private static final int BATCH_SIZE = 20; private static final int DELAY_MILLIS = 300; private static final int MESSAGE_SEARCH = 1; @@ -54,7 +62,7 @@ */ private static final int NANO_TO_MILLI = 1_000_000; - private SearchArticlesFragment searchFragment; + private Parent parentFragment; private View searchResultsDisplay; private View searchResultsContainer; private ListView searchResultsList; @@ -81,7 +89,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_search_results, container, false); searchResultsDisplay = rootView.findViewById(R.id.search_results_display); - searchFragment = (SearchArticlesFragment) getParentFragment(); + parentFragment = (Parent) getParentFragment(); searchResultsContainer = rootView.findViewById(R.id.search_results_container); searchResultsList = (ListView) rootView.findViewById(R.id.search_results_list); @@ -97,7 +105,7 @@ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { PageTitle item = ((SearchResult) getAdapter().getItem(position)).getPageTitle(); - searchFragment.navigateToTitle(item, false, position); + parentFragment.navigateToTitle(item, false, position); } }); @@ -110,8 +118,8 @@ public void onClick(View view) { String suggestion = (String) searchSuggestion.getTag(); if (suggestion != null) { - searchFragment.getFunnel().searchDidYouMean(); - searchFragment.setSearchText(suggestion); + parentFragment.getFunnel().searchDidYouMean(); + parentFragment.setSearchText(suggestion); startSearch(suggestion, true); } } @@ -230,7 +238,7 @@ if (!resultList.isEmpty()) { // Calculate total time taken to display results, in milliseconds final int timeToDisplay = (int) ((System.nanoTime() - startTime) / NANO_TO_MILLI); - searchFragment.getFunnel().searchResults(false, resultList.size(), timeToDisplay); + parentFragment.getFunnel().searchResults(false, resultList.size(), timeToDisplay); } updateProgressBar(false); @@ -277,7 +285,7 @@ } // Calculate total time taken to display results, in milliseconds final int timeToDisplay = (int) ((System.nanoTime() - startTime) / NANO_TO_MILLI); - searchFragment.getFunnel().searchError(false, timeToDisplay); + parentFragment.getFunnel().searchError(false, timeToDisplay); updateProgressBar(false); searchErrorView.setVisibility(View.VISIBLE); @@ -333,7 +341,7 @@ if (!resultList.isEmpty()) { // Calculate total time taken to display results, in milliseconds final int timeToDisplay = (int) ((System.nanoTime() - startTime) / NANO_TO_MILLI); - searchFragment.getFunnel().searchResults(true, resultList.size(), timeToDisplay); + parentFragment.getFunnel().searchResults(true, resultList.size(), timeToDisplay); } // append results to cache... @@ -358,7 +366,7 @@ } // Calculate total time taken to display results, in milliseconds final int timeToDisplay = (int) ((System.nanoTime() - startTime) / NANO_TO_MILLI); - searchFragment.getFunnel().searchError(true, timeToDisplay); + parentFragment.getFunnel().searchError(true, timeToDisplay); updateProgressBar(false); // since this is a follow-up search just show a message @@ -381,8 +389,10 @@ } private void updateProgressBar(boolean enabled) { + parentFragment.setProgressBarEnabled(enabled); Callback callback = callback(); if (callback != null) { + // TODO: remove this callback item after overhaul callback.onSearchProgressBar(enabled); } } @@ -446,12 +456,12 @@ @Override public void onOpenLink(PageTitle title, HistoryEntry entry) { - searchFragment.navigateToTitle(title, false, lastPositionRequested); + parentFragment.navigateToTitle(title, false, lastPositionRequested); } @Override public void onOpenInNewTab(PageTitle title, HistoryEntry entry) { - searchFragment.navigateToTitle(title, true, lastPositionRequested); + parentFragment.navigateToTitle(title, true, lastPositionRequested); } } diff --git a/app/src/main/java/org/wikipedia/views/ViewUtil.java b/app/src/main/java/org/wikipedia/views/ViewUtil.java index d0e65ea..e9fa710 100644 --- a/app/src/main/java/org/wikipedia/views/ViewUtil.java +++ b/app/src/main/java/org/wikipedia/views/ViewUtil.java @@ -1,5 +1,7 @@ package org.wikipedia.views; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -120,5 +122,24 @@ parent.addView(next, index); } + public static void fadeIn(@NonNull View view) { + view.setAlpha(0f); + view.setVisibility(View.VISIBLE); + view.animate().alpha(1f) + .setDuration(view.getResources().getInteger(android.R.integer.config_shortAnimTime)) + .setListener(null); + } + + public static void fadeOut(@NonNull final View view) { + view.animate().alpha(0f) + .setDuration(view.getResources().getInteger(android.R.integer.config_shortAnimTime)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(View.GONE); + } + }); + } + private ViewUtil() { } } diff --git a/app/src/main/res/layout/fragment_overhaul.xml b/app/src/main/res/layout/fragment_overhaul.xml index 6bba102..9ceee6a 100644 --- a/app/src/main/res/layout/fragment_overhaul.xml +++ b/app/src/main/res/layout/fragment_overhaul.xml @@ -1,27 +1,43 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> - <org.wikipedia.overhaul.navtab.NavTabLayout - android:id="@+id/view_nav_view_pager_tab_layout" - android:layout_width="match_parent" - android:layout_height="@dimen/view_nav_view_pager_tab_layout_height" - android:layout_alignParentBottom="true" - app:tabGravity="fill" - android:elevation="12dp" - android:background="@android:color/white" - app:tabIndicatorColor="@color/blue_liberal" /> - <android.support.design.widget.CoordinatorLayout - android:id="@+id/fragment_overhaul_view_pager_container" + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + <org.wikipedia.overhaul.navtab.NavTabLayout + android:id="@+id/view_nav_view_pager_tab_layout" + android:layout_width="match_parent" + android:layout_height="@dimen/view_nav_view_pager_tab_layout_height" + android:layout_alignParentBottom="true" + app:tabGravity="fill" + android:elevation="12dp" + android:background="@android:color/white" + app:tabIndicatorColor="@color/blue_liberal" /> + + <android.support.design.widget.CoordinatorLayout + android:id="@+id/fragment_overhaul_view_pager_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_above="@id/view_nav_view_pager_tab_layout"> + <org.wikipedia.readinglist.NoSwipeViewPager + android:id="@+id/fragment_overhaul_view_pager" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </android.support.design.widget.CoordinatorLayout> + + </RelativeLayout> + + <fragment android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_above="@id/view_nav_view_pager_tab_layout"> - <org.wikipedia.readinglist.NoSwipeViewPager - android:id="@+id/fragment_overhaul_view_pager" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - </android.support.design.widget.CoordinatorLayout> -</RelativeLayout> + android:id="@+id/search_fragment" + android:name="org.wikipedia.search.OverhaulSearchFragment" + android:layout="@id/view_nav_view_pager_tab_layout" + tools:layout="@layout/fragment_search_overhaul" /> + +</FrameLayout> diff --git a/app/src/main/res/layout/fragment_search_overhaul.xml b/app/src/main/res/layout/fragment_search_overhaul.xml new file mode 100644 index 0000000..2443b21 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_overhaul.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/search_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <org.wikipedia.views.StatusBarBlankView + android:id="@+id/empty_status_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/main_toolbar_background" /> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="@color/main_toolbar_background" + android:elevation="4dp"> + + <android.support.v7.widget.Toolbar + android:theme="@style/AppTheme.ToolbarTheme" + android:id="@+id/search_toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="36dp" + android:layout_gravity="center_vertical" + android:layout_marginRight="4dp" + android:layout_marginEnd="4dp" + style="@style/AppTheme.SearchViewStyle"> + + <org.wikipedia.views.CabSearchView + android:id="@+id/search_cab_view" + android:layout_width="0dp" + android:layout_height="36dp" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:textSize="12sp" + android:focusable="true" + android:inputType="text" + android:imeOptions="actionGo|flagNoExtractUi" + app:cabEnabled="false" /> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="2dp" + android:paddingBottom="2dp" + android:paddingLeft="9dp" + android:paddingRight="9dp" + android:layout_marginRight="8dp" + android:layout_marginEnd="8dp" + android:id="@+id/search_lang_button_container" + android:background="?attr/actionBarItemBackground" + android:contentDescription="@string/preference_title_language" + android:clickable="true"> + <TextView + android:id="@+id/search_lang_button" + android:layout_width="30dp" + android:layout_height="30dp" + android:gravity="center" + android:clickable="false" + android:focusable="false" + android:background="@drawable/lang_button_shape" + style="@style/AppTheme.ActionModeStyle" + android:textColor="@android:color/white" /> + </FrameLayout> + + </LinearLayout> + + </android.support.v7.widget.Toolbar> + + <ProgressBar + android:id="@+id/search_progress_bar" + style="@style/Widget.AppCompat.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="-6.5dp" + android:layout_gravity="bottom" + android:elevation="4dp" + android:indeterminate="true" + android:indeterminateTint="@color/blue_progressive" + android:progressBackgroundTint="@color/blue_progressive_dark" + android:visibility="gone" + tools:visibility="visible"/> + + </FrameLayout> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/search_background_color"> + + <fragment + android:id="@+id/fragment_search_results" + android:name="org.wikipedia.search.SearchResultsFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:layout="@layout/fragment_search_results" /> + + <fragment + android:id="@+id/search_panel_recent" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:name="org.wikipedia.search.RecentSearchesFragment" + tools:layout="@layout/fragment_search_recent"/> + + </FrameLayout> + +</LinearLayout> -- To view, visit https://gerrit.wikimedia.org/r/302967 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I37aef4b3274d6b6ae019ac73d12d4db76ed9dd4a Gerrit-PatchSet: 5 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Dbrant <dbr...@wikimedia.org> Gerrit-Reviewer: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: Dbrant <dbr...@wikimedia.org> Gerrit-Reviewer: Mholloway <mhollo...@wikimedia.org> Gerrit-Reviewer: Niedzielski <sniedziel...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits