AllInOneActivity.java revision 04ae768077c6164517ecf16eae1d57fe77923dec
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 (mPreviousView == mCurrentView) { 309 super.onBackPressed(); 310 } else { 311 mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView); 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 frag = new DayFragment(timeMillis, 1); 387 break; 388 case ViewType.WEEK: 389 frag = new DayFragment(timeMillis, 7); 390 break; 391 case ViewType.MONTH: 392 frag = new MonthFragment(false, timeMillis, false); 393 break; 394 case ViewType.EDIT: 395 frag = new EditEventFragment(e, mPreviousView); 396 break; 397 default: 398 throw new IllegalArgumentException( 399 "Must be Agenda, Day, Week, or Month ViewType, not " + viewType); 400 } 401 402 boolean doCommit = false; 403 if (ft == null) { 404 doCommit = true; 405 ft = getFragmentManager().openTransaction(); 406 } 407 408 ft.replace(viewId, frag); 409 if (DEBUG) { 410 Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType); 411 } 412 // If the key is already registered this will replace it 413 mController.registerEventHandler(viewId, (EventHandler) frag); 414 415 if (doCommit) { 416 ft.commit(); 417 } 418 } 419 420 private void setTitleInActionBar(EventInfo event) { 421 if (event.eventType != EventType.GO_TO || mActionBar == null) { 422 return; 423 } 424 425 final long start = event.startTime.toMillis(false /* use isDst */); 426 final long end; 427 if (event.endTime != null) { 428 end = event.endTime.toMillis(false /* use isDst */); 429 } else { 430 end = start; 431 } 432 433 final String msg = Utils.formatDateRange(this, start, end, DateUtils.FORMAT_SHOW_DATE); 434 // TODO: add title here. 435 } 436 437 private void updateHomeClock() { 438 String tz = Utils.getTimeZone(this, mHomeTimeUpdater); 439 if (mIsMultipane && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) 440 && !TextUtils.equals(tz, Time.getCurrentTimezone())) { 441 Time time = new Time(tz); 442 time.setToNow(); 443 long millis = time.toMillis(true); 444 boolean isDST = time.isDst != 0; 445 int flags = DateUtils.FORMAT_SHOW_TIME; 446 if (DateFormat.is24HourFormat(this)) { 447 flags |= DateUtils.FORMAT_24HOUR; 448 } 449 // Formats the time as 450 String timeString = 451 (new StringBuilder(Utils.formatDateRange(this, millis, millis, flags))) 452 .append(" ").append(TimeZone.getTimeZone(tz).getDisplayName( 453 isDST, TimeZone.SHORT, Locale.getDefault())) 454 .toString(); 455 mHomeTime.setText(timeString); 456 mHomeTime.setVisibility(View.VISIBLE); 457 // Update when the minute changes 458 mHomeTime.postDelayed( 459 mHomeTimeUpdater, 460 DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS)); 461 } else { 462 mHomeTime.setVisibility(View.GONE); 463 } 464 } 465 466 @Override 467 public long getSupportedEventTypes() { 468 return EventType.GO_TO | EventType.VIEW_EVENT | EventType.EDIT_EVENT 469 | EventType.CREATE_EVENT; 470 } 471 472 @Override 473 public void handleEvent(EventInfo event) { 474 if (event.eventType == EventType.GO_TO) { 475 if (mCurrentView == ViewType.EDIT) { 476 // If we are leaving the edit view ping it so it has a chance to 477 // save if it needs to 478 EventHandler editHandler = (EventHandler) getFragmentManager().findFragmentById( 479 R.id.main_pane); 480 editHandler.handleEvent(event); 481 } 482 483 // Set title bar 484 // TODO: Currently we don't have an appropriate way to add title bar when 485 // ActionBar is in TabNavigation mode. 486 // setTitleInActionBar(event); 487 488 setMainPane(null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), 489 false, event); 490 491 if (!mIsMultipane) { 492 return; 493 } 494 if (event.viewType == ViewType.MONTH) { 495 // hide minimonth and calendar frag 496 findViewById(R.id.mini_month).setVisibility(View.GONE); 497 findViewById(R.id.calendar_list).setVisibility(View.GONE); 498 } else { 499 // show minimonth and calendar frag 500 findViewById(R.id.mini_month).setVisibility(View.VISIBLE); 501 findViewById(R.id.calendar_list).setVisibility(View.VISIBLE); 502 } 503 } else if (event.eventType == EventType.VIEW_EVENT) { 504 EventInfoFragment fragment = new EventInfoFragment( 505 event.id, event.startTime.toMillis(false), event.endTime.toMillis(false)); 506 fragment.setDialogParams(event.x, event.y); 507 fragment.show(getFragmentManager(), "EventInfoFragment"); 508 } else if (event.eventType == EventType.EDIT_EVENT 509 || event.eventType == EventType.CREATE_EVENT) { 510 setMainPane(null, R.id.main_pane, ViewType.EDIT, -1, true, event); 511 // hide minimonth and calendar frag 512 findViewById(R.id.mini_month).setVisibility(View.GONE); 513 findViewById(R.id.calendar_list).setVisibility(View.GONE); 514 } 515 updateHomeClock(); 516 } 517 518 @Override 519 public void eventsChanged() { 520 mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT); 521 } 522 523 @Override 524 public boolean getAllDay() { 525 return false; 526 } 527 528 @Override 529 public long getSelectedTime() { 530 return 0; 531 } 532 533 @Override 534 public void goTo(Time time, boolean animate) { 535 } 536 537 @Override 538 public void goToToday() { 539 } 540 541 @Override 542 public boolean onQueryTextChanged(String newText) { 543 return false; 544 } 545 546 @Override 547 public boolean onSubmitQuery(String query) { 548 mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, query, 549 getComponentName()); 550 return false; 551 } 552 553 @Override 554 public void onTabSelected(Tab tab, FragmentTransaction ft) { 555 if (tab == mDayTab) { 556 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); 557 } else if (tab == mWeekTab) { 558 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); 559 } else if (tab == mMonthTab) { 560 mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); 561 } else { 562 Log.w(TAG, "TabSelected event from unknown tab: " + tab); 563 } 564 } 565 566 @Override 567 public void onTabReselected(Tab tab, FragmentTransaction ft) { 568 } 569 570 @Override 571 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 572 } 573} 574