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