Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2010 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.quicksearchbox.ui;
     18 
     19 import com.android.quicksearchbox.Corpus;
     20 import com.android.quicksearchbox.Promoter;
     21 import com.android.quicksearchbox.R;
     22 import com.android.quicksearchbox.Suggestions;
     23 
     24 import android.animation.Animator;
     25 import android.animation.ObjectAnimator;
     26 import android.animation.ValueAnimator;
     27 import android.animation.ValueAnimator.AnimatorUpdateListener;
     28 import android.content.ComponentName;
     29 import android.content.Context;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.PackageManager.NameNotFoundException;
     32 import android.content.res.Resources;
     33 import android.database.DataSetObserver;
     34 import android.graphics.drawable.Drawable;
     35 import android.os.Bundle;
     36 import android.util.AttributeSet;
     37 import android.util.Log;
     38 import android.view.Menu;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.widget.ExpandableListAdapter;
     42 import android.widget.ImageView;
     43 import android.widget.PopupMenu;
     44 
     45 /**
     46  * Two-pane variant for the search activity view.
     47  */
     48 public class SearchActivityViewTwoPane extends SearchActivityView {
     49 
     50     private static final int ENTRY_ANIMATION_START_DELAY = 150; // in millis
     51     private static final int ENTRY_ANIMATION_DURATION = 150; // in millis
     52     private static final float ANIMATION_STARTING_WIDTH_FACTOR = 0.5f;
     53     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
     54 
     55     private ImageView mMenuButton;
     56 
     57     // View that shows the results other than the query completions
     58     private ClusteredSuggestionsView mResultsView;
     59     private SuggestionsAdapter<ExpandableListAdapter> mResultsAdapter;
     60     private View mResultsHeader;
     61     private View mSearchPlate;
     62     private boolean mJustCreated;
     63 
     64     public SearchActivityViewTwoPane(Context context) {
     65         super(context);
     66     }
     67 
     68     public SearchActivityViewTwoPane(Context context, AttributeSet attrs) {
     69         super(context, attrs);
     70     }
     71 
     72     public SearchActivityViewTwoPane(Context context, AttributeSet attrs, int defStyle) {
     73         super(context, attrs, defStyle);
     74     }
     75 
     76     @Override
     77     protected void onFinishInflate() {
     78         super.onFinishInflate();
     79 
     80         mMenuButton = (ImageView) findViewById(R.id.menu_button);
     81         mMenuButton.setOnClickListener(new View.OnClickListener() {
     82             public void onClick(View v) {
     83                 showPopupMenu();
     84             }
     85         });
     86 
     87         mResultsView = (ClusteredSuggestionsView) findViewById(R.id.shortcuts);
     88         mResultsAdapter = createClusteredSuggestionsAdapter();
     89         mResultsAdapter.getListAdapter().registerDataSetObserver(new DataSetObserver(){
     90             @Override
     91             public void onChanged() {
     92                 mResultsView.expandAll();
     93             }
     94         });
     95         mResultsView.setOnKeyListener(new SuggestionsViewKeyListener());
     96         mResultsView.setFocusable(true);
     97         mResultsHeader = findViewById(R.id.shortcut_title);
     98         mSearchPlate = findViewById(R.id.left_pane);
     99         mJustCreated = true;
    100 
    101         View dismissBg = findViewById(R.id.dismiss_bg);
    102         dismissBg.setOnClickListener(new View.OnClickListener() {
    103             public void onClick(View v) {
    104                 if (isQueryEmpty() && mExitClickListener != null) {
    105                     mExitClickListener.onClick(v);
    106                 }
    107             }
    108         });
    109     }
    110 
    111     private void showPopupMenu() {
    112         PopupMenu popup = new PopupMenu(getContext(), mMenuButton);
    113         Menu menu = popup.getMenu();
    114         getActivity().createMenuItems(menu, false);
    115         popup.show();
    116     }
    117 
    118     protected SuggestionsAdapter<ExpandableListAdapter> createClusteredSuggestionsAdapter() {
    119         return new DelayingSuggestionsAdapter<ExpandableListAdapter>(
    120                 new ClusteredSuggestionsAdapter(
    121                         getQsbApplication().getSuggestionViewFactory(),
    122                         getContext()));
    123     }
    124 
    125     @Override
    126     public void onResume() {
    127         if (mJustCreated) {
    128             setupEntryAnimations();
    129             mJustCreated = false;
    130         }
    131     }
    132 
    133     @Override
    134     public void onPause() {
    135         super.onPause();
    136         getActivity().overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
    137     }
    138 
    139     private void setupEntryAnimations() {
    140         // TODO: Use the left/top of the source bounds to start the animation from
    141         final int endingWidth = getResources().getDimensionPixelSize(R.dimen.suggestions_width);
    142         final int startingWidth = (int) (endingWidth * ANIMATION_STARTING_WIDTH_FACTOR);
    143 
    144         ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams();
    145         params.width = startingWidth;
    146         mSearchPlate.setLayoutParams(params);
    147 
    148         Animator animator = ObjectAnimator.ofInt(mSearchPlate, "alpha", 0, 255);
    149         animator.setDuration(ENTRY_ANIMATION_DURATION);
    150         ((ValueAnimator)animator).addUpdateListener(new AnimatorUpdateListener() {
    151 
    152             public void onAnimationUpdate(ValueAnimator animator) {
    153                 ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams();
    154                 params.width = startingWidth
    155                         + (int) ((Integer) animator.getAnimatedValue() / 255f
    156                                 * (endingWidth - startingWidth));
    157                 mSearchPlate.setLayoutParams(params);
    158             }
    159         });
    160         animator.setStartDelay(ENTRY_ANIMATION_START_DELAY);
    161         animator.start();
    162 
    163     }
    164 
    165     @Override
    166     public void onStop() {
    167     }
    168 
    169     @Override
    170     public void start() {
    171         super.start();
    172         mResultsAdapter.getListAdapter().registerDataSetObserver(new ResultsObserver());
    173         mResultsView.setSuggestionsAdapter(mResultsAdapter);
    174     }
    175 
    176     @Override
    177     public void destroy() {
    178         mResultsView.setSuggestionsAdapter(null);
    179 
    180         super.destroy();
    181     }
    182 
    183     @Override
    184     protected Drawable getVoiceSearchIcon() {
    185         ComponentName voiceSearch = getVoiceSearch().getComponent();
    186         if (voiceSearch != null) {
    187             // this code copied from Launcher to get the same icon that's displayed on home screen
    188             try {
    189                 PackageManager packageManager = getContext().getPackageManager();
    190                 // Look for the toolbar icon specified in the activity meta-data
    191                 Bundle metaData = packageManager.getActivityInfo(
    192                         voiceSearch, PackageManager.GET_META_DATA).metaData;
    193                 if (metaData != null) {
    194                     int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME);
    195                     if (iconResId != 0) {
    196                         Resources res = packageManager.getResourcesForActivity(voiceSearch);
    197                         if (DBG) Log.d(TAG, "Got toolbar icon from Voice Search");
    198                         return res.getDrawable(iconResId);
    199                     }
    200                 }
    201             } catch (NameNotFoundException e) {
    202                 // Do nothing
    203             }
    204         }
    205         if (DBG) Log.d(TAG, "Failed to get toolbar icon from Voice Search; using default.");
    206         return super.getVoiceSearchIcon();
    207     }
    208 
    209     @Override
    210     public void considerHidingInputMethod() {
    211         // Don't hide keyboard when interacting with suggestions list
    212     }
    213 
    214     @Override
    215     public void hideSuggestions() {
    216         // Never hiding suggestions view in two-pane UI
    217     }
    218 
    219     @Override
    220     public void showSuggestions() {
    221         // Never hiding suggestions view in two-pane UI
    222     }
    223 
    224     @Override
    225     public void showCorpusSelectionDialog() {
    226         // not used
    227     }
    228 
    229     @Override
    230     public void clearSuggestions() {
    231         super.clearSuggestions();
    232         mResultsAdapter.setSuggestions(null);
    233     }
    234 
    235     @Override
    236     public void setMaxPromotedResults(int maxPromoted) {
    237         mResultsView.setLimitSuggestionsToViewHeight(false);
    238         mResultsAdapter.setMaxPromoted(maxPromoted);
    239     }
    240 
    241     @Override
    242     public void limitResultsToViewHeight() {
    243         mResultsView.setLimitSuggestionsToViewHeight(true);
    244     }
    245 
    246     @Override
    247     public void setSuggestionClickListener(SuggestionClickListener listener) {
    248         super.setSuggestionClickListener(listener);
    249         mResultsAdapter.setSuggestionClickListener(listener);
    250     }
    251 
    252     @Override
    253     public void setSuggestions(Suggestions suggestions) {
    254         super.setSuggestions(suggestions);
    255         suggestions.acquire();
    256         mResultsAdapter.setSuggestions(suggestions);
    257     }
    258 
    259     @Override
    260     protected void setCorpus(Corpus corpus) {
    261         super.setCorpus(corpus);
    262         mResultsAdapter.setPromoter(createResultsPromoter());
    263     }
    264 
    265     @Override
    266     protected Promoter createSuggestionsPromoter() {
    267         return getQsbApplication().createWebPromoter();
    268     }
    269 
    270     protected Promoter createResultsPromoter() {
    271         Corpus corpus = getCorpus();
    272         if (corpus == null) {
    273             return getQsbApplication().createResultsPromoter();
    274         } else {
    275             return getQsbApplication().createSingleCorpusResultsPromoter(corpus);
    276         }
    277     }
    278 
    279     protected void onResultsChanged() {
    280         checkHideResultsHeader();
    281     }
    282 
    283     @Override
    284     protected void updateQueryTextView(boolean queryEmpty) {
    285         super.updateQueryTextView(queryEmpty);
    286         if (mSearchCloseButton == null) return;
    287 
    288         if (queryEmpty) {
    289             mSearchCloseButton.setImageResource(R.drawable.ic_clear_disabled);
    290         } else {
    291             mSearchCloseButton.setImageResource(R.drawable.ic_clear);
    292         }
    293     }
    294 
    295     private void checkHideResultsHeader() {
    296         if (mResultsHeader != null) {
    297             if (!mResultsAdapter.isEmpty()) {
    298                 if (DBG) Log.d(TAG, "Results non-empty");
    299                 mResultsHeader.setVisibility(VISIBLE);
    300             } else {
    301                 if (DBG) Log.d(TAG, "Results empty");
    302                 mResultsHeader.setVisibility(INVISIBLE);
    303             }
    304         }
    305     }
    306 
    307     @Override
    308     public Corpus getSearchCorpus() {
    309         return getWebCorpus();
    310     }
    311 
    312     protected class ResultsObserver extends DataSetObserver {
    313         @Override
    314         public void onChanged() {
    315             onResultsChanged();
    316         }
    317     }
    318 
    319 }
    320