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         MenuItem menuItem = menu.findItem(R.id.action_today);
    254         if (Utils.isJellybeanOrLater()) {
    255             LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
    256             Utils.setTodayIcon(
    257                     icon, this, Utils.getTimeZone(SearchActivity.this, mTimeChangesUpdater));
    258         } else {
    259             menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light);
    260         }
    261 
    262         MenuItem item = menu.findItem(R.id.action_search);
    263         item.expandActionView();
    264         item.setOnActionExpandListener(this);
    265         mSearchView = (SearchView) item.getActionView();
    266         Utils.setUpSearchView(mSearchView, this);
    267         mSearchView.setQuery(mQuery, false);
    268         mSearchView.clearFocus();
    269 
    270         return true;
    271     }
    272 
    273     @Override
    274     public boolean onOptionsItemSelected(MenuItem item) {
    275         Time t = null;
    276         final int itemId = item.getItemId();
    277         if (itemId == R.id.action_today) {
    278             t = new Time();
    279             t.setToNow();
    280             mController.sendEvent(this, EventType.GO_TO, t, null, -1, ViewType.CURRENT);
    281             return true;
    282         } else if (itemId == R.id.action_search) {
    283             return false;
    284         } else if (itemId == R.id.action_settings) {
    285             mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
    286             return true;
    287         } else if (itemId == android.R.id.home) {
    288             Utils.returnToCalendarHome(this);
    289             return true;
    290         } else {
    291             return false;
    292         }
    293     }
    294 
    295     @Override
    296     protected void onNewIntent(Intent intent) {
    297         // From the Android Dev Guide: "It's important to note that when
    298         // onNewIntent(Intent) is called, the Activity has not been restarted,
    299         // so the getIntent() method will still return the Intent that was first
    300         // received with onCreate(). This is why setIntent(Intent) is called
    301         // inside onNewIntent(Intent) (just in case you call getIntent() at a
    302         // later time)."
    303         setIntent(intent);
    304         handleIntent(intent);
    305     }
    306 
    307     private void handleIntent(Intent intent) {
    308         if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    309             String query = intent.getStringExtra(SearchManager.QUERY);
    310             search(query, null);
    311         }
    312     }
    313 
    314     @Override
    315     public void onSaveInstanceState(Bundle outState) {
    316         super.onSaveInstanceState(outState);
    317         outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
    318         outState.putString(BUNDLE_KEY_RESTORE_SEARCH_QUERY, mQuery);
    319     }
    320 
    321     @Override
    322     protected void onResume() {
    323         super.onResume();
    324 
    325         Utils.setMidnightUpdater(
    326                 mHandler, mTimeChangesUpdater, Utils.getTimeZone(this, mTimeChangesUpdater));
    327         // Make sure the today icon is up to date
    328         invalidateOptionsMenu();
    329         mTimeChangesReceiver = Utils.setTimeChangesReceiver(this, mTimeChangesUpdater);
    330         mContentResolver.registerContentObserver(Events.CONTENT_URI, true, mObserver);
    331         // We call this in case the user changed the time zone
    332         eventsChanged();
    333     }
    334 
    335     @Override
    336     protected void onPause() {
    337         super.onPause();
    338         Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater);
    339         Utils.clearTimeChangesReceiver(this, mTimeChangesReceiver);
    340         mContentResolver.unregisterContentObserver(mObserver);
    341     }
    342 
    343     @Override
    344     public void eventsChanged() {
    345         mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
    346     }
    347 
    348     @Override
    349     public long getSupportedEventTypes() {
    350         return EventType.VIEW_EVENT | EventType.DELETE_EVENT;
    351     }
    352 
    353     @Override
    354     public void handleEvent(EventInfo event) {
    355         long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
    356         if (event.eventType == EventType.VIEW_EVENT) {
    357             showEventInfo(event);
    358         } else if (event.eventType == EventType.DELETE_EVENT) {
    359             deleteEvent(event.id, event.startTime.toMillis(false), endTime);
    360         }
    361     }
    362 
    363     @Override
    364     public boolean onQueryTextChange(String newText) {
    365         return false;
    366     }
    367 
    368     @Override
    369     public boolean onQueryTextSubmit(String query) {
    370         mQuery = query;
    371         mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, 0, query,
    372                 getComponentName());
    373         return false;
    374     }
    375 
    376     @Override
    377     public boolean onMenuItemActionExpand(MenuItem item) {
    378         return true;
    379     }
    380 
    381     @Override
    382     public boolean onMenuItemActionCollapse(MenuItem item) {
    383         Utils.returnToCalendarHome(this);
    384         return false;
    385     }
    386 }
    387