| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.quicksearchbox.ui; |
| |
| import com.android.quicksearchbox.Corpus; |
| import com.android.quicksearchbox.Promoter; |
| import com.android.quicksearchbox.R; |
| import com.android.quicksearchbox.Suggestions; |
| |
| import android.animation.Animator; |
| import android.animation.ObjectAnimator; |
| import android.animation.ValueAnimator; |
| import android.animation.ValueAnimator.AnimatorUpdateListener; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.res.Resources; |
| import android.database.DataSetObserver; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.ExpandableListAdapter; |
| import android.widget.ImageView; |
| import android.widget.PopupMenu; |
| |
| /** |
| * Two-pane variant for the search activity view. |
| */ |
| public class SearchActivityViewTwoPane extends SearchActivityView { |
| |
| private static final int ENTRY_ANIMATION_START_DELAY = 150; // in millis |
| private static final int ENTRY_ANIMATION_DURATION = 150; // in millis |
| private static final float ANIMATION_STARTING_WIDTH_FACTOR = 0.5f; |
| private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon"; |
| |
| private ImageView mMenuButton; |
| |
| // View that shows the results other than the query completions |
| private ClusteredSuggestionsView mResultsView; |
| private SuggestionsAdapter<ExpandableListAdapter> mResultsAdapter; |
| private View mResultsHeader; |
| private View mSearchPlate; |
| private boolean mJustCreated; |
| |
| public SearchActivityViewTwoPane(Context context) { |
| super(context); |
| } |
| |
| public SearchActivityViewTwoPane(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| public SearchActivityViewTwoPane(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| |
| mMenuButton = (ImageView) findViewById(R.id.menu_button); |
| mMenuButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| showPopupMenu(); |
| } |
| }); |
| |
| mResultsView = (ClusteredSuggestionsView) findViewById(R.id.shortcuts); |
| mResultsAdapter = createClusteredSuggestionsAdapter(); |
| mResultsAdapter.getListAdapter().registerDataSetObserver(new DataSetObserver(){ |
| @Override |
| public void onChanged() { |
| mResultsView.expandAll(); |
| } |
| }); |
| mResultsView.setOnKeyListener(new SuggestionsViewKeyListener()); |
| mResultsView.setFocusable(true); |
| mResultsHeader = findViewById(R.id.shortcut_title); |
| mSearchPlate = findViewById(R.id.left_pane); |
| mJustCreated = true; |
| |
| View dismissBg = findViewById(R.id.dismiss_bg); |
| dismissBg.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| if (isQueryEmpty() && mExitClickListener != null) { |
| mExitClickListener.onClick(v); |
| } |
| } |
| }); |
| } |
| |
| private void showPopupMenu() { |
| PopupMenu popup = new PopupMenu(getContext(), mMenuButton); |
| Menu menu = popup.getMenu(); |
| getActivity().createMenuItems(menu, false); |
| popup.show(); |
| } |
| |
| protected SuggestionsAdapter<ExpandableListAdapter> createClusteredSuggestionsAdapter() { |
| return new DelayingSuggestionsAdapter<ExpandableListAdapter>( |
| new ClusteredSuggestionsAdapter( |
| getQsbApplication().getSuggestionViewFactory(), |
| getContext())); |
| } |
| |
| @Override |
| public void onResume() { |
| if (mJustCreated) { |
| setupEntryAnimations(); |
| mJustCreated = false; |
| } |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| getActivity().overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast); |
| } |
| |
| private void setupEntryAnimations() { |
| // TODO: Use the left/top of the source bounds to start the animation from |
| final int endingWidth = getResources().getDimensionPixelSize(R.dimen.suggestions_width); |
| final int startingWidth = (int) (endingWidth * ANIMATION_STARTING_WIDTH_FACTOR); |
| |
| ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams(); |
| params.width = startingWidth; |
| mSearchPlate.setLayoutParams(params); |
| |
| Animator animator = ObjectAnimator.ofInt(mSearchPlate, "alpha", 0, 255); |
| animator.setDuration(ENTRY_ANIMATION_DURATION); |
| ((ValueAnimator)animator).addUpdateListener(new AnimatorUpdateListener() { |
| |
| public void onAnimationUpdate(ValueAnimator animator) { |
| ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams(); |
| params.width = startingWidth |
| + (int) ((Integer) animator.getAnimatedValue() / 255f |
| * (endingWidth - startingWidth)); |
| mSearchPlate.setLayoutParams(params); |
| } |
| }); |
| animator.setStartDelay(ENTRY_ANIMATION_START_DELAY); |
| animator.start(); |
| |
| } |
| |
| @Override |
| public void onStop() { |
| } |
| |
| @Override |
| public void start() { |
| super.start(); |
| mResultsAdapter.getListAdapter().registerDataSetObserver(new ResultsObserver()); |
| mResultsView.setSuggestionsAdapter(mResultsAdapter); |
| } |
| |
| @Override |
| public void destroy() { |
| mResultsView.setSuggestionsAdapter(null); |
| |
| super.destroy(); |
| } |
| |
| @Override |
| protected Drawable getVoiceSearchIcon() { |
| ComponentName voiceSearch = getVoiceSearch().getComponent(); |
| if (voiceSearch != null) { |
| // this code copied from Launcher to get the same icon that's displayed on home screen |
| try { |
| PackageManager packageManager = getContext().getPackageManager(); |
| // Look for the toolbar icon specified in the activity meta-data |
| Bundle metaData = packageManager.getActivityInfo( |
| voiceSearch, PackageManager.GET_META_DATA).metaData; |
| if (metaData != null) { |
| int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME); |
| if (iconResId != 0) { |
| Resources res = packageManager.getResourcesForActivity(voiceSearch); |
| if (DBG) Log.d(TAG, "Got toolbar icon from Voice Search"); |
| return res.getDrawable(iconResId); |
| } |
| } |
| } catch (NameNotFoundException e) { |
| // Do nothing |
| } |
| } |
| if (DBG) Log.d(TAG, "Failed to get toolbar icon from Voice Search; using default."); |
| return super.getVoiceSearchIcon(); |
| } |
| |
| @Override |
| public void considerHidingInputMethod() { |
| // Don't hide keyboard when interacting with suggestions list |
| } |
| |
| @Override |
| public void hideSuggestions() { |
| // Never hiding suggestions view in two-pane UI |
| } |
| |
| @Override |
| public void showSuggestions() { |
| // Never hiding suggestions view in two-pane UI |
| } |
| |
| @Override |
| public void showCorpusSelectionDialog() { |
| // not used |
| } |
| |
| @Override |
| public void clearSuggestions() { |
| super.clearSuggestions(); |
| mResultsAdapter.setSuggestions(null); |
| } |
| |
| @Override |
| public void setMaxPromotedResults(int maxPromoted) { |
| mResultsView.setLimitSuggestionsToViewHeight(false); |
| mResultsAdapter.setMaxPromoted(maxPromoted); |
| } |
| |
| @Override |
| public void limitResultsToViewHeight() { |
| mResultsView.setLimitSuggestionsToViewHeight(true); |
| } |
| |
| @Override |
| public void setSuggestionClickListener(SuggestionClickListener listener) { |
| super.setSuggestionClickListener(listener); |
| mResultsAdapter.setSuggestionClickListener(listener); |
| } |
| |
| @Override |
| public void setSuggestions(Suggestions suggestions) { |
| super.setSuggestions(suggestions); |
| suggestions.acquire(); |
| mResultsAdapter.setSuggestions(suggestions); |
| } |
| |
| @Override |
| protected void setCorpus(Corpus corpus) { |
| super.setCorpus(corpus); |
| mResultsAdapter.setPromoter(createResultsPromoter()); |
| } |
| |
| @Override |
| protected Promoter createSuggestionsPromoter() { |
| return getQsbApplication().createWebPromoter(); |
| } |
| |
| protected Promoter createResultsPromoter() { |
| Corpus corpus = getCorpus(); |
| if (corpus == null) { |
| return getQsbApplication().createResultsPromoter(); |
| } else { |
| return getQsbApplication().createSingleCorpusResultsPromoter(corpus); |
| } |
| } |
| |
| protected void onResultsChanged() { |
| checkHideResultsHeader(); |
| } |
| |
| @Override |
| protected void updateQueryTextView(boolean queryEmpty) { |
| super.updateQueryTextView(queryEmpty); |
| if (mSearchCloseButton == null) return; |
| |
| if (queryEmpty) { |
| mSearchCloseButton.setImageResource(R.drawable.ic_clear_disabled); |
| } else { |
| mSearchCloseButton.setImageResource(R.drawable.ic_clear); |
| } |
| } |
| |
| private void checkHideResultsHeader() { |
| if (mResultsHeader != null) { |
| if (!mResultsAdapter.isEmpty()) { |
| if (DBG) Log.d(TAG, "Results non-empty"); |
| mResultsHeader.setVisibility(VISIBLE); |
| } else { |
| if (DBG) Log.d(TAG, "Results empty"); |
| mResultsHeader.setVisibility(INVISIBLE); |
| } |
| } |
| } |
| |
| @Override |
| public Corpus getSearchCorpus() { |
| return getWebCorpus(); |
| } |
| |
| protected class ResultsObserver extends DataSetObserver { |
| @Override |
| public void onChanged() { |
| onResultsChanged(); |
| } |
| } |
| |
| } |