SearchActivityViewTwoPane.java revision 04780ccd1ef55fca5234cdad4386d7248bb9b3dd
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.Context;
29import android.database.DataSetObserver;
30import android.util.AttributeSet;
31import android.util.Log;
32import android.view.Menu;
33import android.view.View;
34import android.view.ViewGroup;
35import android.widget.ExpandableListAdapter;
36import android.widget.ImageView;
37import android.widget.PopupMenu;
38
39/**
40 * Two-pane variant for the search activity view.
41 */
42public class SearchActivityViewTwoPane extends SearchActivityView {
43
44    private static final int ENTRY_ANIMATION_START_DELAY = 150; // in millis
45    private static final int ENTRY_ANIMATION_DURATION = 150; // in millis
46    private static final float ANIMATION_STARTING_WIDTH_FACTOR = 0.6f;
47
48    private ImageView mMenuButton;
49
50    // View that shows the results other than the query completions
51    private ClusteredSuggestionsView mResultsView;
52    private SuggestionsAdapter<ExpandableListAdapter> mResultsAdapter;
53    private View mResultsHeader;
54    private View mSearchPlate;
55    private boolean mJustCreated;
56
57    public SearchActivityViewTwoPane(Context context) {
58        super(context);
59    }
60
61    public SearchActivityViewTwoPane(Context context, AttributeSet attrs) {
62        super(context, attrs);
63    }
64
65    public SearchActivityViewTwoPane(Context context, AttributeSet attrs, int defStyle) {
66        super(context, attrs, defStyle);
67    }
68
69    @Override
70    protected void onFinishInflate() {
71        super.onFinishInflate();
72
73        mMenuButton = (ImageView) findViewById(R.id.menu_button);
74        mMenuButton.setOnClickListener(new View.OnClickListener() {
75            public void onClick(View v) {
76                showPopupMenu();
77            }
78        });
79
80        mResultsView = (ClusteredSuggestionsView) findViewById(R.id.shortcuts);
81        mResultsAdapter = createClusteredSuggestionsAdapter();
82        mResultsAdapter.getListAdapter().registerDataSetObserver(new DataSetObserver(){
83            @Override
84            public void onChanged() {
85                mResultsView.expandAll();
86            }
87        });
88        mResultsView.setOnKeyListener(new SuggestionsViewKeyListener());
89        mResultsView.setFocusable(true);
90        mResultsHeader = findViewById(R.id.shortcut_title);
91        mSearchPlate = findViewById(R.id.left_pane);
92        mJustCreated = true;
93    }
94
95    private void showPopupMenu() {
96        PopupMenu popup = new PopupMenu(getContext(), mMenuButton);
97        Menu menu = popup.getMenu();
98        getActivity().createMenuItems(menu, false);
99        popup.show();
100    }
101
102    protected SuggestionsAdapter<ExpandableListAdapter> createClusteredSuggestionsAdapter() {
103        return new DelayingSuggestionsAdapter<ExpandableListAdapter>(
104                new ClusteredSuggestionsAdapter(
105                        getQsbApplication().getSuggestionViewFactory(),
106                        getContext()));
107    }
108
109    @Override
110    public void onResume() {
111        if (mJustCreated) {
112            setupEntryAnimations();
113            mJustCreated = false;
114        }
115    }
116
117    @Override
118    public void onPause() {
119        super.onPause();
120        getActivity().overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
121    }
122
123    private void setupEntryAnimations() {
124        // TODO: Use the left/top of the source bounds to start the animation from
125        final int endingWidth = getResources().getDimensionPixelSize(R.dimen.suggestions_width);
126        final int startingWidth = (int) (endingWidth * ANIMATION_STARTING_WIDTH_FACTOR);
127
128        ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams();
129        params.width = startingWidth;
130        mSearchPlate.setLayoutParams(params);
131
132        Animator animator = ObjectAnimator.ofInt(mSearchPlate, "alpha", 0, 255);
133        animator.setDuration(ENTRY_ANIMATION_DURATION);
134        ((ValueAnimator)animator).addUpdateListener(new AnimatorUpdateListener() {
135
136            public void onAnimationUpdate(ValueAnimator animator) {
137                ViewGroup.LayoutParams params = mSearchPlate.getLayoutParams();
138                params.width = startingWidth
139                        + (int) ((Integer) animator.getAnimatedValue() / 255f
140                                * (endingWidth - startingWidth));
141                mSearchPlate.setLayoutParams(params);
142            }
143        });
144        animator.setStartDelay(ENTRY_ANIMATION_START_DELAY);
145        animator.start();
146
147    }
148
149    @Override
150    public void onStop() {
151    }
152
153    @Override
154    public void start() {
155        super.start();
156        mResultsAdapter.getListAdapter().registerDataSetObserver(new ResultsObserver());
157        mResultsView.setSuggestionsAdapter(mResultsAdapter);
158    }
159
160    @Override
161    public void destroy() {
162        mResultsView.setSuggestionsAdapter(null);
163
164        super.destroy();
165    }
166
167    @Override
168    public void considerHidingInputMethod() {
169        // Don't hide keyboard when interacting with suggestions list
170    }
171
172    @Override
173    public void hideSuggestions() {
174        // Never hiding suggestions view in two-pane UI
175    }
176
177    @Override
178    public void showSuggestions() {
179        // Never hiding suggestions view in two-pane UI
180    }
181
182    @Override
183    public void showCorpusSelectionDialog() {
184        // not used
185    }
186
187    @Override
188    public void clearSuggestions() {
189        super.clearSuggestions();
190        mResultsAdapter.setSuggestions(null);
191    }
192
193    @Override
194    public void setMaxPromotedResults(int maxPromoted) {
195        mResultsView.setLimitSuggestionsToViewHeight(false);
196        mResultsAdapter.setMaxPromoted(maxPromoted);
197    }
198
199    @Override
200    public void limitResultsToViewHeight() {
201        mResultsView.setLimitSuggestionsToViewHeight(true);
202    }
203
204    @Override
205    public void setSuggestionClickListener(SuggestionClickListener listener) {
206        super.setSuggestionClickListener(listener);
207        mResultsAdapter.setSuggestionClickListener(listener);
208    }
209
210    @Override
211    public void setSuggestions(Suggestions suggestions) {
212        super.setSuggestions(suggestions);
213        suggestions.acquire();
214        mResultsAdapter.setSuggestions(suggestions);
215    }
216
217    @Override
218    protected void setCorpus(Corpus corpus) {
219        super.setCorpus(corpus);
220        mResultsAdapter.setPromoter(createResultsPromoter());
221    }
222
223    @Override
224    protected Promoter createSuggestionsPromoter() {
225        return getQsbApplication().createWebPromoter();
226    }
227
228    protected Promoter createResultsPromoter() {
229        Corpus corpus = getCorpus();
230        if (corpus == null) {
231            return getQsbApplication().createResultsPromoter();
232        } else {
233            return getQsbApplication().createSingleCorpusResultsPromoter(corpus);
234        }
235    }
236
237    protected void onResultsChanged() {
238        checkHideResultsHeader();
239    }
240
241    @Override
242    protected void updateQueryTextView(boolean queryEmpty) {
243        super.updateQueryTextView(queryEmpty);
244        if (mSearchCloseButton == null) return;
245
246        if (queryEmpty) {
247            mSearchCloseButton.setImageResource(R.drawable.ic_clear_disabled);
248        } else {
249            mSearchCloseButton.setImageResource(R.drawable.ic_clear);
250        }
251    }
252
253    private void checkHideResultsHeader() {
254        if (mResultsHeader != null) {
255            if (!mResultsAdapter.isEmpty()) {
256                if (DBG) Log.d(TAG, "Results non-empty");
257                mResultsHeader.setVisibility(VISIBLE);
258            } else {
259                if (DBG) Log.d(TAG, "Results empty");
260                mResultsHeader.setVisibility(INVISIBLE);
261            }
262        }
263    }
264
265    @Override
266    public Corpus getSearchCorpus() {
267        return getWebCorpus();
268    }
269
270    protected class ResultsObserver extends DataSetObserver {
271        @Override
272        public void onChanged() {
273            onResultsChanged();
274        }
275    }
276
277}
278