blob: 929e4a61f272f835ce61a245e8a92044cdce422a [file] [log] [blame]
/*
* 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.calendar;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import android.app.ActionBar;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.database.ContentObserver;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.CalendarContract.Events;
import android.provider.SearchRecentSuggestions;
import android.text.format.Time;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnActionExpandListener;
import android.widget.SearchView;
import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.agenda.AgendaFragment;
public class SearchActivity extends Activity implements CalendarController.EventHandler,
SearchView.OnQueryTextListener, OnActionExpandListener {
private static final String TAG = SearchActivity.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int HANDLER_KEY = 0;
protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
protected static final String BUNDLE_KEY_RESTORE_SEARCH_QUERY =
"key_restore_search_query";
// display event details to the side of the event list
private boolean mShowEventDetailsWithAgenda;
private static boolean mIsMultipane;
private CalendarController mController;
private EventInfoFragment mEventInfoFragment;
private long mCurrentEventId = -1;
private String mQuery;
private SearchView mSearchView;
private DeleteEventHelper mDeleteEventHelper;
private Handler mHandler;
private BroadcastReceiver mTimeChangesReceiver;
private ContentResolver mContentResolver;
private final ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
eventsChanged();
}
};
// runs when a timezone was changed and updates the today icon
private final Runnable mTimeChangesUpdater = new Runnable() {
@Override
public void run() {
Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater,
Utils.getTimeZone(SearchActivity.this, mTimeChangesUpdater));
SearchActivity.this.invalidateOptionsMenu();
}
};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// This needs to be created before setContentView
mController = CalendarController.getInstance(this);
mHandler = new Handler();
mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config);
mShowEventDetailsWithAgenda =
Utils.getConfigBool(this, R.bool.show_event_details_with_agenda);
setContentView(R.layout.search);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
mContentResolver = getContentResolver();
if (mIsMultipane) {
getActionBar().setDisplayOptions(
ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
} else {
getActionBar().setDisplayOptions(0,
ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME);
}
// Must be the first to register because this activity can modify the
// list of event handlers in it's handle method. This affects who the
// rest of the handlers the controller dispatches to are.
mController.registerEventHandler(HANDLER_KEY, this);
mDeleteEventHelper = new DeleteEventHelper(this, this,
false /* don't exit when done */);
long millis = 0;
if (icicle != null) {
// Returns 0 if key not found
millis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
if (DEBUG) {
Log.v(TAG, "Restore value from icicle: " + millis);
}
}
if (millis == 0) {
// Didn't find a time in the bundle, look in intent or current time
millis = Utils.timeFromIntentInMillis(getIntent());
}
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query;
if (icicle != null && icicle.containsKey(BUNDLE_KEY_RESTORE_SEARCH_QUERY)) {
query = icicle.getString(BUNDLE_KEY_RESTORE_SEARCH_QUERY);
} else {
query = intent.getStringExtra(SearchManager.QUERY);
}
if ("TARDIS".equalsIgnoreCase(query)) {
Utils.tardis();
}
initFragments(millis, query);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mController.deregisterAllEventHandlers();
CalendarController.removeInstance(this);
}
private void initFragments(long timeMillis, String query) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
AgendaFragment searchResultsFragment = new AgendaFragment(timeMillis, true);
ft.replace(R.id.search_results, searchResultsFragment);
mController.registerEventHandler(R.id.search_results, searchResultsFragment);
ft.commit();
Time t = new Time();
t.set(timeMillis);
search(query, t);
}
private void showEventInfo(EventInfo event) {
if (mShowEventDetailsWithAgenda) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
mEventInfoFragment = new EventInfoFragment(this, event.id,
event.startTime.toMillis(false), event.endTime.toMillis(false),
event.getResponse(), false, EventInfoFragment.DIALOG_WINDOW_STYLE,
null /* No reminders to explicitly pass in. */);
ft.replace(R.id.agenda_event_info, mEventInfoFragment);
ft.commit();
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id);
intent.setData(eventUri);
intent.setClass(this, EventInfoActivity.class);
intent.putExtra(EXTRA_EVENT_BEGIN_TIME,
event.startTime != null ? event.startTime.toMillis(true) : -1);
intent.putExtra(
EXTRA_EVENT_END_TIME, event.endTime != null ? event.endTime.toMillis(true) : -1);
startActivity(intent);
}
mCurrentEventId = event.id;
}
private void search(String searchQuery, Time goToTime) {
// save query in recent queries
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
Utils.getSearchAuthority(this),
CalendarRecentSuggestionsProvider.MODE);
suggestions.saveRecentQuery(searchQuery, null);
EventInfo searchEventInfo = new EventInfo();
searchEventInfo.eventType = EventType.SEARCH;
searchEventInfo.query = searchQuery;
searchEventInfo.viewType = ViewType.AGENDA;
if (goToTime != null) {
searchEventInfo.startTime = goToTime;
}
mController.sendEvent(this, searchEventInfo);
mQuery = searchQuery;
if (mSearchView != null) {
mSearchView.setQuery(mQuery, false);
mSearchView.clearFocus();
}
}
private void deleteEvent(long eventId, long startMillis, long endMillis) {
mDeleteEventHelper.delete(startMillis, endMillis, eventId, -1);
if (mIsMultipane && mEventInfoFragment != null
&& eventId == mCurrentEventId) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.remove(mEventInfoFragment);
ft.commit();
mEventInfoFragment = null;
mCurrentEventId = -1;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.search_title_bar, menu);
// replace the default top layer drawable of the today icon with a custom drawable
// that shows the day of the month of today
MenuItem menuItem = menu.findItem(R.id.action_today);
if (Utils.isJellybeanOrLater()) {
LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
Utils.setTodayIcon(
icon, this, Utils.getTimeZone(SearchActivity.this, mTimeChangesUpdater));
} else {
menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light);
}
MenuItem item = menu.findItem(R.id.action_search);
item.expandActionView();
item.setOnActionExpandListener(this);
mSearchView = (SearchView) item.getActionView();
Utils.setUpSearchView(mSearchView, this);
mSearchView.setQuery(mQuery, false);
mSearchView.clearFocus();
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Time t = null;
final int itemId = item.getItemId();
if (itemId == R.id.action_today) {
t = new Time();
t.setToNow();
mController.sendEvent(this, EventType.GO_TO, t, null, -1, ViewType.CURRENT);
return true;
} else if (itemId == R.id.action_search) {
return false;
} else if (itemId == R.id.action_settings) {
mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
return true;
} else if (itemId == android.R.id.home) {
Utils.returnToCalendarHome(this);
return true;
} else {
return false;
}
}
@Override
protected void onNewIntent(Intent intent) {
// From the Android Dev Guide: "It's important to note that when
// onNewIntent(Intent) is called, the Activity has not been restarted,
// so the getIntent() method will still return the Intent that was first
// received with onCreate(). This is why setIntent(Intent) is called
// inside onNewIntent(Intent) (just in case you call getIntent() at a
// later time)."
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
search(query, null);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
outState.putString(BUNDLE_KEY_RESTORE_SEARCH_QUERY, mQuery);
}
@Override
protected void onResume() {
super.onResume();
Utils.setMidnightUpdater(
mHandler, mTimeChangesUpdater, Utils.getTimeZone(this, mTimeChangesUpdater));
// Make sure the today icon is up to date
invalidateOptionsMenu();
mTimeChangesReceiver = Utils.setTimeChangesReceiver(this, mTimeChangesUpdater);
mContentResolver.registerContentObserver(Events.CONTENT_URI, true, mObserver);
// We call this in case the user changed the time zone
eventsChanged();
}
@Override
protected void onPause() {
super.onPause();
Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater);
Utils.clearTimeChangesReceiver(this, mTimeChangesReceiver);
mContentResolver.unregisterContentObserver(mObserver);
}
@Override
public void eventsChanged() {
mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
}
@Override
public long getSupportedEventTypes() {
return EventType.VIEW_EVENT | EventType.DELETE_EVENT;
}
@Override
public void handleEvent(EventInfo event) {
long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
if (event.eventType == EventType.VIEW_EVENT) {
showEventInfo(event);
} else if (event.eventType == EventType.DELETE_EVENT) {
deleteEvent(event.id, event.startTime.toMillis(false), endTime);
}
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
@Override
public boolean onQueryTextSubmit(String query) {
mQuery = query;
mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, 0, query,
getComponentName());
return false;
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
Utils.returnToCalendarHome(this);
return false;
}
}