Home | History | Annotate | Download | only in calendar
      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 package com.android.calendar;
     17 
     18 import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
     19 import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
     20 
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.app.FragmentManager;
     24 import android.app.FragmentTransaction;
     25 import android.app.SearchManager;
     26 import android.content.BroadcastReceiver;
     27 import android.content.ContentResolver;
     28 import android.content.ContentUris;
     29 import android.content.Intent;
     30 import android.database.ContentObserver;
     31 import android.graphics.drawable.LayerDrawable;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.provider.CalendarContract.Events;
     36 import android.provider.SearchRecentSuggestions;
     37 import android.text.format.Time;
     38 import android.util.Log;
     39 import android.view.Menu;
     40 import android.view.MenuItem;
     41 import android.view.MenuItem.OnActionExpandListener;
     42 import android.widget.SearchView;
     43 
     44 import com.android.calendar.CalendarController.EventInfo;
     45 import com.android.calendar.CalendarController.EventType;
     46 import com.android.calendar.CalendarController.ViewType;
     47 import com.android.calendar.agenda.AgendaFragment;
     48 
     49 public class SearchActivity extends Activity implements CalendarController.EventHandler,
     50         SearchView.OnQueryTextListener, OnActionExpandListener {
     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     protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
     59 
     60     protected static final String BUNDLE_KEY_RESTORE_SEARCH_QUERY =
     61         "key_restore_search_query";
     62 
     63     // display event details to the side of the event list
     64    private boolean mShowEventDetailsWithAgenda;
     65    private static boolean mIsMultipane;
     66 
     67     private CalendarController mController;
     68 
     69     private EventInfoFragment mEventInfoFragment;
     70 
     71     private long mCurrentEventId = -1;
     72 
     73     private String mQuery;
     74 
     75     private SearchView mSearchView;
     76 
     77     private DeleteEventHelper mDeleteEventHelper;
     78 
     79     private Handler mHandler;
     80     private BroadcastReceiver mTimeChangesReceiver;
     81     private ContentResolver mContentResolver;
     82 
     83     private final ContentObserver mObserver = new ContentObserver(new Handler()) {
     84         @Override
     85         public boolean deliverSelfNotifications() {
     86             return true;
     87         }
     88 
     89         @Override
     90         public void onChange(boolean selfChange) {
     91             eventsChanged();
     92         }
     93     };
     94 
     95     // runs when a timezone was changed and updates the today icon
     96     private final Runnable mTimeChangesUpdater = new Runnable() {
     97         @Override
     98         public void run() {
     99             Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater,
    100                     Utils.getTimeZone(SearchActivity.this, mTimeChangesUpdater));
    101             SearchActivity.this.invalidateOptionsMenu();
    102         }
    103     };
    104 
    105     @Override
    106     protected void onCreate(Bundle icicle) {
    107         super.onCreate(icicle);
    108         // This needs to be created before setContentView
    109         mController = CalendarController.getInstance(this);
    110         mHandler = new Handler();
    111 
    112         mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config);
    113         mShowEventDetailsWithAgenda =
    114             Utils.getConfigBool(this, R.bool.show_event_details_with_agenda);
    115 
    116         setContentView(R.layout.search);
    117 
    118         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
    119 
    120         mContentResolver = getContentResolver();
    121 
    122         if (mIsMultipane) {
    123             getActionBar().setDisplayOptions(
    124                     ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
    125         } else {
    126             getActionBar().setDisplayOptions(0,
    127                     ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME);
    128         }
    129 
    130         // Must be the first to register because this activity can modify the
    131         // list of event handlers in it's handle method. This affects who the
    132         // rest of the handlers the controller dispatches to are.
    133         mController.registerEventHandler(HANDLER_KEY, this);
    134 
    135         mDeleteEventHelper = new DeleteEventHelper(this, this,
    136                 false /* don't exit when done */);
    137 
    138         long millis = 0;
    139         if (icicle != null) {
    140             // Returns 0 if key not found
    141             millis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
    142             if (DEBUG) {
    143                 Log.v(TAG, "Restore value from icicle: " + millis);
    144             }
    145         }
    146         if (millis == 0) {
    147             // Didn't find a time in the bundle, look in intent or current time
    148             millis = Utils.timeFromIntentInMillis(getIntent());
    149         }
    150 
    151         Intent intent = getIntent();
    152         if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    153             String query;
    154             if (icicle != null && icicle.containsKey(BUNDLE_KEY_RESTORE_SEARCH_QUERY)) {
    155                 query = icicle.getString(BUNDLE_KEY_RESTORE_SEARCH_QUERY);
    156             } else {
    157                 query = intent.getStringExtra(SearchManager.QUERY);
    158             }
    159             initFragments(millis, query);
    160         }
    161     }
    162 
    163     @Override
    164     protected void onDestroy() {
    165         super.onDestroy();
    166         mController.deregisterAllEventHandlers();
    167         CalendarController.removeInstance(this);
    168     }
    169 
    170     private void initFragments(long timeMillis, String query) {
    171         FragmentManager fragmentManager = getFragmentManager();
    172         FragmentTransaction ft = fragmentManager.beginTransaction();
    173 
    174         AgendaFragment searchResultsFragment = new AgendaFragment(timeMillis, true);
    175         ft.replace(R.id.search_results, searchResultsFragment);
    176         mController.registerEventHandler(R.id.search_results, searchResultsFragment);
    177 
    178         ft.commit();
    179         Time t = new Time();
    180         t.set(timeMillis);
    181         search(query, t);
    182     }
    183 
    184     private void showEventInfo(EventInfo event) {
    185         if (mShowEventDetailsWithAgenda) {
    186             FragmentManager fragmentManager = getFragmentManager();
    187             FragmentTransaction ft = fragmentManager.beginTransaction();
    188 
    189             mEventInfoFragment = new EventInfoFragment(this, event.id,
    190                     event.startTime.toMillis(false), event.endTime.toMillis(false),
    191                     event.getResponse(), false, EventInfoFragment.DIALOG_WINDOW_STYLE);
    192             ft.replace(R.id.agenda_event_info, mEventInfoFragment);
    193             ft.commit();
    194             mController.registerEventHandler(R.id.agenda_event_info, mEventInfoFragment);
    195         } else {
    196             Intent intent = new Intent(Intent.ACTION_VIEW);
    197             Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id);
    198             intent.setData(eventUri);
    199             intent.setClass(this, EventInfoActivity.class);
    200             intent.putExtra(EXTRA_EVENT_BEGIN_TIME,
    201                     event.startTime != null ? event.startTime.toMillis(true) : -1);
    202             intent.putExtra(
    203                     EXTRA_EVENT_END_TIME, event.endTime != null ? event.endTime.toMillis(true) : -1);
    204             startActivity(intent);
    205         }
    206         mCurrentEventId = event.id;
    207     }
    208 
    209     private void search(String searchQuery, Time goToTime) {
    210         // save query in recent queries
    211         SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
    212                 Utils.getSearchAuthority(this),
    213                 CalendarRecentSuggestionsProvider.MODE);
    214         suggestions.saveRecentQuery(searchQuery, null);
    215 
    216 
    217         EventInfo searchEventInfo = new EventInfo();
    218         searchEventInfo.eventType = EventType.SEARCH;
    219         searchEventInfo.query = searchQuery;
    220         searchEventInfo.viewType = ViewType.AGENDA;
    221         if (goToTime != null) {
    222             searchEventInfo.startTime = goToTime;
    223         }
    224         mController.sendEvent(this, searchEventInfo);
    225         mQuery = searchQuery;
    226         if (mSearchView != null) {
    227             mSearchView.setQuery(mQuery, false);
    228             mSearchView.clearFocus();
    229         }
    230     }
    231 
    232     private void deleteEvent(long eventId, long startMillis, long endMillis) {
    233         mDeleteEventHelper.delete(startMillis, endMillis, eventId, -1);
    234         if (mIsMultipane && mEventInfoFragment != null
    235                 && eventId == mCurrentEventId) {
    236             FragmentManager fragmentManager = getFragmentManager();
    237             FragmentTransaction ft = fragmentManager.beginTransaction();
    238             ft.remove(mEventInfoFragment);
    239             ft.commit();
    240             mEventInfoFragment = null;
    241             mController.deregisterEventHandler(R.id.agenda_event_info);
    242             mCurrentEventId = -1;
    243         }
    244     }
    245 
    246     @Override
    247     public boolean onCreateOptionsMenu(Menu menu) {
    248         super.onCreateOptionsMenu(menu);
    249         getMenuInflater().inflate(R.menu.search_title_bar, menu);
    250 
    251         // replace the default top layer drawable of the today icon with a custom drawable
    252         // that shows the day of the month of today
    253         LayerDrawable icon = (LayerDrawable)menu.findItem(R.id.action_today).getIcon();
    254         Utils.setTodayIcon(icon, this, Utils.getTimeZone(SearchActivity.this, mTimeChangesUpdater));
    255 
    256         MenuItem item = menu.findItem(R.id.action_search);
    257         item.expandActionView();
    258         item.setOnActionExpandListener(this);
    259         mSearchView = (SearchView) item.getActionView();
    260         Utils.setUpSearchView(mSearchView, this);
    261         mSearchView.setQuery(mQuery, false);
    262         mSearchView.clearFocus();
    263 
    264         return true;
    265     }
    266 
    267     @Override
    268     public boolean onOptionsItemSelected(MenuItem item) {
    269         Time t = null;
    270         switch (item.getItemId()) {
    271             case R.id.action_today:
    272                 t = new Time();
    273                 t.setToNow();
    274                 mController.sendEvent(this, EventType.GO_TO, t, null, -1, ViewType.CURRENT);
    275                 return true;
    276             case R.id.action_search:
    277                 return false;
    278             case R.id.action_settings:
    279                 mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
    280                 return true;
    281             case android.R.id.home:
    282                 Utils.returnToCalendarHome(this);
    283                 return true;
    284             default:
    285                 return false;
    286         }
    287     }
    288 
    289     @Override
    290     protected void onNewIntent(Intent intent) {
    291         // From the Android Dev Guide: "It's important to note that when
    292         // onNewIntent(Intent) is called, the Activity has not been restarted,
    293         // so the getIntent() method will still return the Intent that was first
    294         // received with onCreate(). This is why setIntent(Intent) is called
    295         // inside onNewIntent(Intent) (just in case you call getIntent() at a
    296         // later time)."
    297         setIntent(intent);
    298         handleIntent(intent);
    299     }
    300 
    301     private void handleIntent(Intent intent) {
    302         if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    303             String query = intent.getStringExtra(SearchManager.QUERY);
    304             search(query, null);
    305         }
    306     }
    307 
    308     @Override
    309     public void onSaveInstanceState(Bundle outState) {
    310         super.onSaveInstanceState(outState);
    311         outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
    312         outState.putString(BUNDLE_KEY_RESTORE_SEARCH_QUERY, mQuery);
    313     }
    314 
    315     @Override
    316     protected void onResume() {
    317         super.onResume();
    318 
    319         Utils.setMidnightUpdater(
    320                 mHandler, mTimeChangesUpdater, Utils.getTimeZone(this, mTimeChangesUpdater));
    321         // Make sure the today icon is up to date
    322         invalidateOptionsMenu();
    323         mTimeChangesReceiver = Utils.setTimeChangesReceiver(this, mTimeChangesUpdater);
    324         mContentResolver.registerContentObserver(Events.CONTENT_URI, true, mObserver);
    325         // We call this in case the user changed the time zone
    326         eventsChanged();
    327     }
    328 
    329     @Override
    330     protected void onPause() {
    331         super.onPause();
    332         Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater);
    333         Utils.clearTimeChangesReceiver(this, mTimeChangesReceiver);
    334         mContentResolver.unregisterContentObserver(mObserver);
    335     }
    336 
    337     @Override
    338     public void eventsChanged() {
    339         mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
    340     }
    341 
    342     @Override
    343     public long getSupportedEventTypes() {
    344         return EventType.VIEW_EVENT | EventType.DELETE_EVENT;
    345     }
    346 
    347     @Override
    348     public void handleEvent(EventInfo event) {
    349         long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
    350         if (event.eventType == EventType.VIEW_EVENT) {
    351             showEventInfo(event);
    352         } else if (event.eventType == EventType.DELETE_EVENT) {
    353             deleteEvent(event.id, event.startTime.toMillis(false), endTime);
    354         }
    355     }
    356 
    357     @Override
    358     public boolean onQueryTextChange(String newText) {
    359         return false;
    360     }
    361 
    362     @Override
    363     public boolean onQueryTextSubmit(String query) {
    364         mQuery = query;
    365         mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, 0, query,
    366                 getComponentName());
    367         return false;
    368     }
    369 
    370     @Override
    371     public boolean onMenuItemActionExpand(MenuItem item) {
    372         return true;
    373     }
    374 
    375     @Override
    376     public boolean onMenuItemActionCollapse(MenuItem item) {
    377         Utils.returnToCalendarHome(this);
    378         return false;
    379     }
    380 }
    381