Home | History | Annotate | Download | only in queries
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.documentsui.queries;
     18 
     19 import static com.android.documentsui.base.Shared.DEBUG;
     20 
     21 import android.annotation.Nullable;
     22 import android.os.Bundle;
     23 import android.provider.DocumentsContract.Root;
     24 import android.text.TextUtils;
     25 import android.util.Log;
     26 import android.view.Menu;
     27 import android.view.MenuItem;
     28 import android.view.MenuItem.OnActionExpandListener;
     29 import android.view.View;
     30 import android.view.View.OnClickListener;
     31 import android.view.View.OnFocusChangeListener;
     32 import android.widget.SearchView;
     33 import android.widget.SearchView.OnQueryTextListener;
     34 
     35 import com.android.documentsui.R;
     36 import com.android.documentsui.base.DocumentInfo;
     37 import com.android.documentsui.base.DocumentStack;
     38 import com.android.documentsui.base.RootInfo;
     39 import com.android.documentsui.base.Shared;
     40 
     41 /**
     42  * Manages searching UI behavior.
     43  */
     44 public class SearchViewManager implements
     45         SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
     46         OnActionExpandListener {
     47 
     48     private static final String TAG = "SearchManager";
     49 
     50     private final SearchManagerListener mListener;
     51     private final CommandInterceptor mCommandProcessor;
     52 
     53     private @Nullable String mCurrentSearch;
     54     private boolean mSearchExpanded;
     55     private boolean mIgnoreNextClose;
     56     private boolean mFullBar;
     57 
     58     private Menu mMenu;
     59     private MenuItem mMenuItem;
     60     private SearchView mSearchView;
     61 
     62     public SearchViewManager(
     63             SearchManagerListener listener,
     64             CommandInterceptor commandProcessor,
     65             @Nullable Bundle savedState) {
     66 
     67         assert (listener != null);
     68         assert (commandProcessor != null);
     69 
     70         mListener = listener;
     71         mCommandProcessor = commandProcessor;
     72         mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null;
     73     }
     74 
     75     public void install(Menu menu, boolean isFullBarSearch) {
     76         mMenu = menu;
     77         mMenuItem = mMenu.findItem(R.id.menu_search);
     78         mSearchView = (SearchView) mMenuItem.getActionView();
     79 
     80         mSearchView.setOnQueryTextListener(this);
     81         mSearchView.setOnCloseListener(this);
     82         mSearchView.setOnSearchClickListener(this);
     83         mSearchView.setOnQueryTextFocusChangeListener(this);
     84 
     85         mFullBar = isFullBarSearch;
     86         if (mFullBar) {
     87             mMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
     88                     | MenuItem.SHOW_AS_ACTION_ALWAYS);
     89             mMenuItem.setOnActionExpandListener(this);
     90             mSearchView.setMaxWidth(Integer.MAX_VALUE);
     91         }
     92 
     93         restoreSearch();
     94     }
     95 
     96     /**
     97      * Used to hide menu icons, when the search is being restored. Needed because search restoration
     98      * is done before onPrepareOptionsMenu(Menu menu) that is overriding the icons visibility.
     99      */
    100     public void updateMenu() {
    101         if (isSearching() && mFullBar) {
    102             mMenu.setGroupVisible(R.id.group_hide_when_searching, false);
    103         }
    104     }
    105 
    106     /**
    107      * @param stack New stack.
    108      */
    109     public void update(DocumentStack stack) {
    110         if (mMenuItem == null) {
    111             if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
    112             return;
    113         }
    114 
    115         if (mCurrentSearch != null) {
    116             mMenuItem.expandActionView();
    117 
    118             mSearchView.setIconified(false);
    119             mSearchView.clearFocus();
    120             mSearchView.setQuery(mCurrentSearch, false);
    121         } else {
    122             mSearchView.clearFocus();
    123             if (!mSearchView.isIconified()) {
    124                 mIgnoreNextClose = true;
    125                 mSearchView.setIconified(true);
    126             }
    127 
    128             if (mMenuItem.isActionViewExpanded()) {
    129                 mMenuItem.collapseActionView();
    130             }
    131         }
    132 
    133         showMenu(stack);
    134     }
    135 
    136     public void showMenu(@Nullable DocumentStack stack) {
    137         final DocumentInfo cwd = stack != null ? stack.peek() : null;
    138 
    139         boolean supportsSearch = true;
    140 
    141         // Searching in archives is not enabled, as archives are backed by
    142         // a different provider than the root provider.
    143         if (cwd != null && cwd.isInArchive()) {
    144             supportsSearch = false;
    145         }
    146 
    147         final RootInfo root = stack != null ? stack.getRoot() : null;
    148         if (root == null || (root.flags & Root.FLAG_SUPPORTS_SEARCH) == 0) {
    149             supportsSearch = false;
    150         }
    151 
    152         if (mMenuItem == null) {
    153             if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
    154             return;
    155         }
    156 
    157         if (!supportsSearch) {
    158             mCurrentSearch = null;
    159         }
    160 
    161         mMenuItem.setVisible(supportsSearch);
    162     }
    163 
    164     /**
    165      * Cancels current search operation. Triggers clearing and collapsing the SearchView.
    166      *
    167      * @return True if it cancels search. False if it does not operate search currently.
    168      */
    169     public boolean cancelSearch() {
    170         if (isExpanded() || isSearching()) {
    171             // If the query string is not empty search view won't get iconified
    172             mSearchView.setQuery("", false);
    173 
    174             if (mFullBar) {
    175                onClose();
    176             } else {
    177                 // Causes calling onClose(). onClose() is triggering directory content update.
    178                 mSearchView.setIconified(true);
    179             }
    180             return true;
    181         }
    182         return false;
    183     }
    184 
    185     /**
    186      * Sets search view into the searching state. Used to restore state after device orientation
    187      * change.
    188      */
    189     private void restoreSearch() {
    190         if (isSearching()) {
    191             if(mFullBar) {
    192                 mMenuItem.expandActionView();
    193             } else {
    194                 mSearchView.setIconified(false);
    195             }
    196             onSearchExpanded();
    197             mSearchView.setQuery(mCurrentSearch, false);
    198             mSearchView.clearFocus();
    199         }
    200     }
    201 
    202     private void onSearchExpanded() {
    203         mSearchExpanded = true;
    204         if(mFullBar) {
    205             mMenu.setGroupVisible(R.id.group_hide_when_searching, false);
    206         }
    207 
    208         mListener.onSearchViewChanged(true);
    209     }
    210 
    211     /**
    212      * Clears the search. Triggers refreshing of the directory content.
    213      * @return True if the default behavior of clearing/dismissing SearchView should be overridden.
    214      *         False otherwise.
    215      */
    216     @Override
    217     public boolean onClose() {
    218         mSearchExpanded = false;
    219         if (mIgnoreNextClose) {
    220             mIgnoreNextClose = false;
    221             return false;
    222         }
    223 
    224         // Refresh the directory if a search was done
    225         if (mCurrentSearch != null) {
    226             mCurrentSearch = null;
    227             mListener.onSearchChanged(mCurrentSearch);
    228         }
    229 
    230         if(mFullBar) {
    231             mMenuItem.collapseActionView();
    232         }
    233         mListener.onSearchFinished();
    234 
    235         mListener.onSearchViewChanged(false);
    236 
    237         return false;
    238     }
    239 
    240     /**
    241      * Called when owning activity is saving state to be used to restore state during creation.
    242      * @param state Bundle to save state too
    243      */
    244     public void onSaveInstanceState(Bundle state) {
    245         state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
    246     }
    247 
    248     /**
    249      * Sets mSearchExpanded. Called when search icon is clicked to start search for both search view
    250      * modes.
    251      */
    252     @Override
    253     public void onClick(View v) {
    254         onSearchExpanded();
    255     }
    256 
    257     @Override
    258     public boolean onQueryTextSubmit(String query) {
    259 
    260         if (mCommandProcessor.accept(query)) {
    261             mSearchView.setQuery("", false);
    262         } else {
    263             mCurrentSearch = query;
    264             mSearchView.clearFocus();
    265             mListener.onSearchChanged(mCurrentSearch);
    266         }
    267 
    268         return true;
    269     }
    270 
    271     /**
    272      * Used to detect and handle back button pressed event when search is expanded.
    273      */
    274     @Override
    275     public void onFocusChange(View v, boolean hasFocus) {
    276         if (!hasFocus) {
    277             if (mCurrentSearch == null) {
    278                 mSearchView.setIconified(true);
    279             } else if (TextUtils.isEmpty(mSearchView.getQuery())) {
    280                 cancelSearch();
    281             }
    282         }
    283     }
    284 
    285     @Override
    286     public boolean onQueryTextChange(String newText) {
    287         return false;
    288     }
    289 
    290     @Override
    291     public boolean onMenuItemActionCollapse(MenuItem item) {
    292         mMenu.setGroupVisible(R.id.group_hide_when_searching, true);
    293 
    294         // Handles case when search view is collapsed by using the arrow on the left of the bar
    295         if (isExpanded() || isSearching()) {
    296             cancelSearch();
    297             return false;
    298         }
    299         return true;
    300     }
    301 
    302     @Override
    303     public boolean onMenuItemActionExpand(MenuItem item) {
    304         return true;
    305     }
    306 
    307     public String getCurrentSearch() {
    308         return mCurrentSearch;
    309     }
    310 
    311     public boolean isSearching() {
    312         return mCurrentSearch != null;
    313     }
    314 
    315     public boolean isExpanded() {
    316         return mSearchExpanded;
    317     }
    318 
    319     public interface SearchManagerListener {
    320         void onSearchChanged(@Nullable String query);
    321         void onSearchFinished();
    322         void onSearchViewChanged(boolean opened);
    323     }
    324 }
    325