AllInOneActivity.java revision 40474a816f1a40f2c601d6cb72ab29b5309cdf32
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.calendar; 18 19import static android.provider.Calendar.EVENT_BEGIN_TIME; 20import static android.provider.Calendar.EVENT_END_TIME; 21 22import com.android.calendar.CalendarController.EventHandler; 23import com.android.calendar.CalendarController.EventInfo; 24import com.android.calendar.CalendarController.EventType; 25import com.android.calendar.CalendarController.ViewType; 26import com.android.calendar.agenda.AgendaFragment; 27import com.android.calendar.event.EditEventFragment; 28import com.android.calendar.month.MonthByWeekFragment; 29import com.android.calendar.selectcalendars.SelectCalendarsFragment; 30 31import dalvik.system.VMRuntime; 32 33import android.app.ActionBar; 34import android.app.ActionBar.Tab; 35import android.app.ActionBar.TabListener; 36import android.app.Activity; 37import android.app.Fragment; 38import android.app.FragmentTransaction; 39import android.content.ContentResolver; 40import android.content.Intent; 41import android.content.SharedPreferences; 42import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 43import android.content.res.Configuration; 44import android.database.ContentObserver; 45import android.net.Uri; 46import android.os.Bundle; 47import android.os.Handler; 48import android.provider.Calendar; 49import android.text.TextUtils; 50import android.text.format.DateFormat; 51import android.text.format.DateUtils; 52import android.text.format.Time; 53import android.util.Log; 54import android.view.Menu; 55import android.view.MenuItem; 56import android.view.View; 57import android.widget.SearchView; 58import android.widget.TextView; 59 60import java.util.Locale; 61import java.util.TimeZone; 62 63public class AllInOneActivity extends Activity implements EventHandler, 64 OnSharedPreferenceChangeListener, SearchView.OnQueryChangeListener, 65 ActionBar.TabListener { 66 private static final String TAG = "AllInOneActivity"; 67 private static final boolean DEBUG = false; 68 private static final long INITIAL_HEAP_SIZE = 4*1024*1024; 69 private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; 70 private static final String BUNDLE_KEY_RESTORE_EDIT = "key_restore_edit"; 71 private static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; 72 private static final int HANDLER_KEY = 0; 73 private static CalendarController mController; 74 private static boolean mIsMultipane; 75 private ContentResolver mContentResolver; 76 private int mPreviousView; 77 private int mCurrentView; 78 private boolean mPaused = true; 79 private boolean mUpdateOnResume = false; 80 private TextView mHomeTime; 81 82 // Action bar and Navigation bar (left side of Action bar) 83 private ActionBar mActionBar; 84 private ActionBar.Tab mDayTab; 85 private ActionBar.Tab mWeekTab; 86 private ActionBar.Tab mMonthTab; 87 88 private Runnable mHomeTimeUpdater = new Runnable() { 89 @Override 90 public void run() { 91 updateHomeClock(); 92 } 93 }; 94 95 // Create an observer so that we can update the views whenever a 96 // Calendar event changes. 97 private ContentObserver mObserver = new ContentObserver(new Handler()) { 98 @Override 99 public boolean deliverSelfNotifications() { 100 return true; 101 } 102 103 @Override 104 public void onChange(boolean selfChange) { 105 eventsChanged(); 106 } 107 }; 108 109 @Override 110 protected void onCreate(Bundle icicle) { 111 super.onCreate(icicle); 112 113 // Eliminate extra GCs during startup by setting the initial heap size to 4MB. 114 // TODO: We should restore the old heap size once the activity reaches the idle state 115 VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE); 116 117 // This needs to be created before setContentView 118 mController = CalendarController.getInstance(this); 119 // Get time from intent or icicle 120 long timeMillis; 121 if (icicle != null) { 122 timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME); 123 } else { 124 timeMillis = Utils.timeFromIntentInMillis(getIntent()); 125 } 126 boolean restoreEdit = icicle != null ? icicle.getBoolean(BUNDLE_KEY_RESTORE_EDIT, false) 127 : false; 128 int viewType; 129 if (restoreEdit) { 130 viewType = ViewType.EDIT; 131 } else { 132 viewType = Utils.getViewTypeFromIntentAndSharedPref(this); 133 } 134 Time t = new Time(); 135 t.set(timeMillis); 136 137 if (icicle != null && getIntent() != null) { 138 Log.d(TAG, "both, icicle:" + icicle.toString() + " intent:" + getIntent().toString()); 139 } else { 140 Log.d(TAG, "not both, icicle:" + icicle + " intent:" + getIntent()); 141 } 142 143 mIsMultipane = (getResources().getConfiguration().screenLayout 144 & Configuration.SCREENLAYOUT_SIZE_XLARGE) != 0; 145 146 // Must be the first to register because this activity can modify the 147 // list of event handlers in it's handle method. This affects who the 148 // rest of the handlers the controller dispatches to are. 149 mController.registerEventHandler(HANDLER_KEY, this); 150 151 setContentView(R.layout.all_in_one); 152 mHomeTime = (TextView) findViewById(R.id.home_time); 153 154 initFragments(timeMillis, viewType, icicle); 155 156 157 // Listen for changes that would require this to be refreshed 158 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); 159 prefs.registerOnSharedPreferenceChangeListener(this); 160 161 mContentResolver = getContentResolver(); 162 163 mActionBar = getActionBar(); 164 mActionBar.setTabNavigationMode(); 165 if (mActionBar == null) { 166 Log.w(TAG, "ActionBar is null."); 167 } else { 168 mDayTab = mActionBar.newTab(); 169 mDayTab.setText(getString(R.string.day_view)); 170 mDayTab.setTabListener(this); 171 mActionBar.addTab(mDayTab); 172 mWeekTab = mActionBar.newTab(); 173 mWeekTab.setText(getString(R.string.week_view)); 174 mWeekTab.setTabListener(this); 175 mActionBar.addTab(mWeekTab); 176 mMonthTab = mActionBar.newTab(); 177 mMonthTab.setText(getString(R.string.month_view)); 178 mMonthTab.setTabListener(this); 179 mActionBar.addTab(mMonthTab); 180 } 181 } 182 183 @Override 184 protected void onResume() { 185 super.onResume(); 186 mContentResolver.registerContentObserver(Calendar.Events.CONTENT_URI, true, mObserver); 187 if (mUpdateOnResume) { 188 initFragments(mController.getTime(), mController.getViewType(), null); 189 mUpdateOnResume = false; 190 } 191 updateHomeClock(); 192 mPaused = false; 193 } 194 195 @Override 196 protected void onPause() { 197 super.onPause(); 198 mPaused = true; 199 mHomeTime.removeCallbacks(mHomeTimeUpdater); 200 mContentResolver.unregisterContentObserver(mObserver); 201 if (isFinishing()) { 202 // Stop listening for changes that would require this to be refreshed 203 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); 204 prefs.unregisterOnSharedPreferenceChangeListener(this); 205 } 206 // FRAG_TODO save highlighted days of the week; 207 if (mController.getViewType() != ViewType.EDIT) { 208 Utils.setDefaultView(this, mController.getViewType()); 209 } 210 } 211 212 @Override 213 protected void onUserLeaveHint() { 214 mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT); 215 super.onUserLeaveHint(); 216 } 217 218 @Override 219 public void onSaveInstanceState(Bundle outState) { 220 super.onSaveInstanceState(outState); 221 222 outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime()); 223 if (mCurrentView == ViewType.EDIT) { 224 outState.putBoolean(BUNDLE_KEY_RESTORE_EDIT, true); 225 outState.putLong(BUNDLE_KEY_EVENT_ID, mController.getEventId()); 226 } 227 } 228 229 @Override 230 protected void onDestroy() { 231 super.onDestroy(); 232 233 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); 234 prefs.unregisterOnSharedPreferenceChangeListener(this); 235 CalendarController.removeInstance(this); 236 } 237 238 private void initFragments(long timeMillis, int viewType, Bundle icicle) { 239 FragmentTransaction ft = getFragmentManager().openTransaction(); 240 241 if (mIsMultipane) { 242 View miniMonth = findViewById(R.id.mini_month); 243 Fragment miniMonthFrag = new MonthByWeekFragment(true); //new MonthFragment(false, timeMillis, true); 244 ft.replace(R.id.mini_month, miniMonthFrag); 245 mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag); 246 247 Fragment selectCalendarsFrag = new SelectCalendarsFragment(); 248 ft.replace(R.id.calendar_list, selectCalendarsFrag); 249 } 250 if (!mIsMultipane || viewType == ViewType.EDIT) { 251 findViewById(R.id.mini_month).setVisibility(View.GONE); 252 findViewById(R.id.calendar_list).setVisibility(View.GONE); 253 } 254 255 EventInfo info = null; 256 if (viewType == ViewType.EDIT) { 257 mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt( 258 GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); 259 260 long eventId = -1; 261 Intent intent = getIntent(); 262 Uri data = intent.getData(); 263 if (data != null) { 264 try { 265 eventId = Long.parseLong(data.getLastPathSegment()); 266 } catch (NumberFormatException e) { 267 if (DEBUG) { 268 Log.d(TAG, "Create new event"); 269 } 270 } 271 } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { 272 eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); 273 } 274 275 long begin = intent.getLongExtra(EVENT_BEGIN_TIME, -1); 276 long end = intent.getLongExtra(EVENT_END_TIME, -1); 277 info = new EventInfo(); 278 if (end != -1) { 279 info.endTime = new Time(); 280 info.endTime.set(end); 281 } 282 if (begin != -1) { 283 info.startTime = new Time(); 284 info.startTime.set(begin); 285 } 286 info.id = eventId; 287 // We set the viewtype so if the user presses back when they are 288 // done editing the controller knows we were in the Edit Event 289 // screen. Likewise for eventId 290 mController.setViewType(viewType); 291 mController.setEventId(eventId); 292 } else { 293 mPreviousView = viewType; 294 } 295 setMainPane(ft, R.id.main_pane, viewType, timeMillis, true, info); 296 297 ft.commit(); // this needs to be after setMainPane() 298 299 Time t = new Time(); 300 t.set(timeMillis); 301 if (viewType != ViewType.EDIT) { 302 mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType); 303 } 304 } 305 306 @Override 307 public void onBackPressed() { 308 if (mCurrentView == ViewType.EDIT || mCurrentView == ViewType.DETAIL) { 309 mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView); 310 } else { 311 super.onBackPressed(); 312 } 313 } 314 315 @Override 316 public boolean onCreateOptionsMenu(Menu menu) { 317 super.onCreateOptionsMenu(menu); 318 319 getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu); 320 321 SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); 322 searchView.setIconifiedByDefault(true); 323 searchView.setOnQueryChangeListener(this); 324 325 return true; 326 } 327 328 @Override 329 public boolean onOptionsItemSelected(MenuItem item) { 330 Time t = null; 331 int viewType = ViewType.CURRENT; 332 switch (item.getItemId()) { 333 case R.id.action_refresh: 334 mController.refreshCalendars(); 335 return true; 336 case R.id.action_today: 337 viewType = ViewType.CURRENT; 338 t = new Time(); 339 t.setToNow(); 340 break; 341 case R.id.action_create_event: 342 mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, 0, 0, 0, 0); 343 return true; 344 case R.id.action_settings: 345 mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0); 346 return true; 347 default: 348 return false; 349 } 350 mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType); 351 return true; 352 } 353 354 @Override 355 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 356 if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) { 357 if (mPaused) { 358 mUpdateOnResume = true; 359 } else { 360 initFragments(mController.getTime(), mController.getViewType(), null); 361 } 362 } 363 } 364 365 private void setMainPane(FragmentTransaction ft, int viewId, int viewType, long timeMillis, 366 boolean force, EventInfo e) { 367 if (!force && mCurrentView == viewType) { 368 return; 369 } 370 371 if (viewType != mCurrentView) { 372 // The rules for this previous view are different than the 373 // controller's and are used for intercepting the back button. 374 if (mCurrentView != ViewType.EDIT && mCurrentView > 0) { 375 mPreviousView = mCurrentView; 376 } 377 mCurrentView = viewType; 378 } 379 // Create new fragment 380 Fragment frag; 381 switch (viewType) { 382 case ViewType.AGENDA: 383 frag = new AgendaFragment(timeMillis); 384 break; 385 case ViewType.DAY: 386 if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) { 387 mActionBar.selectTab(mDayTab); 388 } 389 frag = new DayFragment(timeMillis, 1); 390 break; 391 case ViewType.WEEK: 392 if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) { 393 mActionBar.selectTab(mWeekTab); 394 } 395 frag = new DayFragment(timeMillis, 7); 396 break; 397 case ViewType.MONTH: 398 if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) { 399 mActionBar.selectTab(mMonthTab); 400 } 401 frag = new MonthFragment(false, timeMillis, false); 402 break; 403 case ViewType.EDIT: 404 frag = new EditEventFragment(e, mPreviousView); 405 break; 406 default: 407 throw new IllegalArgumentException( 408 "Must be Agenda, Day, Week, or Month ViewType, not " + viewType); 409 } 410 411 boolean doCommit = false; 412 if (ft == null) { 413 doCommit = true; 414 ft = getFragmentManager().openTransaction(); 415 } 416 417 ft.replace(viewId, frag); 418 419 if (DEBUG) { 420 Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType); 421 } 422 // If the key is already registered this will replace it 423 mController.registerEventHandler(viewId, (EventHandler) frag); 424 425 if (doCommit) { 426 ft.commit(); 427 } 428 } 429 430 private void setTitleInActionBar(EventInfo event) { 431 if (event.eventType != EventType.GO_TO || mActionBar == null) { 432 return; 433 } 434 435 final long start = event.startTime.toMillis(false /* use isDst */); 436 final long end; 437 if (event.endTime != null) { 438 end = event.endTime.toMillis(false /* use isDst */); 439 } else { 440 end = start; 441 } 442 443 final String msg = Utils.formatDateRange(this, start, end, DateUtils.FORMAT_SHOW_DATE); 444 // TODO: add title here. 445 } 446 447 private void updateHomeClock() { 448 String tz = Utils.getTimeZone(this, mHomeTimeUpdater); 449 if (mIsMultipane && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) 450 && !TextUtils.equals(tz, Time.getCurrentTimezone())) { 451 Time time = new Time(tz); 452 time.setToNow(); 453 long millis = time.toMillis(true); 454 boolean isDST = time.isDst != 0; 455 int flags = DateUtils.FORMAT_SHOW_TIME; 456 if (DateFormat.is24HourFormat(this)) { 457 flags |= DateUtils.FORMAT_24HOUR; 458 } 459 // Formats the time as 460 String timeString = 461 (new StringBuilder(Utils.formatDateRange(this, millis, millis, flags))) 462 .append(" ").append(TimeZone.getTimeZone(tz).getDisplayName( 463 isDST, TimeZone.SHORT, Locale.getDefault())) 464 .toString(); 465 mHomeTime.setText(timeString); 466 mHomeTime.setVisibility(View.VISIBLE); 467 // Update when the minute changes 468 mHomeTime.postDelayed( 469 mHomeTimeUpdater, 470 DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS)); 471 } else { 472 mHomeTime.setVisibility(View.GONE); 473 } 474 } 475 476 @Override 477 public long getSupportedEventTypes() { 478 return EventType.GO_TO | EventType.VIEW_EVENT | EventType.EDIT_EVENT 479 | EventType.CREATE_EVENT; 480 } 481 482 @Override 483 public void handleEvent(EventInfo event) { 484 if (event.eventType == EventType.GO_TO) { 485 if (mCurrentView == ViewType.EDIT) { 486 // If we are leaving the edit view ping it so it has a chance to 487 // save if it needs to 488 EventHandler editHandler = (EventHandler) getFragmentManager().findFragmentById( 489 R.id.main_pane); 490 editHandler.handleEvent(event); 491 } 492 493 // Set title bar 494 // TODO: Currently we don't have an appropriate way to add title bar when 495 // ActionBar is in TabNavigation mode. 496 // setTitleInActionBar(event); 497 498 setMainPane(null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), 499 false, event); 500 501 if (!mIsMultipane) { 502 return; 503 } 504 if (event.viewType == ViewType.MONTH) { 505 // hide minimonth and calendar frag 506 findViewById(R.id.mini_month).setVisibility(View.GONE); 507 findViewById(R.id.calendar_list).setVisibility(View.GONE); 508 } else { 509 // show minimonth and calendar frag 510 findViewById(R.id.mini_month).setVisibility(View.VISIBLE); 511 findViewById(R.id.calendar_list).setVisibility(View.VISIBLE); 512 } 513 } else if (event.eventType == EventType.VIEW_EVENT) { 514 EventInfoFragment fragment = new EventInfoFragment( 515 event.id, event.startTime.toMillis(false), event.endTime.toMillis(false)); 516 fragment.setDialogParams(event.x, event.y); 517 fragment.show(getFragmentManager(), "EventInfoFragment"); 518 } else if (event.eventType == EventType.EDIT_EVENT 519 || event.eventType == EventType.CREATE_EVENT) { 520 setMainPane(null, R.id.main_pane, ViewType.EDIT, -1, true, event); 521 // hide minimonth and calendar frag 522 findViewById(R.id.mini_month).setVisibility(View.GONE); 523 findViewById(R.id.calendar_list).setVisibility(View.GONE); 524 } 525 updateHomeClock(); 526 } 527 528 @Override 529 public void eventsChanged() { 530 mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT); 531 } 532 533 @Override 534 public boolean getAllDay() { 535 return false; 536 } 537 538 @Override 539 public long getSelectedTime() { 540 return 0; 541 } 542 543 @Override 544 public void goTo(Time time, boolean animate) { 545 } 546 547 @Override 548 public void goToToday() { 549 } 550 551 @Override 552 public boolean onQueryTextChanged(String newText) { 553 return false; 554 } 555 556 @Override 557 public boolean onSubmitQuery(String query) { 558 mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, query, 559 getComponentName()); 560 return false; 561 } 562 563 @Override 564 public void onTabSelected(Tab tab, FragmentTransaction ft) { 565 if (tab == mDayTab && mCurrentView != ViewType.DAY) { 566 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); 567 } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) { 568 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); 569 } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) { 570 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); 571 } else { 572 Log.w(TAG, "TabSelected event from unknown tab: " + tab); 573 } 574 } 575 576 @Override 577 public void onTabReselected(Tab tab, FragmentTransaction ft) { 578 } 579 580 @Override 581 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 582 } 583} 584