SearchActivity.java revision 6b858fc6f8afad95a075595016c5c22547924886
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 */
16package com.android.calendar;
17
18import static android.provider.Calendar.EVENT_BEGIN_TIME;
19import static android.provider.Calendar.EVENT_END_TIME;
20
21import com.android.calendar.CalendarController.EventInfo;
22import com.android.calendar.CalendarController.EventType;
23import com.android.calendar.CalendarController.ViewType;
24import com.android.calendar.agenda.AgendaFragment;
25
26import dalvik.system.VMRuntime;
27
28import android.app.Activity;
29import android.app.FragmentManager;
30import android.app.FragmentTransaction;
31import android.app.SearchManager;
32import android.content.ContentResolver;
33import android.content.ContentUris;
34import android.content.Intent;
35import android.content.res.Configuration;
36import android.database.ContentObserver;
37import android.net.Uri;
38import android.os.Bundle;
39import android.os.Handler;
40import android.provider.Calendar.Events;
41import android.provider.SearchRecentSuggestions;
42import android.text.format.Time;
43import android.util.Log;
44import android.view.Menu;
45import android.view.MenuItem;
46import android.view.View;
47import android.widget.SearchView;
48
49public class SearchActivity extends Activity
50        implements CalendarController.EventHandler, SearchView.OnQueryChangeListener {
51
52    private static final String TAG = SearchActivity.class.getSimpleName();
53
54    private static final boolean DEBUG = false;
55
56    private static final int HANDLER_KEY = 0;
57
58    private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
59
60    protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
61
62    protected static final String BUNDLE_KEY_RESTORE_SEARCH_QUERY =
63        "key_restore_search_query";
64
65    private static boolean mIsMultipane;
66
67    private CalendarController mController;
68
69    private EventInfoFragment mEventInfoFragment;
70
71    private long mCurrentEventId = -1;
72
73    private DeleteEventHelper mDeleteEventHelper;
74
75    private ContentResolver mContentResolver;
76
77    private ContentObserver mObserver = new ContentObserver(new Handler()) {
78        @Override
79        public boolean deliverSelfNotifications() {
80            return true;
81        }
82
83        @Override
84        public void onChange(boolean selfChange) {
85            eventsChanged();
86        }
87    };
88
89    @Override
90    protected void onCreate(Bundle icicle) {
91        super.onCreate(icicle);
92        // This needs to be created before setContentView
93        mController = CalendarController.getInstance(this);
94
95        mIsMultipane = (getResources().getConfiguration().screenLayout &
96                Configuration.SCREENLAYOUT_SIZE_XLARGE) != 0;
97
98        setContentView(R.layout.search);
99
100        setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
101
102        // Eliminate extra GCs during startup by setting the initial heap size to 4MB.
103        // TODO: We should restore the old heap size once the activity reaches the idle state
104        VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
105
106        mContentResolver = getContentResolver();
107
108        // Must be the first to register because this activity can modify the
109        // list of event handlers in it's handle method. This affects who the
110        // rest of the handlers the controller dispatches to are.
111        mController.registerEventHandler(HANDLER_KEY, this);
112
113        mDeleteEventHelper = new DeleteEventHelper(this, this,
114                false /* don't exit when done */);
115
116        long millis = 0;
117        if (icicle != null) {
118            // Returns 0 if key not found
119            millis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
120            if (DEBUG) {
121                Log.v(TAG, "Restore value from icicle: " + millis);
122            }
123        }
124        if (millis == 0) {
125            // Didn't find a time in the bundle, look in intent or current time
126            millis = Utils.timeFromIntentInMillis(getIntent());
127        }
128
129        Intent intent = getIntent();
130        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
131            String query = intent.getStringExtra(SearchManager.QUERY);
132            initFragments(millis, query);
133        }
134    }
135
136    @Override
137    protected void onDestroy() {
138        super.onDestroy();
139        CalendarController.removeInstance(this);
140    }
141
142    private void initFragments(long timeMillis, String query) {
143        FragmentManager fragmentManager = getFragmentManager();
144        FragmentTransaction ft = fragmentManager.openTransaction();
145
146        AgendaFragment searchResultsFragment = new AgendaFragment(timeMillis);
147        ft.replace(R.id.search_results, searchResultsFragment);
148        mController.registerEventHandler(R.id.search_results, searchResultsFragment);
149        if (!mIsMultipane) {
150            findViewById(R.id.event_info).setVisibility(View.GONE);
151        }
152
153        ft.commit();
154        Time t = new Time();
155        t.set(timeMillis);
156        search(query, t);
157    }
158
159    private void showEventInfo(long eventId, long startMillis, long endMillis) {
160        if (mIsMultipane) {
161            FragmentManager fragmentManager = getFragmentManager();
162            FragmentTransaction ft = fragmentManager.openTransaction();
163
164            mEventInfoFragment =
165                new EventInfoFragment(eventId, startMillis, endMillis);
166            ft.replace(R.id.event_info, mEventInfoFragment);
167            ft.commit();
168        } else {
169            Intent intent = new Intent(Intent.ACTION_VIEW);
170            Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
171            intent.setData(eventUri);
172            intent.setClassName(this, EventInfoActivity.class.getName());
173            intent.putExtra(EVENT_BEGIN_TIME, startMillis);
174            intent.putExtra(EVENT_END_TIME, endMillis);
175            startActivity(intent);
176        }
177        mCurrentEventId = eventId;
178    }
179
180    private void search(String searchQuery, Time goToTime) {
181        // save query in recent queries
182        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
183                CalendarRecentSuggestionsProvider.AUTHORITY,
184                CalendarRecentSuggestionsProvider.MODE);
185        suggestions.saveRecentQuery(searchQuery, null);
186
187
188        EventInfo searchEventInfo = new EventInfo();
189        searchEventInfo.eventType = EventType.SEARCH;
190        searchEventInfo.query = searchQuery;
191        searchEventInfo.viewType = ViewType.AGENDA;
192        if (goToTime != null) {
193            searchEventInfo.startTime = goToTime;
194        }
195        mController.sendEvent(this, searchEventInfo);
196    }
197
198    private void deleteEvent(long eventId, long startMillis, long endMillis) {
199        mDeleteEventHelper.delete(startMillis, endMillis, eventId, -1);
200        if (mIsMultipane && mEventInfoFragment != null
201                && eventId == mCurrentEventId) {
202            FragmentManager fragmentManager = getFragmentManager();
203            FragmentTransaction ft = fragmentManager.openTransaction();
204            ft.remove(mEventInfoFragment);
205            ft.commit();
206            mEventInfoFragment = null;
207            mCurrentEventId = -1;
208        }
209    }
210
211    @Override
212    public boolean onCreateOptionsMenu(Menu menu) {
213        super.onCreateOptionsMenu(menu);
214        getMenuInflater().inflate(R.menu.search_title_bar, menu);
215        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
216        searchView.setIconifiedByDefault(true);
217        searchView.setOnQueryChangeListener(this);
218        return true;
219    }
220
221    @Override
222    public boolean onOptionsItemSelected(MenuItem item) {
223        Time t = null;
224        switch (item.getItemId()) {
225            case R.id.action_today:
226                t = new Time();
227                t.setToNow();
228                mController.sendEvent(this, EventType.GO_TO, t, null, -1, ViewType.CURRENT);
229                return true;
230            case R.id.action_search:
231                onSearchRequested();
232                return true;
233            case R.id.action_settings:
234                mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
235                return true;
236            default:
237                return false;
238        }
239    }
240
241    @Override
242    protected void onNewIntent(Intent intent) {
243        // From the Android Dev Guide: "It's important to note that when
244        // onNewIntent(Intent) is called, the Activity has not been restarted,
245        // so the getIntent() method will still return the Intent that was first
246        // received with onCreate(). This is why setIntent(Intent) is called
247        // inside onNewIntent(Intent) (just in case you call getIntent() at a
248        // later time)."
249        setIntent(intent);
250        handleIntent(intent);
251    }
252
253    private void handleIntent(Intent intent) {
254        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
255            String query = intent.getStringExtra(SearchManager.QUERY);
256            search(query, null);
257        }
258    }
259
260    @Override
261    public void onSaveInstanceState(Bundle outState) {
262        super.onSaveInstanceState(outState);
263        outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
264    }
265
266    @Override
267    protected void onResume() {
268        super.onResume();
269        mContentResolver.registerContentObserver(Events.CONTENT_URI, true, mObserver);
270    }
271
272    @Override
273    protected void onPause() {
274        super.onPause();
275        mContentResolver.unregisterContentObserver(mObserver);
276    }
277
278//    @Override
279//    public boolean onKeyDown(int keyCode, KeyEvent event) {
280//        switch (keyCode) {
281//            case KeyEvent.KEYCODE_DEL:
282//                // Delete the currently selected event (if any)
283//                mAgendaListView.deleteSelectedEvent();
284//                break;
285//        }
286//        return super.onKeyDown(keyCode, event);
287//    }
288
289    @Override
290    public void goToToday() {
291    }
292
293    @Override
294    public void goTo(Time time, boolean animate) {
295    }
296
297    @Override
298    public long getSelectedTime() {
299        return 0;
300    }
301
302    @Override
303    public boolean getAllDay() {
304        return false;
305    }
306
307    @Override
308    public void eventsChanged() {
309        mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
310    }
311
312    @Override
313    public long getSupportedEventTypes() {
314        return EventType.VIEW_EVENT | EventType.DELETE_EVENT;
315    }
316
317    @Override
318    public void handleEvent(EventInfo event) {
319        long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
320        if (event.eventType == EventType.VIEW_EVENT) {
321            showEventInfo(event.id, event.startTime.toMillis(false), endTime);
322        } else if (event.eventType == EventType.DELETE_EVENT) {
323            deleteEvent(event.id, event.startTime.toMillis(false), endTime);
324        }
325    }
326
327    @Override
328    public boolean onQueryTextChanged(String newText) {
329        return false;
330    }
331
332    @Override
333    public boolean onSubmitQuery(String query) {
334        mController.sendEvent(
335                this, EventType.SEARCH, null, null, -1,
336                ViewType.CURRENT, query, getComponentName());
337        return false;
338    }
339}
340