summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CleanSpec.mk1
-rw-r--r--core/java/android/accounts/ChooseAccountTypeActivity.java13
-rw-r--r--core/java/android/accounts/ChooseTypeAndAccountActivity.java171
-rw-r--r--core/java/android/app/ActivityThread.java25
-rw-r--r--core/java/android/app/ApplicationThreadNative.java12
-rw-r--r--core/java/android/app/IApplicationThread.java8
-rw-r--r--core/java/android/app/SharedPreferencesImpl.java4
-rw-r--r--core/java/android/app/StatusBarManager.java9
-rw-r--r--core/java/android/content/ContentProvider.java23
-rw-r--r--core/java/android/content/ContentProviderNative.java124
-rw-r--r--core/java/android/content/IContentProvider.java10
-rw-r--r--core/java/android/content/Intent.java8
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/database/AbstractCursor.java10
-rw-r--r--core/java/android/database/AbstractWindowedCursor.java28
-rw-r--r--core/java/android/database/BulkCursorNative.java16
-rw-r--r--core/java/android/database/BulkCursorToCursorAdaptor.java95
-rw-r--r--core/java/android/database/CrossProcessCursor.java2
-rw-r--r--core/java/android/database/CursorToBulkCursorAdaptor.java234
-rw-r--r--core/java/android/database/CursorWindow.java59
-rw-r--r--core/java/android/database/IBulkCursor.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java17
-rw-r--r--core/java/android/hardware/Camera.java118
-rw-r--r--core/java/android/net/NetworkPolicy.java13
-rw-r--r--core/java/android/provider/ContactsContract.java53
-rw-r--r--core/java/android/provider/Settings.java12
-rw-r--r--core/java/android/view/Display.java5
-rw-r--r--core/java/android/view/TextureView.java14
-rw-r--r--core/java/android/view/View.java41
-rw-r--r--core/java/android/view/WindowManagerPolicy.java20
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java9
-rw-r--r--core/java/android/webkit/BrowserFrame.java14
-rw-r--r--core/java/android/webkit/WebSettings.java22
-rw-r--r--core/java/android/webkit/WebTextView.java3
-rw-r--r--core/java/android/webkit/WebView.java23
-rw-r--r--core/java/android/webkit/WebViewCore.java7
-rw-r--r--core/java/android/webkit/ZoomManager.java24
-rw-r--r--core/java/android/widget/AbsSeekBar.java6
-rw-r--r--core/java/android/widget/AdapterView.java11
-rw-r--r--core/java/android/widget/FastScroller.java139
-rw-r--r--core/java/android/widget/SpellChecker.java87
-rw-r--r--core/java/android/widget/TextView.java1
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java73
-rw-r--r--core/java/com/android/internal/app/ShutdownThread.java1
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java11
-rw-r--r--core/java/com/android/internal/view/menu/BaseMenuPresenter.java11
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuPresenter.java10
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java2
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java2
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java134
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java19
-rw-r--r--core/java/com/android/internal/widget/TransportControlView.java13
-rw-r--r--core/jni/android_database_CursorWindow.cpp312
-rw-r--r--core/jni/android_database_SQLiteQuery.cpp78
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.pngbin727 -> 663 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_cut_holo_light.pngbin769 -> 564 bytes
-rw-r--r--[-rwxr-xr-x]core/res/res/drawable-hdpi/list_selector_background_disabled.9.pngbin537 -> 231 bytes
-rw-r--r--[-rwxr-xr-x]core/res/res/drawable-hdpi/list_selector_background_focus.9.pngbin6207 -> 2136 bytes
-rw-r--r--[-rwxr-xr-x]core/res/res/drawable-hdpi/list_selector_background_longpress.9.pngbin2311 -> 986 bytes
-rw-r--r--[-rwxr-xr-x]core/res/res/drawable-hdpi/list_selector_background_pressed.9.pngbin5453 -> 1996 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_disabled.pngbin0 -> 1059 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_wifi_in_range.pngbin1474 -> 1241 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_speakerphone.pngbin1097 -> 1084 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.pngbin486 -> 421 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_cut_holo_light.pngbin493 -> 357 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_disabled.9.pngbin213 -> 201 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_focus.9.pngbin2553 -> 1102 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_longpress.9.pngbin1051 -> 571 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_pressed.9.pngbin2230 -> 1030 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_disabled.pngbin0 -> 738 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_wifi_in_range.pngbin993 -> 810 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_speakerphone.pngbin796 -> 754 bytes
-rw-r--r--core/res/res/drawable-nodpi/platlogo.pngbin46426 -> 1672 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.pngbin985 -> 969 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.pngbin1291 -> 996 bytes
-rw-r--r--core/res/res/drawable-xhdpi/list_selector_background_disabled.9.pngbin385 -> 280 bytes
-rw-r--r--core/res/res/drawable-xhdpi/list_selector_background_focus.9.pngbin7902 -> 3160 bytes
-rw-r--r--core/res/res/drawable-xhdpi/list_selector_background_longpress.9.pngbin2698 -> 1209 bytes
-rw-r--r--core/res/res/drawable-xhdpi/list_selector_background_pressed.9.pngbin6676 -> 2837 bytes
-rw-r--r--core/res/res/drawable-xhdpi/stat_notify_disabled.pngbin0 -> 1409 bytes
-rw-r--r--core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.pngbin1979 -> 1679 bytes
-rw-r--r--core/res/res/drawable-xhdpi/stat_sys_speakerphone.pngbin1471 -> 1436 bytes
-rw-r--r--core/res/res/layout/keyguard_transport_control.xml9
-rw-r--r--core/res/res/layout/volume_adjust_item.xml5
-rwxr-xr-xcore/res/res/values/strings.xml22
-rwxr-xr-xdata/sounds/AudioPackage7.mk14
-rw-r--r--data/sounds/effects/ogg/Lock.oggbin16459 -> 4661 bytes
-rw-r--r--data/sounds/effects/ogg/Unlock.oggbin11739 -> 4666 bytes
-rw-r--r--data/sounds/effects/wav/Lock.wavbin106496 -> 11352 bytes
-rw-r--r--data/sounds/effects/wav/Unlock.wavbin109436 -> 11352 bytes
-rw-r--r--data/sounds/notifications/ogg/Acrux.oggbin0 -> 10908 bytes
-rw-r--r--data/sounds/notifications/ogg/Bellatrix.oggbin0 -> 15714 bytes
-rw-r--r--data/sounds/notifications/ogg/Pollux.oggbin0 -> 23463 bytes
-rw-r--r--data/sounds/notifications/ogg/Tejat.oggbin0 -> 15866 bytes
-rw-r--r--data/sounds/ringtones/ogg/Girtab.oggbin0 -> 44376 bytes
-rw-r--r--data/sounds/ringtones/ogg/Zeta.oggbin0 -> 77919 bytes
-rw-r--r--docs/html/guide/appendix/media-formats.jd2
-rw-r--r--docs/html/guide/developing/devices/emulator.jd11
-rw-r--r--docs/html/guide/developing/tools/emulator.jd24
-rw-r--r--docs/html/guide/guide_toc.cs56
-rw-r--r--docs/html/guide/practices/screen-compat-mode.jd6
-rw-r--r--docs/html/guide/topics/graphics/2d-graphics.jd710
-rw-r--r--docs/html/guide/topics/graphics/animation.jd976
-rw-r--r--docs/html/guide/topics/graphics/drawable-animation.jd66
-rw-r--r--docs/html/guide/topics/graphics/hardware-accel.jd522
-rw-r--r--docs/html/guide/topics/graphics/index.jd229
-rw-r--r--docs/html/guide/topics/graphics/opengl.jd81
-rw-r--r--docs/html/guide/topics/graphics/prop-animation.jd953
-rw-r--r--docs/html/guide/topics/graphics/view-animation.jd89
-rw-r--r--docs/html/guide/topics/manifest/activity-element.jd60
-rw-r--r--docs/html/guide/topics/manifest/uses-feature-element.jd116
-rw-r--r--docs/html/guide/topics/media/audio-capture.jd253
-rw-r--r--docs/html/guide/topics/media/camera.jd1055
-rw-r--r--docs/html/guide/topics/media/index.jd987
-rw-r--r--docs/html/guide/topics/media/jetplayer.jd70
-rw-r--r--docs/html/guide/topics/media/mediaplayer.jd747
-rw-r--r--docs/html/guide/topics/resources/runtime-changes.jd177
-rw-r--r--docs/html/sdk/android-3.2.jd6
-rw-r--r--docs/html/sdk/android-4.0.jd27
-rw-r--r--docs/html/sdk/eclipse-adt.jd4
-rw-r--r--docs/html/sdk/index.jd16
-rw-r--r--docs/html/sdk/oem-usb.jd8
-rw-r--r--docs/html/sdk/tools-notes.jd9
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java2
-rw-r--r--include/binder/CursorWindow.h291
-rw-r--r--include/camera/CameraParameters.h50
-rw-r--r--include/surfaceflinger/ISurfaceComposer.h12
-rw-r--r--include/surfaceflinger/SurfaceComposerClient.h1
-rw-r--r--libs/binder/CursorWindow.cpp498
-rw-r--r--libs/binder/Parcel.cpp4
-rw-r--r--libs/camera/CameraParameters.cpp2
-rw-r--r--libs/gui/ISurfaceComposer.cpp55
-rw-r--r--libs/gui/SurfaceComposerClient.cpp39
-rw-r--r--libs/rs/rsContext.cpp8
-rw-r--r--media/java/android/media/RemoteControlClient.java17
-rwxr-xr-xmedia/java/android/media/videoeditor/AudioTrack.java6
-rw-r--r--media/java/android/media/videoeditor/MediaArtistNativeHelper.java3
-rwxr-xr-xmedia/java/android/media/videoeditor/MediaProperties.java2
-rw-r--r--media/libstagefright/AwesomePlayer.cpp10
-rw-r--r--media/libstagefright/NuCachedSource2.cpp58
-rw-r--r--media/libstagefright/include/NuCachedSource2.h12
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp6
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.cpp4
-rw-r--r--media/mtp/MtpServer.cpp7
-rw-r--r--packages/SystemUI/AndroidManifest.xml16
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.pngbin1106 -> 1095 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.pngbin3441 -> 3439 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.pngbin435 -> 431 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_signal_inout.pngbin523 -> 509 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.pngbin424 -> 424 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.pngbin430 -> 418 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.pngbin512 -> 505 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.pngbin417 -> 416 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.pngbin816 -> 806 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.pngbin2060 -> 2072 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.pngbin392 -> 386 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.pngbin441 -> 418 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.pngbin386 -> 378 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.pngbin392 -> 396 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.pngbin448 -> 445 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.pngbin387 -> 385 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid00.pngbin0 -> 622 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid01.pngbin0 -> 574 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid02.pngbin0 -> 615 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid03.pngbin0 -> 574 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid04.pngbin0 -> 576 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid05.pngbin0 -> 568 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid06.pngbin0 -> 614 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid07.pngbin0 -> 566 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid08.pngbin0 -> 610 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid09.pngbin0 -> 566 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid10.pngbin0 -> 560 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/nyandroid11.pngbin0 -> 560 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star0.pngbin0 -> 1087 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star1.pngbin0 -> 1096 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star2.pngbin0 -> 1120 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star3.pngbin0 -> 1127 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star4.pngbin0 -> 1132 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star5.pngbin0 -> 1148 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_in.pngbin463 -> 472 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_inout.pngbin581 -> 573 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_out.pngbin462 -> 460 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_in.pngbin465 -> 458 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_inout.pngbin585 -> 563 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_out.pngbin449 -> 441 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_in.pngbin426 -> 410 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_inout.pngbin491 -> 464 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_out.pngbin408 -> 395 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_in.pngbin428 -> 418 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_inout.pngbin493 -> 492 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_out.pngbin413 -> 411 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_in.pngbin541 -> 514 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_inout.pngbin696 -> 676 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_out.pngbin520 -> 500 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_in.pngbin534 -> 529 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_inout.pngbin703 -> 702 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_out.pngbin515 -> 519 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.pngbin1451 -> 1443 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.pngbin4696 -> 4770 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_in.pngbin478 -> 463 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_inout.pngbin590 -> 571 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_out.pngbin452 -> 453 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_in.pngbin465 -> 479 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_inout.pngbin572 -> 583 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_out.pngbin453 -> 446 bytes
-rw-r--r--packages/SystemUI/res/drawable/nyandroid_anim.xml32
-rw-r--r--packages/SystemUI/res/drawable/star_anim.xml26
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/Nyandroid.java253
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/Choreographer.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java309
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java431
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java38
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java7
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardViewBase.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardViewManager.java11
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java15
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java122
-rw-r--r--policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java8
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java34
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java335
-rw-r--r--services/input/InputDispatcher.cpp29
-rw-r--r--services/input/InputDispatcher.h4
-rw-r--r--services/input/tests/InputDispatcher_test.cpp4
-rw-r--r--services/java/com/android/server/AppWidgetService.java165
-rw-r--r--services/java/com/android/server/BackupManagerService.java9
-rw-r--r--services/java/com/android/server/PowerManagerService.java101
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java9
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/java/com/android/server/am/ActivityStack.java22
-rw-r--r--services/java/com/android/server/am/AppErrorDialog.java4
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java19
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java18
-rw-r--r--services/java/com/android/server/pm/Settings.java5
-rw-r--r--services/java/com/android/server/wm/DragState.java6
-rw-r--r--services/java/com/android/server/wm/InputManager.java2
-rw-r--r--services/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java10
-rw-r--r--services/java/com/android/server/wm/Session.java6
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java147
-rw-r--r--services/java/com/android/server/wm/WindowState.java6
-rw-r--r--services/jni/com_android_server_InputManager.cpp31
-rw-r--r--services/surfaceflinger/Android.mk3
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp9
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h2
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp37
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h4
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java14
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java82
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java12
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java1
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java4
-rw-r--r--test-runner/src/android/test/mock/MockContentProvider.java39
-rw-r--r--test-runner/src/android/test/mock/MockIContentProvider.java9
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java15
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java23
265 files changed, 8353 insertions, 4859 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9381437..fb334fc 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -116,6 +116,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/fonts/DroidSans*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/fonts/DroidSans*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index 448b2c0..acc8549 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -43,7 +43,7 @@ import java.util.Set;
* @hide
*/
public class ChooseAccountTypeActivity extends Activity {
- private static final String TAG = "AccountManager";
+ private static final String TAG = "AccountChooser";
private HashMap<String, AuthInfo> mTypeToAuthenticatorInfo = new HashMap<String, AuthInfo>();
private ArrayList<AuthInfo> mAuthenticatorInfosToDisplay;
@@ -52,6 +52,11 @@ public class ChooseAccountTypeActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseAccountTypeActivity.onCreate(savedInstanceState="
+ + savedInstanceState + ")");
+ }
+
// Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes
Set<String> setOfAllowableAccountTypes = null;
String[] validAccountTypes = getIntent().getStringArrayExtra(
@@ -111,8 +116,10 @@ public class ChooseAccountTypeActivity extends Activity {
Bundle bundle = new Bundle();
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, type);
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
- Log.d(TAG, "ChooseAccountTypeActivity.setResultAndFinish: "
- + "selected account type " + type);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseAccountTypeActivity.setResultAndFinish: "
+ + "selected account type " + type);
+ }
finish();
}
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 8cc2002..5f38eb4 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -47,7 +47,7 @@ import java.util.Set;
*/
public class ChooseTypeAndAccountActivity extends Activity
implements AccountManagerCallback<Bundle> {
- private static final String TAG = "AccountManager";
+ private static final String TAG = "AccountChooser";
/**
* A Parcelable ArrayList of Account objects that limits the choosable accounts to those
@@ -100,13 +100,39 @@ public class ChooseTypeAndAccountActivity extends Activity
public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE =
"descriptionTextOverride";
+ public static final int REQUEST_NULL = 0;
+ public static final int REQUEST_CHOOSE_TYPE = 1;
+ public static final int REQUEST_ADD_ACCOUNT = 2;
+
+ private static final String KEY_INSTANCE_STATE_PENDING_REQUEST = "pendingRequest";
+ private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
+
private ArrayList<AccountInfo> mAccountInfos;
+ private int mPendingRequest = REQUEST_NULL;
+ private Parcelable[] mExistingAccounts = null;
+ private Parcelable[] mSavedAccounts = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState="
+ + savedInstanceState + ")");
+ }
+
setContentView(R.layout.choose_type_and_account);
+ if (savedInstanceState != null) {
+ mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);
+ mSavedAccounts =
+ savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
+ mExistingAccounts = null;
+ } else {
+ mPendingRequest = REQUEST_NULL;
+ mSavedAccounts = null;
+ mExistingAccounts = null;
+ }
+
// save some items we use frequently
final AccountManager accountManager = AccountManager.get(this);
final Intent intent = getIntent();
@@ -171,20 +197,6 @@ public class ChooseTypeAndAccountActivity extends Activity
account.equals(selectedAccount)));
}
- // If there are no allowable accounts go directly to add account
- if (mAccountInfos.isEmpty()) {
- startChooseAccountTypeActivity();
- return;
- }
-
- // if there is only one allowable account return it
- if (!intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false)
- && mAccountInfos.size() == 1) {
- Account account = mAccountInfos.get(0).account;
- setResultAndFinish(account.name, account.type);
- return;
- }
-
// there is more than one allowable account. initialize the list adapter to allow
// the user to select an account.
ListView list = (ListView) findViewById(android.R.id.list);
@@ -204,6 +216,37 @@ public class ChooseTypeAndAccountActivity extends Activity
startChooseAccountTypeActivity();
}
});
+
+ if (mPendingRequest == REQUEST_NULL) {
+ // If there are no allowable accounts go directly to add account
+ if (mAccountInfos.isEmpty()) {
+ startChooseAccountTypeActivity();
+ return;
+ }
+
+ // if there is only one allowable account return it
+ if (!intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false)
+ && mAccountInfos.size() == 1) {
+ Account account = mAccountInfos.get(0).account;
+ setResultAndFinish(account.name, account.type);
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseTypeAndAccountActivity.onDestroy()");
+ }
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(final Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_INSTANCE_STATE_PENDING_REQUEST, mPendingRequest);
+ outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
}
// Called when the choose account type activity (for adding an account) returns.
@@ -212,20 +255,75 @@ public class ChooseTypeAndAccountActivity extends Activity
@Override
protected void onActivityResult(final int requestCode, final int resultCode,
final Intent data) {
- if (resultCode == RESULT_OK && data != null) {
- String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
- if (accountType != null) {
- runAddAccountForAuthenticator(accountType);
- return;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (data != null && data.getExtras() != null) data.getExtras().keySet();
+ Bundle extras = data != null ? data.getExtras() : null;
+ Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult(reqCode=" + requestCode
+ + ", resCode=" + resultCode + ", extras=" + extras + ")");
+ }
+
+ // we got our result, so clear the fact that we had a pending request
+ mPendingRequest = REQUEST_NULL;
+ mExistingAccounts = null;
+
+ if (resultCode == RESULT_CANCELED) {
+ return;
+ }
+
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_CHOOSE_TYPE) {
+ if (data != null) {
+ String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
+ if (accountType != null) {
+ runAddAccountForAuthenticator(accountType);
+ return;
+ }
+ }
+ Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find account "
+ + "type, pretending the request was canceled");
+ } else if (requestCode == REQUEST_ADD_ACCOUNT) {
+ String accountName = null;
+ String accountType = null;
+
+ if (data != null) {
+ accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+ accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
+ }
+
+ if (accountName == null || accountType == null) {
+ Account[] currentAccounts = AccountManager.get(this).getAccounts();
+ Set<Account> preExistingAccounts = new HashSet<Account>();
+ for (Parcelable accountParcel : mSavedAccounts) {
+ preExistingAccounts.add((Account) accountParcel);
+ }
+ for (Account account : currentAccounts) {
+ if (!preExistingAccounts.contains(account)) {
+ accountName = account.name;
+ accountType = account.type;
+ break;
+ }
+ }
+ }
+
+ if (accountName != null || accountType != null) {
+ setResultAndFinish(accountName, accountType);
+ return;
+ }
}
+ Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find added "
+ + "account, pretending the request was canceled");
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled");
}
- Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled");
setResult(Activity.RESULT_CANCELED);
finish();
}
protected void runAddAccountForAuthenticator(String type) {
- Log.d(TAG, "selected account type " + type);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "runAddAccountForAuthenticator: " + type);
+ }
final Bundle options = getIntent().getBundleExtra(
ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);
final String[] requiredFeatures = getIntent().getStringArrayExtra(
@@ -233,20 +331,19 @@ public class ChooseTypeAndAccountActivity extends Activity
final String authTokenType = getIntent().getStringExtra(
ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING);
AccountManager.get(this).addAccount(type, authTokenType, requiredFeatures,
- options, this, this, null /* Handler */);
+ options, null /* activity */, this /* callback */, null /* Handler */);
}
public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {
try {
final Bundle accountManagerResult = accountManagerFuture.getResult();
- final String name = accountManagerResult.getString(AccountManager.KEY_ACCOUNT_NAME);
- final String type = accountManagerResult.getString(AccountManager.KEY_ACCOUNT_TYPE);
- if (name != null && type != null) {
- final Bundle bundle = new Bundle();
- bundle.putString(AccountManager.KEY_ACCOUNT_NAME, name);
- bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, type);
- setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
- finish();
+ final Intent intent = (Intent)accountManagerResult.getParcelable(
+ AccountManager.KEY_INTENT);
+ if (intent != null) {
+ mPendingRequest = REQUEST_ADD_ACCOUNT;
+ mExistingAccounts = AccountManager.get(this).getAccounts();
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
return;
}
} catch (OperationCanceledException e) {
@@ -297,12 +394,17 @@ public class ChooseTypeAndAccountActivity extends Activity
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
- Log.d(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
- + "selected account " + accountName + ", " + accountType);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
+ + "selected account " + accountName + ", " + accountType);
+ }
finish();
}
private void startChooseAccountTypeActivity() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "ChooseAccountTypeActivity.startChooseAccountTypeActivity()");
+ }
final Intent intent = new Intent(this, ChooseAccountTypeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
@@ -313,7 +415,8 @@ public class ChooseTypeAndAccountActivity extends Activity
getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY));
intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
getIntent().getStringExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING));
- startActivityForResult(intent, 0);
+ startActivityForResult(intent, REQUEST_CHOOSE_TYPE);
+ mPendingRequest = REQUEST_CHOOSE_TYPE;
}
private static class AccountInfo {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d2facdc..8afe9bf 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -490,6 +490,15 @@ public final class ActivityThread {
// Formatting for checkin service - update version if row format changes
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
+ private void updatePendingConfiguration(Configuration config) {
+ synchronized (mPackages) {
+ if (mPendingConfiguration == null ||
+ mPendingConfiguration.isOtherSeqNewer(config)) {
+ mPendingConfiguration = config;
+ }
+ }
+ }
+
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges) {
queueOrSendMessage(
@@ -530,8 +539,8 @@ public final class ActivityThread {
// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
- List<ResultInfo> pendingResults,
+ ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+ Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
ActivityClientRecord r = new ActivityClientRecord();
@@ -553,6 +562,8 @@ public final class ActivityThread {
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;
+ updatePendingConfiguration(curConfig);
+
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}
@@ -697,12 +708,7 @@ public final class ActivityThread {
}
public void scheduleConfigurationChanged(Configuration config) {
- synchronized (mPackages) {
- if (mPendingConfiguration == null ||
- mPendingConfiguration.isOtherSeqNewer(config)) {
- mPendingConfiguration = config;
- }
- }
+ updatePendingConfiguration(config);
queueOrSendMessage(H.CONFIGURATION_CHANGED, config);
}
@@ -1966,6 +1972,9 @@ public final class ActivityThread {
mProfiler.autoStopProfiler = r.autoStopProfiler;
}
+ // Make sure we are running with the most recent config.
+ handleConfigurationChanged(null, null);
+
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
Activity a = performLaunchActivity(r, customIntent);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index cde06cd..c4a4fea 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -132,6 +132,7 @@ public abstract class ApplicationThreadNative extends Binder
IBinder b = data.readStrongBinder();
int ident = data.readInt();
ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+ Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
Bundle state = data.readBundle();
List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
@@ -142,7 +143,7 @@ public abstract class ApplicationThreadNative extends Binder
ParcelFileDescriptor profileFd = data.readInt() != 0
? data.readFileDescriptor() : null;
boolean autoStopProfiler = data.readInt() != 0;
- scheduleLaunchActivity(intent, b, ident, info, compatInfo, state, ri, pi,
+ scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, state, ri, pi,
notResumed, isForward, profileName, profileFd, autoStopProfiler);
return true;
}
@@ -630,10 +631,10 @@ class ApplicationThreadProxy implements IApplicationThread {
}
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
- List<ResultInfo> pendingResults,
- List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
- String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
+ ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+ Bundle state, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+ String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -641,6 +642,7 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
+ curConfig.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
data.writeBundle(state);
data.writeTypedList(pendingResults);
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 5d200b4..1253fe7 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -53,10 +53,10 @@ public interface IApplicationThread extends IInterface {
void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
- List<ResultInfo> pendingResults,
- List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
- String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
+ ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+ Bundle state, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+ String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
throws RemoteException;
void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, int configChanges,
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 8aee65c..615e8ce 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -29,6 +29,7 @@ import dalvik.system.BlockGuard;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -107,7 +108,8 @@ final class SharedPreferencesImpl implements SharedPreferences {
FileStatus stat = new FileStatus();
if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {
try {
- FileInputStream str = new FileInputStream(mFile);
+ BufferedInputStream str = new BufferedInputStream(
+ new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);
str.close();
} catch (XmlPullParserException e) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index ca64c88..c5ee48d 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -40,15 +40,20 @@ public class StatusBarManager {
public static final int DISABLE_NOTIFICATION_TICKER
= View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
- public static final int DISABLE_NAVIGATION = View.STATUS_BAR_DISABLE_NAVIGATION;
+ public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
+ public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
+ @Deprecated
+ public static final int DISABLE_NAVIGATION =
+ View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
+
public static final int DISABLE_NONE = 0x00000000;
public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS
| DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER
- | DISABLE_SYSTEM_INFO| DISABLE_NAVIGATION | DISABLE_BACK | DISABLE_CLOCK;
+ | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK;
private Context mContext;
private IStatusBarService mService;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 8057d4b..e452f1f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -22,10 +22,6 @@ import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
-import android.database.CursorToBulkCursorAdaptor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
@@ -168,22 +164,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
return ContentProvider.this;
}
- /**
- * Remote version of a query, which returns an IBulkCursor. The bulk
- * cursor should be wrapped with BulkCursorToCursorAdaptor before use.
- */
- public IBulkCursor bulkQuery(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- IContentObserver observer, CursorWindow window) {
- enforceReadPermission(uri);
- Cursor cursor = ContentProvider.this.query(uri, projection,
- selection, selectionArgs, sortOrder);
- if (cursor == null) {
- return null;
- }
- return new CursorToBulkCursorAdaptor(cursor, observer,
- ContentProvider.this.getClass().getName(),
- hasWritePermission(uri), window);
+ @Override
+ public String getProviderName() {
+ return getContentProvider().getClass().getName();
}
public Cursor query(Uri uri, String[] projection,
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 9a20951..b089bf2 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -20,6 +20,7 @@ import android.content.res.AssetFileDescriptor;
import android.database.BulkCursorNative;
import android.database.BulkCursorToCursorAdaptor;
import android.database.Cursor;
+import android.database.CursorToBulkCursorAdaptor;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
import android.database.IBulkCursor;
@@ -65,6 +66,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return new ContentProviderProxy(obj);
}
+ /**
+ * Gets the name of the content provider.
+ * Should probably be part of the {@link IContentProvider} interface.
+ * @return The content provider name.
+ */
+ public abstract String getProviderName();
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -98,33 +106,24 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
}
String sortOrder = data.readString();
- IContentObserver observer = IContentObserver.Stub.
- asInterface(data.readStrongBinder());
- CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
-
- // Flag for whether caller wants the number of
- // rows in the cursor and the position of the
- // "_id" column index (or -1 if non-existent)
- // Only to be returned if binder != null.
- boolean wantsCursorMetadata = data.readInt() != 0;
-
- IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
- selectionArgs, sortOrder, observer, window);
- if (bulkCursor != null) {
- final IBinder binder = bulkCursor.asBinder();
- if (wantsCursorMetadata) {
- final int count = bulkCursor.count();
- final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
- bulkCursor.getColumnNames());
-
- reply.writeNoException();
- reply.writeStrongBinder(binder);
- reply.writeInt(count);
- reply.writeInt(index);
- } else {
- reply.writeNoException();
- reply.writeStrongBinder(binder);
- }
+ IContentObserver observer = IContentObserver.Stub.asInterface(
+ data.readStrongBinder());
+
+ Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder);
+ if (cursor != null) {
+ CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
+ cursor, observer, getProviderName());
+ final IBinder binder = adaptor.asBinder();
+ final int count = adaptor.count();
+ final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
+ adaptor.getColumnNames());
+ final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls();
+
+ reply.writeNoException();
+ reply.writeStrongBinder(binder);
+ reply.writeInt(count);
+ reply.writeInt(index);
+ reply.writeInt(wantsAllOnMoveCalls ? 1 : 0);
} else {
reply.writeNoException();
reply.writeStrongBinder(null);
@@ -324,12 +323,9 @@ final class ContentProviderProxy implements IContentProvider
return mRemote;
}
- // Like bulkQuery() but sets up provided 'adaptor' if not null.
- private IBulkCursor bulkQueryInternal(
- Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- IContentObserver observer, CursorWindow window,
- BulkCursorToCursorAdaptor adaptor) throws RemoteException {
+ public Cursor query(Uri url, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) throws RemoteException {
+ BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -355,63 +351,35 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(selectionArgs[i]);
}
data.writeString(sortOrder);
- data.writeStrongBinder(observer.asBinder());
- window.writeToParcel(data, 0);
-
- // Flag for whether or not we want the number of rows in the
- // cursor and the position of the "_id" column index (or -1 if
- // non-existent). Only to be returned if binder != null.
- final boolean wantsCursorMetadata = (adaptor != null);
- data.writeInt(wantsCursorMetadata ? 1 : 0);
+ data.writeStrongBinder(adaptor.getObserver().asBinder());
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
- IBulkCursor bulkCursor = null;
- IBinder bulkCursorBinder = reply.readStrongBinder();
- if (bulkCursorBinder != null) {
- bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
-
- if (wantsCursorMetadata) {
- int rowCount = reply.readInt();
- int idColumnPosition = reply.readInt();
- if (bulkCursor != null) {
- adaptor.set(bulkCursor, rowCount, idColumnPosition);
- }
- }
+ IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
+ if (bulkCursor != null) {
+ int rowCount = reply.readInt();
+ int idColumnPosition = reply.readInt();
+ boolean wantsAllOnMoveCalls = reply.readInt() != 0;
+ adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls);
+ } else {
+ adaptor.close();
+ adaptor = null;
}
- return bulkCursor;
+ return adaptor;
+ } catch (RemoteException ex) {
+ adaptor.close();
+ throw ex;
+ } catch (RuntimeException ex) {
+ adaptor.close();
+ throw ex;
} finally {
data.recycle();
reply.recycle();
}
}
- public IBulkCursor bulkQuery(Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
- CursorWindow window) throws RemoteException {
- return bulkQueryInternal(
- url, projection, selection, selectionArgs, sortOrder,
- observer, window,
- null /* BulkCursorToCursorAdaptor */);
- }
-
- public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) throws RemoteException {
- //TODO make a pool of windows so we can reuse memory dealers
- CursorWindow window = new CursorWindow(false /* window will be used remotely */);
- BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
- IBulkCursor bulkCursor = bulkQueryInternal(
- url, projection, selection, selectionArgs, sortOrder,
- adaptor.getObserver(), window,
- adaptor);
- if (bulkCursor == null) {
- return null;
- }
- return adaptor;
- }
-
public String getType(Uri url) throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 72bc9c2..2a67ff8 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -18,9 +18,6 @@ package android.content;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -36,13 +33,6 @@ import java.util.ArrayList;
* @hide
*/
public interface IContentProvider extends IInterface {
- /**
- * @hide - hide this because return type IBulkCursor and parameter
- * IContentObserver are system private classes.
- */
- public IBulkCursor bulkQuery(Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
- CursorWindow window) throws RemoteException;
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException;
public String getType(Uri url) throws RemoteException;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2be5153..45a42e4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -57,8 +57,8 @@ import java.util.Set;
*
* <p>An Intent provides a facility for performing late runtime binding between the code in
* different applications. Its most significant use is in the launching of activities, where it
- * can be thought of as the glue between activities. It is basically a passive data structure
- * holding an abstract description of an action to be performed.</p>
+ * can be thought of as the glue between activities. It is basically a passive data structure
+ * holding an abstract description of an action to be performed.</p>
*
* <div class="special reference">
* <h3>Developer Guides</h3>
@@ -2566,7 +2566,7 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
-
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -5291,7 +5291,7 @@ public class Intent implements Parcelable, Cloneable {
if (r != null) {
mSourceBounds = new Rect(r);
} else {
- r = null;
+ mSourceBounds = null;
}
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a3bcc28..decb974 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -362,4 +362,6 @@ interface IPackageManager {
void verifyPendingInstall(int id, int verificationCode);
VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+ boolean isFirstBoot();
}
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 3f23b89..ee6aec6 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -78,13 +78,11 @@ public abstract class AbstractCursor implements CrossProcessCursor {
}
public void deactivate() {
- deactivateInternal();
+ onDeactivateOrClose();
}
- /**
- * @hide
- */
- public void deactivateInternal() {
+ /** @hide */
+ protected void onDeactivateOrClose() {
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
mSelfObserverRegistered = false;
@@ -108,7 +106,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
public void close() {
mClosed = true;
mContentObservable.unregisterAll();
- deactivateInternal();
+ onDeactivateOrClose();
}
/**
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index bfc8123..d0aedd2 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -19,6 +19,11 @@ package android.database;
/**
* A base class for Cursors that store their data in {@link CursorWindow}s.
* <p>
+ * The cursor owns the cursor window it uses. When the cursor is closed,
+ * its window is also closed. Likewise, when the window used by the cursor is
+ * changed, its old window is closed. This policy of strict ownership ensures
+ * that cursor windows are not leaked.
+ * </p><p>
* Subclasses are responsible for filling the cursor window with data during
* {@link #onMove(int, int)}, allocating a new cursor window if necessary.
* During {@link #requery()}, the existing cursor window should be cleared and
@@ -180,4 +185,27 @@ public abstract class AbstractWindowedCursor extends AbstractCursor {
mWindow = null;
}
}
+
+ /**
+ * If there is a window, clear it.
+ * Otherwise, creates a local window.
+ *
+ * @param name The window name.
+ * @hide
+ */
+ protected void clearOrCreateLocalWindow(String name) {
+ if (mWindow == null) {
+ // If there isn't a window set already it will only be accessed locally
+ mWindow = new CursorWindow(name, true /* the window is local only */);
+ } else {
+ mWindow.clear();
+ }
+ }
+
+ /** @hide */
+ @Override
+ protected void onDeactivateOrClose() {
+ super.onDeactivateOrClose();
+ closeWindow();
+ }
}
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 9925a9a..20a9c67 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -62,13 +62,13 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor
data.enforceInterface(IBulkCursor.descriptor);
int startPos = data.readInt();
CursorWindow window = getWindow(startPos);
+ reply.writeNoException();
if (window == null) {
reply.writeInt(0);
- return true;
+ } else {
+ reply.writeInt(1);
+ window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
- reply.writeNoException();
- reply.writeInt(1);
- window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
}
@@ -109,9 +109,8 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor
case REQUERY_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
IContentObserver observer =
- IContentObserver.Stub.asInterface(data.readStrongBinder());
- CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
- int count = requery(observer, window);
+ IContentObserver.Stub.asInterface(data.readStrongBinder());
+ int count = requery(observer);
reply.writeNoException();
reply.writeInt(count);
reply.writeBundle(getExtras());
@@ -294,13 +293,12 @@ final class BulkCursorProxy implements IBulkCursor {
}
}
- public int requery(IContentObserver observer, CursorWindow window) throws RemoteException {
+ public int requery(IContentObserver observer) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IBulkCursor.descriptor);
data.writeStrongInterface(observer);
- window.writeToParcel(data, 0);
boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java
index 9c1b26d..885046b 100644
--- a/core/java/android/database/BulkCursorToCursorAdaptor.java
+++ b/core/java/android/database/BulkCursorToCursorAdaptor.java
@@ -21,44 +21,30 @@ import android.os.RemoteException;
import android.util.Log;
/**
- * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
- * process.
+ * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local process.
*
* {@hide}
*/
public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
private static final String TAG = "BulkCursor";
- private SelfContentObserver mObserverBridge;
+ private SelfContentObserver mObserverBridge = new SelfContentObserver(this);
private IBulkCursor mBulkCursor;
private int mCount;
private String[] mColumns;
private boolean mWantsAllOnMoveCalls;
- public void set(IBulkCursor bulkCursor) {
- mBulkCursor = bulkCursor;
-
- try {
- mCount = mBulkCursor.count();
- mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
-
- // Search for the rowID column index and set it for our parent
- mColumns = mBulkCursor.getColumnNames();
- mRowIdColumnIndex = findRowIdColumnIndex(mColumns);
- } catch (RemoteException ex) {
- Log.e(TAG, "Setup failed because the remote process is dead");
- }
- }
-
/**
- * Version of set() that does fewer Binder calls if the caller
- * already knows BulkCursorToCursorAdaptor's properties.
+ * Initializes the adaptor.
+ * Must be called before first use.
*/
- public void set(IBulkCursor bulkCursor, int count, int idIndex) {
+ public void initialize(IBulkCursor bulkCursor, int count, int idIndex,
+ boolean wantsAllOnMoveCalls) {
mBulkCursor = bulkCursor;
mColumns = null; // lazily retrieved
mCount = count;
mRowIdColumnIndex = idIndex;
+ mWantsAllOnMoveCalls = wantsAllOnMoveCalls;
}
/**
@@ -80,31 +66,34 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
*
* @return A SelfContentObserver hooked up to this Cursor
*/
- public synchronized IContentObserver getObserver() {
- if (mObserverBridge == null) {
- mObserverBridge = new SelfContentObserver(this);
- }
+ public IContentObserver getObserver() {
return mObserverBridge.getContentObserver();
}
+ private void throwIfCursorIsClosed() {
+ if (mBulkCursor == null) {
+ throw new StaleDataException("Attempted to access a cursor after it has been closed.");
+ }
+ }
+
@Override
public int getCount() {
+ throwIfCursorIsClosed();
return mCount;
}
@Override
public boolean onMove(int oldPosition, int newPosition) {
+ throwIfCursorIsClosed();
+
try {
// Make sure we have the proper window
- if (mWindow != null) {
- if (newPosition < mWindow.getStartPosition() ||
- newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
- mWindow = mBulkCursor.getWindow(newPosition);
- } else if (mWantsAllOnMoveCalls) {
- mBulkCursor.onMove(newPosition);
- }
- } else {
- mWindow = mBulkCursor.getWindow(newPosition);
+ if (mWindow == null
+ || newPosition < mWindow.getStartPosition()
+ || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {
+ setWindow(mBulkCursor.getWindow(newPosition));
+ } else if (mWantsAllOnMoveCalls) {
+ mBulkCursor.onMove(newPosition);
}
} catch (RemoteException ex) {
// We tried to get a window and failed
@@ -126,32 +115,36 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
// which is what actually makes the data set invalid.
super.deactivate();
- try {
- mBulkCursor.deactivate();
- } catch (RemoteException ex) {
- Log.w(TAG, "Remote process exception when deactivating");
+ if (mBulkCursor != null) {
+ try {
+ mBulkCursor.deactivate();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Remote process exception when deactivating");
+ }
}
- mWindow = null;
}
@Override
public void close() {
super.close();
- try {
- mBulkCursor.close();
- } catch (RemoteException ex) {
- Log.w(TAG, "Remote process exception when closing");
+
+ if (mBulkCursor != null) {
+ try {
+ mBulkCursor.close();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Remote process exception when closing");
+ } finally {
+ mBulkCursor = null;
+ }
}
- mWindow = null;
}
@Override
public boolean requery() {
+ throwIfCursorIsClosed();
+
try {
- int oldCount = mCount;
- //TODO get the window from a pool somewhere to avoid creating the memory dealer
- mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
- false /* the window will be accessed across processes */));
+ mCount = mBulkCursor.requery(getObserver());
if (mCount != -1) {
mPos = -1;
closeWindow();
@@ -174,6 +167,8 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
@Override
public String[] getColumnNames() {
+ throwIfCursorIsClosed();
+
if (mColumns == null) {
try {
mColumns = mBulkCursor.getColumnNames();
@@ -187,6 +182,8 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
@Override
public Bundle getExtras() {
+ throwIfCursorIsClosed();
+
try {
return mBulkCursor.getExtras();
} catch (RemoteException e) {
@@ -198,6 +195,8 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
@Override
public Bundle respond(Bundle extras) {
+ throwIfCursorIsClosed();
+
try {
return mBulkCursor.respond(extras);
} catch (RemoteException e) {
diff --git a/core/java/android/database/CrossProcessCursor.java b/core/java/android/database/CrossProcessCursor.java
index 77ba3a5..8e6a5aa 100644
--- a/core/java/android/database/CrossProcessCursor.java
+++ b/core/java/android/database/CrossProcessCursor.java
@@ -16,7 +16,7 @@
package android.database;
-public interface CrossProcessCursor extends Cursor{
+public interface CrossProcessCursor extends Cursor {
/**
* returns a pre-filled window, return NULL if no such window
*/
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 8fa4d3b..dd2c9b7 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -24,19 +24,38 @@ import android.util.Log;
/**
* Wraps a BulkCursor around an existing Cursor making it remotable.
+ * <p>
+ * If the wrapped cursor is a {@link AbstractWindowedCursor} then it owns
+ * the cursor window. Otherwise, the adaptor takes ownership of the
+ * cursor itself and ensures it gets closed as needed during deactivation
+ * and requeries.
+ * </p>
*
* {@hide}
*/
public final class CursorToBulkCursorAdaptor extends BulkCursorNative
implements IBinder.DeathRecipient {
private static final String TAG = "Cursor";
- private final CrossProcessCursor mCursor;
- private CursorWindow mWindow;
+
+ private final Object mLock = new Object();
private final String mProviderName;
private ContentObserverProxy mObserver;
- private static final class ContentObserverProxy extends ContentObserver
- {
+ /**
+ * The cursor that is being adapted.
+ * This field is set to null when the cursor is closed.
+ */
+ private CrossProcessCursor mCursor;
+
+ /**
+ * The cursor window used by the cross process cursor.
+ * This field is always null for abstract windowed cursors since they are responsible
+ * for managing the lifetime of their window.
+ */
+ private CursorWindow mWindowForNonWindowedCursor;
+ private boolean mWindowForNonWindowedCursorWasFilled;
+
+ private static final class ContentObserverProxy extends ContentObserver {
protected IContentObserver mRemote;
public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
@@ -69,102 +88,171 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
}
}
- public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
- boolean allowWrite, CursorWindow window) {
+ public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer,
+ String providerName) {
try {
mCursor = (CrossProcessCursor) cursor;
- if (mCursor instanceof AbstractWindowedCursor) {
- AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
- if (windowedCursor.hasWindow()) {
- if (Log.isLoggable(TAG, Log.VERBOSE) || false) {
- Log.v(TAG, "Cross process cursor has a local window before setWindow in "
- + providerName, new RuntimeException());
- }
- }
- windowedCursor.setWindow(window);
- } else {
- mWindow = window;
- mCursor.fillWindow(0, window);
- }
} catch (ClassCastException e) {
- // TODO Implement this case.
throw new UnsupportedOperationException(
"Only CrossProcessCursor cursors are supported across process for now", e);
}
mProviderName = providerName;
- createAndRegisterObserverProxy(observer);
+ synchronized (mLock) {
+ createAndRegisterObserverProxyLocked(observer);
+ }
+ }
+
+ private void closeWindowForNonWindowedCursorLocked() {
+ if (mWindowForNonWindowedCursor != null) {
+ mWindowForNonWindowedCursor.close();
+ mWindowForNonWindowedCursor = null;
+ mWindowForNonWindowedCursorWasFilled = false;
+ }
}
-
+
+ private void disposeLocked() {
+ if (mCursor != null) {
+ unregisterObserverProxyLocked();
+ mCursor.close();
+ mCursor = null;
+ }
+
+ closeWindowForNonWindowedCursorLocked();
+ }
+
+ private void throwIfCursorIsClosed() {
+ if (mCursor == null) {
+ throw new StaleDataException("Attempted to access a cursor after it has been closed.");
+ }
+ }
+
+ @Override
public void binderDied() {
- mCursor.close();
- if (mWindow != null) {
- mWindow.close();
+ synchronized (mLock) {
+ disposeLocked();
}
}
-
+
+ @Override
public CursorWindow getWindow(int startPos) {
- mCursor.moveToPosition(startPos);
-
- if (mWindow != null) {
- if (startPos < mWindow.getStartPosition() ||
- startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
- mCursor.fillWindow(startPos, mWindow);
- }
- return mWindow;
- } else {
- return ((AbstractWindowedCursor)mCursor).getWindow();
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ CursorWindow window;
+ if (mCursor instanceof AbstractWindowedCursor) {
+ AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor)mCursor;
+ window = windowedCursor.getWindow();
+ if (window == null) {
+ window = new CursorWindow(mProviderName, false /*localOnly*/);
+ windowedCursor.setWindow(window);
+ }
+
+ mCursor.moveToPosition(startPos);
+ } else {
+ window = mWindowForNonWindowedCursor;
+ if (window == null) {
+ window = new CursorWindow(mProviderName, false /*localOnly*/);
+ mWindowForNonWindowedCursor = window;
+ }
+
+ mCursor.moveToPosition(startPos);
+
+ if (!mWindowForNonWindowedCursorWasFilled
+ || startPos < window.getStartPosition()
+ || startPos >= window.getStartPosition() + window.getNumRows()) {
+ mCursor.fillWindow(startPos, window);
+ mWindowForNonWindowedCursorWasFilled = true;
+ }
+ }
+
+ // Acquire a reference before returning from this RPC.
+ // The Binder proxy will decrement the reference count again as part of writing
+ // the CursorWindow to the reply parcel as a return value.
+ if (window != null) {
+ window.acquireReference();
+ }
+ return window;
}
}
+ @Override
public void onMove(int position) {
- mCursor.onMove(mCursor.getPosition(), position);
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ mCursor.onMove(mCursor.getPosition(), position);
+ }
}
+ @Override
public int count() {
- return mCursor.getCount();
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.getCount();
+ }
}
+ @Override
public String[] getColumnNames() {
- return mCursor.getColumnNames();
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.getColumnNames();
+ }
}
+ @Override
public void deactivate() {
- maybeUnregisterObserverProxy();
- mCursor.deactivate();
+ synchronized (mLock) {
+ if (mCursor != null) {
+ unregisterObserverProxyLocked();
+ mCursor.deactivate();
+ }
+
+ closeWindowForNonWindowedCursorLocked();
+ }
}
+ @Override
public void close() {
- maybeUnregisterObserverProxy();
- mCursor.close();
+ synchronized (mLock) {
+ disposeLocked();
+ }
}
- public int requery(IContentObserver observer, CursorWindow window) {
- if (mWindow == null) {
- ((AbstractWindowedCursor)mCursor).setWindow(window);
- }
- try {
- if (!mCursor.requery()) {
- return -1;
+ @Override
+ public int requery(IContentObserver observer) {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ closeWindowForNonWindowedCursorLocked();
+
+ try {
+ if (!mCursor.requery()) {
+ return -1;
+ }
+ } catch (IllegalStateException e) {
+ IllegalStateException leakProgram = new IllegalStateException(
+ mProviderName + " Requery misuse db, mCursor isClosed:" +
+ mCursor.isClosed(), e);
+ throw leakProgram;
}
- } catch (IllegalStateException e) {
- IllegalStateException leakProgram = new IllegalStateException(
- mProviderName + " Requery misuse db, mCursor isClosed:" +
- mCursor.isClosed(), e);
- throw leakProgram;
- }
-
- if (mWindow != null) {
- mCursor.fillWindow(0, window);
- mWindow = window;
+
+ unregisterObserverProxyLocked();
+ createAndRegisterObserverProxyLocked(observer);
+ return mCursor.getCount();
}
- maybeUnregisterObserverProxy();
- createAndRegisterObserverProxy(observer);
- return mCursor.getCount();
}
+ @Override
public boolean getWantsAllOnMoveCalls() {
- return mCursor.getWantsAllOnMoveCalls();
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.getWantsAllOnMoveCalls();
+ }
}
/**
@@ -173,7 +261,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
* @param observer the IContentObserver that wants to monitor the cursor
* @throws IllegalStateException if an observer is already registered
*/
- private void createAndRegisterObserverProxy(IContentObserver observer) {
+ private void createAndRegisterObserverProxyLocked(IContentObserver observer) {
if (mObserver != null) {
throw new IllegalStateException("an observer is already registered");
}
@@ -182,7 +270,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
}
/** Unregister the observer if it is already registered. */
- private void maybeUnregisterObserverProxy() {
+ private void unregisterObserverProxyLocked() {
if (mObserver != null) {
mCursor.unregisterContentObserver(mObserver);
mObserver.unlinkToDeath(this);
@@ -190,11 +278,21 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
}
}
+ @Override
public Bundle getExtras() {
- return mCursor.getExtras();
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.getExtras();
+ }
}
+ @Override
public Bundle respond(Bundle extras) {
- return mCursor.respond(extras);
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ return mCursor.respond(extras);
+ }
}
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 2e3ef28..a18a721 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -16,11 +16,12 @@
package android.database;
+import dalvik.system.CloseGuard;
+
import android.content.res.Resources;
import android.database.sqlite.SQLiteClosable;
import android.database.sqlite.SQLiteException;
import android.os.Binder;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -29,6 +30,13 @@ import android.util.SparseIntArray;
/**
* A buffer containing multiple cursor rows.
+ * <p>
+ * A {@link CursorWindow} is read-write when created and used locally. When sent
+ * to a remote process (by writing it to a {@link Parcel}), the remote process
+ * receives a read-only view of the cursor window. Typically the cursor window
+ * will be allocated by the producer, filled with data, and then sent to the
+ * consumer for reading.
+ * </p>
*/
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
@@ -48,10 +56,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
private int mStartPos;
- private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly);
- private static native int nativeInitializeFromBinder(IBinder nativeBinder);
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private static native int nativeCreate(String name,
+ int cursorWindowSize, boolean localOnly);
+ private static native int nativeCreateFromParcel(Parcel parcel);
private static native void nativeDispose(int windowPtr);
- private static native IBinder nativeGetBinder(int windowPtr);
+ private static native void nativeWriteToParcel(int windowPtr, Parcel parcel);
private static native void nativeClear(int windowPtr);
@@ -75,38 +86,59 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
private static native boolean nativePutNull(int windowPtr, int row, int column);
/**
- * Creates a new empty cursor window.
+ * Creates a new empty cursor window and gives it a name.
* <p>
* The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to
* set the number of columns before adding any rows to the cursor.
* </p>
*
+ * @param name The name of the cursor window, or null if none.
* @param localWindow True if this window will be used in this process only,
* false if it might be sent to another processes.
+ *
+ * @hide
*/
- public CursorWindow(boolean localWindow) {
+ public CursorWindow(String name, boolean localWindow) {
mStartPos = 0;
- mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow);
+ mWindowPtr = nativeCreate(name, sCursorWindowSize, localWindow);
if (mWindowPtr == 0) {
throw new CursorWindowAllocationException("Cursor window allocation of " +
(sCursorWindowSize / 1024) + " kb failed. " + printStats());
}
+ mCloseGuard.open("close");
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
}
+ /**
+ * Creates a new empty cursor window.
+ * <p>
+ * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to
+ * set the number of columns before adding any rows to the cursor.
+ * </p>
+ *
+ * @param localWindow True if this window will be used in this process only,
+ * false if it might be sent to another processes.
+ */
+ public CursorWindow(boolean localWindow) {
+ this(null, localWindow);
+ }
+
private CursorWindow(Parcel source) {
- IBinder binder = source.readStrongBinder();
mStartPos = source.readInt();
- mWindowPtr = nativeInitializeFromBinder(binder);
+ mWindowPtr = nativeCreateFromParcel(source);
if (mWindowPtr == 0) {
throw new CursorWindowAllocationException("Cursor window could not be "
+ "created from binder.");
}
+ mCloseGuard.open("close");
}
@Override
protected void finalize() throws Throwable {
try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
dispose();
} finally {
super.finalize();
@@ -114,6 +146,9 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
private void dispose() {
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
if (mWindowPtr != 0) {
recordClosingOfWindow(mWindowPtr);
nativeDispose(mWindowPtr);
@@ -675,8 +710,12 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(nativeGetBinder(mWindowPtr));
dest.writeInt(mStartPos);
+ nativeWriteToParcel(mWindowPtr, dest);
+
+ if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+ releaseReference();
+ }
}
@Override
diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java
index 244c88f..7c96797 100644
--- a/core/java/android/database/IBulkCursor.java
+++ b/core/java/android/database/IBulkCursor.java
@@ -56,7 +56,7 @@ public interface IBulkCursor extends IInterface {
public void close() throws RemoteException;
- public int requery(IContentObserver observer, CursorWindow window) throws RemoteException;
+ public int requery(IContentObserver observer) throws RemoteException;
boolean getWantsAllOnMoveCalls() throws RemoteException;
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 81fe824..a1c36e2 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -89,8 +89,6 @@ public class SQLiteCursor extends AbstractWindowedCursor {
* @param query the {@link SQLiteQuery} object associated with this cursor object.
*/
public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
- // The AbstractCursor constructor needs to do some setup.
- super();
if (query == null) {
throw new IllegalArgumentException("query object cannot be null");
}
@@ -157,12 +155,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
}
private void fillWindow(int startPos) {
- if (mWindow == null) {
- // If there isn't a window set already it will only be accessed locally
- mWindow = new CursorWindow(true /* the window is local only */);
- } else {
- mWindow.clear();
- }
+ clearOrCreateLocalWindow(getDatabase().getPath());
mWindow.setStartPosition(startPos);
int count = getQuery().fillWindow(mWindow);
if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0
@@ -214,16 +207,9 @@ public class SQLiteCursor extends AbstractWindowedCursor {
return mColumns;
}
- private void deactivateCommon() {
- if (false) Log.v(TAG, "<<< Releasing cursor " + this);
- closeWindow();
- if (false) Log.v("DatabaseWindow", "closing window in release()");
- }
-
@Override
public void deactivate() {
super.deactivate();
- deactivateCommon();
mDriver.cursorDeactivated();
}
@@ -231,7 +217,6 @@ public class SQLiteCursor extends AbstractWindowedCursor {
public void close() {
super.close();
synchronized (this) {
- deactivateCommon();
mQuery.close();
mDriver.cursorClosed();
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d65e6df..d338764 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -337,7 +337,7 @@ public class Camera {
* Camera objects are locked by default unless {@link #unlock()} is
* called. Normally {@link #reconnect()} is used instead.
*
- * <p>Since API level 13, camera is automatically locked for applications in
+ * <p>Since API level 14, camera is automatically locked for applications in
* {@link android.media.MediaRecorder#start()}. Applications can use the
* camera (ex: zoom) after recording starts. There is no need to call this
* after recording starts or stops.
@@ -356,7 +356,7 @@ public class Camera {
* which will re-acquire the lock and allow you to continue using the
* camera.
*
- * <p>Since API level 13, camera is automatically locked for applications in
+ * <p>Since API level 14, camera is automatically locked for applications in
* {@link android.media.MediaRecorder#start()}. Applications can use the
* camera (ex: zoom) after recording starts. There is no need to call this
* after recording starts or stops.
@@ -781,7 +781,7 @@ public class Camera {
* @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)
*/
void onAutoFocus(boolean success, Camera camera);
- };
+ }
/**
* Starts camera auto-focus and registers a callback function to run when
@@ -804,11 +804,17 @@ public class Camera {
* {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, flash may be
* fired during auto-focus, depending on the driver and camera hardware.<p>
*
- * Auto-exposure lock {@link android.hardware.Camera.Parameters#getAutoExposureLock()}
+ * <p>Auto-exposure lock {@link android.hardware.Camera.Parameters#getAutoExposureLock()}
* and auto-white balance locks {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()}
* do not change during and after autofocus. But auto-focus routine may stop
* auto-exposure and auto-white balance transiently during focusing.
*
+ * <p>Stopping preview with {@link #stopPreview()}, or triggering still
+ * image capture with {@link #takePicture(Camera.ShutterCallback,
+ * Camera.PictureCallback, Camera.PictureCallback)}, will not change the
+ * the focus position. Applications must call cancelAutoFocus to reset the
+ * focus.</p>
+ *
* @param cb the callback to run
* @see #cancelAutoFocus()
* @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean)
@@ -1059,8 +1065,7 @@ public class Camera {
/**
* Notify the listener of the detected faces in the preview frame.
*
- * @param faces The detected faces in a list sorted by the confidence score.
- * The highest scored face is the first element.
+ * @param faces The detected faces in a list
* @param camera The {@link Camera} service object
*/
void onFaceDetection(Face[] faces, Camera camera);
@@ -1121,7 +1126,7 @@ public class Camera {
/**
* Information about a face identified through camera face detection.
- *
+ *
* <p>When face detection is used with a camera, the {@link FaceDetectionListener} returns a
* list of face objects for use in focusing and metering.</p>
*
@@ -1140,7 +1145,9 @@ public class Camera {
* the field of view. For example, suppose the size of the viewfinder UI
* is 800x480. The rect passed from the driver is (-1000, -1000, 0, 0).
* The corresponding viewfinder rect should be (0, 0, 400, 240). The
- * width and height of the rect will not be 0 or negative.
+ * width and height of the rect will not be 0 or negative. The
+ * coordinates can be smaller than -1000 or bigger than 1000. But at
+ * least one vertex will be within (-1000, -1000) and (1000, 1000).
*
* <p>The direction is relative to the sensor orientation, that is, what
* the sensor sees. The direction is not affected by the rotation or
@@ -1464,6 +1471,8 @@ public class Camera {
private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw";
private static final String KEY_RECORDING_HINT = "recording-hint";
private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";
+ private static final String KEY_VIDEO_STABILIZATION = "video-stabilization";
+ private static final String KEY_VIDEO_STABILIZATION_SUPPORTED = "video-stabilization-supported";
// Parameter key suffix for supported values.
private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -1651,9 +1660,18 @@ public class Camera {
* call {@link #takePicture(Camera.ShutterCallback,
* Camera.PictureCallback, Camera.PictureCallback)} in this mode but the
* subject may not be in focus. Auto focus starts when the parameter is
- * set. Applications should not call {@link
- * #autoFocus(AutoFocusCallback)} in this mode. To stop continuous
- * focus, applications should change the focus mode to other modes.
+ * set.
+ *
+ * <p>Since API level 14, applications can call {@link
+ * #autoFocus(AutoFocusCallback)} in this mode. The focus callback will
+ * immediately return with a boolean that indicates whether the focus is
+ * sharp or not. The focus position is locked after autoFocus call. If
+ * applications want to resume the continuous focus, cancelAutoFocus
+ * must be called. Restarting the preview will not resume the continuous
+ * autofocus. To stop continuous focus, applications should change the
+ * focus mode to other modes.
+ *
+ * @see #FOCUS_MODE_CONTINUOUS_PICTURE
*/
public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
@@ -1661,13 +1679,17 @@ public class Camera {
* Continuous auto focus mode intended for taking pictures. The camera
* continuously tries to focus. The speed of focus change is more
* aggressive than {@link #FOCUS_MODE_CONTINUOUS_VIDEO}. Auto focus
- * starts when the parameter is set. If applications call {@link
- * #autoFocus(AutoFocusCallback)} in this mode, the focus callback will
- * immediately return with a boolean that indicates whether the focus is
- * sharp or not. The apps can then decide if they want to take a picture
- * immediately or to change the focus mode to auto, and run a full
- * autofocus cycle. To stop continuous focus, applications should change
- * the focus mode to other modes.
+ * starts when the parameter is set.
+ *
+ * <p>If applications call {@link #autoFocus(AutoFocusCallback)} in this
+ * mode, the focus callback will immediately return with a boolean that
+ * indicates whether the focus is sharp or not. The apps can then decide
+ * if they want to take a picture immediately or to change the focus
+ * mode to auto, and run a full autofocus cycle. The focus position is
+ * locked after autoFocus call. If applications want to resume the
+ * continuous focus, cancelAutoFocus must be called. Restarting the
+ * preview will not resume the continuous autofocus. To stop continuous
+ * focus, applications should change the focus mode to other modes.
*
* @see #FOCUS_MODE_CONTINUOUS_VIDEO
*/
@@ -2443,7 +2465,7 @@ public class Camera {
*
* @param value new white balance.
* @see #getWhiteBalance()
- * @see #setAutoWhiteBalanceLock()
+ * @see #setAutoWhiteBalanceLock(boolean)
*/
public void setWhiteBalance(String value) {
set(KEY_WHITE_BALANCE, value);
@@ -3059,8 +3081,9 @@ public class Camera {
* when using zoom.</p>
*
* <p>Focus area only has effect if the current focus mode is
- * {@link #FOCUS_MODE_AUTO}, {@link #FOCUS_MODE_MACRO}, or
- * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}.</p>
+ * {@link #FOCUS_MODE_AUTO}, {@link #FOCUS_MODE_MACRO},
+ * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}, or
+ * {@link #FOCUS_MODE_CONTINUOUS_PICTURE}.</p>
*
* @return a list of current focus areas
*/
@@ -3208,6 +3231,59 @@ public class Camera {
return TRUE.equals(str);
}
+ /**
+ * <p>Enables and disables video stabilization. Use
+ * {@link #isVideoStabilizationSupported} to determine if calling this
+ * method is valid.</p>
+ *
+ * <p>Video stabilization reduces the shaking due to the motion of the
+ * camera in both the preview stream and in recorded videos, including
+ * data received from the preview callback. It does not reduce motion
+ * blur in images captured with
+ * {@link Camera#takePicture takePicture}.</p>
+ *
+ * <p>Video stabilization can be enabled and disabled while preview or
+ * recording is active, but toggling it may cause a jump in the video
+ * stream that may be undesirable in a recorded video.</p>
+ *
+ * @param toggle Set to true to enable video stabilization, and false to
+ * disable video stabilization.
+ * @see #isVideoStabilizationSupported()
+ * @see #getVideoStabilization()
+ * @hide
+ */
+ public void setVideoStabilization(boolean toggle) {
+ set(KEY_VIDEO_STABILIZATION, toggle ? TRUE : FALSE);
+ }
+
+ /**
+ * Get the current state of video stabilization. See
+ * {@link #setVideoStabilization} for details of video stabilization.
+ *
+ * @return true if video stabilization is enabled
+ * @see #isVideoStabilizationSupported()
+ * @see #setVideoStabilization(boolean)
+ * @hide
+ */
+ public boolean getVideoStabilization() {
+ String str = get(KEY_VIDEO_STABILIZATION);
+ return TRUE.equals(str);
+ }
+
+ /**
+ * Returns true if video stabilization is supported. See
+ * {@link #setVideoStabilization} for details of video stabilization.
+ *
+ * @return true if video stabilization is supported
+ * @see #setVideoStabilization(boolean)
+ * @see #getVideoStabilization()
+ * @hide
+ */
+ public boolean isVideoStabilizationSupported() {
+ String str = get(KEY_VIDEO_STABILIZATION_SUPPORTED);
+ return TRUE.equals(str);
+ }
+
// Splits a comma delimited string to an ArrayList of String.
// Return null if the passing string is null or the size is 0.
private ArrayList<String> split(String str) {
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index aaad8a1..1b24f0c 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -40,6 +40,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
public long limitBytes;
public long lastSnooze;
+ private static final long DEFAULT_MTU = 1500;
+
public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes,
long lastSnooze) {
this.template = checkNotNull(template, "missing NetworkTemplate");
@@ -71,6 +73,17 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
return 0;
}
+ /**
+ * Test if given measurement is near enough to {@link #limitBytes} to be
+ * considered over-limit.
+ */
+ public boolean isOverLimit(long totalBytes) {
+ // over-estimate, since kernel will trigger limit once first packet
+ // trips over limit.
+ totalBytes += 2 * DEFAULT_MTU;
+ return limitBytes != LIMIT_DISABLED && totalBytes >= limitBytes;
+ }
+
/** {@inheritDoc} */
public int compareTo(NetworkPolicy another) {
if (another == null || another.limitBytes == LIMIT_DISABLED) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8483b4f..4bc0892 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -187,6 +187,59 @@ public final class ContactsContract {
public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
/**
+ * <p>
+ * API for obtaining a pre-authorized version of a URI that normally requires special
+ * permission (beyond READ_CONTACTS) to read. The caller obtaining the pre-authorized URI
+ * must already have the necessary permissions to access the URI; otherwise a
+ * {@link SecurityException} will be thrown.
+ * </p>
+ * <p>
+ * The authorized URI returned in the bundle contains an expiring token that allows the
+ * caller to execute the query without having the special permissions that would normally
+ * be required.
+ * </p>
+ * <p>
+ * This API does not access disk, and should be safe to invoke from the UI thread.
+ * </p>
+ * <p>
+ * Example usage:
+ * <pre>
+ * Uri profileUri = ContactsContract.Profile.CONTENT_VCARD_URI;
+ * Bundle uriBundle = new Bundle();
+ * uriBundle.putParcelable(ContactsContract.Authorization.KEY_URI_TO_AUTHORIZE, uri);
+ * Bundle authResponse = getContext().getContentResolver().call(
+ * ContactsContract.AUTHORITY_URI,
+ * ContactsContract.Authorization.AUTHORIZATION_METHOD,
+ * null, // String arg, not used.
+ * uriBundle);
+ * if (authResponse != null) {
+ * Uri preauthorizedProfileUri = (Uri) authResponse.getParcelable(
+ * ContactsContract.Authorization.KEY_AUTHORIZED_URI);
+ * // This pre-authorized URI can be queried by a caller without READ_PROFILE
+ * // permission.
+ * }
+ * </pre>
+ * </p>
+ * @hide
+ */
+ public static final class Authorization {
+ /**
+ * The method to invoke to create a pre-authorized URI out of the input argument.
+ */
+ public static final String AUTHORIZATION_METHOD = "authorize";
+
+ /**
+ * The key to set in the outbound Bundle with the URI that should be authorized.
+ */
+ public static final String KEY_URI_TO_AUTHORIZE = "uri_to_authorize";
+
+ /**
+ * The key to retrieve from the returned Bundle to obtain the pre-authorized URI.
+ */
+ public static final String KEY_AUTHORIZED_URI = "authorized_uri";
+ }
+
+ /**
* @hide
*/
public static final class Preferences {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bc05078..5754e60 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4053,6 +4053,14 @@ public final class Settings {
public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
/**
+ * Duration in milliseconds before pre-authorized URIs for the contacts
+ * provider should expire.
+ * @hide
+ */
+ public static final String CONTACTS_PREAUTH_URI_EXPIRATION =
+ "contacts_preauth_uri_expiration";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4087,7 +4095,9 @@ public final class Settings {
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE
+ UI_NIGHT_MODE,
+ LOCK_SCREEN_OWNER_INFO,
+ LOCK_SCREEN_OWNER_INFO_ENABLED
};
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3bd0f76..ad2283e 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -117,6 +117,11 @@ public class Display {
outSize.x = getRawWidth();
outSize.y = getRawHeight();
}
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.v(TAG, "Returning display size: " + outSize, here);
+ }
if (DEBUG_DISPLAY_SIZE && doCompat) Slog.v(
TAG, "Returning display size: " + outSize);
} catch (RemoteException e) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index d57132a..1697382 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -502,9 +502,23 @@ public class TextureView extends View {
* @see #isAvailable()
* @see #getBitmap(int, int)
* @see #getBitmap()
+ *
+ * @throws IllegalStateException if the hardware rendering context cannot be
+ * acquired to capture the bitmap
*/
public Bitmap getBitmap(Bitmap bitmap) {
if (bitmap != null && isAvailable()) {
+ AttachInfo info = mAttachInfo;
+ if (info != null && info.mHardwareRenderer != null &&
+ info.mHardwareRenderer.isEnabled()) {
+ if (!info.mHardwareRenderer.validate()) {
+ throw new IllegalStateException("Could not acquire hardware rendering context");
+ }
+ }
+
+ applyUpdate();
+ applyTransformMatrix();
+
mLayer.copyInto(bitmap);
}
return bitmap;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 15544cc..73e5026 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1486,7 +1486,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
| AccessibilityEvent.TYPE_VIEW_FOCUSED
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
- | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
+ | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+ | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED;
/**
* Temporary Rect currently for use in setBackground(). This will probably
@@ -1890,12 +1891,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
- * Flag to hide only the navigation buttons. Don't use this
+ * Flag to hide only the home button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
- *
- * THIS DOES NOT DISABLE THE BACK BUTTON
*/
- public static final int STATUS_BAR_DISABLE_NAVIGATION = 0x00200000;
+ public static final int STATUS_BAR_DISABLE_HOME = 0x00200000;
/**
* @hide
@@ -1903,7 +1902,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
- * Flag to hide only the back button. Don't use this
+ * Flag to hide only the back button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
@@ -1921,6 +1920,28 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
/**
* @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
+ * out of the public fields to keep the undefined bits out of the developer's way.
+ *
+ * Flag to hide only the recent apps button. Don't use this
+ * unless you're a special part of the system UI (i.e., setup wizard, keyguard).
+ */
+ public static final int STATUS_BAR_DISABLE_RECENT = 0x01000000;
+
+ /**
+ * @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility, etc. etc.
+ *
+ * This hides HOME and RECENT and is provided for compatibility with interim implementations.
+ */
+ @Deprecated
+ public static final int STATUS_BAR_DISABLE_NAVIGATION =
+ STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT;
+
+ /**
+ * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
@@ -10125,6 +10146,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
return mHardwareLayer;
}
+ /**
+ * Destroys this View's hardware layer if possible.
+ *
+ * @return True if the layer was destroyed, false otherwise.
+ *
+ * @see #setLayerType(int, android.graphics.Paint)
+ * @see #LAYER_TYPE_HARDWARE
+ */
boolean destroyLayer() {
if (mHardwareLayer != null) {
mHardwareLayer.destroy();
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index bfd2959..17bdff2 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -104,23 +104,23 @@ public interface WindowManagerPolicy {
*/
public final static String EXTRA_HDMI_PLUGGED_STATE = "state";
- // flags for interceptKeyTq
/**
- * Pass this event to the user / app. To be returned from {@link #interceptKeyTq}.
+ * Pass this event to the user / app. To be returned from
+ * {@link #interceptKeyBeforeQueueing}.
*/
public final static int ACTION_PASS_TO_USER = 0x00000001;
/**
* This key event should extend the user activity timeout and turn the lights on.
- * To be returned from {@link #interceptKeyTq}. Do not return this and
- * {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
+ * To be returned from {@link #interceptKeyBeforeQueueing}.
+ * Do not return this and {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
*/
public final static int ACTION_POKE_USER_ACTIVITY = 0x00000002;
/**
* This key event should put the device to sleep (and engage keyguard if necessary)
- * To be returned from {@link #interceptKeyTq}. Do not return this and
- * {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
+ * To be returned from {@link #interceptKeyBeforeQueueing}.
+ * Do not return this and {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
*/
public final static int ACTION_GO_TO_SLEEP = 0x00000004;
@@ -677,10 +677,12 @@ public interface WindowManagerPolicy {
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
- * @return Returns true if the policy consumed the event and it should
- * not be further dispatched.
+ * @return 0 if the key should be dispatched immediately, -1 if the key should
+ * not be dispatched ever, or a positive value indicating the number of
+ * milliseconds by which the key dispatch should be delayed before trying
+ * again.
*/
- public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
+ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
/**
* Called from the input dispatcher thread when an application did not handle
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 25bc559..86dd9df 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -200,15 +200,6 @@ import java.util.List;
* <li>{@link #getBeforeText()} - The text of the source before the change.</li>
* <li>{@link #getContentDescription()} - The content description of the source.</li>
* </ul>
- * <em>Note:</em> This event type is not dispatched to descendants though
- * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
- * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event
- * source {@link android.view.View} and the sub-tree rooted at it will not receive
- * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent)
- * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add
- * text content to such events is by setting the
- * {@link android.R.styleable#View_contentDescription contentDescription} of the source
- * view.</br>
* </p>
* <p>
* <b>View text selection changed</b> - represents the event of changing the text
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index c8b67a8..2cc928f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1204,22 +1204,20 @@ class BrowserFrame extends Handler {
* We delegate the request to CallbackProxy, and route its response to
* {@link #nativeSslClientCert(int, X509Certificate)}.
*/
- private void requestClientCert(int handle, byte[] host_and_port_bytes) {
- String host_and_port = new String(host_and_port_bytes, Charsets.UTF_8);
+ private void requestClientCert(int handle, String hostAndPort) {
SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
- if (table.IsAllowed(host_and_port)) {
+ if (table.IsAllowed(hostAndPort)) {
// previously allowed
nativeSslClientCert(handle,
- table.PrivateKey(host_and_port),
- table.CertificateChain(host_and_port));
- } else if (table.IsDenied(host_and_port)) {
+ table.PrivateKey(hostAndPort),
+ table.CertificateChain(hostAndPort));
+ } else if (table.IsDenied(hostAndPort)) {
// previously denied
nativeSslClientCert(handle, null, null);
} else {
// previously ignored or new
mCallbackProxy.onReceivedClientCertRequest(
- new ClientCertRequestHandler(this, handle, host_and_port, table),
- host_and_port);
+ new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort);
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 9c44138..f1c2bde 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -211,6 +211,7 @@ public class WebSettings {
private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM;
private RenderPriority mRenderPriority = RenderPriority.NORMAL;
private int mOverrideCacheMode = LOAD_DEFAULT;
+ private int mDoubleTapZoom = 100;
private boolean mSaveFormData = true;
private boolean mAutoFillEnabled = false;
private boolean mSavePassword = true;
@@ -769,6 +770,27 @@ public class WebSettings {
}
/**
+ * Set the double-tap zoom of the page in percent. Default is 100.
+ * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
+ * @hide
+ */
+ public void setDoubleTapZoom(int doubleTapZoom) {
+ if (mDoubleTapZoom != doubleTapZoom) {
+ mDoubleTapZoom = doubleTapZoom;
+ mWebView.updateDoubleTapZoom();
+ }
+ }
+
+ /**
+ * Get the double-tap zoom of the page in percent.
+ * @return A percent value describing the double-tap zoom.
+ * @hide
+ */
+ public int getDoubleTapZoom() {
+ return mDoubleTapZoom;
+ }
+
+ /**
* Set the default zoom density of the page. This should be called from UI
* thread.
* @param zoom A ZoomDensity value
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 66371f5..b0ecf09 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -1002,6 +1002,9 @@ import junit.framework.Assert;
| InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| EditorInfo.IME_FLAG_NO_FULLSCREEN;
+ if (!mWebView.nativeFocusCandidateIsSpellcheck()) {
+ inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+ }
if (TEXT_AREA != type
&& mWebView.nativeFocusCandidateHasNextTextfield()) {
imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f46af51..1c22a0b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2074,14 +2074,17 @@ public class WebView extends AbsoluteLayout
* If the value of the encoding parameter is 'base64', then the data must
* be encoded as base64. Otherwise, the data must use ASCII encoding for
* octets inside the range of safe URL characters and use the standard %xx
- * hex encoding of URLs for octets outside that range.
+ * hex encoding of URLs for octets outside that range. For example,
+ * '#', '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
* <p>
* The 'data' scheme URL formed by this method uses the default US-ASCII
* charset. If you need need to set a different charset, you should form a
- * 'data' scheme URL which specifies a charset parameter and call
- * {@link #loadUrl(String)} instead.
+ * 'data' scheme URL which explicitly specifies a charset parameter in the
+ * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
+ * Note that the charset obtained from the mediatype portion of a data URL
+ * always overrides that specified in the HTML or XML document itself.
* @param data A String of data in the given encoding.
- * @param mimeType The MIMEType of the data, e.g. 'text/html'.
+ * @param mimeType The MIME type of the data, e.g. 'text/html'.
* @param encoding The encoding of the data.
*/
public void loadData(String data, String mimeType, String encoding) {
@@ -2985,6 +2988,13 @@ public class WebView extends AbsoluteLayout
return false;
}
+ /**
+ * Update the double-tap zoom.
+ */
+ /* package */ void updateDoubleTapZoom() {
+ mZoomManager.updateDoubleTapZoom();
+ }
+
private int computeRealHorizontalScrollRange() {
if (mDrawHistory) {
return mHistoryWidth;
@@ -7415,6 +7425,10 @@ public class WebView extends AbsoluteLayout
}
}
+ void sendPluginDrawMsg() {
+ mWebViewCore.sendMessage(EventHub.PLUGIN_SURFACE_READY);
+ }
+
/**
* Returns plugin bounds if x/y in content coordinates corresponds to a
* plugin. Otherwise a NULL rectangle is returned.
@@ -9454,6 +9468,7 @@ public class WebView extends AbsoluteLayout
private native boolean nativeFocusCandidateIsTextInput();
/* package */ native int nativeFocusCandidateMaxLength();
/* package */ native boolean nativeFocusCandidateIsAutoComplete();
+ /* package */ native boolean nativeFocusCandidateIsSpellcheck();
/* package */ native String nativeFocusCandidateName();
private native Rect nativeFocusCandidateNodeBounds();
/**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 44688b8..1294a28 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1032,6 +1032,8 @@ public final class WebViewCore {
static final int EXECUTE_JS = 194;
+ static final int PLUGIN_SURFACE_READY = 195;
+
// private message ids
private static final int DESTROY = 200;
@@ -1587,6 +1589,10 @@ public final class WebViewCore {
nativeFullScreenPluginHidden(msg.arg1);
break;
+ case PLUGIN_SURFACE_READY:
+ nativePluginSurfaceReady();
+ break;
+
case ADD_PACKAGE_NAMES:
if (BrowserFrame.sJavaBridge == null) {
throw new IllegalStateException("No WebView " +
@@ -2826,6 +2832,7 @@ public final class WebViewCore {
private native void nativeResume();
private native void nativeFreeMemory();
private native void nativeFullScreenPluginHidden(int npp);
+ private native void nativePluginSurfaceReady();
private native boolean nativeValidNodeAndBounds(int frame, int node,
Rect bounds);
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 7f526e7..206142a 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -145,11 +145,11 @@ class ZoomManager {
private float mInvDefaultScale;
/*
- * The scale factor that is used to determine the zoom level for reading text.
- * The value is initially set to equal the display density.
- * TODO: Support changing this in WebSettings
+ * The logical density of the display. This is a scaling factor for the
+ * Density Independent Pixel unit, where one DIP is one pixel on an
+ * approximately 160 dpi screen (see android.util.DisplayMetrics.density)
*/
- private float mReadingLevelScale;
+ private float mDisplayDensity;
/*
* The scale factor that is used as the minimum increment when going from
@@ -233,11 +233,11 @@ class ZoomManager {
public void init(float density) {
assert density > 0;
+ mDisplayDensity = density;
setDefaultZoomScale(density);
mActualScale = density;
mInvActualScale = 1 / density;
- mReadingLevelScale = density;
- mTextWrapScale = density;
+ mTextWrapScale = getReadingLevelScale();
}
/**
@@ -310,8 +310,11 @@ class ZoomManager {
return mInitialScale > 0 ? mInitialScale : mDefaultScale;
}
+ /**
+ * Returns the zoom scale used for reading text on a double-tap.
+ */
public final float getReadingLevelScale() {
- return mReadingLevelScale;
+ return mDisplayDensity * mWebView.getSettings().getDoubleTapZoom() / 100.0f;
}
public final float getInvDefaultScale() {
@@ -510,6 +513,13 @@ class ZoomManager {
return mZoomScale != 0 || mInHWAcceleratedZoom;
}
+ public void updateDoubleTapZoom() {
+ if (mInZoomOverview) {
+ mTextWrapScale = getReadingLevelScale();
+ refreshZoomScale(true);
+ }
+ }
+
public void refreshZoomScale(boolean reflowText) {
setZoomScale(mActualScale, reflowText, true);
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index df8eb05..bdaf89e 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -335,6 +335,9 @@ public abstract class AbsSeekBar extends ProgressBar {
mTouchDownX = event.getX();
} else {
setPressed(true);
+ if (mThumb != null) {
+ invalidate(mThumb.getBounds()); // This may be within the padding region
+ }
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
@@ -348,6 +351,9 @@ public abstract class AbsSeekBar extends ProgressBar {
final float x = event.getX();
if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {
setPressed(true);
+ if (mThumb != null) {
+ invalidate(mThumb.getBounds()); // This may be within the padding region
+ }
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index fd19b5f..e16a8bd 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -922,15 +922,20 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
if (selectedView != null) {
event.setEnabled(selectedView.isEnabled());
}
+ event.setCurrentItemIndex(getSelectedItemPosition());
event.setFromIndex(getFirstVisiblePosition());
event.setToIndex(getLastVisiblePosition());
event.setItemCount(getAdapter().getCount());
}
private boolean isScrollableForAccessibility() {
- final int itemCount = getAdapter().getCount();
- return itemCount > 0
- && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1);
+ T adapter = getAdapter();
+ if (adapter != null) {
+ final int itemCount = adapter.getCount();
+ return itemCount > 0
+ && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1);
+ }
+ return false;
}
@Override
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 51506e8..083a952 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -29,12 +29,14 @@ import android.os.Handler;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.widget.AbsListView.OnScrollListener;
/**
* Helper class for AbsListView to draw and control the Fast Scroll thumb
*/
class FastScroller {
+ private static final String TAG = "FastScroller";
// Minimum number of pages to justify showing a fast scroll thumb
private static int MIN_PAGES = 4;
@@ -81,15 +83,15 @@ class FastScroller {
private Drawable mOverlayDrawableLeft;
private Drawable mOverlayDrawableRight;
- private int mThumbH;
- private int mThumbW;
- private int mThumbY;
+ int mThumbH;
+ int mThumbW;
+ int mThumbY;
private RectF mOverlayPos;
private int mOverlaySize;
- private AbsListView mList;
- private boolean mScrollCompleted;
+ AbsListView mList;
+ boolean mScrollCompleted;
private int mVisibleItem;
private Paint mPaint;
private int mListOffset;
@@ -105,7 +107,7 @@ class FastScroller {
private Handler mHandler = new Handler();
- private BaseAdapter mListAdapter;
+ BaseAdapter mListAdapter;
private SectionIndexer mSectionIndexer;
private boolean mChangedBounds;
@@ -118,10 +120,36 @@ class FastScroller {
private boolean mMatchDragPosition;
+ float mInitialTouchY;
+ boolean mPendingDrag;
+ private int mScaledTouchSlop;
+
private static final int FADE_TIMEOUT = 1500;
+ private static final int PENDING_DRAG_DELAY = 180;
private final Rect mTmpRect = new Rect();
+ private final Runnable mDeferStartDrag = new Runnable() {
+ public void run() {
+ if (mList.mIsAttached) {
+ beginDrag();
+
+ final int viewHeight = mList.getHeight();
+ // Jitter
+ int newThumbY = (int) mInitialTouchY - mThumbH + 10;
+ if (newThumbY < 0) {
+ newThumbY = 0;
+ } else if (newThumbY + mThumbH > viewHeight) {
+ newThumbY = viewHeight - mThumbH;
+ }
+ mThumbY = newThumbY;
+ scrollTo((float) mThumbY / (viewHeight - mThumbH));
+ }
+
+ mPendingDrag = false;
+ }
+ };
+
public FastScroller(Context context, AbsListView listView) {
mList = listView;
init(context);
@@ -264,6 +292,8 @@ class FastScroller {
ta.recycle();
+ mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
android.os.Build.VERSION_CODES.HONEYCOMB;
@@ -456,7 +486,7 @@ class FastScroller {
return mSections;
}
- private void getSectionsFromIndexer() {
+ void getSectionsFromIndexer() {
Adapter adapter = mList.getAdapter();
mSectionIndexer = null;
if (adapter instanceof HeaderViewListAdapter) {
@@ -489,7 +519,7 @@ class FastScroller {
mListAdapter = null;
}
- private void scrollTo(float position) {
+ void scrollTo(float position) {
int count = mList.getCount();
mScrollCompleted = false;
float fThreshold = (1.0f / count) / 8;
@@ -647,12 +677,45 @@ class FastScroller {
cancelFling.recycle();
}
+ void cancelPendingDrag() {
+ mList.removeCallbacks(mDeferStartDrag);
+ mPendingDrag = false;
+ }
+
+ void startPendingDrag() {
+ mPendingDrag = true;
+ mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY);
+ }
+
+ void beginDrag() {
+ setState(STATE_DRAGGING);
+ if (mListAdapter == null && mList != null) {
+ getSectionsFromIndexer();
+ }
+ if (mList != null) {
+ mList.requestDisallowInterceptTouchEvent(true);
+ mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ }
+
+ cancelFling();
+ }
+
boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (isPointInside(ev.getX(), ev.getY())) {
- setState(STATE_DRAGGING);
- return true;
- }
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) {
+ if (!mList.isInScrollingContainer()) {
+ beginDrag();
+ return true;
+ }
+ mInitialTouchY = ev.getY();
+ startPendingDrag();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ cancelPendingDrag();
+ break;
}
return false;
}
@@ -666,19 +729,32 @@ class FastScroller {
if (action == MotionEvent.ACTION_DOWN) {
if (isPointInside(me.getX(), me.getY())) {
- setState(STATE_DRAGGING);
- if (mListAdapter == null && mList != null) {
- getSectionsFromIndexer();
+ if (!mList.isInScrollingContainer()) {
+ beginDrag();
+ return true;
}
- if (mList != null) {
- mList.requestDisallowInterceptTouchEvent(true);
- mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ mInitialTouchY = me.getY();
+ startPendingDrag();
+ }
+ } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
+ if (mPendingDrag) {
+ // Allow a tap to scroll.
+ beginDrag();
+
+ final int viewHeight = mList.getHeight();
+ // Jitter
+ int newThumbY = (int) me.getY() - mThumbH + 10;
+ if (newThumbY < 0) {
+ newThumbY = 0;
+ } else if (newThumbY + mThumbH > viewHeight) {
+ newThumbY = viewHeight - mThumbH;
}
+ mThumbY = newThumbY;
+ scrollTo((float) mThumbY / (viewHeight - mThumbH));
- cancelFling();
- return true;
+ cancelPendingDrag();
+ // Will hit the STATE_DRAGGING check below
}
- } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
if (mState == STATE_DRAGGING) {
if (mList != null) {
// ViewGroup does the right thing already, but there might
@@ -698,6 +774,23 @@ class FastScroller {
return true;
}
} else if (action == MotionEvent.ACTION_MOVE) {
+ if (mPendingDrag) {
+ final float y = me.getY();
+ if (Math.abs(y - mInitialTouchY) > mScaledTouchSlop) {
+ setState(STATE_DRAGGING);
+ if (mListAdapter == null && mList != null) {
+ getSectionsFromIndexer();
+ }
+ if (mList != null) {
+ mList.requestDisallowInterceptTouchEvent(true);
+ mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ }
+
+ cancelFling();
+ cancelPendingDrag();
+ // Will hit the STATE_DRAGGING check below
+ }
+ }
if (mState == STATE_DRAGGING) {
final int viewHeight = mList.getHeight();
// Jitter
@@ -717,6 +810,8 @@ class FastScroller {
}
return true;
}
+ } else if (action == MotionEvent.ACTION_CANCEL) {
+ cancelPendingDrag();
}
return false;
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 62b078f..510e2d4 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -44,7 +44,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
private final static int MAX_SPELL_BATCH_SIZE = 50;
private final TextView mTextView;
- private final Editable mText;
final SpellCheckerSession mSpellCheckerSession;
final int mCookie;
@@ -64,7 +63,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
public SpellChecker(TextView textView) {
mTextView = textView;
- mText = (Editable) textView.getText();
final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
@@ -121,9 +119,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
return mLength - 1;
}
- private void addSpellCheckSpan(int start, int end) {
+ private void addSpellCheckSpan(Editable editable, int start, int end) {
final int index = nextSpellCheckSpanIndex();
- mText.setSpan(mSpellCheckSpans[index], start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ editable.setSpan(mSpellCheckSpans[index], start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mIds[index] = mSpanSequenceCounter++;
}
@@ -168,8 +166,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
private void spellCheck() {
if (mSpellCheckerSession == null) return;
- final int selectionStart = Selection.getSelectionStart(mText);
- final int selectionEnd = Selection.getSelectionEnd(mText);
+ Editable editable = (Editable) mTextView.getText();
+ final int selectionStart = Selection.getSelectionStart(editable);
+ final int selectionEnd = Selection.getSelectionEnd(editable);
TextInfo[] textInfos = new TextInfo[mLength];
int textInfosCount = 0;
@@ -178,12 +177,12 @@ public class SpellChecker implements SpellCheckerSessionListener {
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
if (spellCheckSpan.isSpellCheckInProgress()) continue;
- final int start = mText.getSpanStart(spellCheckSpan);
- final int end = mText.getSpanEnd(spellCheckSpan);
+ final int start = editable.getSpanStart(spellCheckSpan);
+ final int end = editable.getSpanEnd(spellCheckSpan);
// Do not check this word if the user is currently editing it
if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) {
- final String word = mText.subSequence(start, end).toString();
+ final String word = editable.subSequence(start, end).toString();
spellCheckSpan.setSpellCheckInProgress(true);
textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
}
@@ -202,6 +201,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
@Override
public void onGetSuggestions(SuggestionsInfo[] results) {
+ Editable editable = (Editable) mTextView.getText();
+
for (int i = 0; i < results.length; i++) {
SuggestionsInfo suggestionsInfo = results[i];
if (suggestionsInfo.getCookie() != mCookie) continue;
@@ -217,9 +218,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
if (!isInDictionary && looksLikeTypo) {
- createMisspelledSuggestionSpan(suggestionsInfo, spellCheckSpan);
+ createMisspelledSuggestionSpan(editable, suggestionsInfo, spellCheckSpan);
}
- mText.removeSpan(spellCheckSpan);
+ editable.removeSpan(spellCheckSpan);
break;
}
}
@@ -234,18 +235,18 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
}
- private void createMisspelledSuggestionSpan(SuggestionsInfo suggestionsInfo,
- SpellCheckSpan spellCheckSpan) {
- final int start = mText.getSpanStart(spellCheckSpan);
- final int end = mText.getSpanEnd(spellCheckSpan);
+ private void createMisspelledSuggestionSpan(Editable editable,
+ SuggestionsInfo suggestionsInfo, SpellCheckSpan spellCheckSpan) {
+ final int start = editable.getSpanStart(spellCheckSpan);
+ final int end = editable.getSpanEnd(spellCheckSpan);
// Other suggestion spans may exist on that region, with identical suggestions, filter
// them out to avoid duplicates. First, filter suggestion spans on that exact region.
- SuggestionSpan[] suggestionSpans = mText.getSpans(start, end, SuggestionSpan.class);
+ SuggestionSpan[] suggestionSpans = editable.getSpans(start, end, SuggestionSpan.class);
final int length = suggestionSpans.length;
for (int i = 0; i < length; i++) {
- final int spanStart = mText.getSpanStart(suggestionSpans[i]);
- final int spanEnd = mText.getSpanEnd(suggestionSpans[i]);
+ final int spanStart = editable.getSpanStart(suggestionSpans[i]);
+ final int spanEnd = editable.getSpanEnd(suggestionSpans[i]);
if (spanStart != start || spanEnd != end) {
suggestionSpans[i] = null;
break;
@@ -293,7 +294,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
- mText.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// TODO limit to the word rectangle region
mTextView.invalidate();
@@ -304,22 +305,24 @@ public class SpellChecker implements SpellCheckerSessionListener {
private Object mRange = new Object();
public void init(int start, int end) {
- mText.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ((Editable) mTextView.getText()).setSpan(mRange, start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
public void close() {
- mText.removeSpan(mRange);
+ ((Editable) mTextView.getText()).removeSpan(mRange);
}
public boolean isDone() {
- return mText.getSpanStart(mRange) < 0;
+ return ((Editable) mTextView.getText()).getSpanStart(mRange) < 0;
}
public void parse() {
+ Editable editable = (Editable) mTextView.getText();
// Iterate over the newly added text and schedule new SpellCheckSpans
- final int start = mText.getSpanStart(mRange);
- final int end = mText.getSpanEnd(mRange);
- mWordIterator.setCharSequence(mText, start, end);
+ final int start = editable.getSpanStart(mRange);
+ final int end = editable.getSpanEnd(mRange);
+ mWordIterator.setCharSequence(editable, start, end);
// Move back to the beginning of the current word, if any
int wordStart = mWordIterator.preceding(start);
@@ -333,14 +336,16 @@ public class SpellChecker implements SpellCheckerSessionListener {
wordEnd = mWordIterator.getEnd(wordStart);
}
if (wordEnd == BreakIterator.DONE) {
- mText.removeSpan(mRange);
+ editable.removeSpan(mRange);
return;
}
// We need to expand by one character because we want to include the spans that
// end/start at position start/end respectively.
- SpellCheckSpan[] spellCheckSpans = mText.getSpans(start-1, end+1, SpellCheckSpan.class);
- SuggestionSpan[] suggestionSpans = mText.getSpans(start-1, end+1, SuggestionSpan.class);
+ SpellCheckSpan[] spellCheckSpans = editable.getSpans(start - 1, end + 1,
+ SpellCheckSpan.class);
+ SuggestionSpan[] suggestionSpans = editable.getSpans(start - 1, end + 1,
+ SuggestionSpan.class);
int nbWordsChecked = 0;
boolean scheduleOtherSpellCheck = false;
@@ -350,20 +355,20 @@ public class SpellChecker implements SpellCheckerSessionListener {
// A new word has been created across the interval boundaries with this edit.
// Previous spans (ended on start / started on end) removed, not valid anymore
if (wordStart < start && wordEnd > start) {
- removeSpansAt(start, spellCheckSpans);
- removeSpansAt(start, suggestionSpans);
+ removeSpansAt(editable, start, spellCheckSpans);
+ removeSpansAt(editable, start, suggestionSpans);
}
if (wordStart < end && wordEnd > end) {
- removeSpansAt(end, spellCheckSpans);
- removeSpansAt(end, suggestionSpans);
+ removeSpansAt(editable, end, spellCheckSpans);
+ removeSpansAt(editable, end, suggestionSpans);
}
// Do not create new boundary spans if they already exist
boolean createSpellCheckSpan = true;
if (wordEnd == start) {
for (int i = 0; i < spellCheckSpans.length; i++) {
- final int spanEnd = mText.getSpanEnd(spellCheckSpans[i]);
+ final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
if (spanEnd == start) {
createSpellCheckSpan = false;
break;
@@ -373,7 +378,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
if (wordStart == end) {
for (int i = 0; i < spellCheckSpans.length; i++) {
- final int spanStart = mText.getSpanStart(spellCheckSpans[i]);
+ final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
if (spanStart == end) {
createSpellCheckSpan = false;
break;
@@ -386,7 +391,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
scheduleOtherSpellCheck = true;
break;
}
- addSpellCheckSpan(wordStart, wordEnd);
+ addSpellCheckSpan(editable, wordStart, wordEnd);
nbWordsChecked++;
}
}
@@ -401,23 +406,23 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
if (scheduleOtherSpellCheck) {
- mText.setSpan(mRange, wordStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ editable.setSpan(mRange, wordStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
- mText.removeSpan(mRange);
+ editable.removeSpan(mRange);
}
spellCheck();
}
- private <T> void removeSpansAt(int offset, T[] spans) {
+ private <T> void removeSpansAt(Editable editable, int offset, T[] spans) {
final int length = spans.length;
for (int i = 0; i < length; i++) {
final T span = spans[i];
- final int start = mText.getSpanStart(span);
+ final int start = editable.getSpanStart(span);
if (start > offset) continue;
- final int end = mText.getSpanEnd(span);
+ final int end = editable.getSpanEnd(span);
if (end < offset) continue;
- mText.removeSpan(span);
+ editable.removeSpan(span);
}
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 41daf70..041e8a4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2476,6 +2476,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (gravity != mGravity) {
invalidate();
+ mLayoutAlignment = null;
}
mGravity = gravity;
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 9fbbb3d..a0e125a 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -17,32 +17,79 @@
package com.android.internal.app;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
import android.widget.ImageView;
import android.widget.Toast;
public class PlatLogoActivity extends Activity {
Toast mToast;
+ ImageView mContent;
+ Vibrator mZzz = new Vibrator();
+ int mCount;
+ final Handler mHandler = new Handler();
+
+ Runnable mSuperLongPress = new Runnable() {
+ public void run() {
+ mCount++;
+ mZzz.vibrate(50 * mCount);
+ final float scale = 1f + 0.25f * mCount * mCount;
+ mContent.setScaleX(scale);
+ mContent.setScaleY(scale);
+
+ if (mCount <= 3) {
+ mHandler.postDelayed(mSuperLongPress, ViewConfiguration.getLongPressTimeout());
+ } else {
+ try {
+ startActivity(new Intent(Intent.ACTION_MAIN)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .setClassName("com.android.systemui","com.android.systemui.Nyandroid"));
+ } catch (ActivityNotFoundException ex) {
+ android.util.Log.e("PlatLogoActivity", "Couldn't find platlogo screensaver.");
+ }
+ finish();
+ }
+ }
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mToast = Toast.makeText(this, "REZZZZZZZ...", Toast.LENGTH_SHORT);
+ mToast = Toast.makeText(this, "Android 4.0: Ice Cream Sandwich", Toast.LENGTH_SHORT);
- ImageView content = new ImageView(this);
- content.setImageResource(com.android.internal.R.drawable.platlogo);
- content.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- setContentView(content);
- }
+ mContent = new ImageView(this);
+ mContent.setImageResource(com.android.internal.R.drawable.platlogo);
+ mContent.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_UP) {
- mToast.show();
- }
- return super.dispatchTouchEvent(ev);
+ mContent.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mContent.setPressed(true);
+ mHandler.removeCallbacks(mSuperLongPress);
+ mCount = 0;
+ mHandler.postDelayed(mSuperLongPress, 2*ViewConfiguration.getLongPressTimeout());
+ } else if (action == MotionEvent.ACTION_UP) {
+ if (mContent.isPressed()) {
+ mContent.setPressed(false);
+ mHandler.removeCallbacks(mSuperLongPress);
+ mToast.show();
+ }
+ }
+ return true;
+ }
+ });
+
+ setContentView(mContent);
}
}
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index daabf42..77d0c97 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -109,7 +109,6 @@ public final class ShutdownThread extends Thread {
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
final AlertDialog dialog = new AlertDialog.Builder(context)
- .setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index aabea2c..f25d65f 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -31,9 +31,6 @@ import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageButton;
import java.util.ArrayList;
@@ -71,8 +68,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
int mOpenSubMenuId;
- public ActionMenuPresenter() {
- super(com.android.internal.R.layout.action_menu_layout,
+ public ActionMenuPresenter(Context context) {
+ super(context, com.android.internal.R.layout.action_menu_layout,
com.android.internal.R.layout.action_menu_item_layout);
}
@@ -98,7 +95,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
int width = mWidthLimit;
if (mReserveOverflow) {
if (mOverflowButton == null) {
- mOverflowButton = new OverflowMenuButton(mContext);
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
mOverflowButton.measure(spec, spec);
}
@@ -215,7 +212,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
if (hasOverflow) {
if (mOverflowButton == null) {
- mOverflowButton = new OverflowMenuButton(mContext);
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
}
ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
if (parent != mMenuView) {
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index bec437a..1e06b5a 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -29,8 +29,10 @@ import java.util.ArrayList;
* be reused if possible when items change.
*/
public abstract class BaseMenuPresenter implements MenuPresenter {
+ protected Context mSystemContext;
protected Context mContext;
protected MenuBuilder mMenu;
+ protected LayoutInflater mSystemInflater;
protected LayoutInflater mInflater;
private Callback mCallback;
@@ -44,10 +46,13 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
/**
* Construct a new BaseMenuPresenter.
*
+ * @param context Context for generating system-supplied views
* @param menuLayoutRes Layout resource ID for the menu container view
* @param itemLayoutRes Layout resource ID for a single item view
*/
- public BaseMenuPresenter(int menuLayoutRes, int itemLayoutRes) {
+ public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
+ mSystemContext = context;
+ mSystemInflater = LayoutInflater.from(context);
mMenuLayoutRes = menuLayoutRes;
mItemLayoutRes = itemLayoutRes;
}
@@ -62,7 +67,7 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
@Override
public MenuView getMenuView(ViewGroup root) {
if (mMenuView == null) {
- mMenuView = (MenuView) mInflater.inflate(mMenuLayoutRes, root, false);
+ mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
mMenuView.initialize(mMenu);
updateMenuView(true);
}
@@ -138,7 +143,7 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
* @return The new item view
*/
public MenuView.ItemView createItemView(ViewGroup parent) {
- return (MenuView.ItemView) mInflater.inflate(mItemLayoutRes, parent, false);
+ return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
}
/**
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
index 24ddad6..2439b5d 100644
--- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -22,7 +22,6 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -43,16 +42,15 @@ public class IconMenuPresenter extends BaseMenuPresenter {
private static final String VIEWS_TAG = "android:menu:icon";
private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu";
- public IconMenuPresenter() {
- super(com.android.internal.R.layout.icon_menu_layout,
+ public IconMenuPresenter(Context context) {
+ super(new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu),
+ com.android.internal.R.layout.icon_menu_layout,
com.android.internal.R.layout.icon_menu_item_layout);
}
@Override
public void initForMenu(Context context, MenuBuilder menu) {
- mContext = new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu);
- mInflater = LayoutInflater.from(mContext);
- mMenu = menu;
+ super.initForMenu(context, menu);
mMaxItems = -1;
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index acffa5c..446dab1 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -207,7 +207,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
});
final MenuBuilder menu = (MenuBuilder) mode.getMenu();
- mActionMenuPresenter = new ActionMenuPresenter();
+ mActionMenuPresenter = new ActionMenuPresenter(mContext);
mActionMenuPresenter.setReserveOverflow(true);
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 6d2e823..61bce60 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -373,7 +373,7 @@ public class ActionBarView extends AbsActionBarView {
}
}
if (mActionMenuPresenter == null) {
- mActionMenuPresenter = new ActionMenuPresenter();
+ mActionMenuPresenter = new ActionMenuPresenter(mContext);
mActionMenuPresenter.setCallback(cb);
mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 97bbe52..a2fc6e2 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -17,8 +17,6 @@
package com.android.internal.widget;
-import com.android.internal.R;
-
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -34,11 +32,15 @@ import android.os.Debug;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
import java.util.ArrayList;
import java.util.List;
@@ -57,9 +59,6 @@ public class LockPatternView extends View {
private static final int ASPECT_LOCK_WIDTH = 1; // Fixed width; height will be minimum of (w,h)
private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
- // Vibrator pattern for creating a tactile bump
- private static final long[] DEFAULT_VIBE_PATTERN = {0, 1, 40, 41};
-
private static final boolean PROFILE_DRAWING = false;
private boolean mDrawingProfilingStarted = false;
@@ -100,7 +99,7 @@ public class LockPatternView extends View {
private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
private boolean mInputEnabled = true;
private boolean mInStealthMode = false;
- private boolean mTactileFeedbackEnabled = true;
+ private boolean mEnableHapticFeedback = true;
private boolean mPatternInProgress = false;
private float mDiameterFactor = 0.10f; // TODO: move to attrs
@@ -125,11 +124,6 @@ public class LockPatternView extends View {
private int mBitmapWidth;
private int mBitmapHeight;
-
- private Vibrator vibe; // Vibrator for creating tactile feedback
-
- private long[] mVibePattern;
-
private int mAspect;
private final Matrix mArrowMatrix = new Matrix();
private final Matrix mCircleMatrix = new Matrix();
@@ -248,7 +242,6 @@ public class LockPatternView extends View {
public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
- vibe = new Vibrator();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LockPatternView);
@@ -293,26 +286,6 @@ public class LockPatternView extends View {
mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
}
- // allow vibration pattern to be customized
- mVibePattern = loadVibratePattern(com.android.internal.R.array.config_virtualKeyVibePattern);
- }
-
- private long[] loadVibratePattern(int id) {
- int[] pattern = null;
- try {
- pattern = getResources().getIntArray(id);
- } catch (Resources.NotFoundException e) {
- Log.e(TAG, "Vibrate pattern missing, using default", e);
- }
- if (pattern == null) {
- return DEFAULT_VIBE_PATTERN;
- }
-
- long[] tmpPattern = new long[pattern.length];
- for (int i = 0; i < pattern.length; i++) {
- tmpPattern[i] = pattern[i];
- }
- return tmpPattern;
}
private Bitmap getBitmapFor(int resId) {
@@ -330,7 +303,7 @@ public class LockPatternView extends View {
* @return Whether the view has tactile feedback enabled.
*/
public boolean isTactileFeedbackEnabled() {
- return mTactileFeedbackEnabled;
+ return mEnableHapticFeedback;
}
/**
@@ -350,7 +323,7 @@ public class LockPatternView extends View {
* @param tactileFeedbackEnabled Whether tactile feedback is enabled
*/
public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
- mTactileFeedbackEnabled = tactileFeedbackEnabled;
+ mEnableHapticFeedback = tactileFeedbackEnabled;
}
/**
@@ -401,6 +374,34 @@ public class LockPatternView extends View {
invalidate();
}
+ private void notifyCellAdded() {
+ if (mOnPatternListener != null) {
+ mOnPatternListener.onPatternCellAdded(mPattern);
+ }
+ sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
+ }
+
+ private void notifyPatternStarted() {
+ if (mOnPatternListener != null) {
+ mOnPatternListener.onPatternStart();
+ }
+ sendAccessEvent(R.string.lockscreen_access_pattern_start);
+ }
+
+ private void notifyPatternDetected() {
+ if (mOnPatternListener != null) {
+ mOnPatternListener.onPatternDetected(mPattern);
+ }
+ sendAccessEvent(R.string.lockscreen_access_pattern_detected);
+ }
+
+ private void notifyPatternCleared() {
+ if (mOnPatternListener != null) {
+ mOnPatternListener.onPatternCleared();
+ }
+ sendAccessEvent(R.string.lockscreen_access_pattern_cleared);
+ }
+
/**
* Clear the pattern.
*/
@@ -543,8 +544,10 @@ public class LockPatternView extends View {
addCellToPattern(fillInGapCell);
}
addCellToPattern(cell);
- if (mTactileFeedbackEnabled){
- vibe.vibrate(mVibePattern, -1); // Generate tactile feedback
+ if (mEnableHapticFeedback) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
return cell;
}
@@ -554,9 +557,7 @@ public class LockPatternView extends View {
private void addCellToPattern(Cell newCell) {
mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
mPattern.add(newCell);
- if (mOnPatternListener != null) {
- mOnPatternListener.onPatternCellAdded(mPattern);
- }
+ notifyCellAdded();
}
// helper method to find which cell a point maps to
@@ -619,6 +620,27 @@ public class LockPatternView extends View {
}
@Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+ final int action = event.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ event.setAction(MotionEvent.ACTION_DOWN);
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ event.setAction(MotionEvent.ACTION_MOVE);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ event.setAction(MotionEvent.ACTION_UP);
+ break;
+ }
+ onTouchEvent(event);
+ event.setAction(action);
+ }
+ return super.onHoverEvent(event);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
if (!mInputEnabled || !isEnabled()) {
return false;
@@ -636,10 +658,8 @@ public class LockPatternView extends View {
return true;
case MotionEvent.ACTION_CANCEL:
resetPattern();
- if (mOnPatternListener != null) {
- mPatternInProgress = false;
- mOnPatternListener.onPatternCleared();
- }
+ mPatternInProgress = false;
+ notifyPatternCleared();
if (PROFILE_DRAWING) {
if (mDrawingProfilingStarted) {
Debug.stopMethodTracing();
@@ -661,9 +681,9 @@ public class LockPatternView extends View {
final int patternSizePreHitDetect = mPattern.size();
Cell hitCell = detectAndAddHit(x, y);
final int patternSize = mPattern.size();
- if (hitCell != null && (mOnPatternListener != null) && (patternSize == 1)) {
+ if (hitCell != null && patternSize == 1) {
mPatternInProgress = true;
- mOnPatternListener.onPatternStart();
+ notifyPatternStarted();
}
// note current x and y for rubber banding of in progress patterns
final float dx = Math.abs(x - mInProgressX);
@@ -778,11 +798,17 @@ public class LockPatternView extends View {
}
}
+ private void sendAccessEvent(int resId) {
+ setContentDescription(mContext.getString(resId));
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ setContentDescription(null);
+ }
+
private void handleActionUp(MotionEvent event) {
// report pattern detected
- if (!mPattern.isEmpty() && mOnPatternListener != null) {
+ if (!mPattern.isEmpty()) {
mPatternInProgress = false;
- mOnPatternListener.onPatternDetected(mPattern);
+ notifyPatternDetected();
invalidate();
}
if (PROFILE_DRAWING) {
@@ -798,13 +824,13 @@ public class LockPatternView extends View {
final float x = event.getX();
final float y = event.getY();
final Cell hitCell = detectAndAddHit(x, y);
- if (hitCell != null && mOnPatternListener != null) {
+ if (hitCell != null) {
mPatternInProgress = true;
mPatternDisplayMode = DisplayMode.Correct;
- mOnPatternListener.onPatternStart();
- } else if (mOnPatternListener != null) {
+ notifyPatternStarted();
+ } else {
mPatternInProgress = false;
- mOnPatternListener.onPatternCleared();
+ notifyPatternCleared();
}
if (hitCell != null) {
final float startX = getCenterXForColumn(hitCell.column);
@@ -1061,7 +1087,7 @@ public class LockPatternView extends View {
return new SavedState(superState,
LockPatternUtils.patternToString(mPattern),
mPatternDisplayMode.ordinal(),
- mInputEnabled, mInStealthMode, mTactileFeedbackEnabled);
+ mInputEnabled, mInStealthMode, mEnableHapticFeedback);
}
@Override
@@ -1074,7 +1100,7 @@ public class LockPatternView extends View {
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
- mTactileFeedbackEnabled = ss.isTactileFeedbackEnabled();
+ mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
}
/**
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 01df48a..2e7810f 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -26,6 +26,7 @@ import android.os.SystemClock;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
@@ -52,7 +53,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
private final View mTargetView;
private final KeyboardView mKeyboardView;
private long[] mVibratePattern;
- private final Vibrator mVibrator;
+ private boolean mEnableHaptics = false;
public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
this(context, keyboardView, targetView, true);
@@ -71,7 +72,10 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
mKeyboardView.getLayoutParams().height);
}
mKeyboardView.setOnKeyboardActionListener(this);
- mVibrator = new Vibrator();
+ }
+
+ public void setEnableHaptics(boolean enabled) {
+ mEnableHaptics = enabled;
}
public boolean isAlpha() {
@@ -230,6 +234,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
public void handleBackspace() {
sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+ performHapticFeedback();
}
private void handleShift() {
@@ -272,8 +277,14 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
}
public void onPress(int primaryCode) {
- if (mVibratePattern != null) {
- mVibrator.vibrate(mVibratePattern, -1);
+ performHapticFeedback();
+ }
+
+ private void performHapticFeedback() {
+ if (mEnableHaptics) {
+ mKeyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
}
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 1042a59..73d9f10 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -336,20 +336,27 @@ public class TransportControlView extends FrameLayout implements OnClickListener
if (state == mPlayState) {
return;
}
+ final int imageResId;
+ final int imageDescId;
switch (state) {
case RemoteControlClient.PLAYSTATE_PLAYING:
- mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_pause);
+ imageResId = com.android.internal.R.drawable.ic_media_pause;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
break;
case RemoteControlClient.PLAYSTATE_BUFFERING:
- mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_stop);
+ imageResId = com.android.internal.R.drawable.ic_media_stop;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
break;
case RemoteControlClient.PLAYSTATE_PAUSED:
default:
- mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_play);
+ imageResId = com.android.internal.R.drawable.ic_media_play;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
break;
}
+ mBtnPlay.setImageResource(imageResId);
+ mBtnPlay.setContentDescription(getResources().getString(imageDescId));
mPlayState = state;
}
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index fe1aca0..722aeea 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -57,14 +57,23 @@ static void throwUnknownTypeException(JNIEnv * env, jint type) {
jniThrowException(env, "java/lang/IllegalStateException", msg.string());
}
-static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz,
- jint cursorWindowSize, jboolean localOnly) {
- CursorWindow* window = new CursorWindow(cursorWindowSize);
- if (!window) {
- return 0;
- }
- if (!window->initBuffer(localOnly)) {
- delete window;
+static jint nativeCreate(JNIEnv* env, jclass clazz,
+ jstring nameObj, jint cursorWindowSize, jboolean localOnly) {
+ String8 name;
+ if (nameObj) {
+ const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+ name.setTo(nameStr);
+ env->ReleaseStringUTFChars(nameObj, nameStr);
+ }
+ if (name.size() == 0) {
+ name.setTo("<unnamed>");
+ }
+
+ CursorWindow* window;
+ status_t status = CursorWindow::create(name, cursorWindowSize, localOnly, &window);
+ if (status || !window) {
+ LOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
+ name.string(), cursorWindowSize, status);
return 0;
}
@@ -72,19 +81,13 @@ static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz,
return reinterpret_cast<jint>(window);
}
-static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) {
- sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, binderObj));
- if (memory == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");
- return 0;
- }
+static jint nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
- CursorWindow* window = new CursorWindow();
- if (!window) {
- return 0;
- }
- if (!window->setMemory(memory)) {
- delete window;
+ CursorWindow* window;
+ status_t status = CursorWindow::createFromParcel(parcel, &window);
+ if (status || !window) {
+ LOGE("Could not create CursorWindow from Parcel due to error %d.", status);
return 0;
}
@@ -101,22 +104,26 @@ static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) {
}
}
-static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) {
+static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jint windowPtr,
+ jobject parcelObj) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- if (window) {
- sp<IMemory> memory = window->getMemory();
- if (memory != NULL) {
- sp<IBinder> binder = memory->asBinder();
- return javaObjectForIBinder(env, binder);
- }
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+
+ status_t status = window->writeToParcel(parcel);
+ if (status) {
+ String8 msg;
+ msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
+ jniThrowRuntimeException(env, msg.string());
}
- return NULL;
}
static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Clearing window %p", window);
- window->clear();
+ status_t status = window->clear();
+ if (status) {
+ LOG_WINDOW("Could not clear window. error=%d", status);
+ }
}
static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) {
@@ -127,12 +134,14 @@ static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) {
static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr,
jint columnNum) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- return window->setNumColumns(columnNum);
+ status_t status = window->setNumColumns(columnNum);
+ return status == OK;
}
static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- return window->allocRow() != NULL;
+ status_t status = window->allocRow();
+ return status == OK;
}
static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) {
@@ -145,14 +154,14 @@ static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
// FIXME: This is really broken but we have CTS tests that depend
// on this legacy behavior.
//throwExceptionWithRowCol(env, row, column);
- return FIELD_TYPE_NULL;
+ return CursorWindow::FIELD_TYPE_NULL;
}
- return fieldSlot->type;
+ return window->getFieldSlotType(fieldSlot);
}
static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr,
@@ -160,29 +169,29 @@ static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
throwExceptionWithRowCol(env, row, column);
return NULL;
}
- uint8_t type = fieldSlot->type;
- if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) {
- uint32_t size = fieldSlot->data.buffer.size;
+ int32_t type = window->getFieldSlotType(fieldSlot);
+ if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
+ size_t size;
+ const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
jbyteArray byteArray = env->NewByteArray(size);
if (!byteArray) {
env->ExceptionClear();
throw_sqlite3_exception(env, "Native could not create new byte[]");
return NULL;
}
- env->SetByteArrayRegion(byteArray, 0, size,
- reinterpret_cast<jbyte*>(window->offsetToPtr(fieldSlot->data.buffer.offset)));
+ env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value));
return byteArray;
- } else if (type == FIELD_TYPE_INTEGER) {
+ } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
- } else if (type == FIELD_TYPE_FLOAT) {
+ } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
- } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == CursorWindow::FIELD_TYPE_NULL) {
// do nothing
} else {
throwUnknownTypeException(env, type);
@@ -195,43 +204,37 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
throwExceptionWithRowCol(env, row, column);
return NULL;
}
- uint8_t type = fieldSlot->type;
- if (type == FIELD_TYPE_STRING) {
- uint32_t size = fieldSlot->data.buffer.size;
-#if WINDOW_STORAGE_UTF8
- if (size <= 1) {
+ int32_t type = window->getFieldSlotType(fieldSlot);
+ if (type == CursorWindow::FIELD_TYPE_STRING) {
+ size_t sizeIncludingNull;
+ const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
+ if (sizeIncludingNull <= 1) {
return gEmptyString;
}
// Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF
// doesn't like UTF-8 strings with high codepoints. It actually expects
// Modified UTF-8 with encoded surrogate pairs.
- String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1);
+ String16 utf16(value, sizeIncludingNull - 1);
return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
-#else
- size_t chars = size / sizeof(char16_t);
- return chars ? env->NewString(reinterpret_cast<jchar*>(
- window->getFieldSlotValueString(fieldSlot)), chars)
- : gEmptyString;
-#endif
- } else if (type == FIELD_TYPE_INTEGER) {
+ } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
int64_t value = window->getFieldSlotValueLong(fieldSlot);
char buf[32];
snprintf(buf, sizeof(buf), "%lld", value);
return env->NewStringUTF(buf);
- } else if (type == FIELD_TYPE_FLOAT) {
+ } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
double value = window->getFieldSlotValueDouble(fieldSlot);
char buf[32];
snprintf(buf, sizeof(buf), "%g", value);
return env->NewStringUTF(buf);
- } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == CursorWindow::FIELD_TYPE_NULL) {
return NULL;
- } else if (type == FIELD_TYPE_BLOB) {
+ } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
throw_sqlite3_exception(env, "Unable to convert BLOB to string");
return NULL;
} else {
@@ -281,21 +284,6 @@ static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj,
}
}
-#if !WINDOW_STORAGE_UTF8
-static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj,
- const char16_t* str, size_t len) {
- jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len);
- if (dataObj) {
- if (len) {
- jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
- memcpy(data, str, len * sizeof(jchar));
- env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
- }
- env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len);
- }
-}
-#endif
-
static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) {
jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0);
if (dataObj) {
@@ -308,44 +296,34 @@ static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
throwExceptionWithRowCol(env, row, column);
return;
}
- uint8_t type = fieldSlot->type;
- if (type == FIELD_TYPE_STRING) {
- uint32_t size = fieldSlot->data.buffer.size;
-#if WINDOW_STORAGE_UTF8
- if (size > 1) {
- fillCharArrayBufferUTF(env, bufferObj,
- window->getFieldSlotValueString(fieldSlot), size - 1);
+ int32_t type = window->getFieldSlotType(fieldSlot);
+ if (type == CursorWindow::FIELD_TYPE_STRING) {
+ size_t sizeIncludingNull;
+ const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
+ if (sizeIncludingNull > 1) {
+ fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1);
} else {
clearCharArrayBuffer(env, bufferObj);
}
-#else
- size_t chars = size / sizeof(char16_t);
- if (chars) {
- fillCharArrayBuffer(env, bufferObj,
- window->getFieldSlotValueString(fieldSlot), chars);
- } else {
- clearCharArrayBuffer(env, bufferObj);
- }
-#endif
- } else if (type == FIELD_TYPE_INTEGER) {
+ } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
int64_t value = window->getFieldSlotValueLong(fieldSlot);
char buf[32];
snprintf(buf, sizeof(buf), "%lld", value);
fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
- } else if (type == FIELD_TYPE_FLOAT) {
+ } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
double value = window->getFieldSlotValueDouble(fieldSlot);
char buf[32];
snprintf(buf, sizeof(buf), "%g", value);
fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
- } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == CursorWindow::FIELD_TYPE_NULL) {
clearCharArrayBuffer(env, bufferObj);
- } else if (type == FIELD_TYPE_BLOB) {
+ } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
throw_sqlite3_exception(env, "Unable to convert BLOB to string");
} else {
throwUnknownTypeException(env, type);
@@ -357,29 +335,24 @@ static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
throwExceptionWithRowCol(env, row, column);
return 0;
}
- uint8_t type = fieldSlot->type;
- if (type == FIELD_TYPE_INTEGER) {
+ int32_t type = window->getFieldSlotType(fieldSlot);
+ if (type == CursorWindow::FIELD_TYPE_INTEGER) {
return window->getFieldSlotValueLong(fieldSlot);
- } else if (type == FIELD_TYPE_STRING) {
- uint32_t size = fieldSlot->data.buffer.size;
-#if WINDOW_STORAGE_UTF8
- return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L;
-#else
- size_t chars = size / sizeof(char16_t);
- return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars)
- .string(), NULL, 0) : 0L;
-#endif
- } else if (type == FIELD_TYPE_FLOAT) {
+ } else if (type == CursorWindow::FIELD_TYPE_STRING) {
+ size_t sizeIncludingNull;
+ const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
+ return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
+ } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
return jlong(window->getFieldSlotValueDouble(fieldSlot));
- } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == CursorWindow::FIELD_TYPE_NULL) {
return 0;
- } else if (type == FIELD_TYPE_BLOB) {
+ } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
throw_sqlite3_exception(env, "Unable to convert BLOB to long");
return 0;
} else {
@@ -393,29 +366,24 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr,
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
- field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column);
+ CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
if (!fieldSlot) {
throwExceptionWithRowCol(env, row, column);
return 0.0;
}
- uint8_t type = fieldSlot->type;
- if (type == FIELD_TYPE_FLOAT) {
+ int32_t type = window->getFieldSlotType(fieldSlot);
+ if (type == CursorWindow::FIELD_TYPE_FLOAT) {
return window->getFieldSlotValueDouble(fieldSlot);
- } else if (type == FIELD_TYPE_STRING) {
- uint32_t size = fieldSlot->data.buffer.size;
-#if WINDOW_STORAGE_UTF8
- return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0;
-#else
- size_t chars = size / sizeof(char16_t);
- return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars)
- .string(), NULL) : 0.0;
-#endif
- } else if (type == FIELD_TYPE_INTEGER) {
+ } else if (type == CursorWindow::FIELD_TYPE_STRING) {
+ size_t sizeIncludingNull;
+ const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
+ return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
+ } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
return jdouble(window->getFieldSlotValueLong(fieldSlot));
- } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == CursorWindow::FIELD_TYPE_NULL) {
return 0.0;
- } else if (type == FIELD_TYPE_BLOB) {
+ } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
throw_sqlite3_exception(env, "Unable to convert BLOB to double");
return 0.0;
} else {
@@ -427,82 +395,50 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr,
static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr,
jbyteArray valueObj, jint row, jint column) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column);
- if (fieldSlot == NULL) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
jsize len = env->GetArrayLength(valueObj);
- uint32_t offset = window->alloc(len);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes", len);
- return false;
- }
void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
- window->copyIn(offset, static_cast<const uint8_t*>(value), len);
+ status_t status = window->putBlob(row, column, value, len);
env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
- fieldSlot->type = FIELD_TYPE_BLOB;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = len;
- LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset);
+ if (status) {
+ LOG_WINDOW("Failed to put blob. error=%d", status);
+ return false;
+ }
+
+ LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
return true;
}
static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr,
jstring valueObj, jint row, jint column) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column);
- if (fieldSlot == NULL) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-#if WINDOW_STORAGE_UTF8
- size_t size = env->GetStringUTFLength(valueObj) + 1;
+ size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
-#else
- size_t size = env->GetStringLength(valueObj) * sizeof(jchar);
- const jchar* valueStr = env->GetStringChars(valueObj, NULL);
-#endif
if (!valueStr) {
- LOG_WINDOW("value can't be transfer to UTFChars");
+ LOG_WINDOW("value can't be transferred to UTFChars");
return false;
}
+ status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
+ env->ReleaseStringUTFChars(valueObj, valueStr);
- uint32_t offset = window->alloc(size);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes", size);
-#if WINDOW_STORAGE_UTF8
- env->ReleaseStringUTFChars(valueObj, valueStr);
-#else
- env->ReleaseStringChars(valueObj, valueStr);
-#endif
+ if (status) {
+ LOG_WINDOW("Failed to put string. error=%d", status);
return false;
}
- window->copyIn(offset, reinterpret_cast<const uint8_t*>(valueStr), size);
-
-#if WINDOW_STORAGE_UTF8
- env->ReleaseStringUTFChars(valueObj, valueStr);
-#else
- env->ReleaseStringChars(valueObj, valueStr);
-#endif
-
- fieldSlot->type = FIELD_TYPE_STRING;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = size;
- LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset);
+ LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
return true;
}
static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr,
jlong value, jint row, jint column) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- if (!window->putLong(row, column, value)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
+ status_t status = window->putLong(row, column, value);
+
+ if (status) {
+ LOG_WINDOW("Failed to put long. error=%d", status);
return false;
}
@@ -513,8 +449,10 @@ static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr,
static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr,
jdouble value, jint row, jint column) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- if (!window->putDouble(row, column, value)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
+ status_t status = window->putDouble(row, column, value);
+
+ if (status) {
+ LOG_WINDOW("Failed to put double. error=%d", status);
return false;
}
@@ -525,8 +463,10 @@ static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr,
static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr,
jint row, jint column) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- if (!window->putNull(row, column)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
+ status_t status = window->putNull(row, column);
+
+ if (status) {
+ LOG_WINDOW("Failed to put null. error=%d", status);
return false;
}
@@ -537,14 +477,14 @@ static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr,
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
- { "nativeInitializeEmpty", "(IZ)I",
- (void*)nativeInitializeEmpty },
- { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I",
- (void*)nativeInitializeFromBinder },
+ { "nativeCreate", "(Ljava/lang/String;IZ)I",
+ (void*)nativeCreate },
+ { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I",
+ (void*)nativeCreateFromParcel },
{ "nativeDispose", "(I)V",
(void*)nativeDispose },
- { "nativeGetBinder", "(I)Landroid/os/IBinder;",
- (void*)nativeGetBinder },
+ { "nativeWriteToParcel", "(ILandroid/os/Parcel;)V",
+ (void*)nativeWriteToParcel },
{ "nativeClear", "(I)V",
(void*)nativeClear },
{ "nativeGetNumRows", "(I)I",
diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp
index 022a64c..8170f46 100644
--- a/core/jni/android_database_SQLiteQuery.cpp
+++ b/core/jni/android_database_SQLiteQuery.cpp
@@ -61,7 +61,8 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
window->getNumRows(), window->size(), window->freeSpace());
int numColumns = sqlite3_column_count(statement);
- if (!window->setNumColumns(numColumns)) {
+ status_t status = window->setNumColumns(numColumns);
+ if (status) {
LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns);
jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch");
return 0;
@@ -88,10 +89,10 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
// Allocate a new field directory for the row. This pointer is not reused
// since it may be possible for it to be relocated on a call to alloc() when
// the field data is being allocated.
- field_slot_t* fieldDir = window->allocRow();
- if (!fieldDir) {
- LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d",
- startPos, addedRows);
+ status = window->allocRow();
+ if (status) {
+ LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
+ startPos, addedRows, status);
windowFull = true;
continue;
}
@@ -101,37 +102,28 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
int type = sqlite3_column_type(statement, i);
if (type == SQLITE_TEXT) {
// TEXT data
-#if WINDOW_STORAGE_UTF8
- const uint8_t* text = reinterpret_cast<const uint8_t*>(
+ const char* text = reinterpret_cast<const char*>(
sqlite3_column_text(statement, i));
// SQLite does not include the NULL terminator in size, but does
// ensure all strings are NULL terminated, so increase size by
// one to make sure we store the terminator.
- size_t size = sqlite3_column_bytes(statement, i) + 1;
-#else
- const uint8_t* text = reinterpret_cast<const uint8_t*>(
- sqlite3_column_text16(statement, i));
- size_t size = sqlite3_column_bytes16(statement, i);
-#endif
- int offset = window->alloc(size);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size,
- startPos + addedRows, i);
+ size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
+ status = window->putString(addedRows, i, text, sizeIncludingNull);
+ if (status) {
+ LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
+ sizeIncludingNull, startPos + addedRows, i, status);
windowFull = true;
break;
}
- window->copyIn(offset, text, size);
-
- field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i);
- fieldSlot->type = FIELD_TYPE_STRING;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = size;
- LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + addedRows, i, size);
+ LOG_WINDOW("%d,%d is TEXT with %u bytes",
+ startPos + addedRows, i, sizeIncludingNull);
} else if (type == SQLITE_INTEGER) {
// INTEGER data
int64_t value = sqlite3_column_int64(statement, i);
- if (!window->putLong(addedRows, i, value)) {
- LOG_WINDOW("Failed allocating space for a long in column %d", i);
+ status = window->putLong(addedRows, i, value);
+ if (status) {
+ LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
+ i, status);
windowFull = true;
break;
}
@@ -139,35 +131,33 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
} else if (type == SQLITE_FLOAT) {
// FLOAT data
double value = sqlite3_column_double(statement, i);
- if (!window->putDouble(addedRows, i, value)) {
- LOG_WINDOW("Failed allocating space for a double in column %d", i);
+ status = window->putDouble(addedRows, i, value);
+ if (status) {
+ LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
+ i, status);
windowFull = true;
break;
}
LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
} else if (type == SQLITE_BLOB) {
// BLOB data
- uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i);
- size_t size = sqlite3_column_bytes16(statement, i);
- int offset = window->alloc(size);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size,
- startPos + addedRows, i);
+ const void* blob = sqlite3_column_blob(statement, i);
+ size_t size = sqlite3_column_bytes(statement, i);
+ status = window->putBlob(addedRows, i, blob, size);
+ if (status) {
+ LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
+ size, startPos + addedRows, i, status);
windowFull = true;
break;
}
- window->copyIn(offset, blob, size);
-
- field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i);
- fieldSlot->type = FIELD_TYPE_BLOB;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = size;
- LOG_WINDOW("%d,%d is Blob with %u bytes @ %d",
- startPos + addedRows, i, size, offset);
+ LOG_WINDOW("%d,%d is Blob with %u bytes",
+ startPos + addedRows, i, size);
} else if (type == SQLITE_NULL) {
// NULL field
- if (!window->putNull(addedRows, i)) {
- LOG_WINDOW("Failed allocating space for a null in column %d", i);
+ status = window->putNull(addedRows, i);
+ if (status) {
+ LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
+ i, status);
windowFull = true;
break;
}
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
index 81c52b0..3e00747 100644
--- a/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
index 15500c3..c760936 100644
--- a/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
index 9e1c42a..f8092ea 100755..100644
--- a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
index 5563c80..3a21d35 100755..100644
--- a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
index 72d3a08..19558e9 100755..100644
--- a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
index 7568b30..0f3b444 100755..100644
--- a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_disabled.png b/core/res/res/drawable-hdpi/stat_notify_disabled.png
new file mode 100644
index 0000000..5b5a7dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
index f3aaa27..8148ab8 100644
--- a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_speakerphone.png b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
index a9af4a8..c63ca94 100644
--- a/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
index 85f3cb2..baa5427 100644
--- a/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
index 77f92fb..8e6a93f 100644
--- a/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
index 43c36cb..dbe3953 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
index 53a7eac..da625af 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
index 0818761..fbf7ef0 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
index 8bd86b2..dd2a024 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_disabled.png b/core/res/res/drawable-mdpi/stat_notify_disabled.png
new file mode 100644
index 0000000..9661d31
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_notify_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
index 747cb97..b7e2a6a 100644
--- a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_speakerphone.png b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
index e8c6374..ec7ef0c 100644
--- a/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
index e619ed5..8aa3b9e 100644
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png
index 16632b1..7aa8750 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png
index 6e007c7..d4e4d81 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-xhdpi/list_selector_background_disabled.9.png
index d599976..980bbd7 100644
--- a/core/res/res/drawable-xhdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-xhdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selector_background_focus.9.png b/core/res/res/drawable-xhdpi/list_selector_background_focus.9.png
index 17987f3..79c4577 100644
--- a/core/res/res/drawable-xhdpi/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-xhdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-xhdpi/list_selector_background_longpress.9.png
index 5a64592..73fc783 100644
--- a/core/res/res/drawable-xhdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-xhdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-xhdpi/list_selector_background_pressed.9.png
index 1593577..5b3ebe1 100644
--- a/core/res/res/drawable-xhdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-xhdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_disabled.png b/core/res/res/drawable-xhdpi/stat_notify_disabled.png
new file mode 100644
index 0000000..0a003af
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/stat_notify_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
index 6bb7512..0dbae57 100644
--- a/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_speakerphone.png b/core/res/res/drawable-xhdpi/stat_sys_speakerphone.png
index 3ac1b88..6938ab4 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_transport_control.xml b/core/res/res/layout/keyguard_transport_control.xml
index 2ebe5fc..6e24ce2 100644
--- a/core/res/res/layout/keyguard_transport_control.xml
+++ b/core/res/res/layout/keyguard_transport_control.xml
@@ -66,7 +66,8 @@
android:src="@drawable/ic_media_rew"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"/>
+ android:padding="10dip"
+ android:contentDescription="@string/lockscreen_transport_prev_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -80,7 +81,8 @@
android:clickable="true"
android:src="@drawable/ic_media_play"
android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"/>
+ android:padding="10dip"
+ android:contentDescription="@string/lockscreen_transport_play_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -94,7 +96,8 @@
android:clickable="true"
android:src="@drawable/ic_media_ff"
android:background="?android:attr/selectableItemBackground"
- android:padding="10dip"/>
+ android:padding="10dip"
+ android:contentDescription="@string/lockscreen_transport_next_description"/>
</FrameLayout>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
index fb900f7..d3fa7e9 100644
--- a/core/res/res/layout/volume_adjust_item.xml
+++ b/core/res/res/layout/volume_adjust_item.xml
@@ -27,7 +27,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dip"
- android:layout_marginLeft="8dip"
android:background="?attr/selectableItemBackground"
/>
@@ -38,8 +37,6 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="16dip"
- android:layout_marginRight="8dip" />
+ android:layout_marginRight="16dip" />
</LinearLayout>
-
-
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b1dc252..fb4783b 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -953,7 +953,7 @@
social updates from your friends. Malicious apps can use this to pretend to be a friend
and trick you into revealing passwords or other confidential information.</string>
-
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCalendar">read calendar events plus confidential information</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1831,6 +1831,17 @@
<string name="lockscreen_permanent_disabled_sim_instructions">Your SIM card is permanently disabled.\n
Please contact your wireless service provider to obtain another SIM card.</string>
+ <!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
+ <string name="lockscreen_transport_prev_description">Previous track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button goes to next track. -->
+ <string name="lockscreen_transport_next_description">Next track button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="lockscreen_transport_pause_description">Pause button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="lockscreen_transport_play_description">Play button</string>
+ <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
+ <string name="lockscreen_transport_stop_description">Stop button</string>
+
<!-- Shown in the lock screen when there is emergency calls only mode. -->
<string name="emergency_calls_only" msgid="2485604591272668370">Emergency calls only</string>
@@ -1958,6 +1969,15 @@
<!-- Displayed on lock screen's right tab - turn sound off -->
<string name="lockscreen_sound_off_label">Sound off</string>
+ <!-- Accessibility description sent when user starts drawing a lock pattern. [CHAR LIMIT=NONE] -->
+ <string name="lockscreen_access_pattern_start">Pattern started</string>
+ <!-- Accessibility description sent when the pattern times out and is cleared. [CHAR LIMIT=NONE] -->
+ <string name="lockscreen_access_pattern_cleared">Pattern cleared</string>
+ <!-- Accessibility description sent when user adds a cell to the pattern. [CHAR LIMIT=NONE] -->
+ <string name="lockscreen_access_pattern_cell_added">Cell added</string>
+ <!-- Accessibility description sent when user completes drawing a pattern. [CHAR LIMIT=NONE] -->
+ <string name="lockscreen_access_pattern_detected">Pattern completed</string>
+
<!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="password_keyboard_label_symbol_key">\?123</string>
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index 629ceb3..66edb7a 100755
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -27,35 +27,33 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
- $(LOCAL_PATH)/notifications/ogg/Altair.ogg:system/media/audio/notifications/Altair.ogg \
$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
- $(LOCAL_PATH)/notifications/ogg/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
- $(LOCAL_PATH)/notifications/ogg/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
- $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
- $(LOCAL_PATH)/ringtones/ogg/Acheron.ogg:system/media/audio/ringtones/Acheron.ogg \
$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
- $(LOCAL_PATH)/ringtones/ogg/Cassiopeia.ogg:system/media/audio/ringtones/Cassiopeia.ogg \
$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
- $(LOCAL_PATH)/ringtones/ogg/Nasqueron.ogg:system/media/audio/ringtones/Nasqueron.ogg \
$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
@@ -66,4 +64,4 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
$(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
- $(LOCAL_PATH)/ringtones/ogg/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
+ $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg
index ed845be..e7928e4 100644
--- a/data/sounds/effects/ogg/Lock.ogg
+++ b/data/sounds/effects/ogg/Lock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg
index 0a47b5c..cca9594 100644
--- a/data/sounds/effects/ogg/Unlock.ogg
+++ b/data/sounds/effects/ogg/Unlock.ogg
Binary files differ
diff --git a/data/sounds/effects/wav/Lock.wav b/data/sounds/effects/wav/Lock.wav
index 1655a89..b807636 100644
--- a/data/sounds/effects/wav/Lock.wav
+++ b/data/sounds/effects/wav/Lock.wav
Binary files differ
diff --git a/data/sounds/effects/wav/Unlock.wav b/data/sounds/effects/wav/Unlock.wav
index 2229d03..e1d475e 100644
--- a/data/sounds/effects/wav/Unlock.wav
+++ b/data/sounds/effects/wav/Unlock.wav
Binary files differ
diff --git a/data/sounds/notifications/ogg/Acrux.ogg b/data/sounds/notifications/ogg/Acrux.ogg
new file mode 100644
index 0000000..74ad149
--- /dev/null
+++ b/data/sounds/notifications/ogg/Acrux.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Bellatrix.ogg b/data/sounds/notifications/ogg/Bellatrix.ogg
new file mode 100644
index 0000000..37a1bdc
--- /dev/null
+++ b/data/sounds/notifications/ogg/Bellatrix.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Pollux.ogg b/data/sounds/notifications/ogg/Pollux.ogg
new file mode 100644
index 0000000..2f907fd
--- /dev/null
+++ b/data/sounds/notifications/ogg/Pollux.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Tejat.ogg b/data/sounds/notifications/ogg/Tejat.ogg
new file mode 100644
index 0000000..04ba06c
--- /dev/null
+++ b/data/sounds/notifications/ogg/Tejat.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Girtab.ogg b/data/sounds/ringtones/ogg/Girtab.ogg
new file mode 100644
index 0000000..f6b3a35
--- /dev/null
+++ b/data/sounds/ringtones/ogg/Girtab.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Zeta.ogg b/data/sounds/ringtones/ogg/Zeta.ogg
new file mode 100644
index 0000000..ef64d2f
--- /dev/null
+++ b/data/sounds/ringtones/ogg/Zeta.ogg
Binary files differ
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index ccc63a2..137f138 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -14,7 +14,7 @@ page.title=Android Supported Media Formats
<h2>See also</h2>
<ol>
-<li><a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a></li>
+<li><a href="{@docRoot}guide/topics/media/index.html">Multimedia and Camera</a></li>
</ol>
<h2>Key classes</h2>
diff --git a/docs/html/guide/developing/devices/emulator.jd b/docs/html/guide/developing/devices/emulator.jd
index fe00531..8211275 100644
--- a/docs/html/guide/developing/devices/emulator.jd
+++ b/docs/html/guide/developing/devices/emulator.jd
@@ -32,6 +32,12 @@ parent.link=index.html
</ol>
</li>
</ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a></li>
+ <li><a href="{@docRoot}guide/developing/devices/managing-avds.html">Managing AVDs with AVD Manager</a></li>
+ </ol>
</div>
</div>
@@ -63,8 +69,6 @@ on the data channel.</p>
-
-
<h2 id="overview">Overview</h2>
<p>The Android emulator is a QEMU-based application that provides a virtual ARM
@@ -166,7 +170,8 @@ on the Emulator</a> for information about how to install your application.</p>
<p>To stop an emulator instance, just close the emulator's window.</p>
-
+<p>For a reference of the emulator's startup commands and keyboard mapping, see
+the <a href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a> document.</p>
diff --git a/docs/html/guide/developing/tools/emulator.jd b/docs/html/guide/developing/tools/emulator.jd
index ff667f2..5151ec1 100644
--- a/docs/html/guide/developing/tools/emulator.jd
+++ b/docs/html/guide/developing/tools/emulator.jd
@@ -3,6 +3,25 @@ parent.title=Tools
parent.link=index.html
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#startup-options">Emulator Startup Options</a></li>
+ <li><a href="#KeyMapping">Emulator Keyboard Mapping</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/developing/devices/emulator.html">Using the Android Emulator</a></li>
+ <li><a href="{@docRoot}guide/developing/devices/index.html">Managing Virtual Devices</a></li>
+ </ol>
+
+</div>
+</div>
+
+
<p>The Android SDK includes a mobile device emulator &mdash; a virtual mobile device
that runs on your computer. The emulator lets you develop and test
Android applications without using a physical device.</p>
@@ -451,7 +470,10 @@ Managing Virtual Devices with AVD Manager</a>.
<td>See comments for <code>-skin</code>, above.</td></tr>
</table>
-<h2>Emulator Keyboard Mapping</h2>
+
+
+<h2 id="KeyMapping">Emulator Keyboard Mapping</h2>
+
<p>The table below summarizes the mappings between the emulator keys and and
the keys of your keyboard. </p>
<p class="table-caption"><strong>Table 2.</strong> Emulator keyboard mapping</p>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 18d9a48..f3540e2 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -233,23 +233,32 @@
</ul>
<ul>
- <li class="toggle-list">
+ <li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/graphics/index.html">
<span class="en">Graphics</span>
- </a></div>
+ </a><span class="new-child">new!</span></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/2d-graphics.html">
- <span class="en">2D Graphics</span>
- </a></li>
+ <span class="en">Canvas and Drawables</span></a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/graphics/hardware-accel.html">
+ <span class="en">Hardware Acceleration</span></a>
+ <span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/opengl.html">
- <span class="en">3D with OpenGL</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/topics/graphics/animation.html">
- <span class="en">Property Animation</span>
- </a></li>
+ <span class="en">OpenGL</span>
+ </a><span class="new">updated</span></li>
+ </ul>
+ </li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>guide/topics/graphics/animation.html">
+ <span class="en">Animation</span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>guide/topics/graphics/prop-animation.html">
+ <span class="en">Property Animation</span></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/view-animation.html">
- <span class="en">View Animation</span>
- </a></li>
+ <span class="en">View Animation</span></a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/graphics/drawable-animation.html">
+ <span class="en">Drawable Animation</span></a></li>
</ul>
</li>
<li class="toggle-list">
@@ -258,7 +267,7 @@
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/renderscript/graphics.html">
- <span class="en">3D Graphics</span>
+ <span class="en">Graphics</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>guide/topics/renderscript/compute.html">
@@ -268,9 +277,26 @@
</ul>
</li>
- <li><a href="<?cs var:toroot ?>guide/topics/media/index.html">
- <span class="en">Media</span>
- </a></li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>guide/topics/media/index.html">
+ <span class="en">Multimedia and Camera</span>
+ </a><span class="new">updated</span></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>guide/topics/media/mediaplayer.html">
+ <span class="en">Media Playback</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/media/jetplayer.html">
+ <span class="en">JetPlayer</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/media/camera.html">
+ <span class="en">Camera</span></a>
+ <span class="new">new!</span>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/media/audio-capture.html">
+ <span class="en">Audio Capture</span></a>
+ </li>
+ </ul>
+ </li>
<li>
<a href="<?cs var:toroot ?>guide/topics/clipboard/copy-paste.html">
<span class="en">Copy and Paste</span>
diff --git a/docs/html/guide/practices/screen-compat-mode.jd b/docs/html/guide/practices/screen-compat-mode.jd
index a792386..6a77d60 100644
--- a/docs/html/guide/practices/screen-compat-mode.jd
+++ b/docs/html/guide/practices/screen-compat-mode.jd
@@ -48,7 +48,7 @@ Mode</a>.</p>
to resize for larger screens such as tablets. Since Android 1.6, Android has supported a
variety of screen sizes and does most of the work to resize application layouts so that they
properly fit each screen. However, if your application does not successfully follow the guide to
-<a href="{@docRoot}guide/topics/practices/screens_support.html">Supporting Multiple Screens</a>,
+<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>,
then it might encounter some rendering issues on larger screens. For applications with this
problem, screen compatibility mode can make the application a little more usable on larger
screens.</p>
@@ -83,7 +83,7 @@ which will usually cause artifacts such as blurring and pixelation in your UI.
<p>This was introduced with Android 3.2 to further
assist applications on the latest tablet devices when the applications have not yet
implemented techniques for <a
-href="{@docRoot}guide/topics/practices/screens_support.html">Supporting Multiple
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
Screens</a>.</p>
<p>In general, large screen devices running Android 3.2 or higher allow users to enable
screen compatibility mode when the application does not <strong>explicitly declare that it supports
@@ -211,7 +211,7 @@ href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
which you should want your application to run&mdash;it causes pixelation and blurring in your UI,
due to zooming. The proper way to make your application work well on large screens is to follow the
guide to <a
-href="{@docRoot}guide/topics/practices/screens_support.html">Supporting Multiple Screens</a> and
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a> and
provide alternative layouts for different screen sizes.</p>
<p>By default, when you've set either <a
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index 618cdf8..ac2b47c 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -1,296 +1,484 @@
-page.title=2D Graphics
+page.title=Canvas and Drawables
parent.title=Graphics
parent.link=index.html
@jd:body
-
<div id="qv-wrapper">
<div id="qv">
- <h2>In this document</h2>
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#draw-with-canvas">Draw with a Canvas</a>
<ol>
- <li><a href="#drawables">Drawables</a>
+ <li><a href="#on-view">On a View</a></li>
+ <li><a href="#on-surfaceview">On a SurfaceView</a></li>
+ </ol>
+ </li>
+ <li><a href="#drawables">Drawables</a>
<ol>
<li><a href="#drawables-from-images">Creating from resource images</a></li>
<li><a href="#drawables-from-xml">Creating from resource XML</a></li>
</ol>
- </li>
- <li><a href="#shape-drawable">Shape Drawable</a></li>
- <!-- <li><a href="#state-list">StateListDrawable</a></li> -->
- <li><a href="#nine-patch">Nine-patch</a></li>
- </ol>
+ </li>
+ <li><a href="#shape-drawable">Shape Drawable</a></li>
+ <li><a href="#nine-patch">Nine-patch</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL with the Framework
+APIs</a></li>
+ <li><a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a></li>
+ </ol>
</div>
</div>
-<p>Android offers a custom 2D graphics library for drawing and animating shapes and images.
-The {@link android.graphics.drawable} and {@link android.view.animation}
-packages are where you'll find the common classes used for drawing and animating in two-dimensions.
+<p>The Android framework APIs provides a set 2D drawing APIs that allow you to render your own
+custom graphics onto a canvas or to modify existing Views to customize their look and feel.
+When drawing 2D graphics, you'll typically do so in one of two ways:</p>
+
+<ol type="a">
+ <li>Draw your graphics or animations into a View object from your layout. In this manner,
+ the drawing of your graphics is handled by the system's
+ normal View hierarchy drawing process &mdash; you simply define the graphics to go inside the View.</li>
+ <li>Draw your graphics directly to a Canvas. This way, you personally call the appropriate class's
+ {@link android.view.View#onDraw onDraw()} method (passing it your Canvas), or one of the Canvas
+<code>draw...()</code> methods (like
+ <code>{@link android.graphics.Canvas#drawPicture(Picture,Rect) drawPicture()}</code>). In doing so, you are also in
+ control of any animation.</li>
+</ol>
+
+<p>Option "a," drawing to a View, is your best choice when you want to draw simple graphics that do not
+need to change dynamically and are not part of a performance-intensive game. For example, you should
+draw your graphics into a View when you want to display a static graphic or predefined animation, within
+an otherwise static application. Read <a href="#drawables">Drawables</a> for more information.</li>
</p>
-<p>This document offers an introduction to drawing graphics in your Android application.
-We'll discuss the basics of using Drawable objects to draw
-graphics, how to use a couple subclasses of the Drawable class, and how to
-create animations that either tween (move, stretch, rotate) a single graphic
-or animate a series of graphics (like a roll of film).</p>
-
-
-<h2 id="drawables">Drawables</h2>
-
-<p>A {@link android.graphics.drawable.Drawable} is a general abstraction for "something that can be drawn."
-You'll discover that the Drawable class extends to define a variety of specific kinds of drawable graphics,
-including {@link android.graphics.drawable.BitmapDrawable}, {@link android.graphics.drawable.ShapeDrawable},
-{@link android.graphics.drawable.PictureDrawable}, {@link android.graphics.drawable.LayerDrawable}, and several more.
-Of course, you can also extend these to define your own custom Drawable objects that behave in unique ways.</p>
-
-<p>There are three ways to define and instantiate a Drawable: using an image saved in your project resources;
-using an XML file that defines the Drawable properties; or using the normal class constructors. Below, we'll discuss
-each the first two techniques (using constructors is nothing new for an experienced developer).</p>
-
-
-<h3 id="drawables-from-images">Creating from resource images</h3>
-
-<p>A simple way to add graphics to your application is by referencing an image file from your project resources.
-Supported file types are PNG (preferred), JPG (acceptable) and GIF (discouraged). This technique would
-obviously be preferred for application icons, logos, or other graphics such as those used in a game.</p>
-
-<p>To use an image resource, just add your file to the <code>res/drawable/</code> directory of your project.
-From there, you can reference it from your code or your XML layout.
-Either way, it is referred using a resource ID, which is the file name without the file type
-extension (E.g., <code>my_image.png</code> is referenced as <var>my_image</var>).</p>
-
-<p class="note"><strong>Note:</strong> Image resources placed in <code>res/drawable/</code> may be
-automatically optimized with lossless image compression by the
-<code>aapt</code> tool during the build process. For example, a true-color PNG that does
-not require more than 256 colors may be converted to an 8-bit PNG with a color palette. This
-will result in an image of equal quality but which requires less memory. So be aware that the
-image binaries placed in this directory can change during the build. If you plan on reading
-an image as a bit stream in order to convert it to a bitmap, put your images in the <code>res/raw/</code>
-folder instead, where they will not be optimized.</p>
-
-<h4>Example code</h4>
-<p>The following code snippet demonstrates how to build an {@link android.widget.ImageView} that uses an image
-from drawable resources and add it to the layout.</p>
+<p>Option "b," drawing to a Canvas, is better when your application needs to regularly re-draw itself.
+Applications such as video games should be drawing to the Canvas on its own. However, there's more than
+one way to do this:</p>
+
+<ul>
+ <li>In the same thread as your UI Activity, wherein you create a custom View component in
+ your layout, call <code>{@link android.view.View#invalidate()}</code> and then handle the
+ <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code> callback.</li>
+ <li>Or, in a separate thread, wherein you manage a {@link android.view.SurfaceView} and
+ perform draws to the Canvas as fast as your thread is capable
+ (you do not need to request <code>invalidate()</code>).</li>
+</ul>
+
+<h2 id="draw-with-canvas">Draw with a Canvas</h2>
+
+<p>When you're writing an application in which you would like to perform specialized drawing
+and/or control the animation of graphics,
+you should do so by drawing through a {@link android.graphics.Canvas}. A Canvas works for you as
+a pretense, or interface, to the actual surface upon which your graphics will be drawn &mdash; it
+holds all of your "draw" calls. Via the Canvas, your drawing is actually performed upon an
+underlying {@link android.graphics.Bitmap}, which is placed into the window.</p>
+
+<p>In the event that you're drawing within the <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code>
+callback method, the Canvas is provided for you and you need only place your drawing calls upon it.
+You can also acquire a Canvas from <code>{@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()}</code>,
+when dealing with a SurfaceView object. (Both of these scenarios are discussed in the following sections.)
+However, if you need to create a new Canvas, then you must define the {@link android.graphics.Bitmap}
+upon which drawing will actually be performed. The Bitmap is always required for a Canvas. You can set up
+a new Canvas like this:</p>
<pre>
-LinearLayout mLinearLayout;
-
-protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Create a LinearLayout in which to add the ImageView
- mLinearLayout = new LinearLayout(this);
-
- // Instantiate an ImageView and define its properties
- ImageView i = new ImageView(this);
- i.setImageResource(R.drawable.my_image);
- i.setAdjustViewBounds(true); // set the ImageView bounds to match the Drawable's dimensions
- i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
- // Add the ImageView to the layout and set the layout as the content view
- mLinearLayout.addView(i);
- setContentView(mLinearLayout);
-}
+Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+Canvas c = new Canvas(b);
</pre>
-<p>In other cases, you may want to handle your image resource as a
-{@link android.graphics.drawable.Drawable} object.
-To do so, create a Drawable from the resource like so:
-<pre>
-Resources res = mContext.getResources();
-Drawable myImage = res.getDrawable(R.drawable.my_image);
-</pre>
-
-<p class="warning"><strong>Note:</strong> Each unique resource in your project can maintain only one
-state, no matter how many different objects you may instantiate for it. For example, if you instantiate two
-Drawable objects from the same image resource, then change a property (such as the alpha) for one of the
-Drawables, then it will also affect the other. So when dealing with multiple instances of an image resource,
-instead of directly transforming the Drawable, you should perform a <a href="#tween-animation">tween animation</a>.</p>
+<p>Now your Canvas will draw onto the defined Bitmap. After drawing upon it with the Canvas, you can then carry your
+Bitmap to another Canvas with one of the <code>{@link android.graphics.Canvas#drawBitmap(Bitmap,Matrix,Paint)
+Canvas.drawBitmap(Bitmap,...)}</code> methods. It's recommended that you ultimately draw your final
+graphics through a Canvas offered to you
+by <code>{@link android.view.View#onDraw(Canvas) View.onDraw()}</code> or
+<code>{@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()}</code> (see the following sections).</p>
+
+<p>The {@link android.graphics.Canvas} class has its own set of drawing methods that you can use,
+like <code>drawBitmap(...)</code>, <code>drawRect(...)</code>, <code>drawText(...)</code>, and many more.
+Other classes that you might use also have <code>draw()</code> methods. For example, you'll probably
+have some {@link android.graphics.drawable.Drawable} objects that you want to put on the Canvas. Drawable
+has its own <code>{@link android.graphics.drawable.Drawable#draw(Canvas) draw()}</code> method
+that takes your Canvas as an argument.</p>
+
+
+<h3 id="on-view">On a View</h3>
+
+<p>If your application does not require a significant amount of processing or
+frame-rate speed (perhaps for a chess game, a snake game,
+or another slowly-animated application), then you should consider creating a custom View component
+and drawing with a Canvas in <code>{@link android.view.View#onDraw(Canvas) View.onDraw()}</code>.
+The most convenient aspect of doing so is that the Android framework will
+provide you with a pre-defined Canvas to which you will place your drawing calls.</p>
+
+<p>To start, extend the {@link android.view.View} class (or descendant thereof) and define
+the <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code> callback method. This method will be called by the Android
+framework to request that your View draw itself. This is where you will perform all your calls
+to draw through the {@link android.graphics.Canvas}, which is passed to you through the <code>onDraw()</code> callback.</p>
+
+<p>The Android framework will only call <code>onDraw()</code> as necessary. Each time that
+your application is prepared to be drawn, you must request your View be invalidated by calling
+<code>{@link android.view.View#invalidate()}</code>. This indicates that you'd like your View to be drawn and
+Android will then call your <code>onDraw()</code> method (though is not guaranteed that the callback will
+be instantaneous). </p>
+
+<p>Inside your View component's <code>onDraw()</code>, use the Canvas given to you for all your drawing,
+using various <code>Canvas.draw...()</code> methods, or other class <code>draw()</code> methods that
+take your Canvas as an argument. Once your <code>onDraw()</code> is complete, the Android framework will
+use your Canvas to draw a Bitmap handled by the system.</p>
+
+<p class="note"><strong>Note: </strong> In order to request an invalidate from a thread other than your main
+Activity's thread, you must call <code>{@link android.view.View#postInvalidate()}</code>.</p>
+
+<p>Also read <a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a>
+for a guide to extending a View class, and <a href="2d-graphics.html">2D Graphics: Drawables</a> for
+information on using Drawable objects like images from your resources and other primitive shapes.</p>
+
+<p>For a sample application, see the Snake game, in the SDK samples folder:
+<code>&lt;your-sdk-directory>/samples/Snake/</code>.</p>
+
+<h3 id="on-surfaceview">On a SurfaceView</h3>
+
+<p>The {@link android.view.SurfaceView} is a special subclass of View that offers a dedicated
+drawing surface within the View hierarchy. The aim is to offer this drawing surface to
+an application's secondary thread, so that the application isn't required
+to wait until the system's View hierarchy is ready to draw. Instead, a secondary thread
+that has reference to a SurfaceView can draw to its own Canvas at its own pace.</p>
+
+<p>To begin, you need to create a new class that extends {@link android.view.SurfaceView}. The class should also
+implement {@link android.view.SurfaceHolder.Callback}. This subclass is an interface that will notify you
+with information about the underlying {@link android.view.Surface}, such as when it is created, changed, or destroyed.
+These events are important so that you know when you can start drawing, whether you need
+to make adjustments based on new surface properties, and when to stop drawing and potentially
+kill some tasks. Inside your SurfaceView class is also a good place to define your secondary Thread class, which will
+perform all the drawing procedures to your Canvas.</p>
+
+<p>Instead of handling the Surface object directly, you should handle it via
+a {@link android.view.SurfaceHolder}. So, when your SurfaceView is initialized, get the SurfaceHolder by calling
+<code>{@link android.view.SurfaceView#getHolder()}</code>. You should then notify the SurfaceHolder that you'd
+like to receive SurfaceHolder callbacks (from {@link android.view.SurfaceHolder.Callback}) by calling
+{@link android.view.SurfaceHolder#addCallback(SurfaceHolder.Callback) addCallback()}
+(pass it <var>this</var>). Then override each of the
+{@link android.view.SurfaceHolder.Callback} methods inside your SurfaceView class.</p>
+
+<p>In order to draw to the Surface Canvas from within your second thread, you must pass the thread your SurfaceHandler
+and retrieve the Canvas with <code>{@link android.view.SurfaceHolder#lockCanvas() lockCanvas()}</code>.
+You can now take the Canvas given to you by the SurfaceHolder and do your necessary drawing upon it.
+Once you're done drawing with the Canvas, call
+<code>{@link android.view.SurfaceHolder#unlockCanvasAndPost(Canvas) unlockCanvasAndPost()}</code>, passing it
+your Canvas object. The Surface will now draw the Canvas as you left it. Perform this sequence of locking and
+unlocking the canvas each time you want to redraw.</p>
+
+<p class="note"><strong>Note:</strong> On each pass you retrieve the Canvas from the SurfaceHolder,
+the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the
+entire surface. For example, you can clear the previous state of the Canvas by filling in a color
+with <code>{@link android.graphics.Canvas#drawColor(int) drawColor()}</code> or setting a background image
+with <code>{@link android.graphics.Canvas#drawBitmap(Bitmap,Rect,RectF,Paint) drawBitmap()}</code>. Otherwise,
+you will see traces of the drawings you previously performed.</p>
+
+
+<p>For a sample application, see the Lunar Lander game, in the SDK samples folder:
+<code>&lt;your-sdk-directory>/samples/LunarLander/</code>. Or,
+browse the source in the <a href="{@docRoot}guide/samples/index.html">Sample Code</a> section.</p>
-<h4>Example XML</h4>
-<p>The XML snippet below shows how to add a resource Drawable to an
-{@link android.widget.ImageView} in the XML layout (with some red tint just for fun).
-<pre>
-&lt;ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:tint="#55ff0000"
- android:src="@drawable/my_image"/>
-</pre>
-<p>For more information on using project resources, read about
- <a href="{@docRoot}guide/topics/resources/index.html">Resources and Assets</a>.</p>
+<h2 id="drawables">Drawables</h2>
+<p>Android offers a custom 2D graphics library for drawing shapes and images.
+ The {@link android.graphics.drawable} package is where you'll find the common classes used for
+ drawing in two-dimensions.</p>
+<p>This document discusses the basics of using Drawable objects to draw graphics and how to use a
+couple subclasses of the Drawable class. For information on using Drawables to do frame-by-frame
+animation, see <a href="{@docRoot}guide/topics/animation/frame-animation.html">Frame-by-Frame
+Animation</a>.</p>
-<h3 id="drawables-from-xml">Creating from resource XML</h3>
+<p>A {@link android.graphics.drawable.Drawable} is a general abstraction for "something that can be
+ drawn." You'll discover that the Drawable class extends to define a variety of specific kinds of
+drawable graphics, including {@link android.graphics.drawable.BitmapDrawable}, {@link
+ android.graphics.drawable.ShapeDrawable}, {@link android.graphics.drawable.PictureDrawable},
+{@link android.graphics.drawable.LayerDrawable}, and several more. Of course, you can also extend
+these to define your own custom Drawable objects that behave in unique ways.</p>
-<p>By now, you should be familiar with Android's principles of developing a
-<a href="{@docRoot}guide/topics/ui/index.html">User Interface</a>. Hence, you understand the power
-and flexibility inherent in defining objects in XML. This philosophy caries over from Views to Drawables.
-If there is a Drawable object that you'd like to create, which is not initially dependent on variables defined by
-your application code or user interaction, then defining the Drawable in XML is a good option.
-Even if you expect your Drawable to change its properties during the user's experience with your application,
-you should consider defining the object in XML, as you can always modify properties once it is instantiated.</p>
+<p>There are three ways to define and instantiate a Drawable: using an image saved in your project
+ resources; using an XML file that defines the Drawable properties; or using the normal class
+constructors. Below, we'll discuss each the first two techniques (using constructors is nothing new
+for an experienced developer).</p>
-<p>Once you've defined your Drawable in XML, save the file in the <code>res/drawable/</code> directory of
-your project. Then, retrieve and instantiate the object by calling
-{@link android.content.res.Resources#getDrawable(int) Resources.getDrawable()}, passing it the resource ID
-of your XML file. (See the <a href="#drawable-xml-example">example below</a>.)</p>
-<p>Any Drawable subclass that supports the <code>inflate()</code> method can be defined in
-XML and instantiated by your application.
-Each Drawable that supports XML inflation utilizes specific XML attributes that help define the object
-properties (see the class reference to see what these are). See the class documentation for each
-Drawable subclass for information on how to define it in XML.
+<h3 id="drawables-from-images">Creating from resource images</h3>
-<h4 id="drawable-xml-example">Example</h4>
-<p>Here's some XML that defines a TransitionDrawable:</p>
-<pre>
-&lt;transition xmlns:android="http://schemas.android.com/apk/res/android">
- &lt;item android:drawable="&#64;drawable/image_expand">
- &lt;item android:drawable="&#64;drawable/image_collapse">
-&lt;/transition>
-</pre>
+<p>A simple way to add graphics to your application is by referencing an image file from your
+ project resources. Supported file types are PNG (preferred), JPG (acceptable) and GIF
+(discouraged). This technique would obviously be preferred for application icons, logos, or other
+graphics such as those used in a game.</p>
+
+<p>To use an image resource, just add your file to the <code>res/drawable/</code> directory of your
+ project. From there, you can reference it from your code or your XML layout.
+ Either way, it is referred using a resource ID, which is the file name without the file type
+ extension (E.g., <code>my_image.png</code> is referenced as <var>my_image</var>).</p>
+
+<p class="note"><strong>Note:</strong> Image resources placed in <code>res/drawable/</code> may be
+ automatically optimized with lossless image compression by the
+ <code>aapt</code> tool during the build process. For example, a true-color PNG that does
+ not require more than 256 colors may be converted to an 8-bit PNG with a color palette. This
+ will result in an image of equal quality but which requires less memory. So be aware that the
+ image binaries placed in this directory can change during the build. If you plan on reading
+ an image as a bit stream in order to convert it to a bitmap, put your images in the
+ <code>res/raw/</code> folder instead, where they will not be optimized.</p>
-<p>With this XML saved in the file <code>res/drawable/expand_collapse.xml</code>,
-the following code will instantiate the TransitionDrawable and set it as the content of an ImageView:</p>
+<h4>Example code</h4>
+<p>The following code snippet demonstrates how to build an {@link android.widget.ImageView} that
+ uses an image from drawable resources and add it to the layout.</p>
<pre>
-Resources res = mContext.getResources();
-TransitionDrawable transition = (TransitionDrawable) res.getDrawable(R.drawable.expand_collapse);
-ImageView image = (ImageView) findViewById(R.id.toggle_image);
-image.setImageDrawable(transition);
-</pre>
-<p>Then this transition can be run forward (for 1 second) with:</p>
-<pre>transition.startTransition(1000);</pre>
-
-<p>Refer to the Drawable classes listed above for more information on the XML attributes supported by each.</p>
+ LinearLayout mLinearLayout;
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Create a LinearLayout in which to add the ImageView
+ mLinearLayout = new LinearLayout(this);
-<h2 id="shape-drawable">Shape Drawable</h2>
+ // Instantiate an ImageView and define its properties
+ ImageView i = new ImageView(this);
+ i.setImageResource(R.drawable.my_image);
+ i.setAdjustViewBounds(true); // set the ImageView bounds to match the Drawable's dimensions
+ i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
-<p>When you want to dynamically draw some two-dimensional graphics, a {@link android.graphics.drawable.ShapeDrawable}
-object will probably suit your needs. With a ShapeDrawable, you can programmatically draw
-primitive shapes and style them in any way imaginable.</p>
-
-<p>A ShapeDrawable is an extension of {@link android.graphics.drawable.Drawable}, so you can use one where ever
-a Drawable is expected &mdash; perhaps for the background of a View, set with
-{@link android.view.View#setBackgroundDrawable(android.graphics.drawable.Drawable) setBackgroundDrawable()}.
-Of course, you can also draw your shape as its own custom {@link android.view.View},
-to be added to your layout however you please.
-Because the ShapeDrawable has its own <code>draw()</code> method, you can create a subclass of View that
-draws the ShapeDrawable during the <code>View.onDraw()</code> method.
-Here's a basic extension of the View class that does just this, to draw a ShapeDrawable as a View:</p>
-<pre>
-public class CustomDrawableView extends View {
- private ShapeDrawable mDrawable;
-
- public CustomDrawableView(Context context) {
- super(context);
-
- int x = 10;
- int y = 10;
- int width = 300;
- int height = 50;
-
- mDrawable = new ShapeDrawable(new OvalShape());
- mDrawable.getPaint().setColor(0xff74AC23);
- mDrawable.setBounds(x, y, x + width, y + height);
- }
-
- protected void onDraw(Canvas canvas) {
- mDrawable.draw(canvas);
- }
-}
+ // Add the ImageView to the layout and set the layout as the content view
+ mLinearLayout.addView(i);
+ setContentView(mLinearLayout);
+ }
</pre>
+<p>In other cases, you may want to handle your image resource as a
+ {@link android.graphics.drawable.Drawable} object.
+ To do so, create a Drawable from the resource like so:
+ <pre>
+ Resources res = mContext.getResources();
+ Drawable myImage = res.getDrawable(R.drawable.my_image);
+ </pre>
+
+ <p class="warning"><strong>Note:</strong> Each unique resource in your project can maintain only
+one state, no matter how many different objects you may instantiate for it. For example, if you
+ instantiate two Drawable objects from the same image resource, then change a property (such
+as the alpha) for one of the Drawables, then it will also affect the other. So when dealing with
+multiple instances of an image resource, instead of directly transforming the Drawable, you
+should perform a <a href="{@docRoot}guide/topics/graphics/view-animation.html#tween-animation">tween
+animation</a>.</p>
+
+
+ <h4>Example XML</h4>
+ <p>The XML snippet below shows how to add a resource Drawable to an
+ {@link android.widget.ImageView} in the XML layout (with some red tint just for fun).
+ <pre>
+ &lt;ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tint="#55ff0000"
+ android:src="@drawable/my_image"/>
+ </pre>
+ <p>For more information on using project resources, read about
+ <a href="{@docRoot}guide/topics/resources/index.html">Resources and Assets</a>.</p>
+
+
+ <h3 id="drawables-from-xml">Creating from resource XML</h3>
+
+ <p>By now, you should be familiar with Android's principles of developing a
+ <a href="{@docRoot}guide/topics/ui/index.html">User Interface</a>. Hence, you understand the
+power and flexibility inherent in defining objects in XML. This philosophy caries over from Views
+to Drawables. If there is a Drawable object that you'd like to create, which is not initially
+dependent on variables defined by your application code or user interaction, then defining the
+Drawable in XML is a good option. Even if you expect your Drawable to change its properties
+during the user's experience with your application, you should consider defining the object in
+XML, as you can always modify properties once it is instantiated.</p>
+
+ <p>Once you've defined your Drawable in XML, save the file in the <code>res/drawable/</code>
+ directory of your project. Then, retrieve and instantiate the object by calling
+ {@link android.content.res.Resources#getDrawable(int) Resources.getDrawable()}, passing it the
+ resource ID of your XML file. (See the <a href="#drawable-xml-example">example
+below</a>.)</p>
+
+ <p>Any Drawable subclass that supports the <code>inflate()</code> method can be defined in
+ XML and instantiated by your application. Each Drawable that supports XML inflation utilizes
+specific XML attributes that help define the object
+ properties (see the class reference to see what these are). See the class documentation for each
+ Drawable subclass for information on how to define it in XML.
+
+ <h4 id="drawable-xml-example">Example</h4>
+ <p>Here's some XML that defines a TransitionDrawable:</p>
+ <pre>
+ &lt;transition xmlns:android="http://schemas.android.com/apk/res/android">
+ &lt;item android:drawable="&#64;drawable/image_expand">
+ &lt;item android:drawable="&#64;drawable/image_collapse">
+ &lt;/transition>
+ </pre>
+
+ <p>With this XML saved in the file <code>res/drawable/expand_collapse.xml</code>,
+ the following code will instantiate the TransitionDrawable and set it as the content of an
+ ImageView:</p>
+ <pre>
+ Resources res = mContext.getResources();
+ TransitionDrawable transition = (TransitionDrawable)
+res.getDrawable(R.drawable.expand_collapse);
+ ImageView image = (ImageView) findViewById(R.id.toggle_image);
+ image.setImageDrawable(transition);
+ </pre>
+ <p>Then this transition can be run forward (for 1 second) with:</p>
+ <pre>transition.startTransition(1000);</pre>
+
+ <p>Refer to the Drawable classes listed above for more information on the XML attributes
+supported by each.</p>
+
+
+
+ <h2 id="shape-drawable">Shape Drawable</h2>
+
+ <p>When you want to dynamically draw some two-dimensional graphics, a {@link
+ android.graphics.drawable.ShapeDrawable}
+ object will probably suit your needs. With a ShapeDrawable, you can programmatically draw
+ primitive shapes and style them in any way imaginable.</p>
+
+ <p>A ShapeDrawable is an extension of {@link android.graphics.drawable.Drawable}, so you can use
+one where ever
+ a Drawable is expected &mdash; perhaps for the background of a View, set with
+ {@link android.view.View#setBackgroundDrawable(android.graphics.drawable.Drawable)
+ setBackgroundDrawable()}.
+ Of course, you can also draw your shape as its own custom {@link android.view.View},
+ to be added to your layout however you please.
+ Because the ShapeDrawable has its own <code>draw()</code> method, you can create a subclass of
+View that
+ draws the ShapeDrawable during the <code>View.onDraw()</code> method.
+ Here's a basic extension of the View class that does just this, to draw a ShapeDrawable as a
+ View:</p>
+ <pre>
+ public class CustomDrawableView extends View {
+ private ShapeDrawable mDrawable;
+
+ public CustomDrawableView(Context context) {
+ super(context);
+
+ int x = 10;
+ int y = 10;
+ int width = 300;
+ int height = 50;
+
+ mDrawable = new ShapeDrawable(new OvalShape());
+ mDrawable.getPaint().setColor(0xff74AC23);
+ mDrawable.setBounds(x, y, x + width, y + height);
+ }
+
+ protected void onDraw(Canvas canvas) {
+ mDrawable.draw(canvas);
+ }
+ }
+ </pre>
+
+ <p>In the constructor, a ShapeDrawable is defines as an {@link
+ android.graphics.drawable.shapes.OvalShape}.
+ It's then given a color and the bounds of the shape are set. If you do not set the bounds,
+then the
+ shape will not be drawn, whereas if you don't set the color, it will default to black.</p>
+ <p>With the custom View defined, it can be drawn any way you like. With the sample above, we can
+ draw the shape programmatically in an Activity:</p>
+ <pre>
+ CustomDrawableView mCustomDrawableView;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCustomDrawableView = new CustomDrawableView(this);
+
+ setContentView(mCustomDrawableView);
+ }
+ </pre>
+
+ <p>If you'd like to draw this custom drawable from the XML layout instead of from the Activity,
+ then the CustomDrawable class must override the {@link
+ android.view.View#View(android.content.Context, android.util.AttributeSet) View(Context,
+ AttributeSet)} constructor, which is called when
+ instantiating a View via inflation from XML. Then add a CustomDrawable element to the XML,
+ like so:</p>
+ <pre>
+ &lt;com.example.shapedrawable.CustomDrawableView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+ </pre>
+
+ <p>The ShapeDrawable class (like many other Drawable types in the {@link
+android.graphics.drawable} package)
+ allows you to define various properties of the drawable with public methods.
+ Some properties you might want to adjust include
+ alpha transparency, color filter, dither, opacity and color.</p>
+
+ <p>You can also define primitive drawable shapes using XML. For more information, see the
+ section about Shape Drawables in the <a
-<p>In the constructor, a ShapeDrawable is defines as an {@link android.graphics.drawable.shapes.OvalShape}.
-It's then given a color and the bounds of the shape are set. If you do not set the bounds, then the
-shape will not be drawn, whereas if you don't set the color, it will default to black.</p>
-<p>With the custom View defined, it can be drawn any way you like. With the sample above, we can
-draw the shape programmatically in an Activity:</p>
-<pre>
-CustomDrawableView mCustomDrawableView;
-
-protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mCustomDrawableView = new CustomDrawableView(this);
-
- setContentView(mCustomDrawableView);
-}
-</pre>
-
-<p>If you'd like to draw this custom drawable from the XML layout instead of from the Activity,
-then the CustomDrawable class must override the {@link android.view.View#View(android.content.Context, android.util.AttributeSet) View(Context, AttributeSet)} constructor, which is called when
-instantiating a View via inflation from XML. Then add a CustomDrawable element to the XML,
-like so:</p>
-<pre>
-&lt;com.example.shapedrawable.CustomDrawableView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
-</pre>
-
-<p>The ShapeDrawable class (like many other Drawable types in the {@link android.graphics.drawable} package)
-allows you to define various properties of the drawable with public methods.
-Some properties you might want to adjust include
-alpha transparency, color filter, dither, opacity and color.</p>
-
-<p>You can also define primitive drawable shapes using XML. For more information, see the
-section about Shape Drawables in the <a
href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a>
-document.</p>
-
-<!-- TODO
-<h2 id="state-list">StateListDrawable</h2>
-
-<p>A StateListDrawable is an extension of the DrawableContainer class, making it little different.
-The primary distinction is that the
-StateListDrawable manages a collection of images for the Drawable, instead of just one.
-This means that it can switch the image when you want, without switching objects. However, the
-intention of the StateListDrawable is to automatically change the image used based on the state
-of the object it's attached to.
--->
-
-<h2 id="nine-patch">Nine-patch</h2>
-
-<p>A {@link android.graphics.drawable.NinePatchDrawable} graphic is a stretchable bitmap image, which Android
-will automatically resize to accommodate the contents of the View in which you have placed it as the background.
-An example use of a NinePatch is the backgrounds used by standard Android buttons &mdash;
-buttons must stretch to accommodate strings of various lengths. A NinePatch drawable is a standard PNG
-image that includes an extra 1-pixel-wide border. It must be saved with the extension <code>.9.png</code>,
-and saved into the <code>res/drawable/</code> directory of your project.
-</p>
-<p>
- The border is used to define the stretchable and static areas of
- the image. You indicate a stretchable section by drawing one (or more) 1-pixel-wide
- black line(s) in the left and top part of the border (the other border pixels should
- be fully transparent or white). You can have as many stretchable sections as you want:
- their relative size stays the same, so the largest sections always remain the largest.
-</p>
-<p>
- You can also define an optional drawable section of the image (effectively,
- the padding lines) by drawing a line on the right and bottom lines.
- If a View object sets the NinePatch as its background and then specifies the
- View's text, it will stretch itself so that all the text fits inside only
- the area designated by the right and bottom lines (if included). If the
- padding lines are not included, Android uses the left and top lines to
- define this drawable area.
-</p>
-<p>To clarify the difference between the different lines, the left and top lines define
-which pixels of the image are allowed to be replicated in order to stretch the image.
-The bottom and right lines define the relative area within the image that the contents
-of the View are allowed to lie within.</p>
-<p>
- Here is a sample NinePatch file used to define a button:
-</p>
- <img src="{@docRoot}images/ninepatch_raw.png" alt="" />
-
-<p>This NinePatch defines one stretchable area with the left and top lines
-and the drawable area with the bottom and right lines. In the top image, the dotted grey
-lines identify the regions of the image that will be replicated in order to stretch the image. The pink
-rectangle in the bottom image identifies the region in which the contents of the View are allowed.
-If the contents don't fit in this region, then the image will be stretched so that they do.
+ document.</p>
+
+ <!-- TODO
+ <h2 id="state-list">StateListDrawable</h2>
+
+ <p>A StateListDrawable is an extension of the DrawableContainer class, making it little
+different.
+ The primary distinction is that the
+ StateListDrawable manages a collection of images for the Drawable, instead of just one.
+ This means that it can switch the image when you want, without switching objects. However,
+the
+ intention of the StateListDrawable is to automatically change the image used based on the
+state
+ of the object it's attached to.
+ -->
+
+ <h2 id="nine-patch">Nine-patch</h2>
+
+ <p>A {@link android.graphics.drawable.NinePatchDrawable} graphic is a stretchable bitmap
+image, which Android
+ will automatically resize to accommodate the contents of the View in which you have
+placed it as the background.
+ An example use of a NinePatch is the backgrounds used by standard Android buttons &mdash;
+ buttons must stretch to accommodate strings of various lengths. A NinePatch drawable is a
+standard PNG
+ image that includes an extra 1-pixel-wide border. It must be saved with the extension
+ <code>.9.png</code>,
+ and saved into the <code>res/drawable/</code> directory of your project.
+ </p>
+ <p>
+ The border is used to define the stretchable and static areas of
+ the image. You indicate a stretchable section by drawing one (or more) 1-pixel-wide
+ black line(s) in the left and top part of the border (the other border pixels should
+ be fully transparent or white). You can have as many stretchable sections as you want:
+ their relative size stays the same, so the largest sections always remain the largest.
+ </p>
+ <p>
+ You can also define an optional drawable section of the image (effectively,
+ the padding lines) by drawing a line on the right and bottom lines.
+ If a View object sets the NinePatch as its background and then specifies the
+ View's text, it will stretch itself so that all the text fits inside only
+ the area designated by the right and bottom lines (if included). If the
+ padding lines are not included, Android uses the left and top lines to
+ define this drawable area.
+ </p>
+ <p>To clarify the difference between the different lines, the left and top lines define
+ which pixels of the image are allowed to be replicated in order to stretch the image.
+ The bottom and right lines define the relative area within the image that the contents
+ of the View are allowed to lie within.</p>
+ <p>
+ Here is a sample NinePatch file used to define a button:
+ </p>
+ <img src="{@docRoot}images/ninepatch_raw.png" alt="" />
+
+ <p>This NinePatch defines one stretchable area with the left and top lines
+ and the drawable area with the bottom and right lines. In the top image, the dotted grey
+ lines identify the regions of the image that will be replicated in order to stretch the
+image. The pink
+ rectangle in the bottom image identifies the region in which the contents of the View are
+allowed.
+ If the contents don't fit in this region, then the image will be stretched so that they
+do.
</p>
-<p>The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-patch</a> tool offers
- an extremely handy way to create your NinePatch images, using a WYSIWYG graphics editor. It
+<p>The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-patch</a> tool offers
+ an extremely handy way to create your NinePatch images, using a WYSIWYG graphics editor. It
even raises warnings if the region you've defined for the stretchable area is at risk of
producing drawing artifacts as a result of the pixel replication.
</p>
@@ -298,7 +486,8 @@ producing drawing artifacts as a result of the pixel replication.
<h3>Example XML</h3>
<p>Here's some sample layout XML that demonstrates how to add a NinePatch image to a
-couple of buttons. (The NinePatch image is saved as <code>res/drawable/my_button_background.9.png</code>
+couple of buttons. (The NinePatch image is saved as
+<code>res/drawable/my_button_background.9.png</code>
<pre>
&lt;Button id="@+id/tiny"
android:layout_width="wrap_content"
@@ -318,11 +507,12 @@ couple of buttons. (The NinePatch image is saved as <code>res/drawable/my_button
android:textSize="30sp"
android:background="@drawable/my_button_background"/&gt;
</pre>
-<p>Note that the width and height are set to "wrap_content" to make the button fit neatly around the text.
+<p>Note that the width and height are set to "wrap_content" to make the button fit neatly around the
+text.
</p>
-<p>Below are the two buttons rendered from the XML and NinePatch image shown above.
-Notice how the width and height of the button varies with the text, and the background image
+<p>Below are the two buttons rendered from the XML and NinePatch image shown above.
+Notice how the width and height of the button varies with the text, and the background image
stretches to accommodate it.
</p>
diff --git a/docs/html/guide/topics/graphics/animation.jd b/docs/html/guide/topics/graphics/animation.jd
index e7a07e0..e8996f6 100644
--- a/docs/html/guide/topics/graphics/animation.jd
+++ b/docs/html/guide/topics/graphics/animation.jd
@@ -1,949 +1,63 @@
-page.title=Property Animation
-parent.title=Graphics
-parent.link=index.html
+page.title=Animation
@jd:body
<div id="qv-wrapper">
<div id="qv">
- <h2>In this document</h2>
+ <h2>See also</h2>
<ol>
- <li><a href="#what">What is Property Animation?</a>
- <ol>
- <li><a href="#how">How property animation works</a></li>
- </ol>
- </li>
-
- <li><a href="#value-animator">Animating with ValueAnimator</a></li>
-
- <li><a href="#object-animator">Animating with ObjectAnimator</a></li>
-
- <li><a href="#choreography">Choreographing Multiple Animations with
- AnimatorSet</a></li>
-
- <li><a href="#listeners">Animation Listeners</a></li>
-
- <li><a href="#type-evaluator">Using a TypeEvaluator</a></li>
-
- <li><a href="#interpolators">Using Interpolators</a></li>
-
- <li><a href="#keyframes">Specifying Keyframes</a></li>
-
- <li><a href="#layout">Animating Layout Changes to ViewGroups</a></li>
-
- <li><a href="#views">Animating Views</a>
- <ol>
- <li><a href="#view-prop-animator">ViewPropertyAnimator</a></li>
- </ol>
- </li>
-
- <li><a href="#declaring-xml">Declaring Animations in XML</a></li>
- </ol>
-
- <h2>Key classes</h2>
-
+ <li><a href="{@docRoot}guide/topics/graphics/property-animation.html">Property Animation</a></li>
+ <li><a href="{@docRoot}guide/topics/graphics/view-animation.html">View Animation</a></li>
+ <li><a href="{@docRoot}guide/topics/graphics/drawable-animation.html">Drawable Animation</a></li>
<ol>
- <li><code><a href=
- "/reference/android/animation/ValueAnimator.html">ValueAnimator</a></code></li>
-
- <li><code><a href=
- "/reference/android/animation/ObjectAnimator.html">ObjectAnimator</a></code></li>
-
- <li><code><a href=
- "/reference/android/animation/TypeEvaluator.html">TypeEvaluator</a></code></li>
- </ol>
-
- <h2>Related samples</h2>
-
- <ol>
- <li><a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
- Demos</a></li>
- </ol>
</div>
</div>
- <p>Introduced in Android 3.0, the property animation system is a robust framework that allows you
- to animate almost anything. Property animation is not confined to objects drawn on the screen.
- You can define an animation to change any object property over time, regardless of whether it
- draws to the screen or not.The property animation system also has a few advantages over the view
- animation system, which makes it more flexible to use.</p>
+ <p>The Android framework provides two animation systems: property animation
+ (introduced in Android 3.0) and view animation. Both animation systems are viable options,
+ but the property animation system, in general, is the preferred method to use, because it
+ is more flexible and offers more features. In addition to these two systems, you can utilize Drawable
+animation, which allows you to load drawable resources and display them one frame after
+another.</p>
- <p>The view animation system provides the capability to only animate View objects, so if
- you wanted to animate non-View objects, you had to implement your own code to do so. The view
- animation system also was constrained in the fact that it only exposed a few aspects of a View
- object to animate, such as the scaling and rotation of a View but not the background color for
- instance.</p>
+ <p>The view animation system provides the capability to only animate {@link android.view.View}
+objects, so if you wanted to animate non-{@link android.view.View} objects, you have to implement
+your own code to do so. The view animation system is also constrained in the fact that it only
+exposes a few aspects of a {@link android.view.View} object to animate, such as the scaling and
+rotation of a View but not the background color, for instance.</p>
<p>Another disadvantage of the view animation system is that it only modified where the
View was drawn, and not the actual View itself. For instance, if you animated a button to move
across the screen, the button draws correctly, but the actual location where you can click the
- button does not change, so you have to implement your own logic to handle this. With the property
- animation system, these constraints are completely removed, and you can animate any property of
- any object, including View objects, and the object itself is actually modified.</p>
+ button does not change, so you have to implement your own logic to handle this.</p>
+
+ <p>With the property animation system, these constraints are completely removed, and you can animate
+ any property of any object (Views and non-Views) and the object itself is actually modified.
+ The property animation system is also more robust in the way it carries out animation. At
+ a high level, you assign animators to the properties that you want to animate, such as color,
+ position, or size and can define aspects of the animation such as interpolation and
+ synchronization of multiple animators.</p>
<p>The view animation system, however, takes less time to setup and requires less code to write.
If view animation accomplishes everything that you need to do, or if your existing code already
- works the way you want, there is no need to use the property animation system.</p>
-
- <p class="note"><strong>Tip:</strong> To see how the ADT layout editor allows you to develop and
-preview animations in your layout, watch the <a
-href="http://www.youtube.com/watch?v=Oq05KqjXTvs&feature=player_detailpage#t=1709s">Android
-Developer Tools session</a> from Google I/O '11</p>
-
-
- <h2 id="what">What is Property Animation?</h2>
- A property animation changes a property's (a field in
- an object) value over a specified length of time. To animate something, you specify the
- object property that you want to animate, such as an object's position on the screen, how long
- you want to animate it for, and what values you want to animate between. </p>
-
- <p>The property animation system lets you define the following characteristics of an
- animation:</p>
-
- <ul>
- <li>Duration: You can specify the duration of an animation. The default length is 300 ms.</li>
-
- <li>Time interpolation: You can specify how the values for the property are calculated as a
- function of the animation's current elapsed time.</li>
-
- <li>Repeat count and behavior: You can specify whether or not to have an animation repeat when
- it reaches the end of a duration and how many times to repeat the animation. You can also
- specify whether you want the animation to play back in reverse. Setting it to reverse plays
- the animation forwards then backwards repeatedly, until the number of repeats is reached.</li>
-
- <li>Animator sets: You can group animations into logical sets that play together or
- sequentially or after specified delays.</li>
-
- <li>Frame refresh delay: You can specify how often to refresh frames of your animation. The
- default is set to refresh every 10 ms, but the speed in which your application can refresh frames is
- ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.</li>
- </ul>
-
-
- <h3 id="how">How the property animation system works</h3>
-
- <p>First, let's go over how an animation works with a simple example. Figure 1 depicts a
- hypothetical object that is animated with its <code>x</code> property, which represents its
- horizontal location on a screen. The duration of the animation is set to 40 ms and the distance
- to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves
- horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at
- horizontal position 40. This is an example of an animation with linear interpolation, meaning the
- object moves at a constant speed.</p><img src="{@docRoot}images/animation/animation-linear.png">
-
- <p class="img-caption"><strong>Figure 1.</strong> Example of a linear animation</p>
-
- <p>You can also specify animations to have a non-linear interpolation. Figure 2 illustrates a
- hypothetical object that accelerates at the beginning of the animation, and decelerates at the
- end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the
- beginning, this animation accelerates up to the halfway point then decelerates from the
- halfway point until the end of the animation. As Figure 2 shows, the distance traveled
- at the beginning and end of the animation is less than in the middle.</p><img src=
- "{@docRoot}images/animation/animation-nonlinear.png">
-
- <p class="img-caption"><strong>Figure 2.</strong> Example of a non-linear animation</p>
-
- <p>Let's take a detailed look at how the important components of the property animation system
- would calculate animations like the ones illustrated above. Figure 3 depicts how the main classes
- work with one another.</p><img src="{@docRoot}images/animation/valueanimator.png">
-
- <p class="img-caption"><strong>Figure 3.</strong> How animations are calculated</p>
-
- <p>The {@link android.animation.ValueAnimator} object keeps track of your animation's timing,
- such as how long the animation has been running, and the current value of the property that it is
- animating.</p>
-
- <p>The {@link android.animation.ValueAnimator} encapsulates a {@link
- android.animation.TimeInterpolator}, which defines animation interpolation, and a {@link
- android.animation.TypeEvaluator}, which defines how to calculate values for the property being
- animated. For example, in Figure 2, the {@link android.animation.TimeInterpolator} used would be
- {@link android.view.animation.AccelerateDecelerateInterpolator} and the {@link
- android.animation.TypeEvaluator} would be {@link android.animation.IntEvaluator}.</p>
-
- <p>To start an animation, create a {@link android.animation.ValueAnimator} and give it the
- starting and ending values for the property that you want to animate, along with the duration of
- the animation. When you call {@link android.animation.ValueAnimator#start start()} the animation
- begins. During the whole animation, the {@link android.animation.ValueAnimator} calculates an <em>elapsed fraction</em>
- between 0 and 1, based on the duration of the animation and how much time has elapsed. The
- elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0%
- and 1 meaning 100%. For example, in Figure 1, the elapsed fraction at t = 10 ms would be .25
- because the total duration is t = 40 ms.</p>
-
- <p>When the {@link android.animation.ValueAnimator} is done calculating an elapsed fraction, it
- calls the {@link android.animation.TimeInterpolator} that is currently set, to calculate an
- <em>interpolated fraction</em>. An interpolated fraction maps the elapsed fraction to a new
- fraction that takes into account the time interpolation that is set. For example, in Figure 2,
- because the animation slowly accelerates, the interpolated fraction, about .15, is less than the
- elapsed fraction, .25, at t = 10 ms. In Figure 1, the interpolated fraction is always the same as
- the elapsed fraction.</p>
-
- <p>When the interpolated fraction is calculated, {@link android.animation.ValueAnimator} calls
- the appropriate {@link android.animation.TypeEvaluator}, to calculate the value of the
- property that you are animating, based on the interpolated fraction, the starting value, and the
- ending value of the animation. For example, in Figure 2, the interpolated fraction was .15 at t =
- 10 ms, so the value for the property at that time would be .15 X (40 - 0), or 6.</p>
-
- <!-- <p>When the final value is calculated, the {@link android.animation.ValueAnimator} calls the
- {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate
- onAnimationUpdate()} method. Implement this callback to obtain the property value by
- calling {@link android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} and set the
- value for the property in the object that you are animating. Setting the property doesn't redraw
- the object on the screen, so you need to call {@link
- android.view.View#invalidate invalidate()} to refresh the View that the object
- resides in. If the object is actually a View object, then the system calls {@link
- android.view.View#invalidate invalidate()} when the property is changed.
- The system redraws the window and the {@link android.animation.ValueAnimator}
- repeats the process.</p>-->
-
- <p>The <code>com.example.android.apis.animation</code> package in the <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
- Demos</a> sample project provides many examples on how to use the property
- animation system.</p>
-
- <h2>API Overview</h2>
-
- <p>You can find most of the property animation system's APIs in {@link android.animation
- android.animation}. Because the view animation system already
- defines many interpolators in {@link android.view.animation android.view.animation}, you can use
- those interpolators in the property animation system as well. The following tables describe the main
- components of the property animation system.</p>
-
- <p>The {@link android.animation.Animator} class provides the basic structure for creating
- animations. You normally do not use this class directly as it only provides minimal
- functionality that must be extended to fully support animating values. The following
- subclasses extend {@link android.animation.Animator}:
- </p>
- <p class="table-caption"><strong>Table 1.</strong> Animators</p>
- <table>
- <tr>
- <th>Class</th>
-
- <th>Description</th>
- </tr>
-
- <tr>
- <td>{@link android.animation.ValueAnimator}</td>
-
- <td>The main timing engine for property animation that also computes the values for the
- property to be animated. It has all of the core functionality that calculates animation
- values and contains the timing details of each animation, information about whether an
- animation repeats, listeners that receive update events, and the ability to set custom
- types to evaluate. There are two pieces to animating properties: calculating the animated
- values and setting those values on the object and property that is being animated. {@link
- android.animation.ValueAnimator} does not carry out the second piece, so you must listen
- for updates to values calculated by the {@link android.animation.ValueAnimator} and
- modify the objects that you want to animate with your own logic. See the section about
- <a href="#value-animator">Animating with ValueAnimator</a> for more information.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.ObjectAnimator}</td>
-
- <td>A subclass of {@link android.animation.ValueAnimator} that allows you to set a target
- object and object property to animate. This class updates the property accordingly when
- it computes a new value for the animation. You want to use
- {@link android.animation.ObjectAnimator} most of the time,
- because it makes the process of animating values on target objects much easier. However,
- you sometimes want to use {@link android.animation.ValueAnimator} directly because {@link
- android.animation.ObjectAnimator} has a few more restrictions, such as requiring specific
- acessor methods to be present on the target object.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.AnimatorSet}</td>
-
- <td>Provides a mechanism to group animations together so that they run in
- relation to one another. You can set animations to play together, sequentially, or after
- a specified delay. See the section about <a href="#choreography">Choreographing multiple
- animations with Animator Sets</a> for more information.</td>
- </tr>
- </table>
-
-
- <p>Evaluators tell the property animation system how to calculate values for a given
- property. They take the timing data that is provided by an {@link android.animation.Animator}
- class, the animation's start and end value, and calculate the animated values of the property
- based on this data. The property animation system provides the following evaluators:</p>
- <p class="table-caption"><strong>Table 2.</strong> Evaluators</p>
- <table>
- <tr>
- <th>Class/Interface</th>
-
- <th>Description</th>
- </tr>
-
- <tr>
- <td>{@link android.animation.IntEvaluator}</td>
-
- <td>The default evaluator to calculate values for <code>int</code> properties.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.FloatEvaluator}</td>
-
- <td>The default evaluator to calculate values for <code>float</code> properties.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.ArgbEvaluator}</td>
-
- <td>The default evaluator to calculate values for color properties that are represented
- as hexidecimal values.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.TypeEvaluator}</td>
-
- <td>An interface that allows you to create your own evaluator. If you are animating an
- object property that is <em>not</em> an <code>int</code>, <code>float</code>, or color,
- you must implement the {@link android.animation.TypeEvaluator} interface to specify how
- to compute the object property's animated values. You can also specify a custom {@link
- android.animation.TypeEvaluator} for <code>int</code>, <code>float</code>, and color
- values as well, if you want to process those types differently than the default behavior.
- See the section about <a href="#type-evaluator">Using a TypeEvaluator</a> for more
- information on how to write a custom evaluator.</td>
- </tr>
- </table>
-
-
-
-
- <p>A time interpolator defines how specific values in an animation are calculated as a
- function of time. For example, you can specify animations to happen linearly across the whole
- animation, meaning the animation moves evenly the entire time, or you can specify animations
- to use non-linear time, for example, accelerating at the beginning and decelerating at the
- end of the animation. Table 3 describes the interpolators that are contained in {@link
- android.view.animation android.view.animation}. If none of the provided interpolators suits
- your needs, implement the {@link android.animation.TimeInterpolator} interface and create your own. See <a href=
- "#interpolators">Using interpolators</a> for more information on how to write a custom
- interpolator.</p>
- <p class="table-caption"><strong>Table 3.</strong> Interpolators</p>
- <table>
- <tr>
- <th>Class/Interface</th>
-
- <th>Description</th>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.AccelerateDecelerateInterpolator}</td>
-
- <td>An interpolator whose rate of change starts and ends slowly but accelerates
- through the middle.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.AccelerateInterpolator}</td>
-
- <td>An interpolator whose rate of change starts out slowly and then
- accelerates.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.AnticipateInterpolator}</td>
-
- <td>An interpolator whose change starts backward then flings forward.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.AnticipateOvershootInterpolator}</td>
-
- <td>An interpolator whose change starts backward, flings forward and overshoots
- the target value, then finally goes back to the final value.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.BounceInterpolator}</td>
-
- <td>An interpolator whose change bounces at the end.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.CycleInterpolator}</td>
-
- <td>An interpolator whose animation repeats for a specified number of cycles.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.DecelerateInterpolator}</td>
-
- <td>An interpolator whose rate of change starts out quickly and and then
- decelerates.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.LinearInterpolator}</td>
-
- <td>An interpolator whose rate of change is constant.</td>
- </tr>
-
- <tr>
- <td>{@link android.view.animation.OvershootInterpolator}</td>
-
- <td>An interpolator whose change flings forward and overshoots the last value then
- comes back.</td>
- </tr>
-
- <tr>
- <td>{@link android.animation.TimeInterpolator}</td>
-
- <td>An interface that allows you to implement your own interpolator.</td>
- </tr>
- </table>
-
- <h2 id="value-animator">Animating with ValueAnimator</h2>
-
- <p>The {@link android.animation.ValueAnimator} class lets you animate values of some type for the
- duration of an animation by specifying a set of <code>int</code>, <code>float</code>, or color
- values to animate through. You obtain a {@link android.animation.ValueAnimator} by calling one of
- its factory methods: {@link android.animation.ValueAnimator#ofInt ofInt()}, {@link
- android.animation.ValueAnimator#ofFloat ofFloat()}, or {@link
- android.animation.ValueAnimator#ofObject ofObject()}. For example:</p>
- <pre>
-ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
-animation.setDuration(1000);
-animation.start();
-</pre>
-
- <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the
- animation, between 0 and 1, for a duration of 1000 ms, when the <code>start()</code> method
- runs.</p>
-
- <p>You can also specify a custom type to animate by doing the following:</p>
- <pre>
-ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
-animation.setDuration(1000);
-animation.start();
-</pre>
-
- <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the
- animation, between <code>startPropertyValue</code> and <code>endPropertyValue</code> using the
- logic supplied by <code>MyTypeEvaluator</code> for a duration of 1000 ms, when the {@link
- android.animation.ValueAnimator#start start()} method runs.</p>
-
- <p>The previous code snippets, however, has no real effect on an object, because the {@link
- android.animation.ValueAnimator} does not operate on objects or properties directly. The most likely thing
- that you want to do is modify the objects that you want to animate with these calculated values. You do
- this by defining listeners in the {@link android.animation.ValueAnimator} to appropriately handle important events
- during the animation's lifespan, such as frame updates. When implementing the listeners, you can
- obtain the calculated value for that specific frame refresh by calling {@link
- android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()}. For more information on listeners,
- see the section about <a href="#listeners">Animation Listeners</a>.
-
- <h2 id="object-animator">Animating with ObjectAnimator</h2>
-
- <p>The {@link android.animation.ObjectAnimator} is a subclass of the {@link
- android.animation.ValueAnimator} (discussed in the previous section) and combines the timing
- engine and value computation of {@link android.animation.ValueAnimator} with the ability to
- animate a named property of a target object. This makes animating any object much easier, as you
- no longer need to implement the {@link android.animation.ValueAnimator.AnimatorUpdateListener},
- because the animated property updates automatically.</p>
-
- <p>Instantiating an {@link android.animation.ObjectAnimator} is similar to a {@link
- android.animation.ValueAnimator}, but you also specify the object and the name of that object's property (as
- a String) along with the values to animate between:</p>
- <pre>
-ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
-anim.setDuration(1000);
-anim.start();
-</pre>
-
- <p>To have the {@link android.animation.ObjectAnimator} update properties correctly, you must do
- the following:</p>
-
- <ul>
- <li>The object property that you are animating must have a setter function (in camel case) in the form of
- <code>set&lt;propertyName&gt;()</code>. Because the {@link android.animation.ObjectAnimator}
- automatically updates the property during animation, it must be able to access the property
- with this setter method. For example, if the property name is <code>foo</code>, you need to
- have a <code>setFoo()</code> method. If this setter method does not exist, you have three
- options:
-
- <ul>
- <li>Add the setter method to the class if you have the rights to do so.</li>
-
- <li>Use a wrapper class that you have rights to change and have that wrapper receive the
- value with a valid setter method and forward it to the original object.</li>
-
- <li>Use {@link android.animation.ValueAnimator} instead.</li>
- </ul>
- </li>
-
- <li>If you specify only one value for the <code>values...</code> parameter in one of the {@link
- android.animation.ObjectAnimator} factory methods, it is assumed to be the ending value of the
- animation. Therefore, the object property that you are animating must have a getter function
- that is used to obtain the starting value of the animation. The getter function must be in the
- form of <code>get&lt;propertyName&gt;()</code>. For example, if the property name is
- <code>foo</code>, you need to have a <code>getFoo()</code> method.</li>
-
- <li>The getter (if needed) and setter methods of the property that you are animating must
- operate on the same type as the starting and ending values that you specify to {@link
- android.animation.ObjectAnimator}. For example, you must have
- <code>targetObject.setPropName(float)</code> and <code>targetObject.getPropName(float)</code>
- if you construct the following {@link android.animation.ObjectAnimator}:
- <pre>
-ObjectAnimator.ofFloat(targetObject, "propName", 1f)
-</pre>
- </li>
-
- <li>Depending on what property or object you are animating, you might need to call the {@link
- android.view.View#invalidate invalidate()} method on a View force the screen to redraw itself with the
- updated animated values. You do this in the
- {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate onAnimationUpdate()}
- callback. For example, animating the color property of a Drawable object only cause updates to the
- screen when that object redraws itself. All of the property setters on View, such as
- {@link android.view.View#setAlpha setAlpha()} and {@link android.view.View#setTranslationX setTranslationX()}
- invalidate the View properly, so you do not need to invalidate the View when calling these
- methods with new values. For more information on listeners, see the section about <a href="#listeners">Animation Listeners</a>.
- </li>
- </ul>
-
- <h2 id="choreography">Choreographing Multiple Animations with AnimatorSet</h2>
-
- <p>In many cases, you want to play an animation that depends on when another animation starts or
- finishes. The Android system lets you bundle animations together into an {@link
- android.animation.AnimatorSet}, so that you can specify whether to start animations
- simultaneously, sequentially, or after a specified delay. You can also nest {@link
- android.animation.AnimatorSet} objects within each other.</p>
-
- <p>The following sample code taken from the <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
- Balls</a> sample (modified for simplicity) plays the following {@link android.animation.Animator}
- objects in the following manner:</p>
-
- <ol>
- <li>Plays <code>bounceAnim</code>.</li>
-
- <li>Plays <code>squashAnim1</code>, <code>squashAnim2</code>, <code>stretchAnim1</code>, and
- <code>stretchAnim2</code> at the same time.</li>
-
- <li>Plays <code>bounceBackAnim</code>.</li>
-
- <li>Plays <code>fadeAnim</code>.</li>
- </ol>
- <pre>
-AnimatorSet bouncer = new AnimatorSet();
-bouncer.play(bounceAnim).before(squashAnim1);
-bouncer.play(squashAnim1).with(squashAnim2);
-bouncer.play(squashAnim1).with(stretchAnim1);
-bouncer.play(squashAnim1).with(stretchAnim2);
-bouncer.play(bounceBackAnim).after(stretchAnim2);
-ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
-fadeAnim.setDuration(250);
-AnimatorSet animatorSet = new AnimatorSet();
-animatorSet.play(bouncer).before(fadeAnim);
-animatorSet.start();
-</pre>
-
- <p>For a more complete example on how to use animator sets, see the <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
- Balls</a> sample in APIDemos.</p>
-
-<h2 id="listeners">Animation Listeners</h2>
-<p>
-You can listen for important events during an animation's duration with the listeners described below.
-</p>
-
- <ul>
- <li>{@link android.animation.Animator.AnimatorListener}
-
- <ul>
- <li>{@link android.animation.Animator.AnimatorListener#onAnimationStart onAnimationStart()}
- - Called when the animation starts.</li>
-
- <li>{@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()} -
- Called when the animation ends.</li>
-
- <li>{@link android.animation.Animator.AnimatorListener#onAnimationRepeat
- onAnimationRepeat()} - Called when the animation repeats itself.</li>
-
- <li>{@link android.animation.Animator.AnimatorListener#onAnimationCancel
- onAnimationCancel()} - Called when the animation is canceled. A cancelled animation
- also calls {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()},
- regardless of how they were ended.</li>
- </ul>
- </li>
-
- <li>{@link android.animation.ValueAnimator.AnimatorUpdateListener}
-
- <ul>
- <li>
- <p>{@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate
- onAnimationUpdate()} - called on every frame of the animation. Listen to this event to
- use the calculated values generated by {@link android.animation.ValueAnimator} during an
- animation. To use the value, query the {@link android.animation.ValueAnimator} object
- passed into the event to get the current animated value with the {@link
- android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} method. Implementing this
- listener is required if you use {@link android.animation.ValueAnimator}. </p>
-
- <p>
- Depending on what property or object you are animating, you might need to call
- {@link android.view.View#invalidate invalidate()} on a View to force that area of the
- screen to redraw itself with the new animated values. For example, animating the
- color property of a Drawable object only cause updates to the screen when that object
- redraws itself. All of the property setters on View,
- such as {@link android.view.View#setAlpha setAlpha()} and
- {@link android.view.View#setTranslationX setTranslationX()} invalidate the View
- properly, so you do not need to invalidate the View when calling these methods with new values.
- </p>
-
- </li>
- </ul>
- </li>
- </ul>
-
-<p>You can extend the {@link android.animation.AnimatorListenerAdapter} class instead of
-implementing the {@link android.animation.Animator.AnimatorListener} interface, if you do not
-want to implement all of the methods of the {@link android.animation.Animator.AnimatorListener}
-interface. The {@link android.animation.AnimatorListenerAdapter} class provides empty
-implementations of the methods that you can choose to override.</p>
- <p>For example, the <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
- Balls</a> sample in the API demos creates an {@link android.animation.AnimatorListenerAdapter}
- for just the {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
- callback:</p>
- <pre>
-ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
-fadeAnim.setDuration(250);
-fadeAnim.addListener(new AnimatorListenerAdapter() {
-public void onAnimationEnd(Animator animation) {
- balls.remove(((ObjectAnimator)animation).getTarget());
-}
-</pre>
-
-
- <h2 id="layout">Animating Layout Changes to ViewGroups</h2>
-
- <p>The property animation system provides the capability to animate changes to ViewGroup objects
- as well as provide an easy way to animate View objects themselves.</p>
-
- <p>You can animate layout changes within a ViewGroup with the {@link
- android.animation.LayoutTransition} class. Views inside a ViewGroup can go through an appearing
- and disappearing animation when you add them to or remove them from a ViewGroup or when you call
- a View's {@link android.view.View#setVisibility setVisibility()} method with {@link
- android.view.View#VISIBLE}, android.view.View#INVISIBLE}, or {@link android.view.View#GONE}. The remaining Views in the
- ViewGroup can also animate into their new positions when you add or remove Views. You can define
- the following animations in a {@link android.animation.LayoutTransition} object by calling {@link
- android.animation.LayoutTransition#setAnimator setAnimator()} and passing in an {@link
- android.animation.Animator} object with one of the following {@link
- android.animation.LayoutTransition} constants:</p>
-
- <ul>
- <li><code>APPEARING</code> - A flag indicating the animation that runs on items that are
- appearing in the container.</li>
-
- <li><code>CHANGE_APPEARING</code> - A flag indicating the animation that runs on items that are
- changing due to a new item appearing in the container.</li>
-
- <li><code>DISAPPEARING</code> - A flag indicating the animation that runs on items that are
- disappearing from the container.</li>
-
- <li><code>CHANGE_DISAPPEARING</code> - A flag indicating the animation that runs on items that
- are changing due to an item disappearing from the container.</li>
- </ul>
-
- <p>You can define your own custom animations for these four types of events to customize the look
- of your layout transitions or just tell the animation system to use the default animations.</p>
-
- <p>The <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimations.html">
- LayoutAnimations</a> sample in API Demos shows you how to define animations for layout
- transitions and then set the animations on the View objects that you want to animate.</p>
-
- <p>The <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.html">
- LayoutAnimationsByDefault</a> and its corresponding <a href=
- "{@docRoot}resources/samples/ApiDemos/res/layout/layout_animations_by_default.html">layout_animations_by_default.xml</a>
- layout resource file show you how to enable the default layout transitions for ViewGroups in XML.
- The only thing that you need to do is to set the <code>android:animateLayoutchanges</code>
- attribute to <code>true</code> for the ViewGroup. For example:</p>
- <pre>
-&lt;LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:id="@+id/verticalContainer"
- android:animateLayoutChanges="true" /&gt;
-</pre>
-
- <p>Setting this attribute to true automatically animates Views that are added or removed from the
- ViewGroup as well as the remaining Views in the ViewGroup.</p>
-
- <h2 id="type-evaluator">Using a TypeEvaluator</h2>
-
- <p>If you want to animate a type that is unknown to the Android system, you can create your own
- evaluator by implementing the {@link android.animation.TypeEvaluator} interface. The types that
- are known by the Android system are <code>int</code>, <code>float</code>, or a color, which are
- supported by the {@link android.animation.IntEvaluator}, {@link
- android.animation.FloatEvaluator}, and {@link android.animation.ArgbEvaluator} type
- evaluators.</p>
-
- <p>There is only one method to implement in the {@link android.animation.TypeEvaluator}
- interface, the {@link android.animation.TypeEvaluator#evaluate evaluate()} method. This allows
- the animator that you are using to return an appropriate value for your animated property at the
- current point of the animation. The {@link android.animation.FloatEvaluator} class demonstrates
- how to do this:</p>
- <pre>
-public class FloatEvaluator implements TypeEvaluator {
-
- public Object evaluate(float fraction, Object startValue, Object endValue) {
- float startFloat = ((Number) startValue).floatValue();
- return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
- }
-}
-</pre>
-
- <p class="note"><strong>Note:</strong> When {@link android.animation.ValueAnimator} (or {@link
- android.animation.ObjectAnimator}) runs, it calculates a current elapsed fraction of the
- animation (a value between 0 and 1) and then calculates an interpolated version of that depending
- on what interpolator that you are using. The interpolated fraction is what your {@link
- android.animation.TypeEvaluator} receives through the <code>fraction</code> parameter, so you do
- not have to take into account the interpolator when calculating animated values.</p>
-
- <h2 id="interpolators">Using Interpolators</h2>
-
- <p>An interpolator define how specific values in an animation are calculated as a function of
- time. For example, you can specify animations to happen linearly across the whole animation,
- meaning the animation moves evenly the entire time, or you can specify animations to use
- non-linear time, for example, using acceleration or deceleration at the beginning or end of the
- animation.</p>
-
- <p>Interpolators in the animation system receive a fraction from Animators that represent the
- elapsed time of the animation. Interpolators modify this fraction to coincide with the type of
- animation that it aims to provide. The Android system provides a set of common interpolators in
- the {@link android.view.animation android.view.animation package}. If none of these suit your
- needs, you can implement the {@link android.animation.TimeInterpolator} interface and create your
- own.</p>
-
- <p>As an example, how the default interpolator {@link
- android.view.animation.AccelerateDecelerateInterpolator} and the {@link
- android.view.animation.LinearInterpolator} calculate interpolated fractions are compared below.
- The {@link android.view.animation.LinearInterpolator} has no effect on the elapsed fraction. The {@link
- android.view.animation.AccelerateDecelerateInterpolator} accelerates into the animation and
- decelerates out of it. The following methods define the logic for these interpolators:</p>
-
- <p><strong>AccelerateDecelerateInterpolator</strong></p>
- <pre>
-public float getInterpolation(float input) {
- return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
-}
-</pre>
-
- <p><strong>LinearInterpolator</strong></p>
- <pre>
-public float getInterpolation(float input) {
- return input;
-}
-</pre>
-
- <p>The following table represents the approximate values that are calculated by these
- interpolators for an animation that lasts 1000ms:</p>
-
- <table>
- <tr>
- <th>ms elapsed</th>
-
- <th>Elapsed fraction/Interpolated fraction (Linear)</th>
-
- <th>Interpolated fraction (Accelerate/Decelerate)</th>
- </tr>
-
- <tr>
- <td>0</td>
-
- <td>0</td>
-
- <td>0</td>
- </tr>
-
- <tr>
- <td>200</td>
-
- <td>.2</td>
-
- <td>.1</td>
- </tr>
-
- <tr>
- <td>400</td>
-
- <td>.4</td>
-
- <td>.345</td>
- </tr>
-
- <tr>
- <td>600</td>
-
- <td>.6</td>
-
- <td>.8</td>
- </tr>
-
- <tr>
- <td>800</td>
-
- <td>.8</td>
-
- <td>.9</td>
- </tr>
-
- <tr>
- <td>1000</td>
-
- <td>1</td>
-
- <td>1</td>
- </tr>
- </table>
-
- <p>As the table shows, the {@link android.view.animation.LinearInterpolator} changes the values
- at the same speed, .2 for every 200ms that passes. The {@link
- android.view.animation.AccelerateDecelerateInterpolator} changes the values faster than {@link
- android.view.animation.LinearInterpolator} between 200ms and 600ms and slower between 600ms and
- 1000ms.</p>
-
- <h2 id="keyframes">Specifying Keyframes</h2>
-
- <p>A {@link android.animation.Keyframe} object consists of a time/value pair that lets you define
- a specific state at a specific time of an animation. Each keyframe can also have its own
- interpolator to control the behavior of the animation in the interval between the previous
- keyframe's time and the time of this keyframe.</p>
-
- <p>To instantiate a {@link android.animation.Keyframe} object, you must use one of the factory
- methods, {@link android.animation.Keyframe#ofInt ofInt()}, {@link
- android.animation.Keyframe#ofFloat ofFloat()}, or {@link android.animation.Keyframe#ofObject
- ofObject()} to obtain the appropriate type of {@link android.animation.Keyframe}. You then call
- the {@link android.animation.PropertyValuesHolder#ofKeyframe ofKeyframe()} factory method to
- obtain a {@link android.animation.PropertyValuesHolder} object. Once you have the object, you can
- obtain an animator by passing in the {@link android.animation.PropertyValuesHolder} object and
- the object to animate. The following code snippet demonstrates how to do this:</p>
- <pre>
-Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
-Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
-Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
-PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
-ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
-rotationAnim.setDuration(5000ms);
-</pre>
-
- <p>For a more complete example on how to use keyframes, see the <a href=
- "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/MultiPropertyAnimation.html">
- MultiPropertyAnimation</a> sample in APIDemos.</p>
-
- <h2 id="views">Animating Views</h2>
-
- <p>The property animation system allow streamlined animation of View objects and offerse
- a few advantages over the view animation system. The view
- animation system transformed View objects by changing the way that they were drawn. This was
- handled in the container of each View, because the View itself had no properties to manipulate.
- This resulted in the View being animated, but caused no change in the View object itself. This
- led to behavior such as an object still existing in its original location, even though it was
- drawn on a different location on the screen. In Android 3.0, new properties and the corresponding
- getter and setter methods were added to eliminate this drawback.</p>
- <p>The property animation system
- can animate Views on the screen by changing the actual properties in the View objects. In
- addition, Views also automatically call the {@link android.view.View#invalidate invalidate()}
- method to refresh the screen whenever its properties are changed. The new properties in the {@link
- android.view.View} class that facilitate property animations are:</p>
-
- <ul>
- <li><code>translationX</code> and <code>translationY</code>: These properties control where the
- View is located as a delta from its left and top coordinates which are set by its layout
- container.</li>
-
- <li><code>rotation</code>, <code>rotationX</code>, and <code>rotationY</code>: These properties
- control the rotation in 2D (<code>rotation</code> property) and 3D around the pivot point.</li>
-
- <li><code>scaleX</code> and <code>scaleY</code>: These properties control the 2D scaling of a
- View around its pivot point.</li>
-
- <li><code>pivotX</code> and <code>pivotY</code>: These properties control the location of the
- pivot point, around which the rotation and scaling transforms occur. By default, the pivot
- point is located at the center of the object.</li>
-
- <li><code>x</code> and <code>y</code>: These are simple utility properties to describe the
- final location of the View in its container, as a sum of the left and top values and
- translationX and translationY values.</li>
-
- <li><code>alpha</code>: Represents the alpha transparency on the View. This value is 1 (opaque)
- by default, with a value of 0 representing full transparency (not visible).</li>
- </ul>
-
- <p>To animate a property of a View object, such as its color or rotation value, all you need to
- do is create a property animator and specify the View property that you want to
- animate. For example:</p>
- <pre>
-ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
-</pre>
-
-<p>For more information on creating animators, see the sections on animating with
-<a href="#value-animator">ValueAnimator</a> and <a href="#object-animator">ObjectAnimator</a>.
-</p>
-
-<h3 id="view-prop-animator">Animating with ViewPropertyAnimator</h3>
-<p>The {@link android.view.ViewPropertyAnimator} provides a simple way to animate several
-properties of a {@link android.view.View} in parallel, using a single underlying {@link
-android.animation.Animator}
-object. It behaves much like an {@link android.animation.ObjectAnimator}, because it modifies the
-actual values of the view's properties, but is more efficient when animating many properties at
-once. In addition, the code for using the {@link android.view.ViewPropertyAnimator} is much
-more concise and easier to read. The following code snippets show the differences in using multiple
-{@link android.animation.ObjectAnimator} objects, a single
-{@link android.animation.ObjectAnimator}, and the {@link android.view.ViewPropertyAnimator} when
-simultaneously animating the <code>x</code> and <code>y</code> property of a view.</p>
-
-<p><strong>Multiple ObjectAnimator objects</strong></p>
-<pre>
-ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
-ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
-AnimatorSet animSetXY = new AnimatorSet();
-animSetXY.playTogether(animX, animY);
-animSetXY.start();
-</pre>
-
-<p><strong>One ObjectAnimator</strong></p>
-<pre>
-PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
-PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
-ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
-</pre>
-
-<p><strong>ViewPropertyAnimator</strong></p>
-<pre>
-myView.animate().x(50f).y(100f);
-</pre>
-
-<p>
-For more detailed information about {@link
-android.view.ViewPropertyAnimator}, see the corresponding Android Developers
-<a href="http://android-developers.blogspot.com/2011/05/introducing-viewpropertyanimator.html">blog
-post</a>.</p>
-
-<h2 id="declaring-xml">Declaring Animations in XML</h2>
-
- <p>The property animation system lets you declare property animations with XML instead of doing
- it programmatically. By defining your animations in XML, you can easily reuse your animations
-in multiple activities and more easily edit the animation sequence.</p>
-
-<p>To distinguish animation files that use the new property animation APIs from those that use the
-legacy <a href="{@docRoot}guide/topics/graphics/view-animation.html">view animation</a> framework,
-starting with Android 3.1, you should save the XML files for property animations in the {@code
-res/animator/} directory (instead of {@code res/anim/}). Using the {@code animator} directory name
-is optional, but necessary if you want to use the layout editor tools in the Eclipse ADT plugin (ADT
-11.0.0+), because ADT only searches the {@code res/animator/} directory for property animation
-resources.</p>
-
-<p>The following property animation classes have XML declaration support with the
- following XML tags:</p>
-
- <ul>
- <li>{@link android.animation.ValueAnimator} - <code>&lt;animator&gt;</code></li>
-
- <li>{@link android.animation.ObjectAnimator} - <code>&lt;objectAnimator&gt;</code></li>
-
- <li>{@link android.animation.AnimatorSet} - <code>&lt;set&gt;</code></li>
- </ul>
-
-<p>See <a href="{@docRoot}guide/topics/resources/animation-resource.html#Property">Animation Resources</a>
-
+ works the way you want, there is no need to use the property animation system. It also might
+ make sense to use both animation systems for different situations if the use case arises.</p>
+
+<dl>
+<dt><strong><a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property
+Animation</a></strong></dt>
+<dd>Introduced in Android 3.0 (API level 11), the property animation system lets you
+animate properties of any object, including ones that are not rendered to the screen. The system is
+extensible and lets you animate properties of custom types as well.</dd>
+
+<dt><strong><a href="{@docRoot}guide/topics/graphics/view-animation.html">View
+Animation</a></strong></dt>
+<dd>View Animation is the older system and can only be used for Views. It is relatively easy to
+setup and offers enough capabilities to meet many application's needs.</dd>
+</dl>
+
+<dt><strong><a href="{@docRoot}guide/topics/graphics/drawable-animation.html">Drawable
+Animation</a></strong></dt>
+<dd>Drawable animation involves displaying {@link android.graphics.drawable.Drawable} resources one
+after another, like a roll of film. This method of animation is useful if you want to animate
+things that are easier to represent with Drawable resources, such as a progression of bitmaps.</dd>
diff --git a/docs/html/guide/topics/graphics/drawable-animation.jd b/docs/html/guide/topics/graphics/drawable-animation.jd
new file mode 100644
index 0000000..65bf02f
--- /dev/null
+++ b/docs/html/guide/topics/graphics/drawable-animation.jd
@@ -0,0 +1,66 @@
+page.title=Drawable Animation
+parent.title=Animation
+parent.link=animation.html
+@jd:body
+
+ <p>Drawable animation lets you load a series of Drawable resources one after
+ another to create an animation. This is a traditional animation in the sense that it is created with a sequence of different
+ images, played in order, like a roll of film. The {@link
+ android.graphics.drawable.AnimationDrawable} class is the basis for Drawable animations.</p>
+
+ <p>While you can define the frames of an animation in your code, using the {@link
+ android.graphics.drawable.AnimationDrawable} class API, it's more simply accomplished with a
+ single XML file that lists the frames that compose the animation. The XML file for this kind
+ of animation belongs in the <code>res/drawable/</code> directory of
+ your Android project. In this case, the instructions are the order and duration for each frame of
+ the animation.</p>
+
+ <p>The XML file consists of an <code>&lt;animation-list&gt;</code> element as the root node and a
+ series of child <code>&lt;item&gt;</code> nodes that each define a frame: a drawable resource for
+ the frame and the frame duration. Here's an example XML file for a Drawable animation:</p>
+ <pre>
+&lt;animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="true"&gt;
+ &lt;item android:drawable="@drawable/rocket_thrust1" android:duration="200" /&gt;
+ &lt;item android:drawable="@drawable/rocket_thrust2" android:duration="200" /&gt;
+ &lt;item android:drawable="@drawable/rocket_thrust3" android:duration="200" /&gt;
+&lt;/animation-list&gt;
+</pre>
+
+ <p>This animation runs for just three frames. By setting the <code>android:oneshot</code>
+ attribute of the list to <var>true</var>, it will cycle just once then stop and hold on the last
+ frame. If it is set <var>false</var> then the animation will loop. With this XML saved as
+ <code>rocket_thrust.xml</code> in the <code>res/drawable/</code> directory of the project, it can
+ be added as the background image to a View and then called to play. Here's an example Activity,
+ in which the animation is added to an {@link android.widget.ImageView} and then animated when the
+ screen is touched:</p>
+ <pre>
+AnimationDrawable rocketAnimation;
+
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
+ rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
+ rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
+}
+
+public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ rocketAnimation.start();
+ return true;
+ }
+ return super.onTouchEvent(event);
+}
+</pre>
+
+ <p>It's important to note that the <code>start()</code> method called on the AnimationDrawable
+ cannot be called during the <code>onCreate()</code> method of your Activity, because the
+ AnimationDrawable is not yet fully attached to the window. If you want to play the animation
+ immediately, without requiring interaction, then you might want to call it from the <code>{@link
+ android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> method in your
+ Activity, which will get called when Android brings your window into focus.</p>
+
+ <p>For more information on the XML syntax, available tags and attributes, see <a href=
+ "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
new file mode 100644
index 0000000..c8703a5
--- /dev/null
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -0,0 +1,522 @@
+page.title=Hardware Acceleration
+parent.title=Graphics
+parent.link=index.html
+@jd:body
+
+
+ <div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+
+ <ol>
+ <li><a href="#controlling">Controlling Hardware Acceleration</a></li>
+ <li><a href="#determining">Determining if a View is Hardware Accelerated</a></li>
+ <li><a href="#model">Android Drawing Models</a>
+
+ <ol>
+ <li><a href="#software-model">Software-based drawing model</a></li>
+ <li><a href="#hardware-model">Hardware accelerated drawing model</a></li>
+ </ol>
+ </li>
+
+ <li>
+ <a href="#unsupported">Unsupported Drawing Operations</a>
+ </li>
+
+
+
+ <li>
+ <a href="#layers">View Layers</a>
+
+ <ol>
+ <li><a href="#layers-anims">View Layers and Animations</a></li>
+ </ol>
+ </li>
+
+ <li><a href="#tips">Tips and Tricks</a></li>
+ </ol>
+
+ <h2>See also</h2>
+
+ <ol>
+ <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL with the Framework
+ APIs</a></li>
+
+ <li><a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a></li>
+ </ol>
+ </div>
+ </div>
+
+ <p>Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline is designed to
+ better support hardware acceleration. Hardware acceleration carries out all drawing operations
+ that are performed on a {@link android.view.View}'s canvas using the GPU.</p>
+
+ <p>The easiest way to enable hardware acceleration is to turn it on
+ globally for your entire application. If your application uses only standard views and {@link
+ android.graphics.drawable.Drawable}s, turning it on globally should not cause any adverse
+ effects. However, because hardware acceleration is not supported for all of the 2D drawing
+ operations, turning it on might affect some of your applications that use custom views or drawing
+ calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly
+ rendered pixels. To remedy this, Android gives you the option to enable or disable hardware
+ acceleration at the following levels:</p>
+
+ <ul>
+ <li>Application</li>
+
+ <li>Activity</li>
+
+ <li>Window</li>
+
+ <li>View</li>
+ </ul>
+
+ <p>If your application performs custom drawing, test your application on actual hardware
+devices with hardware acceleration turned on to find any problems. The <a
+href="#drawing-support">Unsupported drawing operations</a> section describes known issues with
+drawing operations that cannot be hardware accelerated and how to work around them.</p>
+
+
+ <h2 id="controlling">Controlling Hardware Acceleration</h2>
+ <p>You can control hardware acceleration at the following levels:</p>
+ <ul>
+ <li>Application</li>
+
+ <li>Activity</li>
+
+ <li>Window</li>
+
+ <li>View</li>
+ </ul>
+
+ <h4>Application level</h4>
+ <p>In your Android manifest file, add the following attribute to the
+ <a href="{@docRoot}guide/topics/manifest/application-element.html">
+ <code>&lt;application&gt;</code></a> tag to enable hardware acceleration for your entire
+ application:</p>
+
+<pre>
+&lt;application android:hardwareAccelerated="true" ...&gt;
+</pre>
+
+ <h4>Activity level</h4>
+ <p>If your application does not behave properly with hardware acceleration turned on globally,
+ you can control it for individual activities as well. To enable or disable hardware acceleration
+ at the activity level, you can use the <code>android:hardwareAccelerated</code>
+ attribute for the <a href="{@docRoot}guide/topics/manifest/activity-element.html">
+ <code>&lt;activity&gt;</code></a> element. The following example enables hardware acceleration
+for the entire application but disables it for one activity:</p>
+
+<pre>
+&lt;application android:hardwareAccelerated="true"&gt;
+ &lt;activity ... /&gt;
+ &lt;activity android:hardwareAccelerated="false" /&gt;
+&lt;/application&gt;
+</pre>
+
+ <h4>Window level</h4>
+ <p>If you need even more fine-grained control, you can enable hardware acceleration for a given
+ window with the following code:</p>
+
+<pre>
+getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+
+</pre>
+
+<p class="note"><strong>Note</strong>: You currently cannot disable hardware acceleration at
+the window level.</p>
+
+ <h4>View level</h4>
+
+ <p>You can disable hardware acceleration for an individual view at runtime with the
+following code:</p>
+
+<pre>
+myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+</pre>
+
+<p class="note"><strong>Note</strong>: You currently cannot enable hardware acceleration at
+the view level. View layers have other functions besides disabling hardware acceleration. See <a
+href="#layers">View layers</a> for more information about their uses.</p>
+
+ <h2 id="determining">Determining if a View is Hardware Accelerated</h2>
+
+ <p>It is sometimes useful for an application to know whether it is currently hardware
+ accelerated, especially for things such as custom views. This is particularly useful if your
+ application does a lot of custom drawing and not all operations are properly supported by the new
+ rendering pipeline.</p>
+
+ <p>There are two different ways to check whether the application is hardware accelerated:</p>
+
+ <ul>
+ <li>{@link android.view.View#isHardwareAccelerated View.isHardwareAccelerated()} returns
+ <code>true</code> if the {@link android.view.View} is attached to a hardware accelerated
+ window.</li>
+
+ <li>{@link android.graphics.Canvas#isHardwareAccelerated Canvas.isHardwareAccelerated()}
+ returns <code>true</code> if the {@link android.graphics.Canvas} is hardware accelerated</li>
+ </ul>
+
+ <p>If you must do this check in your drawing code, use {@link
+ android.graphics.Canvas#isHardwareAccelerated Canvas.isHardwareAccelerated()} instead of {@link
+ android.view.View#isHardwareAccelerated View.isHardwareAccelerated()} when possible. When a view
+ is attached to a hardware accelerated window, it can still be drawn using a non-hardware
+ accelerated Canvas. This happens, for instance, when drawing a view into a bitmap for caching
+ purposes.</p>
+
+
+ <h2 id="model">Android Drawing Models</h2>
+
+ <p>When hardware acceleration is enabled, the Android framework utilizes a new drawing model that
+ utilizes <em>display lists</em> to render your application to the screen. To fully understand
+ display lists and how they might affect your application, it is useful to understand how Android
+ draws views without hardware acceleration as well. The following sections describe the
+ software-based and hardware-accelerated drawing models.</p>
+
+<h3>Software-based drawing model</h3>
+<p>In the software drawing model, views are drawn with the following two steps:</p>
+ <ol>
+ <li>Invalidate the hierarchy</li>
+
+ <li>Draw the hierarchy</li>
+ </ol>
+
+ <p>Whenever an application needs to update a part of its UI, it invokes {@link
+ android.view.View#invalidate invalidate()} (or one of its variants) on any view that has changed
+ content. The invalidation messages are propagated all the way up the view hierarchy to compute
+ the regions of the screen that need to be redrawn (the dirty region). The Android system then
+ draws any view in the hierarchy that intersects with the dirty region. Unfortunately, there are
+ two drawbacks to this drawing model:</p>
+ <ul>
+ <li>First, this model requires execution of a lot of code on every draw pass. For example, if
+your application calls {@link android.view.View#invalidate invalidate()} on a button and that
+button sits on top of another view, the Android system redraws the view even though it hasn't
+changed.</li>
+ <li>The second issue is that the drawing model can hide bugs in your application. Since the
+ Android system redraws views when they intersect the dirty region, a view whose content you
+ changed might be redrawn even though {@link android.view.View#invalidate invalidate()} was not
+ called on it. When this happens, you are relying on another view being invalidated to obtain the
+ proper behavior. This behavior can change every time you modify your application. Because of
+ this, you should always call {@link android.view.View#invalidate invalidate()} on your custom
+ views whenever you modify data or state that affects the view’s drawing code.</li>
+</ul>
+
+ <p class="note"><strong>Note</strong>: Android views automatically call {@link
+ android.view.View#invalidate invalidate()} when their properties change, such as the background
+ color or the text in a {@link android.widget.TextView}.</p>
+
+ <h3>Hardware accelerated drawing model</h3>
+ <p>The Android system still uses {@link android.view.View#invalidate invalidate()} and {@link
+ android.view.View#draw draw()} to request screen updates and to render views, but handles the
+ actual drawing differently. Instead of executing the drawing commands immediately, the Android
+ system records them inside display lists, which contain the output of the view hierarchy’s
+ drawing code. Another optimization is that the Android system only needs to record and update
+ display lists for views marked dirty by an {@link android.view.View#invalidate invalidate()}
+ call. Views that have not been invalidated can be redrawn simply by re-issuing the previously
+ recorded display list. The new drawing model contains three stages:</p>
+
+ <ol>
+ <li>Invalidate the hierarchy</li>
+
+ <li>Record and update display lists</li>
+
+ <li>Draw the display lists</li>
+ </ol>
+
+ <p>With this model, you cannot rely on a view intersecting the dirty region to have its {@link
+ android.view.View#draw draw()} method executed. To ensure that the Android system records a
+ view’s display list, you must call {@link android.view.View#invalidate invalidate()}. Forgetting
+ to do so causes a view to look the same even after changing it, which is an easier bug to find if
+ it happens.</p>
+
+ <p>Using display lists also benefits animation performance because setting specific properties,
+ such as alpha or rotation, does not require invalidating the targeted view (it is done
+ automatically). This optimization also applies to views with display lists (any view when your
+ application is hardware accelerated.) For example, assume there is a {@link
+ android.widget.LinearLayout} that contains a {@link android.widget.ListView} above a {@link
+ android.widget.Button}. The display list for the {@link android.widget.LinearLayout} looks like
+ this:</p>
+
+ <ul>
+ <li>DrawDisplayList(ListView)</li>
+
+ <li>DrawDisplayList(Button)</li>
+ </ul>
+
+ <p>Assume now that you want to change the {@link android.widget.ListView}'s opacity. After
+ invoking <code>setAlpha(0.5f)</code> on the {@link android.widget.ListView}, the display list now
+ contains this:</p>
+
+ <ul>
+ <li>SaveLayerAlpha(0.5)</li>
+
+ <li>DrawDisplayList(ListView)</li>
+
+ <li>Restore</li>
+
+ <li>DrawDisplayList(Button)</li>
+ </ul>
+
+ <p>The complex drawing code of {@link android.widget.ListView} was not executed. Instead, the
+ system only updated the display list of the much simpler {@link android.widget.LinearLayout}. In
+ an application without hardware acceleration enabled, the drawing code of both the list and its
+ parent are executed again.</p>
+
+ <h2 id="unsupported">Unsupported Drawing Operations</h2>
+
+ <p>When hardware accelerated, the 2D rendering pipeline supports the most commonly used {@link
+ android.graphics.Canvas} drawing operations as well as many less-used operations. All of the
+ drawing operations that are used to render applications that ship with Android, default widgets
+ and layouts, and common advanced visual effects such as reflections and tiled textures are
+ supported. The following list describes known operations that are <strong>not supported</strong>
+ with hardware acceleration:</p>
+
+ <ul>
+ <li>
+ <strong>Canvas</strong>
+
+ <ul>
+ <li>{@link android.graphics.Canvas#clipPath clipPath()}</li>
+
+ <li>{@link android.graphics.Canvas#clipRegion clipRegion()}</li>
+
+ <li>{@link android.graphics.Canvas#drawPicture drawPicture()}</li>
+
+ <li>{@link android.graphics.Canvas#drawPosText drawPosText()}</li>
+
+ <li>{@link android.graphics.Canvas#drawTextOnPath drawTextOnPath()}</li>
+
+ <li>{@link android.graphics.Canvas#drawVertices drawVertices()}</li>
+ </ul>
+ </li>
+
+ <li>
+ <strong>Paint</strong>
+
+ <ul>
+ <li>{@link android.graphics.Paint#setLinearText setLinearText()}</li>
+
+ <li>{@link android.graphics.Paint#setMaskFilter setMaskFilter()}</li>
+
+ <li>{@link android.graphics.Paint#setRasterizer setRasterizer()}</li>
+ </ul>
+ </li>
+ </ul>
+
+ <p>In addition, some operations behave differently with hardware acceleration enabled:</p>
+
+ <ul>
+ <li>
+ <strong>Canvas</strong>
+
+ <ul>
+ <li>{@link android.graphics.Canvas#clipRect clipRect()}: <code>XOR</code>,
+ <code>Difference</code> and <code>ReverseDifference</code> clip modes are ignored. 3D
+ transforms do not apply to the clip rectangle</li>
+
+ <li>{@link android.graphics.Canvas#drawBitmapMesh drawBitmapMesh()}: colors array is
+ ignored</li>
+
+ <li>{@link android.graphics.Canvas#drawLines drawLines()}: anti-aliasing is not
+ supported</li>
+
+ <li>{@link android.graphics.Canvas#setDrawFilter setDrawFilter()}: can be set, but is
+ ignored</li>
+ </ul>
+ </li>
+
+ <li>
+ <strong>Paint</strong>
+
+ <ul>
+ <li>{@link android.graphics.Paint#setDither setDither()}: ignored</li>
+
+ <li>{@link android.graphics.Paint#setFilterBitmap setFilterBitmap()}: filtering is always
+ on</li>
+
+ <li>{@link android.graphics.Paint#setShadowLayer setShadowLayer()}: works with text
+ only</li>
+ </ul>
+ </li>
+
+ <li>
+ <strong>ComposeShader</strong>
+
+ <ul>
+ <li>{@link android.graphics.ComposeShader} can only contain shaders of different types (a
+ {@link android.graphics.BitmapShader} and a {@link android.graphics.LinearGradient} for
+ instance, but not two instances of {@link android.graphics.BitmapShader} )</li>
+
+ <li>{@link android.graphics.ComposeShader} cannot contain a {@link
+ android.graphics.ComposeShader}</li>
+ </ul>
+ </li>
+ </ul>
+
+ <p>If your application is affected by any of these missing features or limitations, you can turn
+ off hardware acceleration for just the affected portion of your application by calling
+ {@link android.view.View#setLayerType setLayerType(View.LAYER_TYPE_SOFTWARE, null)}. This way,
+you can still take advantage of hardware acceleratin everywhere else. See <a
+href="#controlling">Controlling Hardware Acceleration</a> for more information on how to enable and
+disable hardware acceleration at different levels in your application.
+
+
+
+ <h2 id="layers">View Layers</h2>
+
+ <p>In all versions of Android, views have had the ability to render into off-screen buffers,
+either by using a view's drawing cache, or by using {@link android.graphics.Canvas#saveLayer
+ Canvas.saveLayer()}. Off-screen buffers, or layers, have several uses. You can use them to get
+ better performance when animating complex views or to apply composition effects. For instance,
+ you can implement fade effects using <code>Canvas.saveLayer()</code> to temporarily render a view
+ into a layer and then composite it back on screen with an opacity factor.</p>
+
+ <p>Beginning in Android 3.0 (API level 11), you have more control on how and when to use layers
+ with the {@link android.view.View#setLayerType View.setLayerType()} method. This API takes two
+ parameters: the type of layer you want to use and an optional {@link android.graphics.Paint}
+ object that describes how the layer should be composited. You can use the {@link
+ android.graphics.Paint} parameter to apply color filters, special blending modes, or opacity to a
+ layer. A view can use one of three layer types:</p>
+
+ <ul>
+ <li>{@link android.view.View#LAYER_TYPE_NONE}: The view is rendered normally and is not backed
+ by an off-screen buffer. This is the default behavior.</li>
+
+ <li>{@link android.view.View#LAYER_TYPE_HARDWARE}: The view is rendered in hardware into a
+ hardware texture if the application is hardware accelerated. If the application is not hardware
+ accelerated, this layer type behaves the same as {@link
+ android.view.View#LAYER_TYPE_SOFTWARE}.</li>
+
+ <li>{@link android.view.View#LAYER_TYPE_SOFTWARE}: The view is rendered in software into a
+ bitmap.</li>
+ </ul>
+
+ <p>The type of layer you use depends on your goal:</p>
+
+ <ul>
+ <li><strong>Performance</strong>: Use a hardware layer type to render a view into a hardware
+ texture. Once a view is rendered into a layer, its drawing code does not have to be executed
+ until the view calls {@link android.view.View#invalidate invalidate()}. Some animations, such as
+ alpha animations, can then be applied directly onto the layer, which is very efficient
+ for the GPU to do.</li>
+
+ <li><strong>Visual effects</strong>: Use a hardware or software layer type and a {@link
+ android.graphics.Paint} to apply special visual treatments to a view. For instance, you can
+ draw a view in black and white using a {@link
+ android.graphics.ColorMatrixColorFilter}.</li>
+
+ <li><strong>Compatibility</strong>: Use a software layer type to force a view to be rendered in
+ software. If a view that is hardware accelerated (for instance, if your whole
+ application is hardware acclerated), is having rendering problems, this is an easy way to work
+around limitations of the hardware rendering
+ pipeline.</li>
+ </ul>
+
+ <h3 id="layers-anims">View layers and animations</h3>
+
+ <p>Hardware layers can deliver faster and smoother animations when your application
+is hardware accelerated. Running an animation at 60 frames per second is not always possible when
+animating complex views that issue a lot of drawing operations. This can be alleviated by
+using hardware layers to render the view to a hardware texture. The hardware texture can
+then be used to animate the view, eliminating the need for the view to constantly redraw itself
+when it is being animated. The view is not redrawn unless you change the view's
+properties, which calls {@link android.view.View#invalidate invalidate()}, or if you call {@link
+android.view.View#invalidate invalidate()} manually. If you are running an animation in
+your application and do not obtain the smooth results you want, consider enabling hardware layers on
+your animated views.</p>
+
+ <p>When a view is backed by a hardware layer, some of its properties are handled by the way the
+ layer is composited on screen. Setting these properties will be efficient because they do not
+ require the view to be invalidated and redrawn. The following list of properties affect the way
+ the layer is composited. Calling the setter for any of these properties results in optimal
+ invalidation and no redrawing of the targeted view:</p>
+
+ <ul>
+ <li><code>alpha</code>: Changes the layer's opacity</li>
+
+ <li><code>x</code>, <code>y</code>, <code>translationX</code>, <code>translationY</code>:
+Changes the layer's position</li>
+
+ <li><code>scaleX</code>, <code>scaleY</code>: Changes the layer's size</li>
+
+ <li><code>rotation</code>, <code>rotationX</code>, <code>rotationY</code>: Changes the
+ layer's orientation in 3D space</li>
+
+ <li><code>pivotX</code>, <code>pivotY</code>: Changes the layer's transformations origin</li>
+ </ul>
+
+ <p>These properties are the names used when animating a view with an {@link
+ android.animation.ObjectAnimator}. If you want to access these properties, call the appropriate
+ setter or getter. For instance, to modify the alpha property, call {@link
+ android.view.View#setAlpha setAlpha()}. The following code snippet shows the most efficient way
+ to rotate a viewiew in 3D around the Y-axis:</p>
+ <pre>
+view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ObjectAnimator.ofFloat(view, "rotationY", 180).start();
+</pre>
+
+ <p>Because hardware layers consume video memory, it is highly recommended that you enable them
+only for the duration of the animation and then disable them after the animation is done. You
+can accomplish this using animation listeners:</p>
+ <pre>
+View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
+animator.addListener(new AnimatorListenerAdapter() {
+ &#064;Override
+ public void onAnimationEnd(Animator animation) {
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+});
+animator.start();
+</pre>
+
+ <p>For more information on property animation, see <a href=
+ "{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>.</p>
+
+ <h2 id="tips">Tips and Tricks</h2>
+
+ <p>Switching to hardware accelerated 2D graphics can instantly increase performance, but you
+ should still design your application to use the GPU effectively by following these
+ recommendations:</p>
+
+ <dl>
+ <dt><strong>Reduce the number of views in your application</strong></dt>
+
+ <dd>The more views the system has to draw, the slower it will be. This applies to the software
+ rendering pipeline as well. Reducing views is one of the easiest ways to optimize your UI.</dd>
+
+ <dt><strong>Avoid overdraw</strong></dt>
+
+ <dd>Do not draw too many layers on top of each other. Remove any views that are completely
+ obscured by other opaque views on top of it. If you need to draw several layers blended on top
+ of each other, consider merging them into a single layer. A good rule of thumb with current
+ hardware is to not draw more than 2.5 times the number of pixels on screen per frame
+ (transparent pixels in a bitmap count!).</dd>
+
+ <dt><strong>Don't create render objects in draw methods</strong></dt>
+
+ <dd>A common mistake is to create a new {@link android.graphics.Paint} or a new {@link
+android.graphics.Path} every time a rendering method is invoked. This forces the garbage
+collector to run more often and also bypasses caches and optimizations in the hardware
+pipeline.</dd>
+
+ <dt><strong>Don't modify shapes too often</strong></dt>
+
+ <dd>Complex shapes, paths, and circles for instance, are rendered using texture masks. Every
+ time you create or modify a path, the hardware pipeline creates a new mask, which can be
+ expensive.</dd>
+
+ <dt><strong>Don't modify bitmaps too often</strong></dt>
+
+ <dd>Every time you change the content of a bitmap, it is uploaded again as a GPU texture the
+ next time you draw it.</dd>
+
+ <dt><strong>Use alpha with care</strong></dt>
+
+ <dd>When you make a view translucent using {@link android.view.View#setAlpha setAlpha()},
+ {@link android.view.animation.AlphaAnimation}, or {@link android.animation.ObjectAnimator}, it
+ is rendered in an off-screen buffer which doubles the required fill-rate. When applying alpha
+ on very large views, consider setting the view's layer type to
+ <code>LAYER_TYPE_HARDWARE</code>.</dd>
+ </dl>
diff --git a/docs/html/guide/topics/graphics/index.jd b/docs/html/guide/topics/graphics/index.jd
index f0a923a..ffa9a39 100644
--- a/docs/html/guide/topics/graphics/index.jd
+++ b/docs/html/guide/topics/graphics/index.jd
@@ -3,208 +3,49 @@ page.title=Graphics
<div id="qv-wrapper">
<div id="qv">
- <h2>In this document</h2>
+ <h2>Topics</h2>
<ol>
- <li><a href="#options">Consider your Options</a></li>
- <li><a href="#draw-to-view">Simple Graphics Inside a View</a></li>
- <li><a href="#draw-with-canvas">Draw with a Canvas</a>
- <ol>
- <li><a href="#on-view">On a View</a></li>
- <li><a href="#on-surfaceview">On a SurfaceView</a></li>
- </ol>
- </li>
- </ol>
- <h2>See also</h2>
- <ol>
- <li><a href="{@docRoot}guide/topics/graphics/opengl.html">3D with OpenGL</a></li>
- <li><a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a></li>
+ <li><a href="{@docRoot}guide/topics/graphics/canvas.html">Canvas and Drawables</a></li>
+ <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a></li>
+ <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li>
</ol>
</div>
</div>
-<p>Android graphics are powered by a custom 2D graphics library, and the framework provides
-support for high performance 3D graphics in the form of OpenGL ES and RenderScript. The most
-common 2D graphics APIs can be found in the {@link android.graphics.drawable drawable package}.
-OpenGL APIs are available from the Khronos {@link javax.microedition.khronos.opengles OpenGL ES} and
-the {@link android.opengl} packages. The RenderScript APIs are available in the
-{@link android.renderscript} package.</p>
-
-<p>When starting a project, it's important to consider exactly what your graphical demands will be.
+<p>When writing an application, it's important to consider exactly what your graphical demands will be.
Varying graphical tasks are best accomplished with varying techniques. For example, graphics and animations
for a rather static application should be implemented much differently than graphics and animations
-for an interactive game or 3D rendering.</p>
-
-<p>Here, we'll discuss a few of the options you have for drawing graphics on Android,
-and which tasks they're best suited for.</p>
-
-<p>If you're specifically looking for information on drawing 3D graphics, this page won't
-help a lot. However, the information below about how to <a href="#draw-with-canvas">Draw with a
-Canvas</a> (and the section on SurfaceView), will give you a quick idea of how you should draw to
-the View hierarchy. For more information on Android's 3D graphics APIs, see
-the <a href="opengl.html">3D with OpenGL</a> and
-<a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> documents.</p>
-
-
-<h2 id="options">Consider your Options</h2>
-
-<p>When drawing 2D graphics, you'll typically do so in one of two ways:</p>
-<ol type="a">
- <li>Draw your graphics or animations into a View object from your layout. In this manner,
- the drawing (and any animation) of your graphics is handled by the system's
- normal View hierarchy drawing process &mdash; you simply define the graphics to go inside the View.</li>
- <li>Draw your graphics directly to a Canvas. This way, you personally call the appropriate class's
- <code>draw()</code> method (passing it your Canvas), or one of the Canvas <code>draw...()</code> methods (like
- <code>{@link android.graphics.Canvas#drawPicture(Picture,Rect) drawPicture()}</code>). In doing so, you are also in
- control of any animation.</li>
-</ol>
-
-<p>Option "a," drawing to a View, is your best choice when you want to draw simple graphics that do not
-need to change dynamically and are not part of a performance-intensive game. For example, you should
-draw your graphics into a View when you want to display a static graphic or predefined animation, within
-an otherwise static application. Read <a href="#draw-to-view">Simple Graphics Inside a View</a>.</li>
-
-<p>Option "b," drawing to a Canvas, is better when your application needs to regularly re-draw itself.
-Basically, any video game should be drawing to the Canvas on its own. However, there's more than
-one way to do this: </p>
-<ul>
- <li>In the same thread as your UI Activity, wherein you create a custom View component in
- your layout, call <code>{@link android.view.View#invalidate()}</code> and then handle the
- <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code> callback..</li>
- <li>Or, in a separate thread, wherein you manage a {@link android.view.SurfaceView} and
- perform draws to the Canvas as fast as your thread is capable
- (you do not need to request <code>invalidate()</code>).</li>
-</ul>
-<p>...Begin by reading <a href="#draw-with-canvas">Draw with a Canvas</a>.</p>
-
-<h2 id="draw-to-view">Simple Graphics Inside a View</h2>
-
-<p>If you'll be drawing some simple graphics (images, shapes, colors, pre-defined animations, etc.),
-then you should probably just draw to the background of a View or
-to the content of an {@link android.widget.ImageView} in your layout.
-In this case, you can skip the rest of this document and learn how to
-draw graphics and animations in the <a href="2d-graphics.html">2D Graphics</a> document.
+for an interactive game. Here, we'll discuss a few of the options you have for drawing graphics
+on Android and which tasks they're best suited for.
</p>
-
-<h2 id="draw-with-canvas">Draw with a Canvas</h2>
-
-<p>When you're writing an application in which you would like to perform specialized drawing
-and/or control the animation of graphics,
-you should do so by drawing through a {@link android.graphics.Canvas}. A Canvas works for you as
-a pretense, or interface, to the actual surface upon which your graphics will be drawn &mdash; it
-holds all of your "draw" calls. Via the Canvas, your drawing is actually performed upon an
-underlying {@link android.graphics.Bitmap}, which is placed into the window.</p>
-
-<p>In the event that you're drawing within the <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code>
-callback method, the Canvas is provided for you and you need only place your drawing calls upon it.
-You can also acquire a Canvas from <code>{@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()}</code>,
-when dealing with a SurfaceView object. (Both of these scenarios are discussed in the following sections.)
-However, if you need to create a new Canvas, then you must define the {@link android.graphics.Bitmap}
-upon which drawing will actually be performed. The Bitmap is always required for a Canvas. You can set up
-a new Canvas like this:</p>
-<pre>
-Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
-Canvas c = new Canvas(b);
-</pre>
-
-<p>Now your Canvas will draw onto the defined Bitmap. After drawing upon it with the Canvas, you can then carry your
-Bitmap to another Canvas with one of the <code>{@link android.graphics.Canvas#drawBitmap(Bitmap,Matrix,Paint)
-Canvas.drawBitmap(Bitmap,...)}</code> methods. It's recommended that you ultimately draw your final
-graphics through a Canvas offered to you
-by <code>{@link android.view.View#onDraw(Canvas) View.onDraw()}</code> or
-<code>{@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()}</code> (see the following sections).</p>
-
-<p>The {@link android.graphics.Canvas} class has its own set of drawing methods that you can use,
-like <code>drawBitmap(...)</code>, <code>drawRect(...)</code>, <code>drawText(...)</code>, and many more.
-Other classes that you might use also have <code>draw()</code> methods. For example, you'll probably
-have some {@link android.graphics.drawable.Drawable} objects that you want to put on the Canvas. Drawable
-has its own <code>{@link android.graphics.drawable.Drawable#draw(Canvas) draw()}</code> method
-that takes your Canvas as an argument.</p>
-
-
-<h3 id="on-view">On a View</h3>
-
-<p>If your application does not require a significant amount of processing or
-frame-rate speed (perhaps for a chess game, a snake game,
-or another slowly-animated application), then you should consider creating a custom View component
-and drawing with a Canvas in <code>{@link android.view.View#onDraw(Canvas) View.onDraw()}</code>.
-The most convenient aspect of doing so is that the Android framework will
-provide you with a pre-defined Canvas to which you will place your drawing calls.</p>
-
-<p>To start, extend the {@link android.view.View} class (or descendant thereof) and define
-the <code>{@link android.view.View#onDraw(Canvas) onDraw()}</code> callback method. This method will be called by the Android
-framework to request that your View draw itself. This is where you will perform all your calls
-to draw through the {@link android.graphics.Canvas}, which is passed to you through the <code>onDraw()</code> callback.</p>
-
-<p>The Android framework will only call <code>onDraw()</code> as necessary. Each time that
-your application is prepared to be drawn, you must request your View be invalidated by calling
-<code>{@link android.view.View#invalidate()}</code>. This indicates that you'd like your View to be drawn and
-Android will then call your <code>onDraw()</code> method (though is not guaranteed that the callback will
-be instantaneous). </p>
-
-<p>Inside your View component's <code>onDraw()</code>, use the Canvas given to you for all your drawing,
-using various <code>Canvas.draw...()</code> methods, or other class <code>draw()</code> methods that
-take your Canvas as an argument. Once your <code>onDraw()</code> is complete, the Android framework will
-use your Canvas to draw a Bitmap handled by the system.</p>
-
-<p class="note"><strong>Note: </strong> In order to request an invalidate from a thread other than your main
-Activity's thread, you must call <code>{@link android.view.View#postInvalidate()}</code>.</p>
-
-<p>Also read <a href="{@docRoot}guide/topics/ui/custom-components.html">Custom Components</a>
-for a guide to extending a View class, and <a href="2d-graphics.html">2D Graphics: Drawables</a> for
-information on using Drawable objects like images from your resources and other primitive shapes.</p>
-
-<p>For a sample application, see the Snake game, in the SDK samples folder:
-<code>&lt;your-sdk-directory>/samples/Snake/</code>.</p>
-
-<h3 id="on-surfaceview">On a SurfaceView</h3>
-
-<p>The {@link android.view.SurfaceView} is a special subclass of View that offers a dedicated
-drawing surface within the View hierarchy. The aim is to offer this drawing surface to
-an application's secondary thread, so that the application isn't required
-to wait until the system's View hierarchy is ready to draw. Instead, a secondary thread
-that has reference to a SurfaceView can draw to its own Canvas at its own pace.</p>
-
-<p>To begin, you need to create a new class that extends {@link android.view.SurfaceView}. The class should also
-implement {@link android.view.SurfaceHolder.Callback}. This subclass is an interface that will notify you
-with information about the underlying {@link android.view.Surface}, such as when it is created, changed, or destroyed.
-These events are important so that you know when you can start drawing, whether you need
-to make adjustments based on new surface properties, and when to stop drawing and potentially
-kill some tasks. Inside your SurfaceView class is also a good place to define your secondary Thread class, which will
-perform all the drawing procedures to your Canvas.</p>
-
-<p>Instead of handling the Surface object directly, you should handle it via
-a {@link android.view.SurfaceHolder}. So, when your SurfaceView is initialized, get the SurfaceHolder by calling
-<code>{@link android.view.SurfaceView#getHolder()}</code>. You should then notify the SurfaceHolder that you'd
-like to receive SurfaceHolder callbacks (from {@link android.view.SurfaceHolder.Callback}) by calling
-{@link android.view.SurfaceHolder#addCallback(SurfaceHolder.Callback) addCallback()}
-(pass it <var>this</var>). Then override each of the
-{@link android.view.SurfaceHolder.Callback} methods inside your SurfaceView class.</p>
-
-<p>In order to draw to the Surface Canvas from within your second thread, you must pass the thread your SurfaceHandler
-and retrieve the Canvas with <code>{@link android.view.SurfaceHolder#lockCanvas() lockCanvas()}</code>.
-You can now take the Canvas given to you by the SurfaceHolder and do your necessary drawing upon it.
-Once you're done drawing with the Canvas, call
-<code>{@link android.view.SurfaceHolder#unlockCanvasAndPost(Canvas) unlockCanvasAndPost()}</code>, passing it
-your Canvas object. The Surface will now draw the Canvas as you left it. Perform this sequence of locking and
-unlocking the canvas each time you want to redraw.</p>
-
-<p class="note"><strong>Note:</strong> On each pass you retrieve the Canvas from the SurfaceHolder,
-the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the
-entire surface. For example, you can clear the previous state of the Canvas by filling in a color
-with <code>{@link android.graphics.Canvas#drawColor(int) drawColor()}</code> or setting a background image
-with <code>{@link android.graphics.Canvas#drawBitmap(Bitmap,Rect,RectF,Paint) drawBitmap()}</code>. Otherwise,
-you will see traces of the drawings you previously performed.</p>
-
-
-<p>For a sample application, see the Lunar Lander game, in the SDK samples folder:
-<code>&lt;your-sdk-directory>/samples/LunarLander/</code>. Or,
-browse the source in the <a href="{@docRoot}guide/samples/index.html">Sample Code</a> section.</p>
-
-
-
-
-
-
+<dl>
+<dt><strong><a href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and
+Drawables</a></strong></dt>
+<dd>Android provides a set of {@link android.view.View} widgets that provide general functionality
+for a wide array of user interfaces. You can also extend these widgets to modify the way they
+look or behave. In addition, you can do your own custom 2D rendering using the various drawing
+methods contained in the {@link android.graphics.Canvas} class or create {@link
+android.graphics.drawable.Drawable} objects for things such as textured buttons or frame-by-frame
+animations.</dd>
+
+<dt><strong><a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware
+Acceleration</a></strong></dt>
+<dd>Beginning in Android 3.0, you can hardware accelerate the majority of
+the drawing done by the Canvas APIs to further increase their performance.</dd>
+
+<dt><strong><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></strong></dt>
+<dd>Android supports OpenGL ES 1.0 and 2.0, with Android framework APIs as well as natively
+with the Native Development Kit (NDK). Using the framework APIs is desireable when you want to add a
+few graphical enhancements to your application that are not supported with the Canvas APIs, or if
+you desire platform independence and don't demand high performance. There is a performance hit in
+using the framework APIs compared to the NDK, so for many graphic intensive applications such as
+games, using the NDK is beneficial (It is important to note though that you can still get adequate
+performance using the framework APIs. For example, the Google Body app is developed entirely
+using the framework APIs). OpenGL with the NDK is also useful if you have a lot of native
+code that you want to port over to Android. For more information about using the NDK, read the
+docs in the <code>docs/</code> directory of the <a href="{@docRoot}sdk/ndk/index.html">NDK
+download.</a></dd>
+</dl>
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index b750858..231f4ef 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -1,4 +1,4 @@
-page.title=3D with OpenGL
+page.title=OpenGL
parent.title=Graphics
parent.link=index.html
@jd:body
@@ -6,7 +6,7 @@ parent.link=index.html
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
-
+
<ol>
<li><a href="#basics">The Basics</a>
<ol>
@@ -14,7 +14,7 @@ parent.link=index.html
</ol>
<li><a href="#manifest">Declaring OpenGL Requirements</a></li>
</li>
- <li><a href="#coordinate-mapping">Mapping Coordinates for Drawn Objects</a>
+ <li><a href="#coordinate-mapping">Mapping Coordinates for Drawn Objects</a>
<ol>
<li><a href="#proj-es1">Projection and camera in ES 1.0</a></li>
<li><a href="#proj-es1">Projection and camera in ES 2.0</a></li>
@@ -78,8 +78,7 @@ OpenGL ES 2.0 API specification.</p>
Kit (NDK). This topic focuses on the Android framework interfaces. For more information about the
NDK, see the <a href="{@docRoot}sdk/ndk/index.html">Android NDK</a>.
-<p>
- There are two foundational classes in the Android framework that let you create and manipulate
+<p>There are two foundational classes in the Android framework that let you create and manipulate
graphics with the OpenGL ES API: {@link android.opengl.GLSurfaceView} and {@link
android.opengl.GLSurfaceView.Renderer}. If your goal is to use OpenGL in your Android application,
understanding how to implement these classes in an activity should be your first objective.
@@ -89,22 +88,22 @@ understanding how to implement these classes in an activity should be your first
<dt><strong>{@link android.opengl.GLSurfaceView}</strong></dt>
<dd>This class is a {@link android.view.View} where you can draw and manipulate objects using
OpenGL API calls and is similar in function to a {@link android.view.SurfaceView}. You can use
- this class by creating an instance of {@link android.opengl.GLSurfaceView} and adding your
+ this class by creating an instance of {@link android.opengl.GLSurfaceView} and adding your
{@link android.opengl.GLSurfaceView.Renderer Renderer} to it. However, if you want to capture
touch screen events, you should extend the {@link android.opengl.GLSurfaceView} class to
- implement the touch listeners, as shown in OpenGL Tutorials for
- <a href="{@docRoot}resources/tutorials/opengl/opengl-es10.html#touch">ES 1.0</a>,
+ implement the touch listeners, as shown in OpenGL Tutorials for
+ <a href="{@docRoot}resources/tutorials/opengl/opengl-es10.html#touch">ES 1.0</a>,
<a href="{@docRoot}resources/tutorials/opengl/opengl-es20.html#touch">ES 2.0</a> and the <a
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity
.html">TouchRotateActivity</a> sample.</dd>
-
+
<dt><strong>{@link android.opengl.GLSurfaceView.Renderer}</strong></dt>
<dd>This interface defines the methods required for drawing graphics in an OpenGL {@link
android.opengl.GLSurfaceView}. You must provide an implementation of this interface as a
separate class and attach it to your {@link android.opengl.GLSurfaceView} instance using
{@link android.opengl.GLSurfaceView#setRenderer(android.opengl.GLSurfaceView.Renderer)
GLSurfaceView.setRenderer()}.
-
+
<p>The {@link android.opengl.GLSurfaceView.Renderer} interface requires that you implement the
following methods:</p>
<ul>
@@ -129,7 +128,7 @@ href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics
android.opengl.GLSurfaceView} geometry changes, including changes in size of the {@link
android.opengl.GLSurfaceView} or orientation of the device screen. For example, the system calls
this method when the device changes from portrait to landscape orientation. Use this method to
- respond to changes in the {@link android.opengl.GLSurfaceView} container.
+ respond to changes in the {@link android.opengl.GLSurfaceView} container.
</li>
</ul>
</dd>
@@ -173,13 +172,13 @@ interface to OpenGL ES 2.0 and is available starting with Android 2.2 (API Level
</ul>
<p>If you'd like to start building an app with OpenGL right away, have a look at the tutorials for
-<a href="{@docRoot}resources/tutorials/opengl/opengl-es10.html">OpenGL ES 1.0</a> or
+<a href="{@docRoot}resources/tutorials/opengl/opengl-es10.html">OpenGL ES 1.0</a> or
<a href="{@docRoot}resources/tutorials/opengl/opengl-es20.html">OpenGL ES 2.0</a>!
</p>
<h2 id="manifest">Declaring OpenGL Requirements</h2>
<p>If your application uses OpenGL features that are not available on all devices, you must include
-these requirements in your <a
+these requirements in your <a
href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a></code> file.
Here are the most common OpenGL manifest declarations:</p>
@@ -200,14 +199,14 @@ shown below.
compression formats, you must declare the formats your application supports in your manifest file
using <a href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html">{@code
&lt;supports-gl-texture&gt;}</a>. For more information about available texture compression
-formats, see <a href="#textures">Texture compression support</a>.
+formats, see <a href="#textures">Texture compression support</a>.
<p>Declaring texture compression requirements in your manifest hides your application from users
with devices that do not support at least one of your declared compression types. For more
information on how Android Market filtering works for texture compressions, see the <a
href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html#market-texture-filtering">
Android Market and texture compression filtering</a> section of the {@code
-&lt;supports-gl-texture&gt;} documentation.</p>
+&lt;supports-gl-texture&gt;} documentation.</p>
</li>
</ul>
@@ -237,7 +236,7 @@ matrix creates a transformation that renders objects from a specific eye positio
<h3 id="proj-es1">Projection and camera view in OpenGL ES 1.0</h3>
<p>In the ES 1.0 API, you apply projection and camera view by creating each matrix and then
adding them to the OpenGL environment.</p>
-
+
<ol>
<li><strong>Projection matrix</strong> - Create a projection matrix using the geometry of the
device screen in order to recalculate object coordinates so they are drawn with correct proportions.
@@ -250,19 +249,19 @@ OpenGL rendering environment.
<pre>
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
-
+
// make adjustments for screen ratio
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION); // set matrix to projection mode
gl.glLoadIdentity(); // reset the matrix to its default state
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); // apply the projection matrix
- }
+ }
</pre>
</li>
<li><strong>Camera transformation matrix</strong> - Once you have adjusted the coordinate system
using a projection matrix, you must also apply a camera view. The following example code shows how
-to modify the {@link
+to modify the {@link
android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.opengles.GL10)
onDrawFrame()} method of a {@link android.opengl.GLSurfaceView.Renderer}
implementation to apply a model view and use the
@@ -276,12 +275,12 @@ which simulates a camera position.
// Set GL_MODELVIEW transformation mode
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity(); // reset the matrix to its default state
-
+
// When using GL_MODELVIEW, you must set the camera view
- GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+ GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
...
}
-</pre>
+</pre>
</li>
</ol>
@@ -294,26 +293,26 @@ tutorial</a>.</p>
<p>In the ES 2.0 API, you apply projection and camera view by first adding a matrix member to
the vertex shaders of your graphics objects. With this matrix member added, you can then
generate and apply projection and camera viewing matrices to your objects.</p>
-
+
<ol>
<li><strong>Add matrix to vertex shaders</strong> - Create a variable for the view projection matrix
and include it as a multiplier of the shader's position. In the following example vertex shader
-code, the included {@code uMVPMatrix} member allows you to apply projection and camera viewing
+code, the included {@code uMVPMatrix} member allows you to apply projection and camera viewing
matrices to the coordinates of objects that use this shader.
<pre>
- private final String vertexShaderCode =
-
+ private final String vertexShaderCode =
+
// This matrix member variable provides a hook to manipulate
// the coordinates of objects that use this vertex shader
"uniform mat4 uMVPMatrix; \n" +
-
+
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
-
+
// the matrix must be included as part of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
-
+
"} \n";
</pre>
<p class="note"><strong>Note:</strong> The example above defines a single transformation matrix
@@ -340,7 +339,7 @@ variable defined in the vertex shader above.
</li>
<li><strong>Create projection and camera viewing matrices</strong> - Generate the projection and
viewing matrices to be applied the graphic objects. The following example code shows how to modify
-the {@link
+the {@link
android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.microedition.khronos.opengles.GL10,
javax.microedition.khronos.egl.EGLConfig) onSurfaceCreated()} and {@link
android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10,
@@ -353,16 +352,16 @@ of the device.
...
// Create a camera view matrix
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
- }
-
+ }
+
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
-
+
float ratio = (float) width / height;
-
+
// create a projection matrix from device screen geometry
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
- }
+ }
</pre>
</li>
@@ -373,16 +372,16 @@ android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.ope
onDrawFrame()} method of a {@link android.opengl.GLSurfaceView.Renderer} implementation to combine
the projection matrix and camera view created in the code above and then apply it to the graphic
objects to be rendered by OpenGL.
-
+
<pre>
public void onDrawFrame(GL10 unused) {
...
// Combine the projection and camera view matrices
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
-
+
// Apply the combined projection and camera view transformations
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
-
+
// Draw objects
...
}
@@ -498,7 +497,7 @@ must run this call on several target devices to determine what compression types
supported.</p>
</li>
<li>Review the output of this method to determine what OpenGL extensions are supported on the
-device.</li>
+device.</li>
</ol>
@@ -514,7 +513,7 @@ should carefully consider the following factors before starting development with
than the ES 1.0/1.1 APIs. However, the performance difference can vary depending on the Android
device your OpenGL application is running on, due to differences in the implementation of the OpenGL
graphics pipeline.</li>
- <li><strong>Device Compatibility</strong> - Developers should consider the types of devices,
+ <li><strong>Device Compatibility</strong> - Developers should consider the types of devices,
Android versions and the OpenGL ES versions available to their customers. For more information
on OpenGL compatibility across devices, see the <a href="#compatibility">OpenGL Versions and Device
Compatibility</a> section.</li>
@@ -526,7 +525,7 @@ of control by providing a fully programmable pipeline through the use of shaders
direct control of the graphics processing pipeline, developers can create effects that would be
very difficult to generate using the 1.0/1.1 API.</li>
</ul>
-
+
<p>While performance, compatibility, convenience, control and other factors may influence your
decision, you should pick an OpenGL API version based on what you think provides the best experience
for your users.</p>
diff --git a/docs/html/guide/topics/graphics/prop-animation.jd b/docs/html/guide/topics/graphics/prop-animation.jd
new file mode 100644
index 0000000..be24788
--- /dev/null
+++ b/docs/html/guide/topics/graphics/prop-animation.jd
@@ -0,0 +1,953 @@
+page.title=Property Animation
+parent.title=Animation
+parent.link=animation.html
+@jd:body
+
+ <div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+
+ <ol>
+ <li><a href="#how">How Property Animation Works</a></li>
+
+ <li><a href="#value-animator">Animating with ValueAnimator</a></li>
+
+ <li><a href="#object-animator">Animating with ObjectAnimator</a></li>
+
+ <li><a href="#choreography">Choreographing Multiple Animations with
+ AnimatorSet</a></li>
+
+ <li><a href="#listeners">Animation Listeners</a></li>
+
+ <li><a href="#type-evaluator">Using a TypeEvaluator</a></li>
+
+ <li><a href="#interpolators">Using Interpolators</a></li>
+
+ <li><a href="#keyframes">Specifying Keyframes</a></li>
+
+ <li><a href="#layout">Animating Layout Changes to ViewGroups</a></li>
+
+ <li><a href="#views">Animating Views</a>
+ <ol>
+ <li><a href="#view-prop-animator">ViewPropertyAnimator</a></li>
+ </ol>
+ </li>
+
+ <li><a href="#declaring-xml">Declaring Animations in XML</a></li>
+ </ol>
+
+ <h2>Key classes</h2>
+
+ <ol>
+ <li><code><a href=
+ "/reference/android/animation/ValueAnimator.html">ValueAnimator</a></code></li>
+
+ <li><code><a href=
+ "/reference/android/animation/ObjectAnimator.html">ObjectAnimator</a></code></li>
+
+ <li><code><a href=
+ "/reference/android/animation/TypeEvaluator.html">TypeEvaluator</a></code></li>
+ </ol>
+
+ <h2>Related samples</h2>
+
+ <ol>
+ <li><a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
+ Demos</a></li>
+ </ol>
+ </div>
+ </div>
+ <p>The property animation system is a robust framework that allows you
+ to animate almost anything. You can define an animation to change any object property over time,
+ regardless of whether it draws to the screen or not. A property animation changes a property's
+ (a field in an object) value over a specified length of time. To animate something, you specify the
+ object property that you want to animate, such as an object's position on the screen, how long
+ you want to animate it for, and what values you want to animate between. </p>
+
+ <p>The property animation system lets you define the following characteristics of an
+ animation:</p>
+
+ <ul>
+ <li>Duration: You can specify the duration of an animation. The default length is 300 ms.</li>
+
+ <li>Time interpolation: You can specify how the values for the property are calculated as a
+ function of the animation's current elapsed time.</li>
+
+ <li>Repeat count and behavior: You can specify whether or not to have an animation repeat when
+ it reaches the end of a duration and how many times to repeat the animation. You can also
+ specify whether you want the animation to play back in reverse. Setting it to reverse plays
+ the animation forwards then backwards repeatedly, until the number of repeats is reached.</li>
+
+ <li>Animator sets: You can group animations into logical sets that play together or
+ sequentially or after specified delays.</li>
+
+ <li>Frame refresh delay: You can specify how often to refresh frames of your animation. The
+ default is set to refresh every 10 ms, but the speed in which your application can refresh frames is
+ ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.</li>
+ </ul>
+
+
+ <h2 id="how">How Property Animation Works</h2>
+
+ <p>First, let's go over how an animation works with a simple example. Figure 1 depicts a
+ hypothetical object that is animated with its <code>x</code> property, which represents its
+ horizontal location on a screen. The duration of the animation is set to 40 ms and the distance
+ to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves
+ horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at
+ horizontal position 40. This is an example of an animation with linear interpolation, meaning the
+ object moves at a constant speed.</p><img src="{@docRoot}images/animation/animation-linear.png">
+
+ <p class="img-caption"><strong>Figure 1.</strong> Example of a linear animation</p>
+
+ <p>You can also specify animations to have a non-linear interpolation. Figure 2 illustrates a
+ hypothetical object that accelerates at the beginning of the animation, and decelerates at the
+ end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the
+ beginning, this animation accelerates up to the halfway point then decelerates from the
+ halfway point until the end of the animation. As Figure 2 shows, the distance traveled
+ at the beginning and end of the animation is less than in the middle.</p><img src=
+ "{@docRoot}images/animation/animation-nonlinear.png">
+
+ <p class="img-caption"><strong>Figure 2.</strong> Example of a non-linear animation</p>
+
+ <p>Let's take a detailed look at how the important components of the property animation system
+ would calculate animations like the ones illustrated above. Figure 3 depicts how the main classes
+ work with one another.</p><img src="{@docRoot}images/animation/valueanimator.png">
+
+ <p class="img-caption"><strong>Figure 3.</strong> How animations are calculated</p>
+
+ <p>The {@link android.animation.ValueAnimator} object keeps track of your animation's timing,
+ such as how long the animation has been running, and the current value of the property that it is
+ animating.</p>
+
+ <p>The {@link android.animation.ValueAnimator} encapsulates a {@link
+ android.animation.TimeInterpolator}, which defines animation interpolation, and a {@link
+ android.animation.TypeEvaluator}, which defines how to calculate values for the property being
+ animated. For example, in Figure 2, the {@link android.animation.TimeInterpolator} used would be
+ {@link android.view.animation.AccelerateDecelerateInterpolator} and the {@link
+ android.animation.TypeEvaluator} would be {@link android.animation.IntEvaluator}.</p>
+
+ <p>To start an animation, create a {@link android.animation.ValueAnimator} and give it the
+ starting and ending values for the property that you want to animate, along with the duration of
+ the animation. When you call {@link android.animation.ValueAnimator#start start()} the animation
+ begins. During the whole animation, the {@link android.animation.ValueAnimator} calculates an <em>elapsed fraction</em>
+ between 0 and 1, based on the duration of the animation and how much time has elapsed. The
+ elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0%
+ and 1 meaning 100%. For example, in Figure 1, the elapsed fraction at t = 10 ms would be .25
+ because the total duration is t = 40 ms.</p>
+
+ <p>When the {@link android.animation.ValueAnimator} is done calculating an elapsed fraction, it
+ calls the {@link android.animation.TimeInterpolator} that is currently set, to calculate an
+ <em>interpolated fraction</em>. An interpolated fraction maps the elapsed fraction to a new
+ fraction that takes into account the time interpolation that is set. For example, in Figure 2,
+ because the animation slowly accelerates, the interpolated fraction, about .15, is less than the
+ elapsed fraction, .25, at t = 10 ms. In Figure 1, the interpolated fraction is always the same as
+ the elapsed fraction.</p>
+
+ <p>When the interpolated fraction is calculated, {@link android.animation.ValueAnimator} calls
+ the appropriate {@link android.animation.TypeEvaluator}, to calculate the value of the
+ property that you are animating, based on the interpolated fraction, the starting value, and the
+ ending value of the animation. For example, in Figure 2, the interpolated fraction was .15 at t =
+ 10 ms, so the value for the property at that time would be .15 X (40 - 0), or 6.</p>
+
+ <!-- <p>When the final value is calculated, the {@link android.animation.ValueAnimator} calls the
+ {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate
+ onAnimationUpdate()} method. Implement this callback to obtain the property value by
+ calling {@link android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} and set the
+ value for the property in the object that you are animating. Setting the property doesn't redraw
+ the object on the screen, so you need to call {@link
+ android.view.View#invalidate invalidate()} to refresh the View that the object
+ resides in. If the object is actually a View object, then the system calls {@link
+ android.view.View#invalidate invalidate()} when the property is changed.
+ The system redraws the window and the {@link android.animation.ValueAnimator}
+ repeats the process.</p>-->
+
+ <p>The <code>com.example.android.apis.animation</code> package in the <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
+ Demos</a> sample project provides many examples on how to use the property
+ animation system.</p>
+
+ <h2>API Overview</h2>
+
+ <p>You can find most of the property animation system's APIs in {@link android.animation
+ android.animation}. Because the view animation system already
+ defines many interpolators in {@link android.view.animation android.view.animation}, you can use
+ those interpolators in the property animation system as well. The following tables describe the main
+ components of the property animation system.</p>
+
+ <p>The {@link android.animation.Animator} class provides the basic structure for creating
+ animations. You normally do not use this class directly as it only provides minimal
+ functionality that must be extended to fully support animating values. The following
+ subclasses extend {@link android.animation.Animator}:
+ </p>
+ <p class="table-caption"><strong>Table 1.</strong> Animators</p>
+ <table>
+ <tr>
+ <th>Class</th>
+
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.ValueAnimator}</td>
+
+ <td>The main timing engine for property animation that also computes the values for the
+ property to be animated. It has all of the core functionality that calculates animation
+ values and contains the timing details of each animation, information about whether an
+ animation repeats, listeners that receive update events, and the ability to set custom
+ types to evaluate. There are two pieces to animating properties: calculating the animated
+ values and setting those values on the object and property that is being animated. {@link
+ android.animation.ValueAnimator} does not carry out the second piece, so you must listen
+ for updates to values calculated by the {@link android.animation.ValueAnimator} and
+ modify the objects that you want to animate with your own logic. See the section about
+ <a href="#value-animator">Animating with ValueAnimator</a> for more information.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.ObjectAnimator}</td>
+
+ <td>A subclass of {@link android.animation.ValueAnimator} that allows you to set a target
+ object and object property to animate. This class updates the property accordingly when
+ it computes a new value for the animation. You want to use
+ {@link android.animation.ObjectAnimator} most of the time,
+ because it makes the process of animating values on target objects much easier. However,
+ you sometimes want to use {@link android.animation.ValueAnimator} directly because {@link
+ android.animation.ObjectAnimator} has a few more restrictions, such as requiring specific
+ acessor methods to be present on the target object.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.AnimatorSet}</td>
+
+ <td>Provides a mechanism to group animations together so that they run in
+ relation to one another. You can set animations to play together, sequentially, or after
+ a specified delay. See the section about <a href="#choreography">Choreographing multiple
+ animations with Animator Sets</a> for more information.</td>
+ </tr>
+ </table>
+
+
+ <p>Evaluators tell the property animation system how to calculate values for a given
+ property. They take the timing data that is provided by an {@link android.animation.Animator}
+ class, the animation's start and end value, and calculate the animated values of the property
+ based on this data. The property animation system provides the following evaluators:</p>
+ <p class="table-caption"><strong>Table 2.</strong> Evaluators</p>
+ <table>
+ <tr>
+ <th>Class/Interface</th>
+
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.IntEvaluator}</td>
+
+ <td>The default evaluator to calculate values for <code>int</code> properties.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.FloatEvaluator}</td>
+
+ <td>The default evaluator to calculate values for <code>float</code> properties.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.ArgbEvaluator}</td>
+
+ <td>The default evaluator to calculate values for color properties that are represented
+ as hexidecimal values.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.TypeEvaluator}</td>
+
+ <td>An interface that allows you to create your own evaluator. If you are animating an
+ object property that is <em>not</em> an <code>int</code>, <code>float</code>, or color,
+ you must implement the {@link android.animation.TypeEvaluator} interface to specify how
+ to compute the object property's animated values. You can also specify a custom {@link
+ android.animation.TypeEvaluator} for <code>int</code>, <code>float</code>, and color
+ values as well, if you want to process those types differently than the default behavior.
+ See the section about <a href="#type-evaluator">Using a TypeEvaluator</a> for more
+ information on how to write a custom evaluator.</td>
+ </tr>
+ </table>
+
+
+
+
+ <p>A time interpolator defines how specific values in an animation are calculated as a
+ function of time. For example, you can specify animations to happen linearly across the whole
+ animation, meaning the animation moves evenly the entire time, or you can specify animations
+ to use non-linear time, for example, accelerating at the beginning and decelerating at the
+ end of the animation. Table 3 describes the interpolators that are contained in {@link
+ android.view.animation android.view.animation}. If none of the provided interpolators suits
+ your needs, implement the {@link android.animation.TimeInterpolator} interface and create your own. See <a href=
+ "#interpolators">Using interpolators</a> for more information on how to write a custom
+ interpolator.</p>
+ <p class="table-caption"><strong>Table 3.</strong> Interpolators</p>
+ <table>
+ <tr>
+ <th>Class/Interface</th>
+
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.AccelerateDecelerateInterpolator}</td>
+
+ <td>An interpolator whose rate of change starts and ends slowly but accelerates
+ through the middle.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.AccelerateInterpolator}</td>
+
+ <td>An interpolator whose rate of change starts out slowly and then
+ accelerates.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.AnticipateInterpolator}</td>
+
+ <td>An interpolator whose change starts backward then flings forward.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.AnticipateOvershootInterpolator}</td>
+
+ <td>An interpolator whose change starts backward, flings forward and overshoots
+ the target value, then finally goes back to the final value.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.BounceInterpolator}</td>
+
+ <td>An interpolator whose change bounces at the end.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.CycleInterpolator}</td>
+
+ <td>An interpolator whose animation repeats for a specified number of cycles.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.DecelerateInterpolator}</td>
+
+ <td>An interpolator whose rate of change starts out quickly and and then
+ decelerates.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.LinearInterpolator}</td>
+
+ <td>An interpolator whose rate of change is constant.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.view.animation.OvershootInterpolator}</td>
+
+ <td>An interpolator whose change flings forward and overshoots the last value then
+ comes back.</td>
+ </tr>
+
+ <tr>
+ <td>{@link android.animation.TimeInterpolator}</td>
+
+ <td>An interface that allows you to implement your own interpolator.</td>
+ </tr>
+ </table>
+
+ <h2 id="value-animator">Animating with ValueAnimator</h2>
+
+ <p>The {@link android.animation.ValueAnimator} class lets you animate values of some type for the
+ duration of an animation by specifying a set of <code>int</code>, <code>float</code>, or color
+ values to animate through. You obtain a {@link android.animation.ValueAnimator} by calling one of
+ its factory methods: {@link android.animation.ValueAnimator#ofInt ofInt()}, {@link
+ android.animation.ValueAnimator#ofFloat ofFloat()}, or {@link
+ android.animation.ValueAnimator#ofObject ofObject()}. For example:</p>
+ <pre>
+ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
+animation.setDuration(1000);
+animation.start();
+</pre>
+
+ <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the
+ animation, between 0 and 1, for a duration of 1000 ms, when the <code>start()</code> method
+ runs.</p>
+
+ <p>You can also specify a custom type to animate by doing the following:</p>
+ <pre>
+ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
+animation.setDuration(1000);
+animation.start();
+</pre>
+
+ <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the
+ animation, between <code>startPropertyValue</code> and <code>endPropertyValue</code> using the
+ logic supplied by <code>MyTypeEvaluator</code> for a duration of 1000 ms, when the {@link
+ android.animation.ValueAnimator#start start()} method runs.</p>
+
+ <p>The previous code snippets, however, has no real effect on an object, because the {@link
+ android.animation.ValueAnimator} does not operate on objects or properties directly. The most likely thing
+ that you want to do is modify the objects that you want to animate with these calculated values. You do
+ this by defining listeners in the {@link android.animation.ValueAnimator} to appropriately handle important events
+ during the animation's lifespan, such as frame updates. When implementing the listeners, you can
+ obtain the calculated value for that specific frame refresh by calling {@link
+ android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()}. For more information on listeners,
+ see the section about <a href="#listeners">Animation Listeners</a>.
+
+ <h2 id="object-animator">Animating with ObjectAnimator</h2>
+
+ <p>The {@link android.animation.ObjectAnimator} is a subclass of the {@link
+ android.animation.ValueAnimator} (discussed in the previous section) and combines the timing
+ engine and value computation of {@link android.animation.ValueAnimator} with the ability to
+ animate a named property of a target object. This makes animating any object much easier, as you
+ no longer need to implement the {@link android.animation.ValueAnimator.AnimatorUpdateListener},
+ because the animated property updates automatically.</p>
+
+ <p>Instantiating an {@link android.animation.ObjectAnimator} is similar to a {@link
+ android.animation.ValueAnimator}, but you also specify the object and the name of that object's property (as
+ a String) along with the values to animate between:</p>
+ <pre>
+ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
+anim.setDuration(1000);
+anim.start();
+</pre>
+
+ <p>To have the {@link android.animation.ObjectAnimator} update properties correctly, you must do
+ the following:</p>
+
+ <ul>
+ <li>The object property that you are animating must have a setter function (in camel case) in the form of
+ <code>set&lt;propertyName&gt;()</code>. Because the {@link android.animation.ObjectAnimator}
+ automatically updates the property during animation, it must be able to access the property
+ with this setter method. For example, if the property name is <code>foo</code>, you need to
+ have a <code>setFoo()</code> method. If this setter method does not exist, you have three
+ options:
+
+ <ul>
+ <li>Add the setter method to the class if you have the rights to do so.</li>
+
+ <li>Use a wrapper class that you have rights to change and have that wrapper receive the
+ value with a valid setter method and forward it to the original object.</li>
+
+ <li>Use {@link android.animation.ValueAnimator} instead.</li>
+ </ul>
+ </li>
+
+ <li>If you specify only one value for the <code>values...</code> parameter in one of the {@link
+ android.animation.ObjectAnimator} factory methods, it is assumed to be the ending value of the
+ animation. Therefore, the object property that you are animating must have a getter function
+ that is used to obtain the starting value of the animation. The getter function must be in the
+ form of <code>get&lt;propertyName&gt;()</code>. For example, if the property name is
+ <code>foo</code>, you need to have a <code>getFoo()</code> method.</li>
+
+ <li>The getter (if needed) and setter methods of the property that you are animating must
+ operate on the same type as the starting and ending values that you specify to {@link
+ android.animation.ObjectAnimator}. For example, you must have
+ <code>targetObject.setPropName(float)</code> and <code>targetObject.getPropName(float)</code>
+ if you construct the following {@link android.animation.ObjectAnimator}:
+ <pre>
+ObjectAnimator.ofFloat(targetObject, "propName", 1f)
+</pre>
+ </li>
+
+ <li>Depending on what property or object you are animating, you might need to call the {@link
+ android.view.View#invalidate invalidate()} method on a View force the screen to redraw itself with the
+ updated animated values. You do this in the
+ {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate onAnimationUpdate()}
+ callback. For example, animating the color property of a Drawable object only cause updates to the
+ screen when that object redraws itself. All of the property setters on View, such as
+ {@link android.view.View#setAlpha setAlpha()} and {@link android.view.View#setTranslationX setTranslationX()}
+ invalidate the View properly, so you do not need to invalidate the View when calling these
+ methods with new values. For more information on listeners, see the section about <a href="#listeners">Animation Listeners</a>.
+ </li>
+ </ul>
+
+ <h2 id="choreography">Choreographing Multiple Animations with AnimatorSet</h2>
+
+ <p>In many cases, you want to play an animation that depends on when another animation starts or
+ finishes. The Android system lets you bundle animations together into an {@link
+ android.animation.AnimatorSet}, so that you can specify whether to start animations
+ simultaneously, sequentially, or after a specified delay. You can also nest {@link
+ android.animation.AnimatorSet} objects within each other.</p>
+
+ <p>The following sample code taken from the <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
+ Balls</a> sample (modified for simplicity) plays the following {@link android.animation.Animator}
+ objects in the following manner:</p>
+
+ <ol>
+ <li>Plays <code>bounceAnim</code>.</li>
+
+ <li>Plays <code>squashAnim1</code>, <code>squashAnim2</code>, <code>stretchAnim1</code>, and
+ <code>stretchAnim2</code> at the same time.</li>
+
+ <li>Plays <code>bounceBackAnim</code>.</li>
+
+ <li>Plays <code>fadeAnim</code>.</li>
+ </ol>
+ <pre>
+AnimatorSet bouncer = new AnimatorSet();
+bouncer.play(bounceAnim).before(squashAnim1);
+bouncer.play(squashAnim1).with(squashAnim2);
+bouncer.play(squashAnim1).with(stretchAnim1);
+bouncer.play(squashAnim1).with(stretchAnim2);
+bouncer.play(bounceBackAnim).after(stretchAnim2);
+ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
+fadeAnim.setDuration(250);
+AnimatorSet animatorSet = new AnimatorSet();
+animatorSet.play(bouncer).before(fadeAnim);
+animatorSet.start();
+</pre>
+
+ <p>For a more complete example on how to use animator sets, see the <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
+ Balls</a> sample in APIDemos.</p>
+
+<h2 id="listeners">Animation Listeners</h2>
+<p>
+You can listen for important events during an animation's duration with the listeners described below.
+</p>
+
+ <ul>
+ <li>{@link android.animation.Animator.AnimatorListener}
+
+ <ul>
+ <li>{@link android.animation.Animator.AnimatorListener#onAnimationStart onAnimationStart()}
+ - Called when the animation starts.</li>
+
+ <li>{@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()} -
+ Called when the animation ends.</li>
+
+ <li>{@link android.animation.Animator.AnimatorListener#onAnimationRepeat
+ onAnimationRepeat()} - Called when the animation repeats itself.</li>
+
+ <li>{@link android.animation.Animator.AnimatorListener#onAnimationCancel
+ onAnimationCancel()} - Called when the animation is canceled. A cancelled animation
+ also calls {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()},
+ regardless of how they were ended.</li>
+ </ul>
+ </li>
+
+ <li>{@link android.animation.ValueAnimator.AnimatorUpdateListener}
+
+ <ul>
+ <li>
+ <p>{@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate
+ onAnimationUpdate()} - called on every frame of the animation. Listen to this event to
+ use the calculated values generated by {@link android.animation.ValueAnimator} during an
+ animation. To use the value, query the {@link android.animation.ValueAnimator} object
+ passed into the event to get the current animated value with the {@link
+ android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} method. Implementing this
+ listener is required if you use {@link android.animation.ValueAnimator}. </p>
+
+ <p>
+ Depending on what property or object you are animating, you might need to call
+ {@link android.view.View#invalidate invalidate()} on a View to force that area of the
+ screen to redraw itself with the new animated values. For example, animating the
+ color property of a Drawable object only cause updates to the screen when that object
+ redraws itself. All of the property setters on View,
+ such as {@link android.view.View#setAlpha setAlpha()} and
+ {@link android.view.View#setTranslationX setTranslationX()} invalidate the View
+ properly, so you do not need to invalidate the View when calling these methods with new values.
+ </p>
+
+ </li>
+ </ul>
+ </li>
+ </ul>
+
+<p>You can extend the {@link android.animation.AnimatorListenerAdapter} class instead of
+implementing the {@link android.animation.Animator.AnimatorListener} interface, if you do not
+want to implement all of the methods of the {@link android.animation.Animator.AnimatorListener}
+interface. The {@link android.animation.AnimatorListenerAdapter} class provides empty
+implementations of the methods that you can choose to override.</p>
+ <p>For example, the <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing
+ Balls</a> sample in the API demos creates an {@link android.animation.AnimatorListenerAdapter}
+ for just the {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
+ callback:</p>
+ <pre>
+ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
+fadeAnim.setDuration(250);
+fadeAnim.addListener(new AnimatorListenerAdapter() {
+public void onAnimationEnd(Animator animation) {
+ balls.remove(((ObjectAnimator)animation).getTarget());
+}
+</pre>
+
+
+ <h2 id="layout">Animating Layout Changes to ViewGroups</h2>
+
+ <p>The property animation system provides the capability to animate changes to ViewGroup objects
+ as well as provide an easy way to animate View objects themselves.</p>
+
+ <p>You can animate layout changes within a ViewGroup with the {@link
+ android.animation.LayoutTransition} class. Views inside a ViewGroup can go through an appearing
+ and disappearing animation when you add them to or remove them from a ViewGroup or when you call
+ a View's {@link android.view.View#setVisibility setVisibility()} method with {@link
+ android.view.View#VISIBLE}, android.view.View#INVISIBLE}, or {@link android.view.View#GONE}. The remaining Views in the
+ ViewGroup can also animate into their new positions when you add or remove Views. You can define
+ the following animations in a {@link android.animation.LayoutTransition} object by calling {@link
+ android.animation.LayoutTransition#setAnimator setAnimator()} and passing in an {@link
+ android.animation.Animator} object with one of the following {@link
+ android.animation.LayoutTransition} constants:</p>
+
+ <ul>
+ <li><code>APPEARING</code> - A flag indicating the animation that runs on items that are
+ appearing in the container.</li>
+
+ <li><code>CHANGE_APPEARING</code> - A flag indicating the animation that runs on items that are
+ changing due to a new item appearing in the container.</li>
+
+ <li><code>DISAPPEARING</code> - A flag indicating the animation that runs on items that are
+ disappearing from the container.</li>
+
+ <li><code>CHANGE_DISAPPEARING</code> - A flag indicating the animation that runs on items that
+ are changing due to an item disappearing from the container.</li>
+ </ul>
+
+ <p>You can define your own custom animations for these four types of events to customize the look
+ of your layout transitions or just tell the animation system to use the default animations.</p>
+
+ <p>The <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimations.html">
+ LayoutAnimations</a> sample in API Demos shows you how to define animations for layout
+ transitions and then set the animations on the View objects that you want to animate.</p>
+
+ <p>The <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.html">
+ LayoutAnimationsByDefault</a> and its corresponding <a href=
+ "{@docRoot}resources/samples/ApiDemos/res/layout/layout_animations_by_default.html">layout_animations_by_default.xml</a>
+ layout resource file show you how to enable the default layout transitions for ViewGroups in XML.
+ The only thing that you need to do is to set the <code>android:animateLayoutchanges</code>
+ attribute to <code>true</code> for the ViewGroup. For example:</p>
+ <pre>
+&lt;LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/verticalContainer"
+ android:animateLayoutChanges="true" /&gt;
+</pre>
+
+ <p>Setting this attribute to true automatically animates Views that are added or removed from the
+ ViewGroup as well as the remaining Views in the ViewGroup.</p>
+
+ <h2 id="type-evaluator">Using a TypeEvaluator</h2>
+
+ <p>If you want to animate a type that is unknown to the Android system, you can create your own
+ evaluator by implementing the {@link android.animation.TypeEvaluator} interface. The types that
+ are known by the Android system are <code>int</code>, <code>float</code>, or a color, which are
+ supported by the {@link android.animation.IntEvaluator}, {@link
+ android.animation.FloatEvaluator}, and {@link android.animation.ArgbEvaluator} type
+ evaluators.</p>
+
+ <p>There is only one method to implement in the {@link android.animation.TypeEvaluator}
+ interface, the {@link android.animation.TypeEvaluator#evaluate evaluate()} method. This allows
+ the animator that you are using to return an appropriate value for your animated property at the
+ current point of the animation. The {@link android.animation.FloatEvaluator} class demonstrates
+ how to do this:</p>
+ <pre>
+public class FloatEvaluator implements TypeEvaluator {
+
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ float startFloat = ((Number) startValue).floatValue();
+ return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
+ }
+}
+</pre>
+
+ <p class="note"><strong>Note:</strong> When {@link android.animation.ValueAnimator} (or {@link
+ android.animation.ObjectAnimator}) runs, it calculates a current elapsed fraction of the
+ animation (a value between 0 and 1) and then calculates an interpolated version of that depending
+ on what interpolator that you are using. The interpolated fraction is what your {@link
+ android.animation.TypeEvaluator} receives through the <code>fraction</code> parameter, so you do
+ not have to take into account the interpolator when calculating animated values.</p>
+
+ <h2 id="interpolators">Using Interpolators</h2>
+
+ <p>An interpolator define how specific values in an animation are calculated as a function of
+ time. For example, you can specify animations to happen linearly across the whole animation,
+ meaning the animation moves evenly the entire time, or you can specify animations to use
+ non-linear time, for example, using acceleration or deceleration at the beginning or end of the
+ animation.</p>
+
+ <p>Interpolators in the animation system receive a fraction from Animators that represent the
+ elapsed time of the animation. Interpolators modify this fraction to coincide with the type of
+ animation that it aims to provide. The Android system provides a set of common interpolators in
+ the {@link android.view.animation android.view.animation package}. If none of these suit your
+ needs, you can implement the {@link android.animation.TimeInterpolator} interface and create your
+ own.</p>
+
+ <p>As an example, how the default interpolator {@link
+ android.view.animation.AccelerateDecelerateInterpolator} and the {@link
+ android.view.animation.LinearInterpolator} calculate interpolated fractions are compared below.
+ The {@link android.view.animation.LinearInterpolator} has no effect on the elapsed fraction. The {@link
+ android.view.animation.AccelerateDecelerateInterpolator} accelerates into the animation and
+ decelerates out of it. The following methods define the logic for these interpolators:</p>
+
+ <p><strong>AccelerateDecelerateInterpolator</strong></p>
+ <pre>
+public float getInterpolation(float input) {
+ return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
+}
+</pre>
+
+ <p><strong>LinearInterpolator</strong></p>
+ <pre>
+public float getInterpolation(float input) {
+ return input;
+}
+</pre>
+
+ <p>The following table represents the approximate values that are calculated by these
+ interpolators for an animation that lasts 1000ms:</p>
+
+ <table>
+ <tr>
+ <th>ms elapsed</th>
+
+ <th>Elapsed fraction/Interpolated fraction (Linear)</th>
+
+ <th>Interpolated fraction (Accelerate/Decelerate)</th>
+ </tr>
+
+ <tr>
+ <td>0</td>
+
+ <td>0</td>
+
+ <td>0</td>
+ </tr>
+
+ <tr>
+ <td>200</td>
+
+ <td>.2</td>
+
+ <td>.1</td>
+ </tr>
+
+ <tr>
+ <td>400</td>
+
+ <td>.4</td>
+
+ <td>.345</td>
+ </tr>
+
+ <tr>
+ <td>600</td>
+
+ <td>.6</td>
+
+ <td>.8</td>
+ </tr>
+
+ <tr>
+ <td>800</td>
+
+ <td>.8</td>
+
+ <td>.9</td>
+ </tr>
+
+ <tr>
+ <td>1000</td>
+
+ <td>1</td>
+
+ <td>1</td>
+ </tr>
+ </table>
+
+ <p>As the table shows, the {@link android.view.animation.LinearInterpolator} changes the values
+ at the same speed, .2 for every 200ms that passes. The {@link
+ android.view.animation.AccelerateDecelerateInterpolator} changes the values faster than {@link
+ android.view.animation.LinearInterpolator} between 200ms and 600ms and slower between 600ms and
+ 1000ms.</p>
+
+ <h2 id="keyframes">Specifying Keyframes</h2>
+
+ <p>A {@link android.animation.Keyframe} object consists of a time/value pair that lets you define
+ a specific state at a specific time of an animation. Each keyframe can also have its own
+ interpolator to control the behavior of the animation in the interval between the previous
+ keyframe's time and the time of this keyframe.</p>
+
+ <p>To instantiate a {@link android.animation.Keyframe} object, you must use one of the factory
+ methods, {@link android.animation.Keyframe#ofInt ofInt()}, {@link
+ android.animation.Keyframe#ofFloat ofFloat()}, or {@link android.animation.Keyframe#ofObject
+ ofObject()} to obtain the appropriate type of {@link android.animation.Keyframe}. You then call
+ the {@link android.animation.PropertyValuesHolder#ofKeyframe ofKeyframe()} factory method to
+ obtain a {@link android.animation.PropertyValuesHolder} object. Once you have the object, you can
+ obtain an animator by passing in the {@link android.animation.PropertyValuesHolder} object and
+ the object to animate. The following code snippet demonstrates how to do this:</p>
+ <pre>
+Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
+Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
+Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
+PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
+ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
+rotationAnim.setDuration(5000ms);
+</pre>
+
+ <p>For a more complete example on how to use keyframes, see the <a href=
+ "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/MultiPropertyAnimation.html">
+ MultiPropertyAnimation</a> sample in APIDemos.</p>
+
+ <h2 id="views">Animating Views</h2>
+
+ <p>The property animation system allow streamlined animation of View objects and offerse
+ a few advantages over the view animation system. The view
+ animation system transformed View objects by changing the way that they were drawn. This was
+ handled in the container of each View, because the View itself had no properties to manipulate.
+ This resulted in the View being animated, but caused no change in the View object itself. This
+ led to behavior such as an object still existing in its original location, even though it was
+ drawn on a different location on the screen. In Android 3.0, new properties and the corresponding
+ getter and setter methods were added to eliminate this drawback.</p>
+ <p>The property animation system
+ can animate Views on the screen by changing the actual properties in the View objects. In
+ addition, Views also automatically call the {@link android.view.View#invalidate invalidate()}
+ method to refresh the screen whenever its properties are changed. The new properties in the {@link
+ android.view.View} class that facilitate property animations are:</p>
+
+ <ul>
+ <li><code>translationX</code> and <code>translationY</code>: These properties control where the
+ View is located as a delta from its left and top coordinates which are set by its layout
+ container.</li>
+
+ <li><code>rotation</code>, <code>rotationX</code>, and <code>rotationY</code>: These properties
+ control the rotation in 2D (<code>rotation</code> property) and 3D around the pivot point.</li>
+
+ <li><code>scaleX</code> and <code>scaleY</code>: These properties control the 2D scaling of a
+ View around its pivot point.</li>
+
+ <li><code>pivotX</code> and <code>pivotY</code>: These properties control the location of the
+ pivot point, around which the rotation and scaling transforms occur. By default, the pivot
+ point is located at the center of the object.</li>
+
+ <li><code>x</code> and <code>y</code>: These are simple utility properties to describe the
+ final location of the View in its container, as a sum of the left and top values and
+ translationX and translationY values.</li>
+
+ <li><code>alpha</code>: Represents the alpha transparency on the View. This value is 1 (opaque)
+ by default, with a value of 0 representing full transparency (not visible).</li>
+ </ul>
+
+ <p>To animate a property of a View object, such as its color or rotation value, all you need to
+ do is create a property animator and specify the View property that you want to
+ animate. For example:</p>
+ <pre>
+ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
+</pre>
+
+<p>For more information on creating animators, see the sections on animating with
+<a href="#value-animator">ValueAnimator</a> and <a href="#object-animator">ObjectAnimator</a>.
+</p>
+
+<h3 id="view-prop-animator">Animating with ViewPropertyAnimator</h3>
+<p>The {@link android.view.ViewPropertyAnimator} provides a simple way to animate several
+properties of a {@link android.view.View} in parallel, using a single underlying {@link
+android.animation.Animator}
+object. It behaves much like an {@link android.animation.ObjectAnimator}, because it modifies the
+actual values of the view's properties, but is more efficient when animating many properties at
+once. In addition, the code for using the {@link android.view.ViewPropertyAnimator} is much
+more concise and easier to read. The following code snippets show the differences in using multiple
+{@link android.animation.ObjectAnimator} objects, a single
+{@link android.animation.ObjectAnimator}, and the {@link android.view.ViewPropertyAnimator} when
+simultaneously animating the <code>x</code> and <code>y</code> property of a view.</p>
+
+<p><strong>Multiple ObjectAnimator objects</strong></p>
+<pre>
+ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
+ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
+AnimatorSet animSetXY = new AnimatorSet();
+animSetXY.playTogether(animX, animY);
+animSetXY.start();
+</pre>
+
+<p><strong>One ObjectAnimator</strong></p>
+<pre>
+PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
+PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
+ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
+</pre>
+
+<p><strong>ViewPropertyAnimator</strong></p>
+<pre>
+myView.animate().x(50f).y(100f);
+</pre>
+
+<p>
+For more detailed information about {@link
+android.view.ViewPropertyAnimator}, see the corresponding Android Developers
+<a href="http://android-developers.blogspot.com/2011/05/introducing-viewpropertyanimator.html">blog
+post</a>.</p>
+
+<h2 id="declaring-xml">Declaring Animations in XML</h2>
+
+ <p>The property animation system lets you declare property animations with XML instead of doing
+ it programmatically. By defining your animations in XML, you can easily reuse your animations
+in multiple activities and more easily edit the animation sequence.</p>
+
+<p>To distinguish animation files that use the new property animation APIs from those that use the
+legacy <a href="{@docRoot}guide/topics/graphics/view-animation.html">view animation</a> framework,
+starting with Android 3.1, you should save the XML files for property animations in the {@code
+res/animator/} directory (instead of {@code res/anim/}). Using the {@code animator} directory name
+is optional, but necessary if you want to use the layout editor tools in the Eclipse ADT plugin (ADT
+11.0.0+), because ADT only searches the {@code res/animator/} directory for property animation
+resources.</p>
+
+<p>The following property animation classes have XML declaration support with the
+ following XML tags:</p>
+
+ <ul>
+ <li>{@link android.animation.ValueAnimator} - <code>&lt;animator&gt;</code></li>
+
+ <li>{@link android.animation.ObjectAnimator} - <code>&lt;objectAnimator&gt;</code></li>
+
+ <li>{@link android.animation.AnimatorSet} - <code>&lt;set&gt;</code></li>
+ </ul>
+
+<p>The following example plays the two sets of object animations sequentially, with the first nested
+set playing two object animations together:</p>
+
+<pre>
+&lt;set android:ordering="sequentially"&gt;
+ &lt;set&gt;
+ &lt;objectAnimator
+ android:propertyName="x"
+ android:duration="500"
+ android:valueTo="400"
+ android:valueType="intType"/&gt;
+ &lt;objectAnimator
+ android:propertyName="y"
+ android:duration="500"
+ android:valueTo="300"
+ android:valueType="intType"/&gt;
+ &lt;/set&gt;
+ &lt;objectAnimator
+ android:propertyName="alpha"
+ android:duration="500"
+ android:valueTo="1f"/&gt;
+&lt;/set&gt;
+</pre>
+ <p>In order to run this animation, you must inflate the XML resources in your code to an {@link
+ android.animation.AnimatorSet} object, and then set the target objects for all of the animations
+ before starting the animation set. Calling {@link android.animation.AnimatorSet#setTarget
+ setTarget()} sets a single target object for all children of the {@link
+ android.animation.AnimatorSet} as a convenience. The following code shows how to do this:</p>
+
+<pre>
+AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
+ R.anim.property_animator);
+set.setTarget(myObject);
+set.start();
+</pre>
+
+<p>For information about the XML syntax for defining property animations, see <a
+href="{@docRoot}guide/topics/resources/animation-resource.html#Property">Animation Resources</a>.
+
diff --git a/docs/html/guide/topics/graphics/view-animation.jd b/docs/html/guide/topics/graphics/view-animation.jd
index eff6f70..3ccda8b 100644
--- a/docs/html/guide/topics/graphics/view-animation.jd
+++ b/docs/html/guide/topics/graphics/view-animation.jd
@@ -1,27 +1,14 @@
page.title=View Animation
-parent.title=Graphics
-parent.link=index.html
+parent.title=Animation
+parent.link=animation.html
@jd:body
- <div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#tween-animation">Tween animation</a></li>
- <li><a href="#frame-animation">Frame animation</a></li>
- </ol>
- </div>
- </div>
-
- You can use View Animation in any View object to
- perform tweened animation and frame by frame animation. Tween animation calculates the animation
- given information such as the start point, end point, size, rotation, and other common aspects of
- an animation. Frame by frame animation lets you load a series of Drawable resources one after
- another to create an animation.
-
- <h2 id="tween-animation">Tween Animation</h2>
+ <p>You can use the view animation system to perform tweened animation on Views. Tween animation
+ calculates the animation with information such as the start point, end point, size, rotation, and
+ other common aspects of an animation.
+ </p>
<p>A tween animation can perform a series of simple transformations (position, size, rotation,
and transparency) on the contents of a View object. So, if you have a {@link
@@ -126,67 +113,3 @@ spaceshipImage.startAnimation(hyperspaceJumpAnimation);
Even so, the animation will still be drawn beyond the bounds of its View and will not be clipped.
However, clipping <em>will occur</em> if the animation exceeds the bounds of the parent View.</p>
- <h2 id="frame-animation">Frame Animation</h2>
-
- <p>This is a traditional animation in the sense that it is created with a sequence of different
- images, played in order, like a roll of film. The {@link
- android.graphics.drawable.AnimationDrawable} class is the basis for frame animations.</p>
-
- <p>While you can define the frames of an animation in your code, using the {@link
- android.graphics.drawable.AnimationDrawable} class API, it's more simply accomplished with a
- single XML file that lists the frames that compose the animation. Like the tween animation above,
- the XML file for this kind of animation belongs in the <code>res/drawable/</code> directory of
- your Android project. In this case, the instructions are the order and duration for each frame of
- the animation.</p>
-
- <p>The XML file consists of an <code>&lt;animation-list&gt;</code> element as the root node and a
- series of child <code>&lt;item&gt;</code> nodes that each define a frame: a drawable resource for
- the frame and the frame duration. Here's an example XML file for a frame-by-frame animation:</p>
- <pre>
-&lt;animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="true"&gt;
- &lt;item android:drawable="@drawable/rocket_thrust1" android:duration="200" /&gt;
- &lt;item android:drawable="@drawable/rocket_thrust2" android:duration="200" /&gt;
- &lt;item android:drawable="@drawable/rocket_thrust3" android:duration="200" /&gt;
-&lt;/animation-list&gt;
-</pre>
-
- <p>This animation runs for just three frames. By setting the <code>android:oneshot</code>
- attribute of the list to <var>true</var>, it will cycle just once then stop and hold on the last
- frame. If it is set <var>false</var> then the animation will loop. With this XML saved as
- <code>rocket_thrust.xml</code> in the <code>res/drawable/</code> directory of the project, it can
- be added as the background image to a View and then called to play. Here's an example Activity,
- in which the animation is added to an {@link android.widget.ImageView} and then animated when the
- screen is touched:</p>
- <pre>
-AnimationDrawable rocketAnimation;
-
-public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
- rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
- rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
-}
-
-public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- rocketAnimation.start();
- return true;
- }
- return super.onTouchEvent(event);
-}
-</pre>
-
- <p>It's important to note that the <code>start()</code> method called on the AnimationDrawable
- cannot be called during the <code>onCreate()</code> method of your Activity, because the
- AnimationDrawable is not yet fully attached to the window. If you want to play the animation
- immediately, without requiring interaction, then you might want to call it from the <code>{@link
- android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> method in your
- Activity, which will get called when Android brings your window into focus.</p>
-
- <p>For more information on the XML syntax, available tags and attributes, see <a href=
- "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
-</body>
-</html>
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index b2a78fe..02a8a8e 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -10,8 +10,8 @@ parent.link=manifest-intro.html
android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"]
android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale",
"touchscreen", "keyboard", "keyboardHidden",
- "navigation", "orientation", "screenLayout",
- "fontScale", "uiMode"]
+ "navigation", "screenLayout", "fontScale", "uiMode",
+ "orientation", "screenSize", "smallestScreenSize"]
android:<a href="#enabled">enabled</a>=["true" | "false"]
android:<a href="#exclude">excludeFromRecents</a>=["true" | "false"]
android:<a href="#exported">exported</a>=["true" | "false"]
@@ -205,10 +205,6 @@ separated by '{@code |}' &mdash; for example, "{@code locale|navigation|orientat
<td>"{@code navigation}"</td>
<td>The navigation type (trackball/dpad) has changed. (This should never normally happen.)</td>
</tr><tr>
- <td>"{@code orientation}"</td>
- <td>The screen orientation has changed &mdash; the user has rotated
- the device.</td>
- </tr><tr>
<td>"{@code screenLayout}"</td>
<td>The screen layout has changed &mdash; this might be caused by a
different display being activated.</td>
@@ -221,7 +217,34 @@ separated by '{@code |}' &mdash; for example, "{@code locale|navigation|orientat
<td>The user interface mode has changed &mdash; this can be caused when the user places the
device into a desk/car dock or when the the night mode changes. See {@link
android.app.UiModeManager}. <em>Introduced in API Level 8</em>.</td>
- </tr>
+ </tr><tr>
+ <td>"{@code orientation}"</td>
+ <td>The screen orientation has changed &mdash; the user has rotated the device.
+ <p class="note"><strong>Note:</strong> If your application targets API level 13 or higher (as
+declared by the <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+minSdkVersion}</a> and <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+targetSdkVersion}</a> attributes), then you should also declare the {@code "screenSize"}
+configuration, because it also changes when a device switches between portrait and landscape
+orientations.</p></td>
+ </tr><tr>
+ <td>"{@code screenSize}"</td>
+ <td>The current available screen size has changed. This represents a change in the currently
+available size, relative to the current aspect ratio, so will change when the user switches between
+landscape and portrait. However, if your application targets API level 12 or lower, then your
+activity always handles this configuration change itself (this configuration change does not restart
+your activity, even when running on an Android 3.2 or higher device).
+ <p><em>Added in API level 13.</em></p></td>
+ </tr><tr>
+ <td>"{@code smallestScreenSize}"</td>
+ <td>The physical screen size has changed. This represents a change in size regardless of
+orientation, so will only change when the actual physical screen size has changed such as switching
+to an external display. A change to this configuration corresponds to a change in the <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">
+smallestWidth configuration</a>. However, if your application targets API level 12 or lower, then
+your activity always handles this configuration change itself (this configuration change does not
+restart your activity, even when running on an Android 3.2 or higher device).
+ <p><em>Added in API level 13.</em></p></td>
+ </tr>
</table>
<p>
@@ -581,8 +604,9 @@ resource usage.
</p></dd>
<dt><a name="screen"></a>{@code android:screenOrientation}</dt>
-<dd>The orientation of the activity's display on the device.
-The value can be any one of the following strings:
+<dd>The orientation of the activity's display on the device.
+
+<p>The value can be any one of the following strings:</p>
<table>
<tr>
@@ -640,7 +664,23 @@ is ignored, so the display will not rotate based on how the user moves the devic
distinction, the system chooses the orientation using the same policy as for the "{@code
unspecified}" setting.</td>
</tr>
-</table></dd>
+</table>
+
+<p class="note"><strong>Note:</strong> When you declare one of the landscape or portrait values,
+it is considered a hard requirement for the orientation in which the activity runs. As such,
+the value you declare enables filtering by services such as Android Market so your application is
+available only to devices that support the orientation required by your activities. For
+example, if you declare either {@code "landscape"}, {@code "reverseLandscape"}, or
+{@code "sensorLandscape"}, then your application will be available only to devices that support
+landscape orientation. However, you should also explicitly declare that
+your application requires either portrait or landscape orientation with the <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a>
+element. For example, <code>&lt;uses-feature
+android:name="android.hardware.screen.portrait"/></code>. This is purely a filtering behavior
+provided by Android Market (and other services that support it) and the platform itself does not
+control whether your app can be installed when a device supports only certain orientations.</p>
+
+</dd>
<dt><a name="state"></a>{@code android:stateNotNeeded}</dt>
<dd>Whether or not the activity can be killed and successfully restarted
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 49d6b62..9f80638 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -111,7 +111,7 @@ the device before installing an application. However, other services
with your application. For this reason, it's very important that you declare all of
the features (from the list below) that your application uses. </p>
-<p>For some features, there may exist a specfic attribute that allows you to define
+<p>For some features, there may exist a specific attribute that allows you to define
a version of the feature, such as the version of Open GL used (declared with
<a href="#glEsVersion"><code>glEsVersion</code></a>). Other features that either do or do not
exist for a device, such as a camera, are declared using the
@@ -277,7 +277,7 @@ application absolutely requires the feature and cannot function properly without
it (<code>"true"</code>), or whether the application prefers to use the feature
if available, but is designed to run without it (<code>"false"</code>).</p>
-<p>Android Market handles explictly declared features in this way: </p>
+<p>Android Market handles explicitly declared features in this way: </p>
<ul>
<li>If a feature is explicitly declared as being required, Android Market adds
@@ -287,7 +287,7 @@ For example:
<pre>&lt;uses-feature android:name="android.hardware.camera" android:required="true" /&gt;</pre></li>
<li>If a feature is explicitly declared as <em>not</em> being required, Android
Market <em>does not</em> add the feature to the list of required features. For
-that reason, an explicity declared non-required feature is never considered when
+that reason, an explicitly declared non-required feature is never considered when
filtering the application. Even if the device does not provide the declared
feature, Android Market will still consider the application compatible with the
device and will show it to the user, unless other filtering rules apply. For
@@ -650,6 +650,40 @@ the device.</td>
<td>The application uses the device's proximity sensor.</td>
<td></td>
</tr>
+
+<tr>
+ <td rowspan="2">Screen</td>
+ <td><code>android.hardware.screen.landscape</code></td>
+ <td>The application requires landscape orientation.</td>
+ <td rowspan="2">
+ <p>For example, if your app requires portrait orientation, you should declare
+<code>&lt;uses-feature android:name="android.hardware.screen.portrait"/></code> so that only devices
+that support portrait orientation (whether always or by user choice) can install your app. If your
+application <em>supports</em> both orientations, then you don't need to declare either.</p>
+ <p>Both orientations are assumed <em>not required</em>, by default, so your app may be installed
+on devices that support one or both orientations. However, if any of your activities request that
+they run in a specific orientation, using the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code
+android:screenOrientation}</a> attribute, then this also declares that the application requires that
+orientation. For example, if you declare <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code
+android:screenOrientation}</a> with either {@code "landscape"}, {@code "reverseLandscape"}, or
+{@code "sensorLandscape"}, then your application will be available only to devices that support
+landscape orientation. As a best practice, you should still declare your requirement for this
+orientation using a {@code &lt;uses-feature&gt;} element. If you declare an orientation for your
+activity using <a href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code
+android:screenOrientation}</a>, but don't actually <em>require</em> it, you can disable the
+requirement by declaring the orientation with a {@code &lt;uses-feature&gt;} element and include
+{@code android:required="false"}.</p>
+ <p>For backwards compatibility, any device running a platform version that supports only API
+level 12 or lower is assumed to support both landscape and portrait.</p>
+ </td>
+</tr>
+<tr>
+ <td><code>android.hardware.screen.portrait</code></td>
+ <td>The application requires portrait orientation.</td>
+</tr>
+
<tr>
<td rowspan="3">Telephony</td>
<td><code>android.hardware.telephony</code></td>
@@ -672,17 +706,18 @@ device.</td>
</tr>
<tr>
- <td rowspan="5">Touchscreen</td>
+ <td rowspan="7">Touchscreen</td>
<td><code>android.hardware.faketouch</code></td>
<td>The application uses basic touch interaction events, such as "click down", "click
up", and drag.</td>
- <td>When declared, this indicates that the application is compatible with a device that offers an
-emulated touchscreen ("fake touch" interface), or better. A device that offers a fake touch
-interface provides a user input system that emulates a subset of touchscreen capabilities. For
-example, a mouse or remote control that drives an on-screen cursor provides a fake touch interface.
-If your application requires only basic point and click interaction, you should declare this
-feature. Because this is the minimum level of touch interaction, your app will also be compatible
-with devices that offer more complex touch interfaces.
+ <td><p>When declared as required, this indicates that the application is compatible with a device
+only if it offers an emulated touchscreen ("fake touch" interface), or better. A device that offers
+a fake touch interface provides a user input system that emulates a subset of touchscreen
+capabilities. For example, a mouse or remote control that drives an on-screen cursor provides a fake
+touch interface. If your application requires basic point and click interaction (in other
+words, it won't work with <em>only</em> a d-pad controller), you should declare this feature.
+Because this is the minimum level of touch interaction, your app will also be compatible with
+devices that offer more complex touch interfaces.</p>
<p class="note"><strong>Note:</strong> Because applications require the {@code
android.hardware.touchscreen} feature by default, if you want your application to be available to
devices that provide a fake touch interface, you must also explicitly declare that a touch screen is
@@ -690,18 +725,53 @@ devices that provide a fake touch interface, you must also explicitly declare th
android:name="android.hardware.touchscreen" <strong>android:required="false"</strong>
/&gt;}</p></td>
</tr>
+
+<tr>
+ <td><code>android.hardware.faketouch.multitouch.distinct</code></td>
+ <td>The application performs distinct tracking of two or more "fingers" on a fake touch
+interface. This is a superset of the faketouch feature.</td>
+ <td><p>When declared as required, this indicates that the application is compatible with a device
+only if it supports touch emulation for events that supports distinct tracking of two or more
+fingers, or better.</p>
+ <p>Unlike the distinct multitouch defined by {@code
+android.hardware.touchscreen.multitouch.distinct}, input devices that support distinct multi-touch
+with a fake touch interface will not support all two-finger gestures, because the input is
+being transformed to cursor movement on the screen. That is, single finger gestures on such a device
+move a cursor; two-finger swipes will result in single-finger touch events; other two-finger
+gestures will result in the corresponding two-finger touch event. An example device that supports
+distinct multi-touch with a fake touch interface is one that provides a trackpad for cursor movement
+which also supports two or more fingers.</p></td>
+</tr>
+
+<tr>
+ <td><code>android.hardware.faketouch.multitouch.jazzhand</code></td>
+ <td>The application performs distinct tracking of five or more "fingers" on a fake touch
+interface. This is a superset of the faketouch feature.</td>
+ <td><p>When declared as required, this indicates that the application is compatible with a device
+only if it supports touch emulation for events that supports distinct tracking of five or more
+fingers.</p>
+ <p>Unlike the distinct multitouch defined by {@code
+android.hardware.touchscreen.multitouch.jazzhand}, input devices that support jazzhand multi-touch
+with a fake touch interface will not support all five-finger gestures, because the input is being
+transformed to cursor movement on the screen. That is, single finger gestures on such a device move
+a cursor; multi-finger gestures will result in single-finger touch events; other multi-finger
+gestures will result in the corresponding multi-finger touch event. An example device that supports
+distinct multi-touch with a fake touch interface is one that provides a trackpad for cursor movement
+which also supports five or more fingers.</p></td>
+</tr>
+
<tr>
<td><code>android.hardware.touchscreen</code></td>
<td>The application uses touchscreen capabilities for gestures that are more interactive
-than basic touch events, such as a fling. This is a superset of the faketouch features.</td>
- <td>By default, your application requires this. As such, your application is
-<em>not</em> available to devices that provide only an emulated touch interface ("fake touch"), by
-default. If you want your application available to devices that provide a fake touch interface,
-you must explicitly declare that a touch screen is not required, by
-declaring {@code android.hardware.touchscreen} with {@code android:required="false"}. You should
-do so even if your application uses&mdash;but does not <em>require</em>&mdash;a real touch screen
-interface.
-<p>If your application <em>does require</em> a basic touch interface (in order to perform touch
+than basic touch events, such as a fling. This is a superset of the basic faketouch feature.</td>
+ <td><p>By default, your application requires this. As such, your application is <em>not</em>
+available to devices that provide only an emulated touch interface ("fake touch"), by default. If
+you want your application available to devices that provide a fake touch interface (or even devices
+that provide only a d-pad controller), you must explicitly declare that a touch screen is not
+required, by declaring {@code android.hardware.touchscreen} with {@code android:required="false"}.
+You should do so even if your application uses&mdash;but does not <em>require</em>&mdash;a real
+touch screen interface.</p>
+<p>If your application <em>does require</em> a touch interface (in order to perform touch
gestures such as a fling), then you don't need to do anything, because this is required by default.
However, it's best if you explicitly declare all features used by your application, so you should
still declare this if your app uses it.</p>
@@ -712,7 +782,7 @@ should declare the advanced touch screen features below.</p></td>
<td><code>android.hardware.touchscreen.multitouch</code></td>
<td>The application uses basic two-point multitouch capabilities on the device
screen, such as for pinch gestures, but does not need to track touches independently. This
-is a superset of touchscreen features.</td>
+is a superset of touchscreen feature.</td>
<td>This implicitly declares the <code>android.hardware.touchscreen</code> parent feature, unless
declared with <code>android:required="false"</code>. </td>
</tr>
@@ -720,7 +790,7 @@ declared with <code>android:required="false"</code>. </td>
<td><code>android.hardware.touchscreen.multitouch.distinct</code></td>
<td>Subfeature. The application uses advanced multipoint multitouch
capabilities on the device screen, such as for tracking two or more points fully
-independently. This is a superset of multitouch features.</td>
+independently. This is a superset of multitouch feature.</td>
<td rowspan="2">This implicitly declares the <code>android.hardware.touchscreen.multitouch</code>
parent feature, unless declared with <code>android:required="false"</code>. </td>
</tr>
@@ -728,7 +798,7 @@ parent feature, unless declared with <code>android:required="false"</code>. </td
<td><code>android.hardware.touchscreen.multitouch.jazzhand</code></td>
<td>The application uses advanced multipoint multitouch
capabilities on the device screen, for tracking up to five points fully
-independently. This is a superset of distinct multitouch features.</td>
+independently. This is a superset of distinct multitouch feature.</td>
</tr>
<tr>
diff --git a/docs/html/guide/topics/media/audio-capture.jd b/docs/html/guide/topics/media/audio-capture.jd
new file mode 100644
index 0000000..75d294b
--- /dev/null
+++ b/docs/html/guide/topics/media/audio-capture.jd
@@ -0,0 +1,253 @@
+page.title=Audio Capture
+parent.title=Multimedia and Camera
+parent.link=index.html
+@jd:body
+
+ <div id="qv-wrapper">
+ <div id="qv">
+
+<h2>In this document</h2>
+<ol>
+<li><a href="#audiocapture">Performing Audio Capture</a>
+ <ol>
+ <li><a href='#example'>Code Example</a></li>
+ </ol>
+</li>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.media.MediaRecorder}</li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+ <li><a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li>
+ <li><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a>
+</ol>
+
+</div>
+</div>
+
+<p>The Android multimedia framework includes support for capturing and encoding a variety of common
+audio formats, so that you can easily integrate audio into your applications. You can record audio
+using the {@link android.media.MediaRecorder} APIs if supported by the device hardware.</p>
+
+<p>This document shows you how to write an application that captures audio from a device
+microphone, save the audio and play it back.</p>
+
+<p class="note"><strong>Note:</strong> The Android Emulator does not have the ability to capture
+audio, but actual devices are likely to provide these capabilities.</p>
+
+<h2 id="audiocapture">Performing Audio Capture</h2>
+
+<p>Audio capture from the device is a bit more complicated than audio and video playback, but still
+fairly simple:</p>
+<ol>
+ <li>Create a new instance of {@link android.media.MediaRecorder android.media.MediaRecorder}.</li>
+ <li>Set the audio source using
+ {@link android.media.MediaRecorder#setAudioSource MediaRecorder.setAudioSource()}. You will
+probably want to use
+ <code>MediaRecorder.AudioSource.MIC</code>.</li>
+ <li>Set output file format using
+ {@link android.media.MediaRecorder#setOutputFormat MediaRecorder.setOutputFormat()}.
+ </li>
+ <li>Set output file name using
+ {@link android.media.MediaRecorder#setOutputFile MediaRecorder.setOutputFile()}.
+ </li>
+ <li>Set the audio encoder using
+ {@link android.media.MediaRecorder#setAudioEncoder MediaRecorder.setAudioEncoder()}.
+ </li>
+ <li>Call {@link android.media.MediaRecorder#prepare MediaRecorder.prepare()}
+ on the MediaRecorder instance.</li>
+ <li>To start audio capture, call
+ {@link android.media.MediaRecorder#start MediaRecorder.start()}. </li>
+ <li>To stop audio capture, call {@link android.media.MediaRecorder#stop MediaRecorder.stop()}.
+ <li>When you are done with the MediaRecorder instance, call
+{@link android.media.MediaRecorder#release MediaRecorder.release()} on it. Calling
+{@link android.media.MediaRecorder#release MediaRecorder.release()} is always recommended to
+free the resource immediately.</li>
+</ol>
+
+<h3 id="example">Example: Record audio and play the recorded audio</h3>
+<p>The example class below illustrates how to set up, start and stop audio capture, and to play the
+recorded audio file.</p>
+<pre>
+/*
+ * The application needs to have the permission to write to external storage
+ * if the output file is written to the external storage, and also the
+ * permission to record audio. These permissions must be set in the
+ * application's AndroidManifest.xml file, with something like:
+ *
+ * &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
+ * &lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&gt;
+ *
+ */
+package com.android.audiorecordtest;
+
+import android.app.Activity;
+import android.widget.LinearLayout;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.content.Context;
+import android.util.Log;
+import android.media.MediaRecorder;
+import android.media.MediaPlayer;
+
+import java.io.IOException;
+
+
+public class AudioRecordTest extends Activity
+{
+ private static final String LOG_TAG = "AudioRecordTest";
+ private static String mFileName = null;
+
+ private RecordButton mRecordButton = null;
+ private MediaRecorder mRecorder = null;
+
+ private PlayButton mPlayButton = null;
+ private MediaPlayer mPlayer = null;
+
+ private void onRecord(boolean start) {
+ if (start) {
+ startRecording();
+ } else {
+ stopRecording();
+ }
+ }
+
+ private void onPlay(boolean start) {
+ if (start) {
+ startPlaying();
+ } else {
+ stopPlaying();
+ }
+ }
+
+ private void startPlaying() {
+ mPlayer = new MediaPlayer();
+ try {
+ mPlayer.setDataSource(mFileName);
+ mPlayer.prepare();
+ mPlayer.start();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "prepare() failed");
+ }
+ }
+
+ private void stopPlaying() {
+ mPlayer.release();
+ mPlayer = null;
+ }
+
+ private void startRecording() {
+ mRecorder = new MediaRecorder();
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mRecorder.setOutputFile(mFileName);
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+
+ try {
+ mRecorder.prepare();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "prepare() failed");
+ }
+
+ mRecorder.start();
+ }
+
+ private void stopRecording() {
+ mRecorder.stop();
+ mRecorder.release();
+ mRecorder = null;
+ }
+
+ class RecordButton extends Button {
+ boolean mStartRecording = true;
+
+ OnClickListener clicker = new OnClickListener() {
+ public void onClick(View v) {
+ onRecord(mStartRecording);
+ if (mStartRecording) {
+ setText("Stop recording");
+ } else {
+ setText("Start recording");
+ }
+ mStartRecording = !mStartRecording;
+ }
+ };
+
+ public RecordButton(Context ctx) {
+ super(ctx);
+ setText("Start recording");
+ setOnClickListener(clicker);
+ }
+ }
+
+ class PlayButton extends Button {
+ boolean mStartPlaying = true;
+
+ OnClickListener clicker = new OnClickListener() {
+ public void onClick(View v) {
+ onPlay(mStartPlaying);
+ if (mStartPlaying) {
+ setText("Stop playing");
+ } else {
+ setText("Start playing");
+ }
+ mStartPlaying = !mStartPlaying;
+ }
+ };
+
+ public PlayButton(Context ctx) {
+ super(ctx);
+ setText("Start playing");
+ setOnClickListener(clicker);
+ }
+ }
+
+ public AudioRecordTest() {
+ mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
+ mFileName += "/audiorecordtest.3gp";
+ }
+
+ &#64;Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ LinearLayout ll = new LinearLayout(this);
+ mRecordButton = new RecordButton(this);
+ ll.addView(mRecordButton,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0));
+ mPlayButton = new PlayButton(this);
+ ll.addView(mPlayButton,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0));
+ setContentView(ll);
+ }
+
+ &#64;Override
+ public void onPause() {
+ super.onPause();
+ if (mRecorder != null) {
+ mRecorder.release();
+ mRecorder = null;
+ }
+
+ if (mPlayer != null) {
+ mPlayer.release();
+ mPlayer = null;
+ }
+ }
+}
+</pre> \ No newline at end of file
diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd
new file mode 100644
index 0000000..877bded
--- /dev/null
+++ b/docs/html/guide/topics/media/camera.jd
@@ -0,0 +1,1055 @@
+page.title=Camera
+parent.title=Multimedia and Camera
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#considerations">Considerations</a></li>
+ <li><a href="#basics">The Basics</a>
+ <li><a href="#manifest">Manifest Declarations</a></li>
+ <li><a href="#intents">Using Existing Camera Apps</a>
+ <ol>
+ <li><a href="#intent-image">Image capture intent</a></li>
+ <li><a href="#intent-video">Video capture intent</a></li>
+ <li><a href="#intent-receive">Receiving camera intent result</a></li>
+ </ol>
+ <li><a href="#custom-camera">Building a Camera App</a>
+ <ol>
+ <li><a href="#detect-camera">Detecting camera hardware</a></li>
+ <li><a href="#access-camera">Accessing cameras</a></li>
+ <li><a href="#check-camera-features">Checking camera features</a></li>
+ <li><a href="#camera-preview">Creating a preview class</a></li>
+ <li><a href="#preview-layout">Placing preview in a layout</a></li>
+ <li><a href="#capture-picture">Capturing pictures</a></li>
+ <li><a href="#capture-video">Capturing videos</a></li>
+ <li><a href="#release-camera">Releasing the camera</a></li>
+ </ol>
+ </li>
+ <li><a href="#saving-media">Saving Media Files</a></li>
+ </ol>
+ <h2>Key Classes</h2>
+ <ol>
+ <li>{@link android.hardware.Camera}</li>
+ <li>{@link android.view.SurfaceView}</li>
+ <li>{@link android.media.MediaRecorder}</li>
+ <li>{@link android.content.Intent}</li>
+ </ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}reference/android/hardware/Camera.html">Camera</a></li>
+ <li><a href="{@docRoot}reference/android/media/MediaRecorder.html">MediaRecorder</a></li>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li>
+ </ol>
+ </div>
+</div>
+
+
+<p>The Android framework includes support for various cameras and camera features available on
+devices, allowing you to capture pictures and videos in your applications. This document discusses a
+quick, simple approach to image and video capture and outlines an advanced approach for creating
+custom camera experiences for your users.</p>
+
+<h2 id="considerations">Considerations</h2>
+<p>Before enabling your application to use cameras on Android devices, you should consider a few
+questions about how your app intends to use this hardware feature.</p>
+
+<ul>
+ <li><strong>Camera Requirement</strong> - Is the use of a camera so important to your
+application that you do not want your application installed on a device that does not have a
+camera? If so, you should declare the <a href="#manifest">camera requirement in your
+manifest</a>.</li>
+
+ <li><strong>Quick Picture or Customized Camera</strong> - How will your application use the
+camera? Are you just interested in snapping a quick picture or video clip, or will your application
+provide a new way to use cameras? For a getting a quick snap or clip, consider
+<a href="#intents">Using Existing Camera Apps</a>. For developing a customized camera feature, check
+out the <a href="#custom-camera">Building a Camera App</a> section.</li>
+
+ <li><strong>Storage</strong> - Are the images or videos your application generates intended to be
+only visible to your application or shared so that other applications such as Gallery or other
+media and social apps can use them? Do you want the pictures and videos to be available even if your
+application is uninstalled? Check out the <a href="#saving-media">Saving Media Files</a> section to
+see how to implement these options.</li>
+</ul>
+
+
+
+<h2 id="basics">The Basics</h2>
+<p>The Android framework supports capturing images and video through the
+{@link android.hardware.Camera} API or camera {@link android.content.Intent}. Here are the relevant
+classes:</p>
+
+<dl>
+ <dt>{@link android.hardware.Camera}</dt>
+ <dd>This class is the primary API for controlling device cameras. This class is used to take
+pictures or videos when you are building a camera application.</a>.</dd>
+
+ <dt>{@link android.view.SurfaceView}</dt>
+ <dd>This class is used to present a live camera preview to the user.</dd>
+
+ <dt>{@link android.media.MediaRecorder}</dt>
+ <dd>This class is used to record video from the camera.</dd>
+
+ <dt>{@link android.content.Intent}</dt>
+ <dd>An intent action type of {@link android.provider.MediaStore#ACTION_IMAGE_CAPTURE
+MediaStore.ACTION_IMAGE_CAPTURE} or {@link android.provider.MediaStore#ACTION_VIDEO_CAPTURE
+MediaStore.ACTION_VIDEO_CAPTURE} can be used to capture images or videos without directly
+using the {@link android.hardware.Camera} object.</dd>
+</dl>
+
+
+<h2 id="manifest">Manifest Declarations</h2>
+<p>Before starting development on your application with the Camera API, you should make sure
+your manifest has the appropriate declarations to allow use of camera hardware and other
+related features.</p>
+
+<ul>
+ <li><strong>Camera Permission</strong> - Your application must request permission to use a device
+camera.
+<pre>
+&lt;uses-permission android:name=&quot;android.permission.CAMERA&quot; /&gt;
+</pre>
+ <p class="note"><strong>Note:</strong> If you are using the camera <a href="#intents">via an
+intent</a>, your application does not need to request this permission.</p>
+ </li>
+ <li><strong>Camera Features</strong> - Your application must also declare use of camera features,
+for example:
+<pre>
+&lt;uses-feature android:name=&quot;android.hardware.camera&quot; /&gt;
+</pre>
+ <p>For a list of camera features, see the manifest <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html#features-reference">Features
+Reference</a>.</p>
+ <p>Adding camera features to your manifest causes Android Market to prevent your application from
+being installed to devices that do not include a camera or do not support the camera features you
+specify. For more information about using feature-based filtering with Android Market, see <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html#market-feature-filtering">Android
+Market and Feature-Based Filtering</a>.</p>
+ <p>If your application <em>can use</em> a camera or camera feature for proper operation, but does
+not <em>require</em> it, you should specify this in the manifest by including the {@code
+android:required} attribute, and setting it to {@code false}:</p>
+<pre>
+&lt;uses-feature android:name="android.hardware.camera" android:required="false" /&gt;
+</pre>
+
+ </li>
+ <li><strong>Storage Permission</strong> - If your application saves images or videos to the
+device's external storage (SD Card), you must also specify this in the manifest.
+<pre>
+&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot; /&gt;
+</pre>
+ </li>
+ <li><strong>Audio Recording Permission</strong> - For recording audio with video capture, your
+application must request the audio capture permission.
+<pre>
+&lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&gt;
+</pre>
+ </li>
+</ul>
+
+
+<h2 id="intents">Using Existing Camera Apps</h2>
+<p>A quick way to enable taking pictures or videos in your application without a lot of extra code
+is to use an {@link android.content.Intent} to invoke an existing Android camera application. A
+camera intent makes a request to capture a picture or video clip through an existing camera app and
+then returns control back to your application. This section shows you how to capture an image or
+video using this technique.</p>
+
+<p>The procedure for invoking a camera intent follows these general steps:</p>
+
+<ol>
+ <li><strong>Compose a Camera Intent</strong> - Create an {@link android.content.Intent} that
+requests an image or video, using one of these intent types:
+ <ul>
+ <li>{@link android.provider.MediaStore#ACTION_IMAGE_CAPTURE MediaStore.ACTION_IMAGE_CAPTURE} -
+Intent action type for requesting an image from an existing camera application.</li>
+ <li>{@link android.provider.MediaStore#ACTION_VIDEO_CAPTURE MediaStore.ACTION_VIDEO_CAPTURE} -
+Intent action type for requesting a video from an existing camera application. </li>
+ </ul>
+ </li>
+ <li><strong>Start the Camera Intent</strong> - Use the {@link
+android.app.Activity#startActivityForResult(android.content.Intent, int) startActivityForResult()}
+method to execute the camera intent. After you start the intent, the Camera application user
+interface appears on the device screen and the user can take a picture or video.</li>
+ <li><strong>Receive the Intent Result</strong> - Set up an {@link
+android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()} method
+in your application to receive the callback and data from the camera intent. When the user
+finishes taking a picture or video (or cancels the operation), the system calls this method.</li>
+</ol>
+
+
+<h3 id="intent-image">Image capture intent</h3>
+<p>Capturing images using a camera intent is quick way to enable your application to take pictures
+with minimal coding. An image capture intent can include the following extra information:</p>
+
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_OUTPUT MediaStore.EXTRA_OUTPUT} - This setting
+requires a {@link android.net.Uri} object specifying a path and file name where you'd like to
+save the picture. This setting is optional but strongly recommended. If you do not specify this
+value, the camera application saves the requested picture in the default location with a default
+name, specified in the returned intent's {@link android.content.Intent#getData() Intent.getData()}
+field.</li>
+</ul>
+
+<p>The following example demonstrates how to construct a image capture intent and execute it.
+The {@code getOutputMediaFileUri()} method in this example refers to the sample code shown in <a
+href= "#saving-media">Saving Media Files</a>.</p>
+
+<pre>
+private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
+private Uri fileUri;
+
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // create Intent to take a picture and return control to the calling application
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+
+ fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
+
+ // start the image capture Intent
+ startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
+}
+</pre>
+
+<p>When the {@link android.app.Activity#startActivityForResult(android.content.Intent, int)
+startActivityForResult()} method is executed, users see a camera application interface.
+After the user finishes taking a picture (or cancels the operation), the user interface returns to
+your application, and you must intercept the {@link
+android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()}
+method to receive the result of the intent and continue your application execution. For information
+on how to receive the completed intent, see <a href="#intent-receive">Receiving Camera Intent
+Result</a>.</p>
+
+
+<h3 id="intent-video">Video capture intent</h3>
+<p>Capturing video using a camera intent is a quick way to enable your application to take videos
+with minimal coding. A video capture intent can include the following extra information:</p>
+
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_OUTPUT MediaStore.EXTRA_OUTPUT} - This setting
+requires a {@link android.net.Uri} specifying a path and file name where you'd like to save the
+video. This setting is optional but strongly recommended. If you do not specify this value, the
+Camera application saves the requested video in the default location with a default name, specified
+in the returned intent's {@link android.content.Intent#getData() Intent.getData()} field.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_VIDEO_QUALITY MediaStore.EXTRA_VIDEO_QUALITY} -
+This value can be 0 for lowest quality and smallest file size or 1 for highest quality and
+larger file size.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_DURATION_LIMIT MediaStore.EXTRA_DURATION_LIMIT} -
+Set this value to limit the length, in seconds, of the video being captured.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_SIZE_LIMIT MediaStore.EXTRA_SIZE_LIMIT} -
+Set this value to limit the file size, in bytes, of the video being captured.
+</li>
+</ul>
+
+<p>The following example demonstrates how to construct a video capture intent and execute it.
+The {@code getOutputMediaFileUri()} method in this example refers to the sample code shown in <a
+href= "#saving-media">Saving Media Files</a>.</p>
+
+<pre>
+private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
+private Uri fileUri;
+
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ //create new Intent
+ Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+
+ fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO); // create a file to save the video
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
+
+ intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high
+
+ // start the Video Capture Intent
+ startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
+}
+</pre>
+
+<p>When the {@link
+android.app.Activity#startActivityForResult(android.content.Intent, int)
+startActivityForResult()} method is executed, users see a modified camera application interface.
+After the user finishes taking a video (or cancels the operation), the user interface
+returns to your application, and you must intercept the {@link
+android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()}
+method to receive the result of the intent and continue your application execution. For information
+on how to receive the completed intent, see the next section.</p>
+
+<h3 id="intent-receive">Receiving camera intent result</h3>
+<p>Once you have constructed and executed an image or video camera intent, your application must be
+configured to receive the result of the intent. This section shows you how to intercept the callback
+from a camera intent so your application can do further processing of the captured image or
+video.</p>
+
+<p>In order to receive the result of an intent, you must override the {@link
+android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()} in the
+activity that started the intent. The following example demonstrates how to override {@link
+android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()} to
+capture the result of the <a href="#intent-image">image camera intent</a> or <a
+href="#intent-video">video camera intent</a> examples shown in the previous sections.</p>
+
+<pre>
+private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
+private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
+
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
+ if (resultCode == RESULT_OK) {
+ // Image captured and saved to fileUri specified in the Intent
+ Toast.makeText(this, "Image saved to:\n" +
+ data.getData(), Toast.LENGTH_LONG).show();
+ } else if (resultCode == RESULT_CANCELED) {
+ // User cancelled the image capture
+ } else {
+ // Image capture failed, advise user
+ }
+ }
+
+ if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
+ if (resultCode == RESULT_OK) {
+ // Video captured and saved to fileUri specified in the Intent
+ Toast.makeText(this, "Video saved to:\n" +
+ data.getData(), Toast.LENGTH_LONG).show();
+ } else if (resultCode == RESULT_CANCELED) {
+ // User cancelled the video capture
+ } else {
+ // Video capture failed, advise user
+ }
+ }
+}
+</pre>
+
+<p>Once your activity receives a successful result, the captured image or video is available in the
+specified location for your application to access.</p>
+
+
+
+<h2 id="custom-camera">Building a Camera App</h2>
+<p>Some developers may require a camera user interface that is customized to the look of their
+application or provides special features. Creating a customized camera activity requires more
+code than <a href="#intents">using an intent</a>, but it can provide a more compelling experience
+for your users.</p>
+
+<p>The general steps for creating a custom camera interface for your application are as follows:</p>
+
+<ul>
+ <li><strong>Detect and Access Camera</strong> - Create code to check for the existence of
+cameras and request access.</li>
+ <li><strong>Create a Preview Class</strong> - Create a camera preview class that extends {@link
+android.view.SurfaceView} and implements the {@link android.view.SurfaceHolder} interface. This
+class previews the live images from the camera.</li>
+ <li><strong>Build a Preview Layout</strong> - Once you have the camera preview class, create a
+view layout that incorporates the preview and the user interface controls you want.</li>
+ <li><strong>Setup Listeners for Capture</strong> - Connect listeners for your interface
+controls to start image or video capture in response to user actions, such as pressing a
+button.</li>
+ <li><strong>Capture and Save Files</strong> - Setup the code for capturing pictures or
+videos and saving the output.</li>
+ <li><strong>Release the Camera</strong> - After using the camera, your application must
+properly release it for use by other applications.</li>
+</ul>
+
+<p>Camera hardware is a shared resource that must be carefully managed so your application does
+not collide with other applications that may also want to use it. The following sections discusses
+how to detect camera hardware, how to request access to a camera and how to release it when your
+application is done using it.</p>
+
+<p class="caution"><strong>Caution:</strong> Remember to release the {@link android.hardware.Camera}
+object by calling the {@link android.hardware.Camera#release() Camera.release()} when your
+application is done using it! If your application does not properly release the camera, all
+subsequent attempts to access the camera, including those by your own application, will fail and may
+cause your or other applications to be shut down.</p>
+
+
+<h3 id="detect-camera">Detecting camera hardware</h3>
+<p>If your application does not specifically require a camera using a manifest declaration, you
+should check to see if a camera is available at runtime. To perform this check, use the {@link
+android.content.pm.PackageManager#hasSystemFeature(java.lang.String)
+PackageManager.hasSystemFeature()} method, as shown in the example code below:</p>
+
+<pre>
+/** Check if this device has a camera */
+private boolean checkCameraHardware(Context context) {
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
+ // this device has a camera
+ return true;
+ } else {
+ // no camera on this device
+ return false;
+ }
+}
+</pre>
+
+<p>Android devices can have multiple cameras, for example a back-facing camera for photography and a
+front-facing camera for video calls. Android 2.3 (API Level 9) and later allows you to check the
+number of cameras available on a device using the {@link
+android.hardware.Camera#getNumberOfCameras() Camera.getNumberOfCameras()} method.</p>
+
+<h3 id="access-camera">Accessing cameras</h3>
+<p>If you have determined that the device on which your application is running has a camera, you
+must request to access it by getting an instance of {@link android.hardware.Camera} (unless you
+are using an <a href="#intents">intent to access the camera</a>). </p>
+
+<p>To access the primary camera, use the {@link android.hardware.Camera#open() Camera.open()} method
+and be sure to catch any exceptions, as shown in the code below:</p>
+
+<pre>
+/** A safe way to get an instance of the Camera object. */
+public static Camera getCameraInstance(){
+ Camera c = null;
+ try {
+ c = Camera.open(); // attempt to get a Camera instance
+ }
+ catch (Exception e){
+ // Camera is not available (in use or does not exist)
+ }
+ return c; // returns null if camera is unavailable
+}
+</pre>
+
+<p class="caution"><strong>Caution:</strong> Always check for exceptions when using {@link
+android.hardware.Camera#open() Camera.open()}. Failing to check for exceptions if the camera is in
+use or does not exist will cause your application to be shut down by the system.</p>
+
+<p>On devices running Android 2.3 (API Level 9) or higher, you can access specific cameras using
+{@link android.hardware.Camera#open(int) Camera.open(int)}. The example code above will access
+the first, back-facing camera on a device with more than one camera.</p>
+
+<h3 id="check-camera-features">Checking camera features</h3>
+<p>Once you obtain access to a camera, you can get further information about its capabilties using
+the {@link android.hardware.Camera#getParameters() Camera.getParameters()} method and checking the
+returned {@link android.hardware.Camera.Parameters} object for supported capabilities. When using
+API Level 9 or higher, use the {@link android.hardware.Camera#getCameraInfo(int,
+android.hardware.Camera.CameraInfo) Camera.getCameraInfo()} to determine if a camera is on the front
+or back of the device, and the orientation of the image.</p>
+
+
+
+<h3 id="camera-preview">Creating a preview class</h3>
+<p>For users to effectively take pictures or video, they must be able to see what the device camera
+sees. A camera preview class is a {@link android.view.SurfaceView} that can display the live image
+data coming from a camera, so users can frame and capture a picture or video.</p>
+
+<p>The following example code demonstrates how to create a basic camera preview class that can be
+included in a {@link android.view.View} layout. This class implements {@link
+android.view.SurfaceHolder.Callback SurfaceHolder.Callback} in order to capture the callback events
+for creating and destroying the view, which are needed for assigning the camera preview input.</p>
+
+<pre>
+/** A basic Camera preview class */
+public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
+ private SurfaceHolder mHolder;
+ private Camera mCamera;
+
+ public CameraPreview(Context context, Camera camera) {
+ super(context);
+ mCamera = camera;
+
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed.
+ mHolder = getHolder();
+ mHolder.addCallback(this);
+ // deprecated setting, but required on Android versions prior to 3.0
+ mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ // The Surface has been created, now tell the camera where to draw the preview.
+ try {
+ mCamera.setPreviewDisplay(holder);
+ mCamera.startPreview();
+ } catch (IOException e) {
+ Log.d(TAG, "Error setting camera preview: " + e.getMessage());
+ }
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // empty. Take care of releasing the Camera preview in your activity.
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ // If your preview can change or rotate, take care of those events here.
+ // Make sure to stop the preview before resizing or reformatting it.
+
+ if (mHolder.getSurface() == null){
+ // preview surface does not exist
+ return;
+ }
+
+ // stop preview before making changes
+ try {
+ mCamera.stopPreview();
+ } catch (Exception e){
+ // ignore: tried to stop a non-existent preview
+ }
+
+ // make any resize, rotate or reformatting changes here
+
+ // start preview with new settings
+ try {
+ mCamera.setPreviewDisplay(mHolder);
+ mCamera.startPreview();
+
+ } catch (Exception e){
+ Log.d(TAG, "Error starting camera preview: " + e.getMessage());
+ }
+ }
+}
+</pre>
+
+
+<h3 id="preview-layout">Placing preview in a layout</h3>
+<p>A camera preview class, such as the example shown in the previous section, must be placed in the
+layout of an activity along with other user interface controls for taking a picture or video. This
+section shows you how to build a basic layout and activity for the preview.</p>
+
+<p>The following layout code provides a very basic view that can be used to display a camera
+preview. In this example, the {@link android.widget.FrameLayout} element is meant to be the
+container for the camera preview class. This layout type is used so that additional picture
+information or controls can be overlayed on the live camera preview images.</p>
+
+<pre>
+&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
+ android:orientation=&quot;horizontal&quot;
+ android:layout_width=&quot;fill_parent&quot;
+ android:layout_height=&quot;fill_parent&quot;
+ &gt;
+ &lt;FrameLayout
+ android:id=&quot;@+id/camera_preview&quot;
+ android:layout_width=&quot;fill_parent&quot;
+ android:layout_height=&quot;fill_parent&quot;
+ android:layout_weight=&quot;1&quot;
+ /&gt;
+
+ &lt;Button
+ android:id=&quot;@+id/button_capture&quot;
+ android:text=&quot;Capture&quot;
+ android:layout_width=&quot;wrap_content&quot;
+ android:layout_height=&quot;wrap_content&quot;
+ android:layout_gravity=&quot;center&quot;
+ /&gt;
+&lt;/LinearLayout&gt;
+</pre>
+
+<p>On most devices, the default orientation of the camera preview is landscape. This example layout
+specifies a horizontal (landscape) layout and the code below fixes the orientation of the
+application to landscape. For simplicity in rendering a camera preview, you should change your
+application's preview activity orientation to landscape by adding the following to your
+manifest.</p>
+
+<pre>
+&lt;activity android:name=&quot;.CameraActivity&quot;
+ android:label=&quot;@string/app_name&quot;
+
+ android:screenOrientation=&quot;landscape&quot;&gt;
+ &lt;!-- configure this activity to use landscape orientation --&gt;
+
+ &lt;intent-filter&gt;
+ &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+ &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
+ &lt;/intent-filter&gt;
+&lt;/activity&gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> A camera preview does not have to be in landscape mode.
+Starting in Android 2.2 (API Level 8), you can use the {@link
+android.hardware.Camera#setDisplayOrientation(int) setDisplayOrientation()} method to set the
+rotation of the preview image. In order to change preview orientation as the user re-orients the
+phone, within the {@link
+android.view.SurfaceHolder.Callback#surfaceChanged(android.view.SurfaceHolder, int, int, int)
+surfaceChanged()} method of your preview class, first stop the preview with {@link
+android.hardware.Camera#stopPreview() Camera.stopPreview()} change the orientation and then
+start the preview again with {@link android.hardware.Camera#startPreview()
+Camera.startPreview()}.</p>
+
+<p>In the activity for your camera view, add your preview class to the {@link
+android.widget.FrameLayout} element shown in the example above. Your camera activity must also
+ensure that it releases the camera when it is paused or shut down. The following example shows how
+to modify a camera activity to attach the preview class shown in <a href="#camera-preview">Creating
+a preview class</a>.</p>
+
+<pre>
+public class CameraActivity extends Activity {
+
+ private Camera mCamera;
+ private CameraPreview mPreview;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // Create an instance of Camera
+ mCamera = getCameraInstance();
+
+ // Create our Preview view and set it as the content of our activity.
+ mPreview = new CameraPreview(this, mCamera);
+ FrameLayout preview = (FrameLayout) findViewById(id.camera_preview);
+ preview.addView(mPreview);
+ }
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> The {@code getCameraInstance()} method in the example above
+refers to the example method shown in <a href="#access-camera">Accessing cameras</a>.</p>
+
+
+<h3 id="capture-picture">Capturing pictures</h3>
+<p>Once you have built a preview class and a view layout in which to display it, you are ready to
+start capturing images with your application. In your application code, you must set up listeners
+for your user interface controls to respond to a user action by taking a picture.</p>
+
+<p>In order to retrieve a picture, use the {@link
+android.hardware.Camera#takePicture(android.hardware.Camera.ShutterCallback,
+android.hardware.Camera.PictureCallback, android.hardware.Camera.PictureCallback)
+Camera.takePicture()} method. This method takes three parameters which receive data from the camera.
+In order to receive data in a JPEG format, you must implement an {@link
+android.hardware.Camera.PictureCallback} interface to receive the image data and
+write it to a file. The following code shows a basic implementation of the {@link
+android.hardware.Camera.PictureCallback} interface to save an image received from the camera.</p>
+
+<pre>
+private PictureCallback mPicture = new PictureCallback() {
+
+ &#64;Override
+ public void onPictureTaken(byte[] data, Camera camera) {
+
+ File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
+ if (pictureFile == null){
+ Log.d(TAG, "Error creating media file, check storage permissions: " +
+ e.getMessage());
+ return;
+ }
+
+ try {
+ FileOutputStream fos = new FileOutputStream(pictureFile);
+ fos.write(data);
+ fos.close();
+ } catch (FileNotFoundException e) {
+ Log.d(TAG, "File not found: " + e.getMessage());
+ } catch (IOException e) {
+ Log.d(TAG, "Error accessing file: " + e.getMessage());
+ }
+ }
+};
+</pre>
+
+<p>Trigger capturing an image by calling the {@link
+android.hardware.Camera#takePicture(android.hardware.Camera.ShutterCallback,
+android.hardware.Camera.PictureCallback, android.hardware.Camera.PictureCallback)
+Camera.takePicture()} method. The following example code shows how to call this method from a
+button {@link android.view.View.OnClickListener}.</p>
+
+<pre>
+// Add a listener to the Capture button
+Button captureButton = (Button) findViewById(id.button_capture);
+ captureButton.setOnClickListener(
+ new View.OnClickListener() {
+ &#64;Override
+ public void onClick(View v) {
+ // get an image from the camera
+ mCamera.takePicture(null, null, mPicture);
+ }
+ }
+);
+</pre>
+
+<p class="note"><strong>Note:</strong> The {@code mPicture} member in the following example refers
+to the example code above.</p>
+
+<p class="caution"><strong>Caution:</strong> Remember to release the {@link android.hardware.Camera}
+object by calling the {@link android.hardware.Camera#release() Camera.release()} when your
+application is done using it! For information about how to release the camera, see <a
+href="#release-camera">Releasing the camera</a>.</p>
+
+
+<h3 id="capture-video">Capturing videos</h3>
+
+<p>Video capture using the Android framework requires careful management of the {@link
+android.hardware.Camera} object and coordination with the {@link android.media.MediaRecorder}
+class. When recording video with {@link android.hardware.Camera}, you must manage the {@link
+android.hardware.Camera#lock() Camera.lock()} and {@link android.hardware.Camera#unlock()
+Camera.unlock()} calls to allow {@link android.media.MediaRecorder} access to the camera hardware,
+in addition to the {@link android.hardware.Camera#open() Camera.open()} and {@link
+android.hardware.Camera#release() Camera.release()} calls.</p>
+
+<p class="note"><strong>Note:</strong> Starting with Android 4.0 (API level 14), the {@link
+android.hardware.Camera#lock() Camera.lock()} and {@link android.hardware.Camera#unlock()
+Camera.unlock()} calls are managed for you automatically.</p>
+
+<p>Unlike taking pictures with a device camera, capturing video requires a very particular call
+order. You must follow a specific order of execution to successfully prepare for and capture video
+with your application, as detailed below.</p>
+
+<ol>
+ <li><strong>Open Camera</strong> - Use the {@link android.hardware.Camera#open() Camera.open()}
+to get an instance of the camera object.</li>
+ <li><strong>Connect Preview</strong> - Prepare a live camera image preview by connecting a {@link
+android.view.SurfaceView} to the camera using {@link
+android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder) Camera.setPreviewDisplay()}.
+ </li>
+ <li><strong>Start Preview</strong> - Call {@link android.hardware.Camera#startPreview()
+Camera.startPreview()} to begin displaying the live camera images.</li>
+ <li><strong>Start Recording Video</strong> - The following steps must be completed <em>in
+order</em> to successfully record video:
+ <ol style="list-style-type: lower-alpha;">
+ <li><strong>Unlock the Camera</strong> - Unlock the camera for use by {@link
+android.media.MediaRecorder} by calling {@link android.hardware.Camera#unlock()
+Camera.unlock()}.</li>
+ <li><strong>Configure MediaRecorder</strong> - Call in the following {@link
+android.media.MediaRecorder} methods <em>in this order</em>. For more information, see the {@link
+android.media.MediaRecorder} reference documentation.
+ <ol>
+ <li>{@link android.media.MediaRecorder#setCamera(android.hardware.Camera)
+setCamera()} - Set the camera to be used for video capture, use your application's current instance
+of {@link android.hardware.Camera}.</li>
+ <li>{@link android.media.MediaRecorder#setAudioSource(int) setAudioSource()} - Set the
+audio source, use {@link android.media.MediaRecorder.AudioSource#CAMCORDER
+MediaRecorder.AudioSource.CAMCORDER}. </li>
+ <li>{@link android.media.MediaRecorder#setVideoSource(int) setVideoSource()} - Set
+the video source, use {@link android.media.MediaRecorder.VideoSource#CAMERA
+MediaRecorder.VideoSource.CAMERA}.</li>
+ <li>Set the video output format and encoding. For Android 2.2 (API Level 8) and
+higher, use the {@link android.media.MediaRecorder#setProfile(android.media.CamcorderProfile)
+MediaRecorder.setProfile} method, and get a profile instance using {@link
+android.media.CamcorderProfile#get(int) CamcorderProfile.get()}. For versions of Android prior to
+2.2, you must set the video output format and encoding parameters:
+ <ol style="list-style-type: lower-roman;">
+ <li>{@link android.media.MediaRecorder#setOutputFormat(int) setOutputFormat()} - Set
+the output format, specify the default setting or {@link
+android.media.MediaRecorder.OutputFormat#MPEG_4 MediaRecorder.OutputFormat.MPEG_4}.</li>
+ <li>{@link android.media.MediaRecorder#setAudioEncoder(int) setAudioEncoder()} - Set
+the sound encoding type, specify the default setting or {@link
+android.media.MediaRecorder.AudioEncoder#AMR_NB MediaRecorder.AudioEncoder.AMR_NB}.</li>
+ <li>{@link android.media.MediaRecorder#setVideoEncoder(int) setVideoEncoder()} - Set
+the video encoding type, specify the default setting or {@link
+android.media.MediaRecorder.VideoEncoder#MPEG_4_SP MediaRecorder.VideoEncoder.MPEG_4_SP}.</li>
+ </ol>
+ </li>
+ <li>{@link android.media.MediaRecorder#setOutputFile(java.lang.String) setOutputFile()} -
+Set the output file, use {@code getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()} from the example
+method in the <a href="#saving-media">Saving Media Files</a> section.</li>
+ <li>{@link android.media.MediaRecorder#setPreviewDisplay(android.view.Surface)
+setPreviewDisplay()} - Specify the {@link android.view.SurfaceView} preview layout element for
+your application. Use the same object you specified for <strong>Connect Preview</strong>.</li>
+ </ol>
+ <p class="caution"><strong>Caution:</strong> You must call these {@link
+android.media.MediaRecorder} configuration methods <em>in this order</em>, otherwise your
+application will encounter errors and the recording will fail.</p>
+ </li>
+ <li><strong>Prepare MediaRecorder</strong> - Prepare the {@link android.media.MediaRecorder}
+with provided configuration settings by calling {@link android.media.MediaRecorder#prepare()
+MediaRecorder.prepare()}.</li>
+ <li><strong>Start MediaRecorder</strong> - Start recording video by calling {@link
+android.media.MediaRecorder#start() MediaRecorder.start()}.</li>
+ </ol>
+ </li>
+ <li><strong>Stop Recording Video</strong> - Call the following methods <em>in order</em>, to
+successfully complete a video recording:
+ <ol style="list-style-type: lower-alpha;">
+ <li><strong>Stop MediaRecorder</strong> - Stop recording video by calling {@link
+android.media.MediaRecorder#stop() MediaRecorder.stop()}.</li>
+ <li><strong>Reset MediaRecorder</strong> - Optionally, remove the configuration settings from
+the recorder by calling {@link android.media.MediaRecorder#reset() MediaRecorder.reset()}.</li>
+ <li><strong>Release MediaRecorder</strong> - Release the {@link android.media.MediaRecorder}
+by calling {@link android.media.MediaRecorder#release() MediaRecorder.release()}.</li>
+ <li><strong>Lock the Camera</strong> - Lock the camera so that future {@link
+android.media.MediaRecorder} sessions can use it by calling {@link android.hardware.Camera#lock()
+Camera.lock()}. Starting with Android 4.0 (API level 14), this call is not required unless the
+{@link android.media.MediaRecorder#prepare() MediaRecorder.prepare()} call fails.</li>
+ </ol>
+ </li>
+ <li><strong>Stop the Preview</strong> - When your activity has finished using the camera, stop the
+preview using {@link android.hardware.Camera#stopPreview() Camera.stopPreview()}.</li>
+ <li><strong>Release Camera</strong> - Release the camera so that other applications can use
+it by calling {@link android.hardware.Camera#release() Camera.release()}.</li>
+</ol>
+
+<p class="note"><strong>Note:</strong> It is possible to use {@link android.media.MediaRecorder}
+without creating a camera preview first and skip the first few steps of this process. However,
+since users typically prefer to see a preview before starting a recording, that process is not
+discussed here.</p>
+
+<h4 id="configuring-mediarecorder">Configuring MediaRecorder</h4>
+<p>When using the {@link android.media.MediaRecorder} class to record video, you must perform
+configuration steps in a <em>specific order</em> and then call the {@link
+android.media.MediaRecorder#prepare() MediaRecorder.prepare()} method to check and implement the
+configuration. The following example code demonstrates how to properly configure and prepare the
+{@link android.media.MediaRecorder} class for video recording.</p>
+
+<pre>
+private boolean prepareVideoRecorder(){
+
+ mCamera = getCameraInstance();
+ mMediaRecorder = new MediaRecorder();
+
+ // Step 1: Unlock and set camera to MediaRecorder
+ mCamera.unlock();
+ mMediaRecorder.setCamera(mCamera);
+
+ // Step 2: Set sources
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+
+ // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
+ mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
+
+ // Step 4: Set output file
+ mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
+
+ // Step 5: Set the preview output
+ mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());
+
+ // Step 6: Prepare configured MediaRecorder
+ try {
+ mMediaRecorder.prepare();
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
+ releaseMediaRecorder();
+ return false;
+ } catch (IOException e) {
+ Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
+ releaseMediaRecorder();
+ return false;
+ }
+ return true;
+}
+</pre>
+
+<p>Prior to Android 2.2 (API Level 8), you must set the output format and encoding formats
+parameters directly, instead of using {@link android.media.CamcorderProfile}. This approach is
+demonstrated in the following code:</p>
+
+<pre>
+ // Step 3: Set output format and encoding (for versions prior to API Level 8)
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
+</pre>
+
+<p>The following video recording parameters for {@link android.media.MediaRecorder} are given
+default settings, however, you may want to adjust these settings for your application:</p>
+
+<ul>
+ <li>{@link android.media.MediaRecorder#setVideoEncodingBitRate(int)
+setVideoEncodingBitRate()}</li>
+ <li>{@link android.media.MediaRecorder#setVideoSize(int, int) setVideoSize()}</li>
+ <li>{@link android.media.MediaRecorder#setVideoFrameRate(int) setVideoFrameRate()}</li>
+ <li>{@link android.media.MediaRecorder#setAudioEncodingBitRate(int)
+setAudioEncodingBitRate()}</li> <li>{@link android.media.MediaRecorder#setAudioChannels(int)
+setAudioChannels()}</li>
+ <li>{@link android.media.MediaRecorder#setAudioSamplingRate(int) setAudioSamplingRate()}</li>
+</ul>
+
+<h4 id="start-stop-mediarecorder">Starting and Stopping MediaRecorder</h4>
+<p>When starting and stopping video recording using the {@link android.media.MediaRecorder} class,
+you must follow a specific order, as listed below.</p>
+
+<ol>
+ <li>Unlock the camera with {@link android.hardware.Camera#unlock() Camera.unlock()}</li>
+ <li>Configure {@link android.media.MediaRecorder} as shown in the code example above</li>
+ <li>Start recording using {@link android.media.MediaRecorder#start()
+MediaRecorder.start()}</li>
+ <li>Record the video</li>
+ <li>Stop recording using {@link
+android.media.MediaRecorder#stop() MediaRecorder.stop()}</li>
+ <li>Release the media recorder with {@link android.media.MediaRecorder#release()
+MediaRecorder.release()}</li>
+ <li>Lock the camera using {@link android.hardware.Camera#lock() Camera.lock()}</li>
+</ol>
+
+<p>The following example code demonstrates how to wire up a button to properly start and stop
+video recording using the camera and the {@link android.media.MediaRecorder} class.</p>
+
+<p class="note"><strong>Note:</strong> When completing a video recording, do not release the camera
+or else your preview will be stopped.</p>
+
+<pre>
+private boolean isRecording = false;
+
+// Add a listener to the Capture button
+Button captureButton = (Button) findViewById(id.button_capture);
+captureButton.setOnClickListener(
+ new View.OnClickListener() {
+ &#64;Override
+ public void onClick(View v) {
+ if (isRecording) {
+ // stop recording and release camera
+ mMediaRecorder.stop(); // stop the recording
+ releaseMediaRecorder(); // release the MediaRecorder object
+ mCamera.lock(); // take camera access back from MediaRecorder
+
+ // inform the user that recording has stopped
+ setCaptureButtonText("Capture");
+ isRecording = false;
+ } else {
+ // initialize video camera
+ if (prepareVideoRecorder()) {
+ // Camera is available and unlocked, MediaRecorder is prepared,
+ // now you can start recording
+ mMediaRecorder.start();
+
+ // inform the user that recording has started
+ setCaptureButtonText("Stop");
+ isRecording = true;
+ } else {
+ // prepare didn't work, release the camera
+ releaseMediaRecorder();
+ // inform user
+ }
+ }
+ }
+ }
+);
+</pre>
+
+<p class="note"><strong>Note:</strong> In the above example, the {@code prepareVideoRecorder()}
+method refers to the example code shown in <a
+href="#configuring-mediarecorder">Configuring MediaRecorder</a>. This method takes care of locking
+the camera, configuring and preparing the {@link android.media.MediaRecorder} instance.</p>
+
+
+<h3 id="release-camera">Releasing the camera</h3>
+<p>Cameras are a resource that is shared by applications on a device. Your application can make
+use of the camera after getting an instance of {@link android.hardware.Camera}, and you must be
+particularly careful to release the camera object when your application stops using it, and as
+soon as your application is paused ({@link android.app.Activity#onPause() Activity.onPause()}). If
+your application does not properly release the camera, all subsequent attempts to access the camera,
+including those by your own application, will fail and may cause your or other applications to be
+shut down.</p>
+
+<p>To release an instance of the {@link android.hardware.Camera} object, use the {@link
+android.hardware.Camera#release() Camera.release()} method, as shown in the example code below.</p>
+
+<pre>
+public class CameraActivity extends Activity {
+ private Camera mCamera;
+ private SurfaceView mPreview;
+ private MediaRecorder mMediaRecorder;
+
+ ...
+
+ &#64;Override
+ protected void onPause() {
+ super.onPause();
+ releaseMediaRecorder(); // if you are using MediaRecorder, release it first
+ releaseCamera(); // release the camera immediately on pause event
+ }
+
+ private void releaseMediaRecorder(){
+ if (mMediaRecorder != null) {
+ mMediaRecorder.reset(); // clear recorder configuration
+ mMediaRecorder.release(); // release the recorder object
+ mMediaRecorder = null;
+ mCamera.lock(); // lock camera for later use
+ }
+ }
+
+ private void releaseCamera(){
+ if (mCamera != null){
+ mCamera.release(); // release the camera for other applications
+ mCamera = null;
+ }
+ }
+}
+</pre>
+
+<p class="caution"><strong>Caution:</strong> If your application does not properly release the
+camera, all subsequent attempts to access the camera, including those by your own application, will
+fail and may cause your or other applications to be shut down.</p>
+
+
+<h2 id="saving-media">Saving Media Files</h2>
+<p>Media files created by users such as pictures and videos should be saved to a device's external
+storage directory (SD Card) to conserve system space and to allow users to access these files
+without their device. There are many possible directory locations to save media files on a device,
+however there are only two standard locations you should consider as a developer:</p>
+
+<ul>
+ <li><strong>{@link android.os.Environment#getExternalStoragePublicDirectory(java.lang.String)
+Environment.getExternalStoragePublicDirectory}({@link android.os.Environment#DIRECTORY_PICTURES
+Environment.DIRECTORY_PICTURES})</strong> - This method returns the standard, shared and recommended
+location for saving pictures and videos. This directory is shared (public), so other applications
+can easily discover, read, change and delete files saved in this location. If your application is
+uninstalled by the user, media files saved to this location will not be removed. To avoid
+interfering with users existing pictures and videos, you should create a sub-directory for your
+application's media files within this directory, as shown in the code sample below. This method is
+available in Android 2.2 (API Level 8), for equivalent calls in earlier API versions, see <a
+href="{@docRoot}guide/topics/data/data-storage.html#SavingSharedFiles">Saving Shared Files</a>.</li>
+ <li><strong>{@link android.content.Context#getExternalFilesDir(java.lang.String)
+Context.getExternalFilesDir}({@link android.os.Environment#DIRECTORY_PICTURES
+Environment.DIRECTORY_PICTURES})</strong> - This method returns a standard location for saving
+pictures and videos which are associated with your application. If your application is uninstalled,
+any files saved in this location are removed. Security is not enforced for files in this
+location and other applications may read, change and delete them.</li>
+</ul>
+
+<p>The following example code demonstrates how to create a {@link java.io.File} or {@link
+android.net.Uri} location for a media file that can be used when invoking a device's camera with
+an {@link android.content.Intent} or as part of a <a href="#custom-camera">Building a Camera
+App</a>.</p>
+
+<pre>
+public static final int MEDIA_TYPE_IMAGE = 1;
+public static final int MEDIA_TYPE_VIDEO = 2;
+
+/** Create a file Uri for saving an image or video */
+private static Uri getOutputMediaFileUri(int type){
+ return Uri.fromFile(getOutputMediaFile(type));
+}
+
+/** Create a File for saving an image or video */
+private static Uri getOutputMediaFile(int type){
+ // To be safe, you should check that the SDCard is mounted
+ // using Environment.getExternalStorageState() before doing this.
+
+ File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES), "MyCameraApp");
+ // This location works best if you want the created images to be shared
+ // between applications and persist after your app has been uninstalled.
+
+ // Create the storage directory if it does not exist
+ if (! mediaStorageDir.exists()){
+ if (! mediaStorageDir.mkdirs()){
+ Log.d("MyCameraApp", "failed to create directory");
+ return null;
+ }
+ }
+
+ // Create a media file name
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ File mediaFile;
+ if (type == MEDIA_TYPE_IMAGE){
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "IMG_"+ timeStamp + ".jpg");
+ } else if(type == MEDIA_TYPE_VIDEO) {
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "VID_"+ timeStamp + ".mp4");
+ } else {
+ return null;
+ }
+
+ return mediaFile;
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> {@link
+android.os.Environment#getExternalStoragePublicDirectory(java.lang.String)
+Environment.getExternalStoragePublicDirectory()} is available in Android 2.2 (API Level 8) or
+higher. If you are targeting devices with earlier versions of Android, use {@link
+android.os.Environment#getExternalStorageDirectory() Environment.getExternalStorageDirectory()}
+instead. For more information, see <a
+href="{@docRoot}guide/topics/data/data-storage.html#SavingSharedFiles">Saving Shared Files</a>.</p>
+
+<p>For more information about saving files on an Android device, see <a
+href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.</p> \ No newline at end of file
diff --git a/docs/html/guide/topics/media/index.jd b/docs/html/guide/topics/media/index.jd
index 06e6208..7c1754f 100644
--- a/docs/html/guide/topics/media/index.jd
+++ b/docs/html/guide/topics/media/index.jd
@@ -1,971 +1,62 @@
-page.title=Media
+page.title=Multimedia and Camera
@jd:body
<div id="qv-wrapper">
<div id="qv">
-<h2>Quickview</h2>
-<ul>
-<li>MediaPlayer APIs allow you to play and record media</li>
-<li>You can handle data from raw resources, files, and streams</li>
-<li>The platform supports a variety of media formats. See <a
-href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li>
-</ul>
-
-<h2>In this document</h2>
+<h2>Topics</h2>
<ol>
-<li><a href="#mediaplayer">Using MediaPlayer</a>
- <ol>
- <li><a href='#preparingasync'>Asynchronous Preparation</a></li>
- <li><a href='#managestate'>Managing State</a></li>
- <li><a href='#releaseplayer'>Releasing the MediaPlayer</a></li>
- </ol>
-</li>
-<li><a href="#mpandservices">Using a Service with MediaPlayer</a>
- <ol>
- <li><a href="#asyncprepare">Running asynchronously</a></li>
- <li><a href="#asyncerror">Handling asynchronous errors</a></li>
- <li><a href="#wakelocks">Using wake locks</a></li>
- <li><a href="#foregroundserv">Running as a foreground service</a></li>
- <li><a href="#audiofocus">Handling audio focus</a></li>
- <li><a href="#cleanup">Performing cleanup</a></li>
- </ol>
-</li>
-<li><a href="#noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</a>
-<li><a href="#viacontentresolver">Retrieving Media from a Content Resolver</a>
-<li><a href="#jetcontent">Playing JET content</a>
-<li><a href="#audiocapture">Performing Audio Capture</a>
+<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a></li>
+<li><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></li>
+<li><a href="{@docRoot}guide/topics/media/camera.html">Camera</a></li>
+<li><a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a></li>
</ol>
<h2>Key classes</h2>
<ol>
<li>{@link android.media.MediaPlayer}</li>
+<li>{@link android.media.JetPlayer}</li>
+<li>{@link android.hardware.Camera}</li>
<li>{@link android.media.MediaRecorder}</li>
<li>{@link android.media.AudioManager}</li>
-<li>{@link android.media.JetPlayer}</li>
<li>{@link android.media.SoundPool}</li>
</ol>
<h2>See also</h2>
<ol>
-<li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li>
-<li><a href="{@docRoot}guide/topics/media/jet/jetcreator_manual.html">JetCreator User Manual</a></li>
+<li></li>
+<li><a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li>
+<li><a href="{@docRoot}guide/topics/media/jet/jetcreator_manual.html">JetCreator User
+Manual</a></li>
</ol>
</div>
</div>
-<p>The Android multimedia framework includes support for encoding and decoding a
-variety of common media types, so that you can easily integrate audio,
-video and images into your applications. You can play audio or video from media files stored in your
-application's resources (raw resources), from standalone files in the filesystem, or from a data
-stream arriving over a network connection, all using {@link android.media.MediaPlayer} APIs.</p>
-
-<p>You can also record audio and video using the {@link android.media.MediaRecorder} APIs if
-supported by the device hardware. Note that the emulator doesn't have hardware to capture audio or
-video, but actual mobile devices are likely to provide these capabilities.</p>
-
-<p>This document shows you how to write a media-playing application that interacts with the user and
-the system in order to obtain good performance and a pleasant user experience.</p>
-
-<p class="note"><strong>Note:</strong> You can play back the audio data only to the standard output
-device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound
-files in the conversation audio during a call.</p>
-
-
-<h2 id="mediaplayer">Using MediaPlayer</h2>
-
-<p>One of the most important components of the media framework is the
-{@link android.media.MediaPlayer MediaPlayer}
-class. An object of this class can fetch, decode, and play both audio and video
-with minimal setup. It supports several different media sources such as:
-<ul>
- <li>Local resources</li>
- <li>Internal URIs, such as one you might obtain from a Content Resolver</li>
- <li>External URLs (streaming)</li>
-</ul>
-</p>
-
-<p>For a list of media formats that Android supports,
-see the <a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media
-Formats</a> document. </p>
-
-<p>Here is an example
-of how to play audio that's available as a local raw resource (saved in your application's
-{@code res/raw/} directory):</p>
-
-<pre>MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
-mediaPlayer.start(); // no need to call prepare(); create() does that for you
-</pre>
-
-<p>In this case, a "raw" resource is a file that the system does not
-try to parse in any particular way. However, the content of this resource should not
-be raw audio. It should be a properly encoded and formatted media file in one
-of the supported formats.</p>
-
-<p>And here is how you might play from a URI available locally in the system
-(that you obtained through a Content Resolver, for instance):</p>
-
-<pre>Uri myUri = ....; // initialize Uri here
-MediaPlayer mediaPlayer = new MediaPlayer();
-mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
-mediaPlayer.setDataSource(getApplicationContext(), myUri);
-mediaPlayer.prepare();
-mediaPlayer.start();</pre>
-
-<p>Playing from a remote URL via HTTP streaming looks like this:</p>
-
-<pre>String url = "http://........"; // your URL here
-MediaPlayer mediaPlayer = new MediaPlayer();
-mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
-mediaPlayer.setDataSource(url);
-mediaPlayer.prepare(); // might take long! (for buffering, etc)
-mediaPlayer.start();</pre>
-
-<p class="note"><strong>Note:</strong>
-If you're passing a URL to stream an online media file, the file must be capable of
-progressive download.</p>
-
-<p class="caution"><strong>Caution:</strong> You must either catch or pass
-{@link java.lang.IllegalArgumentException} and {@link java.io.IOException} when using
-{@link android.media.MediaPlayer#setDataSource setDataSource()}, because
-the file you are referencing might not exist.</p>
-
-<h3 id='#preparingasync'>Asynchronous Preparation</h3>
-
-<p>Using {@link android.media.MediaPlayer MediaPlayer} can be straightforward in
-principle. However, it's important to keep in mind that a few more things are
-necessary to integrate it correctly with a typical Android application. For
-example, the call to {@link android.media.MediaPlayer#prepare prepare()} can
-take a long time to execute, because
-it might involve fetching and decoding media data. So, as is the case with any
-method that may take long to execute, you should <strong>never call it from your
-application's UI thread</strong>. Doing that will cause the UI to hang until the method returns,
-which is a very bad user experience and can cause an ANR (Application Not Responding) error. Even if
-you expect your resource to load quickly, remember that anything that takes more than a tenth
-of a second to respond in the UI will cause a noticeable pause and will give
-the user the impression that your application is slow.</p>
-
-<p>To avoid hanging your UI thread, spawn another thread to
-prepare the {@link android.media.MediaPlayer} and notify the main thread when done. However, while
-you could write the threading logic
-yourself, this pattern is so common when using {@link android.media.MediaPlayer} that the framework
-supplies a convenient way to accomplish this task by using the
-{@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. This method
-starts preparing the media in the background and returns immediately. When the media
-is done preparing, the {@link android.media.MediaPlayer.OnPreparedListener#onPrepared onPrepared()}
-method of the {@link android.media.MediaPlayer.OnPreparedListener
-MediaPlayer.OnPreparedListener}, configured through
-{@link android.media.MediaPlayer#setOnPreparedListener setOnPreparedListener()} is called.</p>
-
-<h3 id='#managestate'>Managing State</h3>
-
-<p>Another aspect of a {@link android.media.MediaPlayer} that you should keep in mind is
-that it's state-based. That is, the {@link android.media.MediaPlayer} has an internal state
-that you must always be aware of when writing your code, because certain operations
-are only valid when then player is in specific states. If you perform an operation while in the
-wrong state, the system may throw an exception or cause other undesireable behaviors.</p>
-
-<p>The documentation in the
-{@link android.media.MediaPlayer MediaPlayer} class shows a complete state diagram,
-that clarifies which methods move the {@link android.media.MediaPlayer} from one state to another.
-For example, when you create a new {@link android.media.MediaPlayer}, it is in the <em>Idle</em>
-state. At that point, you should initialize it by calling
-{@link android.media.MediaPlayer#setDataSource setDataSource()}, bringing it
-to the <em>Initialized</em> state. After that, you have to prepare it using either the
-{@link android.media.MediaPlayer#prepare prepare()} or
-{@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. When
-the {@link android.media.MediaPlayer} is done preparing, it will then enter the <em>Prepared</em>
-state, which means you can call {@link android.media.MediaPlayer#start start()}
-to make it play the media. At that point, as the diagram illustrates,
-you can move between the <em>Started</em>, <em>Paused</em> and <em>PlaybackCompleted</em> states by
-calling such methods as
-{@link android.media.MediaPlayer#start start()},
-{@link android.media.MediaPlayer#pause pause()}, and
-{@link android.media.MediaPlayer#seekTo seekTo()},
-amongst others. When you
-call {@link android.media.MediaPlayer#stop stop()}, however, notice that you
-cannot call {@link android.media.MediaPlayer#start start()} again until you
-prepare the {@link android.media.MediaPlayer} again.</p>
-
-<p>Always keep <a href='{@docRoot}images/mediaplayer_state_diagram.gif'>the state diagram</a>
-in mind when writing code that interacts with a
-{@link android.media.MediaPlayer} object, because calling its methods from the wrong state is a
-common cause of bugs.</p>
-
-<h3 id='#releaseplayer'>Releasing the MediaPlayer</h3>
-
-<p>A {@link android.media.MediaPlayer MediaPlayer} can consume valuable
-system resources.
-Therefore, you should always take extra precautions to make sure you are not
-hanging on to a {@link android.media.MediaPlayer} instance longer than necessary. When you
-are done with it, you should always call
-{@link android.media.MediaPlayer#release release()} to make sure any
-system resources allocated to it are properly released. For example, if you are
-using a {@link android.media.MediaPlayer} and your activity receives a call to {@link
-android.app.Activity#onStop onStop()}, you must release the {@link android.media.MediaPlayer},
-because it
-makes little sense to hold on to it while your activity is not interacting with
-the user (unless you are playing media in the background, which is discussed in the next section).
-When your activity is resumed or restarted, of course, you need to
-create a new {@link android.media.MediaPlayer} and prepare it again before resuming playback.</p>
-
-<p>Here's how you should release and then nullify your {@link android.media.MediaPlayer}:</p>
-<pre>
-mediaPlayer.release();
-mediaPlayer = null;
-</pre>
-
-<p>As an example, consider the problems that could happen if you
-forgot to release the {@link android.media.MediaPlayer} when your activity is stopped, but create a
-new one when the activity starts again. As you may know, when the user changes the
-screen orientation (or changes the device configuration in another way),
-the system handles that by restarting the activity (by default), so you might quickly
-consume all of the system resources as the user
-rotates the device back and forth between portrait and landscape, because at each
-orientation change, you create a new {@link android.media.MediaPlayer} that you never
-release. (For more information about runtime restarts, see <a
-href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>.)</p>
-
-<p>You may be wondering what happens if you want to continue playing
-"background media" even when the user leaves your activity, much in the same
-way that the built-in Music application behaves. In this case, what you need is
-a {@link android.media.MediaPlayer MediaPlayer} controlled by a {@link android.app.Service}, as
-discussed in <a href="mpandservices">Using a Service with MediaPlayer</a>.</p>
-
-<h2 id="mpandservices">Using a Service with MediaPlayer</h2>
-
-<p>If you want your media to play in the background even when your application
-is not onscreen&mdash;that is, you want it to continue playing while the user is
-interacting with other applications&mdash;then you must start a
-{@link android.app.Service Service} and control the
-{@link android.media.MediaPlayer MediaPlayer} instance from there.
-You should be careful about this setup, because the user and the system have expectations
-about how an application running a background service should interact with the rest of the
-system. If your application does not fulfil those expectations, the user may
-have a bad experience. This section describes the main issues that you should be
-aware of and offers suggestions about how to approach them.</p>
-
-
-<h3 id="asyncprepare">Running asynchronously</h3>
-
-<p>First of all, like an {@link android.app.Activity Activity}, all work in a
-{@link android.app.Service Service} is done in a single thread by
-default&mdash;in fact, if you're running an activity and a service from the same application, they
-use the same thread (the "main thread") by default. Therefore, services need to
-process incoming intents quickly
-and never perform lengthy computations when responding to them. If any heavy
-work or blocking calls are expected, you must do those tasks asynchronously: either from
-another thread you implement yourself, or using the framework's many facilities
-for asynchronous processing.</p>
-
-<p>For instance, when using a {@link android.media.MediaPlayer} from your main thread,
-you should call {@link android.media.MediaPlayer#prepareAsync prepareAsync()} rather than
-{@link android.media.MediaPlayer#prepare prepare()}, and implement
-a {@link android.media.MediaPlayer.OnPreparedListener MediaPlayer.OnPreparedListener}
-in order to be notified when the preparation is complete and you can start playing.
-For example:</p>
-
-<pre>
-public class MyService extends Service implements MediaPlayer.OnPreparedListener {
- private static final ACTION_PLAY = "com.example.action.PLAY";
- MediaPlayer mMediaPlayer = null;
-
- public int onStartCommand(Intent intent, int flags, int startId) {
- ...
- if (intent.getAction().equals(ACTION_PLAY)) {
- mMediaPlayer = ... // initialize it here
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.prepareAsync(); // prepare async to not block main thread
- }
- }
-
- /** Called when MediaPlayer is ready */
- public void onPrepared(MediaPlayer player) {
-   player.start();
- }
-}
-</pre>
-
-
-<h3 id="asyncerror">Handling asynchronous errors</h3>
-
-<p>On synchronous operations, errors would normally
-be signaled with an exception or an error code, but whenever you use asynchronous
-resources, you should make sure your application is notified
-of errors appropriately. In the case of a {@link android.media.MediaPlayer MediaPlayer},
-you can accomplish this by implementing a
-{@link android.media.MediaPlayer.OnErrorListener MediaPlayer.OnErrorListener} and
-setting it in your {@link android.media.MediaPlayer} instance:</p>
-
-<pre>
-public class MyService extends Service implements MediaPlayer.OnErrorListener {
- MediaPlayer mMediaPlayer;
-
- public void initMediaPlayer() {
- // ...initialize the MediaPlayer here...
-
- mMediaPlayer.setOnErrorListener(this);
- }
-
- &#64;Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- // ... react appropriately ...
- // The MediaPlayer has moved to the Error state, must be reset!
- }
-}
-</pre>
-
-<p>It's important to remember that when an error occurs, the {@link android.media.MediaPlayer}
-moves to the <em>Error</em> state (see the documentation for the
-{@link android.media.MediaPlayer MediaPlayer} class for the full state diagram)
-and you must reset it before you can use it again.
-
-
-<h3 id="wakelocks">Using wake locks</h3>
-
-<p>When designing applications that play media
-in the background, the device may go to sleep
-while your service is running. Because the Android system tries to conserve
-battery while the device is sleeping, the system tries to shut off any
-of the phone's features that are
-not necessary, including the CPU and the WiFi hardware.
-However, if your service is playing or streaming music, you want to prevent
-the system from interfering with your playback.</p>
-
-<p>In order to ensure that your service continues to run under
-those conditions, you have to use "wake locks." A wake lock is a way to signal to
-the system that your application is using some feature that should
-stay available even if the phone is idle.</p>
-
-<p class="caution"><strong>Notice:</strong> You should always use wake locks sparingly and hold them
-only for as long as truly necessary, because they significantly reduce the battery life of the
-device.</p>
-
-<p>To ensure that the CPU continues running while your {@link android.media.MediaPlayer} is
-playing, call the {@link android.media.MediaPlayer#setWakeMode
-setWakeMode()} method when initializing your {@link android.media.MediaPlayer}. Once you do,
-the {@link android.media.MediaPlayer} holds the specified lock while playing and releases the lock
-when paused or stopped:</p>
-
-<pre>
-mMediaPlayer = new MediaPlayer();
-// ... other initialization here ...
-mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
-</pre>
-
-<p>However, the wake lock acquired in this example guarantees only that the CPU remains awake. If
-you are streaming media over the
-network and you are using Wi-Fi, you probably want to hold a
-{@link android.net.wifi.WifiManager.WifiLock WifiLock} as
-well, which you must acquire and release manually. So, when you start preparing the
-{@link android.media.MediaPlayer} with the remote URL, you should create and acquire the Wi-Fi lock.
-For example:</p>
-
-<pre>
-WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
-
-wifiLock.acquire();
-</pre>
-
-<p>When you pause or stop your media, or when you no longer need the
-network, you should release the lock:</p>
-
-<pre>
-wifiLock.release();
-</pre>
-
-
-<h3 id="foregroundserv">Running as a foreground service</h3>
-
-<p>Services are often used for performing background tasks, such as fetching emails,
-synchronizing data, downloading content, amongst other possibilities. In these
-cases, the user is not actively aware of the service's execution, and probably
-wouldn't even notice if some of these services were interrupted and later restarted.</p>
-
-<p>But consider the case of a service that is playing music. Clearly this is a service that the user
-is actively aware of and the experience would be severely affected by any interruptions.
-Additionally, it's a service that the user will likely wish to interact with during its execution.
-In this case, the service should run as a "foreground service." A
-foreground service holds a higher level of importance within the system&mdash;the system will
-almost never kill the service, because it is of immediate importance to the user. When running
-in the foreground, the service also must provide a status bar notification to ensure that users are
-aware of the running service and allow them to open an activity that can interact with the
-service.</p>
-
-<p>In order to turn your service into a foreground service, you must create a
-{@link android.app.Notification Notification} for the status bar and call
-{@link android.app.Service#startForeground startForeground()} from the {@link
-android.app.Service}. For example:</p>
-
-<pre>String songName;
-// assign the song name to songName
-PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
-                new Intent(getApplicationContext(), MainActivity.class),
-                PendingIntent.FLAG_UPDATE_CURRENT);
-Notification notification = new Notification();
-notification.tickerText = text;
-notification.icon = R.drawable.play0;
-notification.flags |= Notification.FLAG_ONGOING_EVENT;
-notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
-                "Playing: " + songName, pi);
-startForeground(NOTIFICATION_ID, notification);
-</pre>
-
-<p>While your service is running in the foreground, the notification you
-configured is visible in the notification area of the device. If the user
-selects the notification, the system invokes the {@link android.app.PendingIntent} you supplied. In
-the example above, it opens an activity ({@code MainActivity}).</p>
-
-<p>Figure 1 shows how your notification appears to the user:</p>
-
-<img src='images/notification1.png' />
-&nbsp;&nbsp;
-<img src='images/notification2.png' />
-<p class="img-caption"><strong>Figure 1.</strong> Screenshots of a foreground service's notification, showing the notification icon in the status bar (left) and the expanded view (right).</p>
-
-<p>You should only hold on to the "foreground service" status while your
-service is actually performing something the user is actively aware of. Once
-that is no longer true, you should release it by calling
-{@link android.app.Service#stopForeground stopForeground()}:</p>
-
-<pre>
-stopForeground(true);
-</pre>
-
-<p>For more information, see the documentation about <a
-href="{@docRoot}guide/topics/fundamentals/services.html#Foreground">Services</a> and
-<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>.</p>
-
-
-<h3 id="audiofocus">Handling audio focus</h3>
-
-<p>Even though only one activity can run at any given time, Android is a
-multi-tasking environment. This poses a particular challenge to applications
-that use audio, because there is only one audio output and there may be several
-media services competing for its use. Before Android 2.2, there was no built-in
-mechanism to address this issue, which could in some cases lead to a bad user
-experience. For example, when a user is listening to
-music and another application needs to notify the user of something very important,
-the user might not hear the notification tone due to the loud music. Starting with
-Android 2.2, the platform offers a way for applications to negotiate their
-use of the device's audio output. This mechanism is called Audio Focus.</p>
-
-<p>When your application needs to output audio such as music or a notification,
-you should always request audio focus. Once it has focus, it can use the sound output freely, but it should
-always listen for focus changes. If it is notified that it has lost the audio
-focus, it should immediately either kill the audio or lower it to a quiet level
-(known as "ducking"&mdash;there is a flag that indicates which one is appropriate) and only resume
-loud playback after it receives focus again.</p>
-
-<p>Audio Focus is cooperative in nature. That is, applications are expected
-(and highly encouraged) to comply with the audio focus guidelines, but the
-rules are not enforced by the system. If an application wants to play loud
-music even after losing audio focus, nothing in the system will prevent that.
-However, the user is more likely to have a bad experience and will be more
-likely to uninstall the misbehaving application.</p>
-
-<p>To request audio focus, you must call
-{@link android.media.AudioManager#requestAudioFocus requestAudioFocus()} from the {@link
-android.media.AudioManager}, as the example below demonstrates:</p>
-
-<pre>
-AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
-
-if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- // could not get audio focus.
-}
-</pre>
-
-<p>The first parameter to {@link android.media.AudioManager#requestAudioFocus requestAudioFocus()}
-is an {@link android.media.AudioManager.OnAudioFocusChangeListener
-AudioManager.OnAudioFocusChangeListener},
-whose {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange
-onAudioFocusChange()} method is called whenever there is a change in audio focus. Therefore, you
-should also implement this interface on your service and activities. For example:</p>
-
-<pre>
-class MyService extends Service
- implements AudioManager.OnAudioFocusChangeListener {
- // ....
- public void onAudioFocusChange(int focusChange) {
- // Do something based on focus change...
- }
-}
-</pre>
-
-<p>The <code>focusChange</code> parameter tells you how the audio focus has changed, and
-can be one of the following values (they are all constants defined in
-{@link android.media.AudioManager AudioManager}):</p>
-
-<ul>
-<li>{@link android.media.AudioManager#AUDIOFOCUS_GAIN}: You have gained the audio focus.</li>
-
-<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS}: You have lost the audio focus for a
-presumably long time.
-You must stop all audio playback. Because you should expect not to have focus back
-for a long time, this would be a good place to clean up your resources as much
-as possible. For example, you should release the {@link android.media.MediaPlayer}.</li>
-
-<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}: You have
-temporarily lost audio focus, but should receive it back shortly. You must stop
-all audio playback, but you can keep your resources because you will probably get
-focus back shortly.</li>
-
-<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}: You have temporarily
-lost audio focus,
-but you are allowed to continue to play audio quietly (at a low volume) instead
-of killing audio completely.</li>
-</ul>
-
-<p>Here is an example implementation:</p>
-
-<pre>
-public void onAudioFocusChange(int focusChange) {
- switch (focusChange) {
- case AudioManager.AUDIOFOCUS_GAIN:
- // resume playback
- if (mMediaPlayer == null) initMediaPlayer();
- else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
- mMediaPlayer.setVolume(1.0f, 1.0f);
- break;
-
- case AudioManager.AUDIOFOCUS_LOSS:
- // Lost focus for an unbounded amount of time: stop playback and release media player
- if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
- mMediaPlayer.release();
- mMediaPlayer = null;
- break;
-
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- // Lost focus for a short time, but we have to stop
- // playback. We don't release the media player because playback
- // is likely to resume
- if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
- break;
-
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
- // Lost focus for a short time, but it's ok to keep playing
- // at an attenuated level
- if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
- break;
- }
-}
-</pre>
-
-<p>Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2)
-and above, so if you want to support previous
-versions of Android, you should adopt a backward compatibility strategy that
-allows you to use this feature if available, and fall back seamlessly if not.</p>
-
-<p>You can achieve backward compatibility either by calling the audio focus methods by reflection
-or by implementing all the audio focus features in a separate class (say,
-<code>AudioFocusHelper</code>). Here is an example of such a class:</p>
-
-<pre>
-public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
- AudioManager mAudioManager;
-
- // other fields here, you'll probably hold a reference to an interface
- // that you can use to communicate the focus changes to your Service
-
- public AudioFocusHelper(Context ctx, /* other arguments here */) {
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- // ...
- }
-
- public boolean requestFocus() {
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
- mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
- }
-
- public boolean abandonFocus() {
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
- mAudioManager.abandonAudioFocus(this);
- }
-
- &#64;Override
- public void onAudioFocusChange(int focusChange) {
- // let your service know about the focus change
- }
-}
-</pre>
-
-
-<p>You can create an instance of <code>AudioFocusHelper</code> class only if you detect that
-the system is running API level 8 or above. For example:</p>
-
-<pre>
-if (android.os.Build.VERSION.SDK_INT &gt;= 8) {
- mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
-} else {
- mAudioFocusHelper = null;
-}
-</pre>
-
-
-<h3 id="cleanup">Performing cleanup</h3>
-
-<p>As mentioned earlier, a {@link android.media.MediaPlayer} object can consume a significant
-amount of system resources, so you should keep it only for as long as you need and call
-{@link android.media.MediaPlayer#release release()} when you are done with it. It's important
-to call this cleanup method explicitly rather than rely on system garbage collection because
-it might take some time before the garbage collector reclaims the {@link android.media.MediaPlayer},
-as it's only sensitive to memory needs and not to shortage of other media-related resources.
-So, in the case when you're using a service, you should always override the
-{@link android.app.Service#onDestroy onDestroy()} method to make sure you are releasing
-the {@link android.media.MediaPlayer}:</p>
-
-<pre>
-public class MyService extends Service {
- MediaPlayer mMediaPlayer;
- // ...
-
- &#64;Override
- public void onDestroy() {
- if (mMediaPlayer != null) mMediaPlayer.release();
- }
-}
-</pre>
-
-<p>You should always look for other opportunities to release your {@link android.media.MediaPlayer}
-as well, apart from releasing it when being shut down. For example, if you expect not
-to be able to play media for an extended period of time (after losing audio focus, for example),
-you should definitely release your existing {@link android.media.MediaPlayer} and create it again
-later. On the
-other hand, if you only expect to stop playback for a very short time, you should probably
-hold on to your {@link android.media.MediaPlayer} to avoid the overhead of creating and preparing it
-again.</p>
-
-
-
-<h2 id="noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</h2>
-
-<p>Many well-written applications that play audio automatically stop playback when an event
-occurs that causes the audio to become noisy (ouput through external speakers). For instance,
-this might happen when a user is listening to music through headphones and accidentally
-disconnects the headphones from the device. However, this behavior does not happen automatically.
-If you don't implement this feature, audio plays out of the device's external speakers, which
-might not be what the user wants.</p>
-
-<p>You can ensure your app stops playing music in these situations by handling
-the {@link android.media.AudioManager#ACTION_AUDIO_BECOMING_NOISY} intent, for which you can register a receiver by
-adding the following to your manifest:</p>
-
-<pre>
-&lt;receiver android:name=".MusicIntentReceiver"&gt;
- &lt;intent-filter&gt;
- &lt;action android:name="android.media.AUDIO_BECOMING_NOISY" /&gt;
- &lt;/intent-filter&gt;
-&lt;/receiver&gt;
-</pre>
-
-<p>This registers the <code>MusicIntentReceiver</code> class as a broadcast receiver for that
-intent. You should then implement this class:</p>
-
-<pre>
-public class MusicIntentReceiver implements android.content.BroadcastReceiver {
- &#64;Override
- public void onReceive(Context ctx, Intent intent) {
- if (intent.getAction().equals(
- android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
- // signal your service to stop playback
- // (via an Intent, for instance)
- }
- }
-}
-</pre>
-
-
-
-
-<h2 id="viacontentresolver">Retrieving Media from a Content Resolver</h2>
-
-<p>Another feature that may be useful in a media player application is the ability to
-retrieve music that the user has on the device. You can do that by querying the {@link
-android.content.ContentResolver} for external media:</p>
-
-<pre>
-ContentResolver contentResolver = getContentResolver();
-Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
-Cursor cursor = contentResolver.query(uri, null, null, null, null);
-if (cursor == null) {
- // query failed, handle error.
-} else if (!cursor.moveToFirst()) {
- // no media on the device
-} else {
- int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
- int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
- do {
- long thisId = cursor.getLong(idColumn);
- String thisTitle = cursor.getString(titleColumn);
- // ...process entry...
- } while (cursor.moveToNext());
-}
-</pre>
-
-<p>To use this with the {@link android.media.MediaPlayer}, you can do this:</p>
-
-<pre>
-long id = /* retrieve it from somewhere */;
-Uri contentUri = ContentUris.withAppendedId(
- android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
-
-mMediaPlayer = new MediaPlayer();
-mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
-mMediaPlayer.setDataSource(getApplicationContext(), contentUri);
-
-// ...prepare and start...
-</pre>
-
-
-
-<h2 id="jetcontent">Playing JET content</h2>
-
-<p>The Android platform includes a JET engine that lets you add interactive playback of JET audio
-content in your applications. You can create JET content for interactive playback using the
-JetCreator authoring application that ships with the SDK. To play and manage JET content from your
-application, use the {@link android.media.JetPlayer JetPlayer} class.</p>
-
-<p>For a description of JET concepts and instructions on how to use the JetCreator authoring tool,
-see the <a href="{@docRoot}guide/topics/media/jet/jetcreator_manual.html">JetCreator User
-Manual</a>. The tool is available on Windows, OS X, and Linux platforms (Linux does not
-support auditioning of imported assets like with the Windows and OS X versions).
-</p>
-
-<p>Here's an example of how to set up JET playback from a <code>.jet</code> file stored on the SD card:</p>
-
-<pre>
-JetPlayer jetPlayer = JetPlayer.getJetPlayer();
-jetPlayer.loadJetFile("/sdcard/level1.jet");
-byte segmentId = 0;
-
-// queue segment 5, repeat once, use General MIDI, transpose by -1 octave
-jetPlayer.queueJetSegment(5, -1, 1, -1, 0, segmentId++);
-// queue segment 2
-jetPlayer.queueJetSegment(2, -1, 0, 0, 0, segmentId++);
-
-jetPlayer.play();
-</pre>
-
-<p>The SDK includes an example application &mdash; JetBoy &mdash; that shows how to use {@link
-android.media.JetPlayer JetPlayer} to create an interactive music soundtrack in your game. It also
-illustrates how to use JET events to synchronize music and game logic. The application is located at
-<code>&lt;sdk&gt;/platforms/android-1.5/samples/JetBoy</code>.</p>
-
-
-<h2 id="audiocapture">Performing Audio Capture</h2>
-
-<p>Audio capture from the device is a bit more complicated than audio and video playback, but still fairly simple:</p>
-<ol>
- <li>Create a new instance of {@link android.media.MediaRecorder android.media.MediaRecorder}.</li>
- <li>Set the audio source using
- {@link android.media.MediaRecorder#setAudioSource MediaRecorder.setAudioSource()}. You will probably want to use
- <code>MediaRecorder.AudioSource.MIC</code>.</li>
- <li>Set output file format using
- {@link android.media.MediaRecorder#setOutputFormat MediaRecorder.setOutputFormat()}.
- </li>
- <li>Set output file name using
- {@link android.media.MediaRecorder#setOutputFile MediaRecorder.setOutputFile()}.
- </li>
- <li>Set the audio encoder using
- {@link android.media.MediaRecorder#setAudioEncoder MediaRecorder.setAudioEncoder()}.
- </li>
- <li>Call {@link android.media.MediaRecorder#prepare MediaRecorder.prepare()}
- on the MediaRecorder instance.</li>
- <li>To start audio capture, call
- {@link android.media.MediaRecorder#start MediaRecorder.start()}. </li>
- <li>To stop audio capture, call {@link android.media.MediaRecorder#stop MediaRecorder.stop()}.
- <li>When you are done with the MediaRecorder instance, call
-{@link android.media.MediaRecorder#release MediaRecorder.release()} on it. Calling
-{@link android.media.MediaRecorder#release MediaRecorder.release()} is always recommended to
-free the resource immediately.</li>
-</ol>
-
-<h3>Example: Record audio and play the recorded audio</h3>
-<p>The example class below illustrates how to set up, start and stop audio capture, and to play the recorded audio file.</p>
-<pre>
-/*
- * The application needs to have the permission to write to external storage
- * if the output file is written to the external storage, and also the
- * permission to record audio. These permissions must be set in the
- * application's AndroidManifest.xml file, with something like:
- *
- * &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
- * &lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&gt;
- *
- */
-package com.android.audiorecordtest;
-
-import android.app.Activity;
-import android.widget.LinearLayout;
-import android.os.Bundle;
-import android.os.Environment;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.content.Context;
-import android.util.Log;
-import android.media.MediaRecorder;
-import android.media.MediaPlayer;
-
-import java.io.IOException;
-
-
-public class AudioRecordTest extends Activity
-{
- private static final String LOG_TAG = "AudioRecordTest";
- private static String mFileName = null;
-
- private RecordButton mRecordButton = null;
- private MediaRecorder mRecorder = null;
-
- private PlayButton mPlayButton = null;
- private MediaPlayer mPlayer = null;
-
- private void onRecord(boolean start) {
- if (start) {
- startRecording();
- } else {
- stopRecording();
- }
- }
-
- private void onPlay(boolean start) {
- if (start) {
- startPlaying();
- } else {
- stopPlaying();
- }
- }
-
- private void startPlaying() {
- mPlayer = new MediaPlayer();
- try {
- mPlayer.setDataSource(mFileName);
- mPlayer.prepare();
- mPlayer.start();
- } catch (IOException e) {
- Log.e(LOG_TAG, "prepare() failed");
- }
- }
-
- private void stopPlaying() {
- mPlayer.release();
- mPlayer = null;
- }
-
- private void startRecording() {
- mRecorder = new MediaRecorder();
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setOutputFile(mFileName);
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
-
- try {
- mRecorder.prepare();
- } catch (IOException e) {
- Log.e(LOG_TAG, "prepare() failed");
- }
-
- mRecorder.start();
- }
-
- private void stopRecording() {
- mRecorder.stop();
- mRecorder.release();
- mRecorder = null;
- }
-
- class RecordButton extends Button {
- boolean mStartRecording = true;
-
- OnClickListener clicker = new OnClickListener() {
- public void onClick(View v) {
- onRecord(mStartRecording);
- if (mStartRecording) {
- setText("Stop recording");
- } else {
- setText("Start recording");
- }
- mStartRecording = !mStartRecording;
- }
- };
-
- public RecordButton(Context ctx) {
- super(ctx);
- setText("Start recording");
- setOnClickListener(clicker);
- }
- }
-
- class PlayButton extends Button {
- boolean mStartPlaying = true;
-
- OnClickListener clicker = new OnClickListener() {
- public void onClick(View v) {
- onPlay(mStartPlaying);
- if (mStartPlaying) {
- setText("Stop playing");
- } else {
- setText("Start playing");
- }
- mStartPlaying = !mStartPlaying;
- }
- };
-
- public PlayButton(Context ctx) {
- super(ctx);
- setText("Start playing");
- setOnClickListener(clicker);
- }
- }
-
- public AudioRecordTest() {
- mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
- mFileName += "/audiorecordtest.3gp";
- }
-
- &#64;Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- LinearLayout ll = new LinearLayout(this);
- mRecordButton = new RecordButton(this);
- ll.addView(mRecordButton,
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- 0));
- mPlayButton = new PlayButton(this);
- ll.addView(mPlayButton,
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- 0));
- setContentView(ll);
- }
-
- &#64;Override
- public void onPause() {
- super.onPause();
- if (mRecorder != null) {
- mRecorder.release();
- mRecorder = null;
- }
-
- if (mPlayer != null) {
- mPlayer.release();
- mPlayer = null;
- }
- }
-}
-</pre>
-
-
-
+<p>The Android multimedia framework includes support for capturing and playing audio, video and
+images in a variety of common media types, so that you can easily integrate them into your
+applications. You can play audio or video from media files stored in your application's resources,
+from standalone files in the file system, or from a data stream arriving over a
+network connection, all using the {@link android.media.MediaPlayer} or {@link
+android.media.JetPlayer} APIs. You can also record audio, video and take pictures using the {@link
+android.media.MediaRecorder} and {@link android.hardware.Camera} APIs if supported by the device
+hardware.</p>
+
+<p>The following topics show you how to use the Android framework to implement multimedia capture
+and playback.</p>
+
+<dl>
+ <dt><strong><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a></strong></dt>
+ <dd>How to play audio and video in your application.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></strong></dt>
+ <dd>How to play interactive audio and video in your application using content created with
+JetCreator.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/media/camera.html">Camera</a></strong></dt>
+ <dd>How to use a device camera to take pictures or video in your application.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/media/audio-capture.html">Audio
+Capture</a></strong></dt>
+ <dd>How to record sound in your application.</dd>
+</dl> \ No newline at end of file
diff --git a/docs/html/guide/topics/media/jetplayer.jd b/docs/html/guide/topics/media/jetplayer.jd
new file mode 100644
index 0000000..f3d55f9
--- /dev/null
+++ b/docs/html/guide/topics/media/jetplayer.jd
@@ -0,0 +1,70 @@
+page.title=JetPlayer
+parent.title=Multimedia and Camera
+parent.link=index.html
+@jd:body
+
+ <div id="qv-wrapper">
+ <div id="qv">
+
+<h2>In this document</h2>
+<ol>
+<li><a href="#jetcontent">Playing JET content</a>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.media.JetPlayer}</li>
+</ol>
+
+<h2>Related Samples</h2>
+<ol>
+<li><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+<li><a href="{@docRoot}guide/topics/media/jet/jetcreator_manual.html">JetCreator User
+Manual</a></li>
+<li><a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li>
+<li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li>
+<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>The Android platform includes a JET engine that lets you add interactive playback of JET audio
+content in your applications. You can create JET content for interactive playback using the
+JetCreator authoring application that ships with the SDK. To play and manage JET content from your
+application, use the {@link android.media.JetPlayer JetPlayer} class.</p>
+
+
+<h2 id="jetcontent">Playing JET content</h2>
+
+<p>This section shows you how to write, set up and play JET content. For a description of JET
+concepts and instructions on how to use the JetCreator authoring tool, see the <a
+href="{@docRoot}guide/topics/media/jet/jetcreator_manual.html">JetCreator User
+Manual</a>. The tool is available on Windows, OS X, and Linux platforms (Linux does not
+support auditioning of imported assets like with the Windows and OS X versions).
+</p>
+
+<p>Here's an example of how to set up JET playback from a <code>.jet</code> file stored on the SD
+card:</p>
+
+<pre>
+JetPlayer jetPlayer = JetPlayer.getJetPlayer();
+jetPlayer.loadJetFile("/sdcard/level1.jet");
+byte segmentId = 0;
+
+// queue segment 5, repeat once, use General MIDI, transpose by -1 octave
+jetPlayer.queueJetSegment(5, -1, 1, -1, 0, segmentId++);
+// queue segment 2
+jetPlayer.queueJetSegment(2, -1, 0, 0, 0, segmentId++);
+
+jetPlayer.play();
+</pre>
+
+<a>The SDK includes an example application &mdash; JetBoy &mdash; that shows how to use {@link
+android.media.JetPlayer JetPlayer} to create an interactive music soundtrack in your game. It also
+illustrates how to use JET events to synchronize music and game logic. The application is located at
+<a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a>.</p> \ No newline at end of file
diff --git a/docs/html/guide/topics/media/mediaplayer.jd b/docs/html/guide/topics/media/mediaplayer.jd
new file mode 100644
index 0000000..b3ca7dd
--- /dev/null
+++ b/docs/html/guide/topics/media/mediaplayer.jd
@@ -0,0 +1,747 @@
+page.title=Media Playback
+parent.title=Multimedia and Camera
+parent.link=index.html
+@jd:body
+
+ <div id="qv-wrapper">
+ <div id="qv">
+
+<h2>In this document</h2>
+<ol>
+<li><a href="#basics">The Basics</a>
+<li><a href="#manifest">Manifest Declarations</a></li>
+<li><a href="#mediaplayer">Using MediaPlayer</a>
+ <ol>
+ <li><a href='#preparingasync'>Asynchronous Preparation</a></li>
+ <li><a href='#managestate'>Managing State</a></li>
+ <li><a href='#releaseplayer'>Releasing the MediaPlayer</a></li>
+ </ol>
+</li>
+<li><a href="#mpandservices">Using a Service with MediaPlayer</a>
+ <ol>
+ <li><a href="#asyncprepare">Running asynchronously</a></li>
+ <li><a href="#asyncerror">Handling asynchronous errors</a></li>
+ <li><a href="#wakelocks">Using wake locks</a></li>
+ <li><a href="#foregroundserv">Running as a foreground service</a></li>
+ <li><a href="#audiofocus">Handling audio focus</a></li>
+ <li><a href="#cleanup">Performing cleanup</a></li>
+ </ol>
+</li>
+<li><a href="#noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</a>
+<li><a href="#viacontentresolver">Retrieving Media from a Content Resolver</a>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.media.MediaPlayer}</li>
+<li>{@link android.media.AudioManager}</li>
+<li>{@link android.media.SoundPool}</li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+<li><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></li>
+<li><a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a></li>
+<li><a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li>
+<li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>The Android multimedia framework includes support for playing variety of common media types, so
+that you can easily integrate audio, video and images into your applications. You can play audio or
+video from media files stored in your application's resources (raw resources), from standalone files
+in the filesystem, or from a data stream arriving over a network connection, all using {@link
+android.media.MediaPlayer} APIs.</p>
+
+<p>This document shows you how to write a media-playing application that interacts with the user and
+the system in order to obtain good performance and a pleasant user experience.</p>
+
+<p class="note"><strong>Note:</strong> You can play back the audio data only to the standard output
+device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound
+files in the conversation audio during a call.</p>
+
+<h2 id="basics">The Basics</h2>
+<p>The following classes are used to play sound and video in the Android framework:</p>
+
+<dl>
+ <dt>{@link android.media.MediaPlayer}</dt>
+ <dd>This class is the primary API for playing sound and video.</dd>
+ <dt>{@link android.media.AudioManager}</dt>
+ <dd>This class manages audio sources and audio output on a device.</dd>
+</dl>
+
+<h2 id="manifest">Manifest Declarations</h2>
+<p>Before starting development on your application using MediaPlayer, make sure your manifest has
+the appropriate declarations to allow use of related features.</p>
+
+<ul>
+ <li><strong>Internet Permission</strong> - If you are using MediaPlayer to stream network-based
+content, your application must request network access.
+<pre>
+&lt;uses-permission android:name="android.permission.INTERNET" /&gt;
+</pre>
+ </li>
+ <li><strong>Wake Lock Permission</strong> - If your player application needs to keep the screen
+from dimming or the processor from sleeping, or uses the {@link
+android.media.MediaPlayer#setScreenOnWhilePlaying(boolean) MediaPlayer.setScreenOnWhilePlaying()} or
+{@link android.media.MediaPlayer#setWakeMode(android.content.Context, int)
+MediaPlayer.setWakeMode()} methods, you must request this permission.
+<pre>
+&lt;uses-permission android:name="android.permission.WAKE_LOCK" /&gt;
+</pre>
+ </li>
+</ul>
+
+<h2 id="mediaplayer">Using MediaPlayer</h2>
+<p>One of the most important components of the media framework is the
+{@link android.media.MediaPlayer MediaPlayer}
+class. An object of this class can fetch, decode, and play both audio and video
+with minimal setup. It supports several different media sources such as:
+<ul>
+ <li>Local resources</li>
+ <li>Internal URIs, such as one you might obtain from a Content Resolver</li>
+ <li>External URLs (streaming)</li>
+</ul>
+</p>
+
+<p>For a list of media formats that Android supports,
+see the <a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media
+Formats</a> document. </p>
+
+<p>Here is an example
+of how to play audio that's available as a local raw resource (saved in your application's
+{@code res/raw/} directory):</p>
+
+<pre>MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
+mediaPlayer.start(); // no need to call prepare(); create() does that for you
+</pre>
+
+<p>In this case, a "raw" resource is a file that the system does not
+try to parse in any particular way. However, the content of this resource should not
+be raw audio. It should be a properly encoded and formatted media file in one
+of the supported formats.</p>
+
+<p>And here is how you might play from a URI available locally in the system
+(that you obtained through a Content Resolver, for instance):</p>
+
+<pre>Uri myUri = ....; // initialize Uri here
+MediaPlayer mediaPlayer = new MediaPlayer();
+mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+mediaPlayer.setDataSource(getApplicationContext(), myUri);
+mediaPlayer.prepare();
+mediaPlayer.start();</pre>
+
+<p>Playing from a remote URL via HTTP streaming looks like this:</p>
+
+<pre>String url = "http://........"; // your URL here
+MediaPlayer mediaPlayer = new MediaPlayer();
+mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+mediaPlayer.setDataSource(url);
+mediaPlayer.prepare(); // might take long! (for buffering, etc)
+mediaPlayer.start();</pre>
+
+<p class="note"><strong>Note:</strong>
+If you're passing a URL to stream an online media file, the file must be capable of
+progressive download.</p>
+
+<p class="caution"><strong>Caution:</strong> You must either catch or pass
+{@link java.lang.IllegalArgumentException} and {@link java.io.IOException} when using
+{@link android.media.MediaPlayer#setDataSource setDataSource()}, because
+the file you are referencing might not exist.</p>
+
+<h3 id='preparingasync'>Asynchronous Preparation</h3>
+
+<p>Using {@link android.media.MediaPlayer MediaPlayer} can be straightforward in
+principle. However, it's important to keep in mind that a few more things are
+necessary to integrate it correctly with a typical Android application. For
+example, the call to {@link android.media.MediaPlayer#prepare prepare()} can
+take a long time to execute, because
+it might involve fetching and decoding media data. So, as is the case with any
+method that may take long to execute, you should <strong>never call it from your
+application's UI thread</strong>. Doing that will cause the UI to hang until the method returns,
+which is a very bad user experience and can cause an ANR (Application Not Responding) error. Even if
+you expect your resource to load quickly, remember that anything that takes more than a tenth
+of a second to respond in the UI will cause a noticeable pause and will give
+the user the impression that your application is slow.</p>
+
+<p>To avoid hanging your UI thread, spawn another thread to
+prepare the {@link android.media.MediaPlayer} and notify the main thread when done. However, while
+you could write the threading logic
+yourself, this pattern is so common when using {@link android.media.MediaPlayer} that the framework
+supplies a convenient way to accomplish this task by using the
+{@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. This method
+starts preparing the media in the background and returns immediately. When the media
+is done preparing, the {@link android.media.MediaPlayer.OnPreparedListener#onPrepared onPrepared()}
+method of the {@link android.media.MediaPlayer.OnPreparedListener
+MediaPlayer.OnPreparedListener}, configured through
+{@link android.media.MediaPlayer#setOnPreparedListener setOnPreparedListener()} is called.</p>
+
+<h3 id='managestate'>Managing State</h3>
+
+<p>Another aspect of a {@link android.media.MediaPlayer} that you should keep in mind is
+that it's state-based. That is, the {@link android.media.MediaPlayer} has an internal state
+that you must always be aware of when writing your code, because certain operations
+are only valid when then player is in specific states. If you perform an operation while in the
+wrong state, the system may throw an exception or cause other undesireable behaviors.</p>
+
+<p>The documentation in the
+{@link android.media.MediaPlayer MediaPlayer} class shows a complete state diagram,
+that clarifies which methods move the {@link android.media.MediaPlayer} from one state to another.
+For example, when you create a new {@link android.media.MediaPlayer}, it is in the <em>Idle</em>
+state. At that point, you should initialize it by calling
+{@link android.media.MediaPlayer#setDataSource setDataSource()}, bringing it
+to the <em>Initialized</em> state. After that, you have to prepare it using either the
+{@link android.media.MediaPlayer#prepare prepare()} or
+{@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. When
+the {@link android.media.MediaPlayer} is done preparing, it will then enter the <em>Prepared</em>
+state, which means you can call {@link android.media.MediaPlayer#start start()}
+to make it play the media. At that point, as the diagram illustrates,
+you can move between the <em>Started</em>, <em>Paused</em> and <em>PlaybackCompleted</em> states by
+calling such methods as
+{@link android.media.MediaPlayer#start start()},
+{@link android.media.MediaPlayer#pause pause()}, and
+{@link android.media.MediaPlayer#seekTo seekTo()},
+amongst others. When you
+call {@link android.media.MediaPlayer#stop stop()}, however, notice that you
+cannot call {@link android.media.MediaPlayer#start start()} again until you
+prepare the {@link android.media.MediaPlayer} again.</p>
+
+<p>Always keep <a href='{@docRoot}images/mediaplayer_state_diagram.gif'>the state diagram</a>
+in mind when writing code that interacts with a
+{@link android.media.MediaPlayer} object, because calling its methods from the wrong state is a
+common cause of bugs.</p>
+
+<h3 id='releaseplayer'>Releasing the MediaPlayer</h3>
+
+<p>A {@link android.media.MediaPlayer MediaPlayer} can consume valuable
+system resources.
+Therefore, you should always take extra precautions to make sure you are not
+hanging on to a {@link android.media.MediaPlayer} instance longer than necessary. When you
+are done with it, you should always call
+{@link android.media.MediaPlayer#release release()} to make sure any
+system resources allocated to it are properly released. For example, if you are
+using a {@link android.media.MediaPlayer} and your activity receives a call to {@link
+android.app.Activity#onStop onStop()}, you must release the {@link android.media.MediaPlayer},
+because it
+makes little sense to hold on to it while your activity is not interacting with
+the user (unless you are playing media in the background, which is discussed in the next section).
+When your activity is resumed or restarted, of course, you need to
+create a new {@link android.media.MediaPlayer} and prepare it again before resuming playback.</p>
+
+<p>Here's how you should release and then nullify your {@link android.media.MediaPlayer}:</p>
+<pre>
+mediaPlayer.release();
+mediaPlayer = null;
+</pre>
+
+<p>As an example, consider the problems that could happen if you
+forgot to release the {@link android.media.MediaPlayer} when your activity is stopped, but create a
+new one when the activity starts again. As you may know, when the user changes the
+screen orientation (or changes the device configuration in another way),
+the system handles that by restarting the activity (by default), so you might quickly
+consume all of the system resources as the user
+rotates the device back and forth between portrait and landscape, because at each
+orientation change, you create a new {@link android.media.MediaPlayer} that you never
+release. (For more information about runtime restarts, see <a
+href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>.)</p>
+
+<p>You may be wondering what happens if you want to continue playing
+"background media" even when the user leaves your activity, much in the same
+way that the built-in Music application behaves. In this case, what you need is
+a {@link android.media.MediaPlayer MediaPlayer} controlled by a {@link android.app.Service}, as
+discussed in <a href="mpandservices">Using a Service with MediaPlayer</a>.</p>
+
+<h2 id="mpandservices">Using a Service with MediaPlayer</h2>
+
+<p>If you want your media to play in the background even when your application
+is not onscreen&mdash;that is, you want it to continue playing while the user is
+interacting with other applications&mdash;then you must start a
+{@link android.app.Service Service} and control the
+{@link android.media.MediaPlayer MediaPlayer} instance from there.
+You should be careful about this setup, because the user and the system have expectations
+about how an application running a background service should interact with the rest of the
+system. If your application does not fulfil those expectations, the user may
+have a bad experience. This section describes the main issues that you should be
+aware of and offers suggestions about how to approach them.</p>
+
+
+<h3 id="asyncprepare">Running asynchronously</h3>
+
+<p>First of all, like an {@link android.app.Activity Activity}, all work in a
+{@link android.app.Service Service} is done in a single thread by
+default&mdash;in fact, if you're running an activity and a service from the same application, they
+use the same thread (the "main thread") by default. Therefore, services need to
+process incoming intents quickly
+and never perform lengthy computations when responding to them. If any heavy
+work or blocking calls are expected, you must do those tasks asynchronously: either from
+another thread you implement yourself, or using the framework's many facilities
+for asynchronous processing.</p>
+
+<p>For instance, when using a {@link android.media.MediaPlayer} from your main thread,
+you should call {@link android.media.MediaPlayer#prepareAsync prepareAsync()} rather than
+{@link android.media.MediaPlayer#prepare prepare()}, and implement
+a {@link android.media.MediaPlayer.OnPreparedListener MediaPlayer.OnPreparedListener}
+in order to be notified when the preparation is complete and you can start playing.
+For example:</p>
+
+<pre>
+public class MyService extends Service implements MediaPlayer.OnPreparedListener {
+ private static final ACTION_PLAY = "com.example.action.PLAY";
+ MediaPlayer mMediaPlayer = null;
+
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ ...
+ if (intent.getAction().equals(ACTION_PLAY)) {
+ mMediaPlayer = ... // initialize it here
+ mMediaPlayer.setOnPreparedListener(this);
+ mMediaPlayer.prepareAsync(); // prepare async to not block main thread
+ }
+ }
+
+ /** Called when MediaPlayer is ready */
+ public void onPrepared(MediaPlayer player) {
+   player.start();
+ }
+}
+</pre>
+
+
+<h3 id="asyncerror">Handling asynchronous errors</h3>
+
+<p>On synchronous operations, errors would normally
+be signaled with an exception or an error code, but whenever you use asynchronous
+resources, you should make sure your application is notified
+of errors appropriately. In the case of a {@link android.media.MediaPlayer MediaPlayer},
+you can accomplish this by implementing a
+{@link android.media.MediaPlayer.OnErrorListener MediaPlayer.OnErrorListener} and
+setting it in your {@link android.media.MediaPlayer} instance:</p>
+
+<pre>
+public class MyService extends Service implements MediaPlayer.OnErrorListener {
+ MediaPlayer mMediaPlayer;
+
+ public void initMediaPlayer() {
+ // ...initialize the MediaPlayer here...
+
+ mMediaPlayer.setOnErrorListener(this);
+ }
+
+ &#64;Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ // ... react appropriately ...
+ // The MediaPlayer has moved to the Error state, must be reset!
+ }
+}
+</pre>
+
+<p>It's important to remember that when an error occurs, the {@link android.media.MediaPlayer}
+moves to the <em>Error</em> state (see the documentation for the
+{@link android.media.MediaPlayer MediaPlayer} class for the full state diagram)
+and you must reset it before you can use it again.
+
+
+<h3 id="wakelocks">Using wake locks</h3>
+
+<p>When designing applications that play media
+in the background, the device may go to sleep
+while your service is running. Because the Android system tries to conserve
+battery while the device is sleeping, the system tries to shut off any
+of the phone's features that are
+not necessary, including the CPU and the WiFi hardware.
+However, if your service is playing or streaming music, you want to prevent
+the system from interfering with your playback.</p>
+
+<p>In order to ensure that your service continues to run under
+those conditions, you have to use "wake locks." A wake lock is a way to signal to
+the system that your application is using some feature that should
+stay available even if the phone is idle.</p>
+
+<p class="caution"><strong>Notice:</strong> You should always use wake locks sparingly and hold them
+only for as long as truly necessary, because they significantly reduce the battery life of the
+device.</p>
+
+<p>To ensure that the CPU continues running while your {@link android.media.MediaPlayer} is
+playing, call the {@link android.media.MediaPlayer#setWakeMode
+setWakeMode()} method when initializing your {@link android.media.MediaPlayer}. Once you do,
+the {@link android.media.MediaPlayer} holds the specified lock while playing and releases the lock
+when paused or stopped:</p>
+
+<pre>
+mMediaPlayer = new MediaPlayer();
+// ... other initialization here ...
+mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+</pre>
+
+<p>However, the wake lock acquired in this example guarantees only that the CPU remains awake. If
+you are streaming media over the
+network and you are using Wi-Fi, you probably want to hold a
+{@link android.net.wifi.WifiManager.WifiLock WifiLock} as
+well, which you must acquire and release manually. So, when you start preparing the
+{@link android.media.MediaPlayer} with the remote URL, you should create and acquire the Wi-Fi lock.
+For example:</p>
+
+<pre>
+WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
+ .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
+
+wifiLock.acquire();
+</pre>
+
+<p>When you pause or stop your media, or when you no longer need the
+network, you should release the lock:</p>
+
+<pre>
+wifiLock.release();
+</pre>
+
+
+<h3 id="foregroundserv">Running as a foreground service</h3>
+
+<p>Services are often used for performing background tasks, such as fetching emails,
+synchronizing data, downloading content, amongst other possibilities. In these
+cases, the user is not actively aware of the service's execution, and probably
+wouldn't even notice if some of these services were interrupted and later restarted.</p>
+
+<p>But consider the case of a service that is playing music. Clearly this is a service that the user
+is actively aware of and the experience would be severely affected by any interruptions.
+Additionally, it's a service that the user will likely wish to interact with during its execution.
+In this case, the service should run as a "foreground service." A
+foreground service holds a higher level of importance within the system&mdash;the system will
+almost never kill the service, because it is of immediate importance to the user. When running
+in the foreground, the service also must provide a status bar notification to ensure that users are
+aware of the running service and allow them to open an activity that can interact with the
+service.</p>
+
+<p>In order to turn your service into a foreground service, you must create a
+{@link android.app.Notification Notification} for the status bar and call
+{@link android.app.Service#startForeground startForeground()} from the {@link
+android.app.Service}. For example:</p>
+
+<pre>String songName;
+// assign the song name to songName
+PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
+                new Intent(getApplicationContext(), MainActivity.class),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+Notification notification = new Notification();
+notification.tickerText = text;
+notification.icon = R.drawable.play0;
+notification.flags |= Notification.FLAG_ONGOING_EVENT;
+notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
+                "Playing: " + songName, pi);
+startForeground(NOTIFICATION_ID, notification);
+</pre>
+
+<p>While your service is running in the foreground, the notification you
+configured is visible in the notification area of the device. If the user
+selects the notification, the system invokes the {@link android.app.PendingIntent} you supplied. In
+the example above, it opens an activity ({@code MainActivity}).</p>
+
+<p>Figure 1 shows how your notification appears to the user:</p>
+
+<img src='images/notification1.png' />
+&nbsp;&nbsp;
+<img src='images/notification2.png' />
+<p class="img-caption"><strong>Figure 1.</strong> Screenshots of a foreground service's
+notification, showing the notification icon in the status bar (left) and the expanded view
+(right).</p>
+
+<p>You should only hold on to the "foreground service" status while your
+service is actually performing something the user is actively aware of. Once
+that is no longer true, you should release it by calling
+{@link android.app.Service#stopForeground stopForeground()}:</p>
+
+<pre>
+stopForeground(true);
+</pre>
+
+<p>For more information, see the documentation about <a
+href="{@docRoot}guide/topics/fundamentals/services.html#Foreground">Services</a> and
+<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>.</p>
+
+
+<h3 id="audiofocus">Handling audio focus</h3>
+
+<p>Even though only one activity can run at any given time, Android is a
+multi-tasking environment. This poses a particular challenge to applications
+that use audio, because there is only one audio output and there may be several
+media services competing for its use. Before Android 2.2, there was no built-in
+mechanism to address this issue, which could in some cases lead to a bad user
+experience. For example, when a user is listening to
+music and another application needs to notify the user of something very important,
+the user might not hear the notification tone due to the loud music. Starting with
+Android 2.2, the platform offers a way for applications to negotiate their
+use of the device's audio output. This mechanism is called Audio Focus.</p>
+
+<p>When your application needs to output audio such as music or a notification,
+you should always request audio focus. Once it has focus, it can use the sound output freely, but it
+should
+always listen for focus changes. If it is notified that it has lost the audio
+focus, it should immediately either kill the audio or lower it to a quiet level
+(known as "ducking"&mdash;there is a flag that indicates which one is appropriate) and only resume
+loud playback after it receives focus again.</p>
+
+<p>Audio Focus is cooperative in nature. That is, applications are expected
+(and highly encouraged) to comply with the audio focus guidelines, but the
+rules are not enforced by the system. If an application wants to play loud
+music even after losing audio focus, nothing in the system will prevent that.
+However, the user is more likely to have a bad experience and will be more
+likely to uninstall the misbehaving application.</p>
+
+<p>To request audio focus, you must call
+{@link android.media.AudioManager#requestAudioFocus requestAudioFocus()} from the {@link
+android.media.AudioManager}, as the example below demonstrates:</p>
+
+<pre>
+AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+
+if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ // could not get audio focus.
+}
+</pre>
+
+<p>The first parameter to {@link android.media.AudioManager#requestAudioFocus requestAudioFocus()}
+is an {@link android.media.AudioManager.OnAudioFocusChangeListener
+AudioManager.OnAudioFocusChangeListener},
+whose {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange
+onAudioFocusChange()} method is called whenever there is a change in audio focus. Therefore, you
+should also implement this interface on your service and activities. For example:</p>
+
+<pre>
+class MyService extends Service
+ implements AudioManager.OnAudioFocusChangeListener {
+ // ....
+ public void onAudioFocusChange(int focusChange) {
+ // Do something based on focus change...
+ }
+}
+</pre>
+
+<p>The <code>focusChange</code> parameter tells you how the audio focus has changed, and
+can be one of the following values (they are all constants defined in
+{@link android.media.AudioManager AudioManager}):</p>
+
+<ul>
+<li>{@link android.media.AudioManager#AUDIOFOCUS_GAIN}: You have gained the audio focus.</li>
+
+<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS}: You have lost the audio focus for a
+presumably long time.
+You must stop all audio playback. Because you should expect not to have focus back
+for a long time, this would be a good place to clean up your resources as much
+as possible. For example, you should release the {@link android.media.MediaPlayer}.</li>
+
+<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}: You have
+temporarily lost audio focus, but should receive it back shortly. You must stop
+all audio playback, but you can keep your resources because you will probably get
+focus back shortly.</li>
+
+<li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}: You have temporarily
+lost audio focus,
+but you are allowed to continue to play audio quietly (at a low volume) instead
+of killing audio completely.</li>
+</ul>
+
+<p>Here is an example implementation:</p>
+
+<pre>
+public void onAudioFocusChange(int focusChange) {
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_GAIN:
+ // resume playback
+ if (mMediaPlayer == null) initMediaPlayer();
+ else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
+ mMediaPlayer.setVolume(1.0f, 1.0f);
+ break;
+
+ case AudioManager.AUDIOFOCUS_LOSS:
+ // Lost focus for an unbounded amount of time: stop playback and release media player
+ if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ break;
+
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ // Lost focus for a short time, but we have to stop
+ // playback. We don't release the media player because playback
+ // is likely to resume
+ if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
+ break;
+
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+ // Lost focus for a short time, but it's ok to keep playing
+ // at an attenuated level
+ if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
+ break;
+ }
+}
+</pre>
+
+<p>Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2)
+and above, so if you want to support previous
+versions of Android, you should adopt a backward compatibility strategy that
+allows you to use this feature if available, and fall back seamlessly if not.</p>
+
+<p>You can achieve backward compatibility either by calling the audio focus methods by reflection
+or by implementing all the audio focus features in a separate class (say,
+<code>AudioFocusHelper</code>). Here is an example of such a class:</p>
+
+<pre>
+public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
+ AudioManager mAudioManager;
+
+ // other fields here, you'll probably hold a reference to an interface
+ // that you can use to communicate the focus changes to your Service
+
+ public AudioFocusHelper(Context ctx, /* other arguments here */) {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ // ...
+ }
+
+ public boolean requestFocus() {
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
+ mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ }
+
+ public boolean abandonFocus() {
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
+ mAudioManager.abandonAudioFocus(this);
+ }
+
+ &#64;Override
+ public void onAudioFocusChange(int focusChange) {
+ // let your service know about the focus change
+ }
+}
+</pre>
+
+
+<p>You can create an instance of <code>AudioFocusHelper</code> class only if you detect that
+the system is running API level 8 or above. For example:</p>
+
+<pre>
+if (android.os.Build.VERSION.SDK_INT &gt;= 8) {
+ mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
+} else {
+ mAudioFocusHelper = null;
+}
+</pre>
+
+
+<h3 id="cleanup">Performing cleanup</h3>
+
+<p>As mentioned earlier, a {@link android.media.MediaPlayer} object can consume a significant
+amount of system resources, so you should keep it only for as long as you need and call
+{@link android.media.MediaPlayer#release release()} when you are done with it. It's important
+to call this cleanup method explicitly rather than rely on system garbage collection because
+it might take some time before the garbage collector reclaims the {@link android.media.MediaPlayer},
+as it's only sensitive to memory needs and not to shortage of other media-related resources.
+So, in the case when you're using a service, you should always override the
+{@link android.app.Service#onDestroy onDestroy()} method to make sure you are releasing
+the {@link android.media.MediaPlayer}:</p>
+
+<pre>
+public class MyService extends Service {
+ MediaPlayer mMediaPlayer;
+ // ...
+
+ &#64;Override
+ public void onDestroy() {
+ if (mMediaPlayer != null) mMediaPlayer.release();
+ }
+}
+</pre>
+
+<p>You should always look for other opportunities to release your {@link android.media.MediaPlayer}
+as well, apart from releasing it when being shut down. For example, if you expect not
+to be able to play media for an extended period of time (after losing audio focus, for example),
+you should definitely release your existing {@link android.media.MediaPlayer} and create it again
+later. On the
+other hand, if you only expect to stop playback for a very short time, you should probably
+hold on to your {@link android.media.MediaPlayer} to avoid the overhead of creating and preparing it
+again.</p>
+
+
+
+<h2 id="noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</h2>
+
+<p>Many well-written applications that play audio automatically stop playback when an event
+occurs that causes the audio to become noisy (ouput through external speakers). For instance,
+this might happen when a user is listening to music through headphones and accidentally
+disconnects the headphones from the device. However, this behavior does not happen automatically.
+If you don't implement this feature, audio plays out of the device's external speakers, which
+might not be what the user wants.</p>
+
+<p>You can ensure your app stops playing music in these situations by handling
+the {@link android.media.AudioManager#ACTION_AUDIO_BECOMING_NOISY} intent, for which you can
+register a receiver by
+adding the following to your manifest:</p>
+
+<pre>
+&lt;receiver android:name=".MusicIntentReceiver"&gt;
+ &lt;intent-filter&gt;
+ &lt;action android:name="android.media.AUDIO_BECOMING_NOISY" /&gt;
+ &lt;/intent-filter&gt;
+&lt;/receiver&gt;
+</pre>
+
+<p>This registers the <code>MusicIntentReceiver</code> class as a broadcast receiver for that
+intent. You should then implement this class:</p>
+
+<pre>
+public class MusicIntentReceiver implements android.content.BroadcastReceiver {
+ &#64;Override
+ public void onReceive(Context ctx, Intent intent) {
+ if (intent.getAction().equals(
+ android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
+ // signal your service to stop playback
+ // (via an Intent, for instance)
+ }
+ }
+}
+</pre>
+
+
+
+
+<h2 id="viacontentresolver">Retrieving Media from a Content Resolver</h2>
+
+<p>Another feature that may be useful in a media player application is the ability to
+retrieve music that the user has on the device. You can do that by querying the {@link
+android.content.ContentResolver} for external media:</p>
+
+<pre>
+ContentResolver contentResolver = getContentResolver();
+Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+Cursor cursor = contentResolver.query(uri, null, null, null, null);
+if (cursor == null) {
+ // query failed, handle error.
+} else if (!cursor.moveToFirst()) {
+ // no media on the device
+} else {
+ int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
+ int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
+ do {
+ long thisId = cursor.getLong(idColumn);
+ String thisTitle = cursor.getString(titleColumn);
+ // ...process entry...
+ } while (cursor.moveToNext());
+}
+</pre>
+
+<p>To use this with the {@link android.media.MediaPlayer}, you can do this:</p>
+
+<pre>
+long id = /* retrieve it from somewhere */;
+Uri contentUri = ContentUris.withAppendedId(
+ android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
+
+mMediaPlayer = new MediaPlayer();
+mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+mMediaPlayer.setDataSource(getApplicationContext(), contentUri);
+
+// ...prepare and start...
+</pre> \ No newline at end of file
diff --git a/docs/html/guide/topics/resources/runtime-changes.jd b/docs/html/guide/topics/resources/runtime-changes.jd
index 74a9073..871b063 100644
--- a/docs/html/guide/topics/resources/runtime-changes.jd
+++ b/docs/html/guide/topics/resources/runtime-changes.jd
@@ -25,80 +25,78 @@ Orientation Change</a></li>
<p>Some device configurations can change during runtime
(such as screen orientation, keyboard availability, and language). When such a change occurs,
Android restarts the running
-Activity ({@link android.app.Activity#onDestroy()} is called, followed by {@link
+{@link android.app.Activity} ({@link android.app.Activity#onDestroy()} is called, followed by {@link
android.app.Activity#onCreate(Bundle) onCreate()}). The restart behavior is designed to help your
application adapt to new configurations by automatically reloading your application with
-alternative resources.</p>
+alternative resources that match the new device configuration.</p>
-<p>To properly handle a restart, it is important that your Activity restores its previous
+<p>To properly handle a restart, it is important that your activity restores its previous
state through the normal <a
href="{@docRoot}guide/topics/fundamentals/activities.html#Lifecycle">Activity
lifecycle</a>, in which Android calls
{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} before it destroys
-your Activity so that you can save data about the application state. You can then restore the state
+your activity so that you can save data about the application state. You can then restore the state
during {@link android.app.Activity#onCreate(Bundle) onCreate()} or {@link
-android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}. To test
-that your application restarts itself with the application state intact, you should
-invoke configuration changes (such as changing the screen orientation) while performing various
-tasks in your application.</p>
+android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.</p>
-<p>Your application should be able to restart at any time without loss of user data or
-state in order to handle events such as when the user receives an incoming phone call and then
-returns to your application (read about the
-<a href="{@docRoot}guide/topics/fundamentals/activities.html#Lifecycle">Activity lifecycle</a>).</p>
+<p>To test that your application restarts itself with the application state intact, you should
+invoke configuration changes (such as changing the screen orientation) while performing various
+tasks in your application. Your application should be able to restart at any time without loss of
+user data or state in order to handle events such as configuration changes or when the user receives
+an incoming phone call and then returns to your application much later after your application
+process may have been destroyed. To learn how you can restore your activity state, read about the <a
+href="{@docRoot}guide/topics/fundamentals/activities.html#Lifecycle">Activity lifecycle</a>.</p>
<p>However, you might encounter a situation in which restarting your application and
restoring significant amounts of data can be costly and create a poor user experience. In such a
-situation, you have two options:</p>
+situation, you have two other options:</p>
<ol type="a">
<li><a href="#RetainingAnObject">Retain an object during a configuration change</a>
- <p>Allow your Activity to restart when a configuration changes, but carry a stateful
-{@link java.lang.Object} to the new instance of your Activity.</p>
+ <p>Allow your activity to restart when a configuration changes, but carry a stateful
+{@link java.lang.Object} to the new instance of your activity.</p>
</li>
<li><a href="#HandlingTheChange">Handle the configuration change yourself</a>
- <p>Prevent the system from restarting your Activity during certain configuration
-changes and receive a callback when the configurations do change, so that you can manually update
-your Activity as necessary.</p>
+ <p>Prevent the system from restarting your activity during certain configuration
+changes, but receive a callback when the configurations do change, so that you can manually update
+your activity as necessary.</p>
</li>
</ol>
<h2 id="RetainingAnObject">Retaining an Object During a Configuration Change</h2>
-<p>If restarting your Activity requires that you recover large sets of data, re-establish a
-network connection, or perform other intensive operations, then a full restart due to a
-configuration change might
-be an unpleasant user experience. Also, it may not be possible for you to completely
-maintain your Activity state with the {@link android.os.Bundle} that the system saves for you during
-the Activity lifecycle&mdash;it is not designed to carry large objects (such as bitmaps) and the
-data within it must be serialized then deserialized, which can consume a lot of memory and make the
-configuration change slow. In such a situation, you can alleviate the burden of reinitializing
-your Activity by retaining a stateful Object when your Activity is restarted due to a configuration
-change.</p>
-
-<p>To retain an Object during a runtime configuration change:</p>
+<p>If restarting your activity requires that you recover large sets of data, re-establish a network
+connection, or perform other intensive operations, then a full restart due to a configuration change
+might be a slow user experience. Also, it might not be possible for you to completely restore your
+activity state with the {@link android.os.Bundle} that the system saves for you with the {@link
+android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} callback&mdash;it is not
+designed to carry large objects (such as bitmaps) and the data within it must be serialized then
+deserialized, which can consume a lot of memory and make the configuration change slow. In such a
+situation, you can alleviate the burden of reinitializing your activity by retaining a stateful
+{@link java.lang.Object} when your activity is restarted due to a configuration change.</p>
+
+<p>To retain an object during a runtime configuration change:</p>
<ol>
<li>Override the {@link android.app.Activity#onRetainNonConfigurationInstance()} method to return
-the Object you would like to retain.</li>
- <li>When your Activity is created again, call {@link
-android.app.Activity#getLastNonConfigurationInstance()} to recover your Object.</li>
+the object you would like to retain.</li>
+ <li>When your activity is created again, call {@link
+android.app.Activity#getLastNonConfigurationInstance()} to recover your object.</li>
</ol>
-<p>Android calls {@link android.app.Activity#onRetainNonConfigurationInstance()} between {@link
-android.app.Activity#onStop()} and {@link
-android.app.Activity#onDestroy()} when it shuts down your Activity due to a configuration
-change. In your implementation of {@link
-android.app.Activity#onRetainNonConfigurationInstance()}, you can return any {@link
-java.lang.Object} that you need in order to efficiently restore your state after the configuration
-change.</p>
+<p>When the Android system shuts down your activity due to a configuration change, it calls {@link
+android.app.Activity#onRetainNonConfigurationInstance()} between the {@link
+android.app.Activity#onStop()} and {@link android.app.Activity#onDestroy()} callbacks. In your
+implementation of {@link android.app.Activity#onRetainNonConfigurationInstance()}, you can return
+any {@link java.lang.Object} that you need in order to efficiently restore your state after the
+configuration change.</p>
<p>A scenario in which this can be valuable is if your application loads a lot of data from the
-web. If the user changes the orientation of the device and the Activity restarts, your application
+web. If the user changes the orientation of the device and the activity restarts, your application
must re-fetch the data, which could be slow. What you can do instead is implement
{@link android.app.Activity#onRetainNonConfigurationInstance()} to return an object carrying your
-data and then retrieve the data when your Activity starts again with {@link
+data and then retrieve the data when your activity starts again with {@link
android.app.Activity#getLastNonConfigurationInstance()}. For example:</p>
<pre>
@@ -113,11 +111,11 @@ public Object onRetainNonConfigurationInstance() {
should never pass an object that is tied to the {@link android.app.Activity}, such as a {@link
android.graphics.drawable.Drawable}, an {@link android.widget.Adapter}, a {@link android.view.View}
or any other object that's associated with a {@link android.content.Context}. If you do, it will
-leak all the Views and resources of the original Activity instance. (To leak the resources
+leak all the views and resources of the original activity instance. (Leaking resources
means that your application maintains a hold on them and they cannot be garbage-collected, so
lots of memory can be lost.)</p>
-<p>Then retrieve the {@code data} when your Activity starts again:</p>
+<p>Then retrieve the data when your activity starts again:</p>
<pre>
&#64;Override
@@ -133,11 +131,10 @@ public void onCreate(Bundle savedInstanceState) {
}
</pre>
-<p>In this case, {@link android.app.Activity#getLastNonConfigurationInstance()} retrieves
-the data saved by {@link android.app.Activity#onRetainNonConfigurationInstance()}. If {@code data}
-is null (which happens when the
-Activity starts due to any reason other than a configuration change) then the data object is loaded
-from the original source.</p>
+<p>In this case, {@link android.app.Activity#getLastNonConfigurationInstance()} returns the data
+saved by {@link android.app.Activity#onRetainNonConfigurationInstance()}. If {@code data} is null
+(which happens when the activity starts due to any reason other than a configuration change) then
+this code loads the data object from the original source.</p>
@@ -147,27 +144,27 @@ from the original source.</p>
<p>If your application doesn't need to update resources during a specific configuration
change <em>and</em> you have a performance limitation that requires you to
-avoid the Activity restart, then you can declare that your Activity handles the configuration change
-itself, which prevents the system from restarting your Activity.</p>
+avoid the activity restart, then you can declare that your activity handles the configuration change
+itself, which prevents the system from restarting your activity.</p>
<p class="note"><strong>Note:</strong> Handling the configuration change yourself can make it much
more difficult to use alternative resources, because the system does not automatically apply them
-for you. This technique should be considered a last resort and is not recommended for most
-applications.</p>
+for you. This technique should be considered a last resort when you must avoid restarts due to a
+configuration change and is not recommended for most applications.</p>
-<p>To declare that your Activity handles a configuration change, edit the appropriate <a
-href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> element
-in your manifest file to include the <a
+<p>To declare that your activity handles a configuration change, edit the appropriate <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> element in
+your manifest file to include the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
+android:configChanges}</a> attribute with a value that represents the configuration you want to
+handle. Possible values are listed in the documentation for the <a
href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
-android:configChanges}</a> attribute with a string value that represents the configuration that you
-want to handle. Possible values are listed in the documentation for
-the <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
-android:configChanges}</a> attribute (the most commonly used values are {@code orientation} to
-handle when the screen orientation changes and {@code keyboardHidden} to handle when the
-keyboard availability changes). You can declare multiple configuration values in the attribute
-by separating them with a pipe character ("|").</p>
-
-<p>For example, the following manifest snippet declares an Activity that handles both the
+android:configChanges}</a> attribute (the most commonly used values are {@code "orientation"} to
+prevent restarts when the screen orientation changes and {@code "keyboardHidden"} to prevent
+restarts when the keyboard availability changes). You can declare multiple configuration values in
+the attribute by separating them with a pipe {@code |} character.</p>
+
+<p>For example, the following manifest code declares an activity that handles both the
screen orientation change and keyboard availability change:</p>
<pre>
@@ -176,20 +173,32 @@ screen orientation change and keyboard availability change:</p>
android:label="@string/app_name">
</pre>
-<p>Now when one of these configurations change, {@code MyActivity} is not restarted.
-Instead, the Activity receives a call to {@link
+<p>Now, when one of these configurations change, {@code MyActivity} does not restart.
+Instead, the {@code MyActivity} receives a call to {@link
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. This method
is passed a {@link android.content.res.Configuration} object that specifies
the new device configuration. By reading fields in the {@link android.content.res.Configuration},
you can determine the new configuration and make appropriate changes by updating
the resources used in your interface. At the
-time this method is called, your Activity's {@link android.content.res.Resources} object is updated
+time this method is called, your activity's {@link android.content.res.Resources} object is updated
to return resources based on the new configuration, so you can easily
-reset elements of your UI without the system restarting your Activity.</p>
+reset elements of your UI without the system restarting your activity.</p>
+
+<p class="caution"><strong>Caution:</strong> Beginning with Android 3.2 (API level 13), <strong>the
+"screen size" also changes</strong> when the device switches between portrait and landscape
+orientation. Thus, if you want to prevent runtime restarts due to orientation change when developing
+for API level 13 or higher (as declared by the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> and <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+attributes), you must include the {@code "screenSize"} value in addition to the {@code
+"orientation"} value. That is, you must decalare {@code
+android:configChanges="orientation|screenSize"}. However, if your application targets API level
+12 or lower, then your activity always handles this configuration change itself (this configuration
+change does not restart your activity, even when running on an Android 3.2 or higher device).</p>
<p>For example, the following {@link
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} implementation
-checks the availability of a hardware keyboard and the current device orientation:</p>
+checks the current device orientation:</p>
<pre>
&#64;Override
@@ -202,12 +211,6 @@ public void onConfigurationChanged(Configuration newConfig) {
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
- // Checks whether a hardware keyboard is available
- if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
- Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
- } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
- Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
- }
}
</pre>
@@ -216,7 +219,8 @@ configurations, not just the ones that have changed. Most of the time, you won't
the configuration has changed and can simply re-assign all your resources that provide alternatives
to the configuration that you're handling. For example, because the {@link
android.content.res.Resources} object is now updated, you can reset
-any {@link android.widget.ImageView}s with {@link android.widget.ImageView#setImageResource(int)}
+any {@link android.widget.ImageView}s with {@link android.widget.ImageView#setImageResource(int)
+setImageResource()}
and the appropriate resource for the new configuration is used (as described in <a
href="providing-resources.html#AlternateResources">Providing Resources</a>).</p>
@@ -226,9 +230,9 @@ from the {@link android.content.res.Configuration} class. For documentation abou
to use with each field, refer to the appropriate field in the {@link
android.content.res.Configuration} reference.</p>
-<p class="note"><strong>Remember:</strong> When you declare your Activity to handle a configuration
+<p class="note"><strong>Remember:</strong> When you declare your activity to handle a configuration
change, you are responsible for resetting any elements for which you provide alternatives. If you
-declare your Activity to handle the orientation change and have images that should change
+declare your activity to handle the orientation change and have images that should change
between landscape and portrait, you must re-assign each resource to each element during {@link
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.</p>
@@ -236,13 +240,14 @@ android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChange
changes, you can instead <em>not</em> implement {@link
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. In
which case, all of the resources used before the configuration change are still used
-and you've only avoided the restart of your Activity. However, your application should always be
-able to shutdown and restart with its previous state intact. Not only because
-there are other configuration changes that you cannot prevent from restarting your application but
-also in order to handle events such as when the user receives an incoming phone call and then
-returns to your application.</p>
-
-<p>For more about which configuration changes you can handle in your Activity, see the <a
+and you've only avoided the restart of your activity. However, your application should always be
+able to shutdown and restart with its previous state intact, so you should not consider this
+technique an escape from retaining your state during normal activity lifecycle. Not only because
+there are other configuration changes that you cannot prevent from restarting your application, but
+also because you should handle events such as when the user leaves your application and it gets
+destroyed before the user returns to it.</p>
+
+<p>For more about which configuration changes you can handle in your activity, see the <a
href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
android:configChanges}</a> documentation and the {@link android.content.res.Configuration}
class.</p>
diff --git a/docs/html/sdk/android-3.2.jd b/docs/html/sdk/android-3.2.jd
index ea2b4ed..aeaf9c8 100644
--- a/docs/html/sdk/android-3.2.jd
+++ b/docs/html/sdk/android-3.2.jd
@@ -550,7 +550,11 @@ portrait orientation.</li>
<p>A typical application that functions properly in both landscape and portrait orientations would not normally need to declare an orientation requirement. Rather, an application designed primarily for one orientation, such as an app designed for a television, could declare one of the constants to ensure that it isn't available to devices that don't provide that orientation.</p>
-<p>If the application is targeting API level 12 or lower, the platform assumes that if app has not specified whether it requires portrait or landscape, both orientations are required.</p>
+<p>If any of activities declared in the manifest request that they run in a specific orientation,
+using the <a href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code
+android:screenOrientation}</a> attribute, then this also declares that the application
+requires that orientation.</p>
+
</li>
<li>Other feature constants
diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd
index 8f7ac55..619c907 100644
--- a/docs/html/sdk/android-4.0.jd
+++ b/docs/html/sdk/android-4.0.jd
@@ -71,7 +71,12 @@ class="toggle-content-img" alt="" />
<div class="toggle-content-toggleme" style="padding-left:2em;">
<dl>
-<dt>Initial release. SDK Tools r14 or higher is recommended.</dt>
+<dt>Initial release. SDK Tools r14 or higher is required.
+<p class="note"><strong>Important:</strong> To download the new Android
+4.0 system components from the Android SDK Manager, you must first update the
+SDK tools to revision 14 and restart the Android SDK Manager. If you do not,
+the Android 4.0 system components will not be available for download.</p>
+</dt>
</dl>
</div>
@@ -165,7 +170,7 @@ which the system will then process into the appropriate thumbnail and display ph
<p>The {@link android.provider.ContactsContract.Intents#INVITE_CONTACT} intent action allows you to
invoke an action that indicates the user wants to add a contact to a social network that understand
-this intent and use it to invite the contact specified in the contact to that social network.</p>
+this intent and use it to invite the contact specified in the contact to that social network.</p>
<p>Apps that use a sync adapter to provide information about contacts can register with the system
to
@@ -290,7 +295,7 @@ face</li>
located</li>
</ul>
-
+
<h4>Focus and Metering Areas</h4>
<p>Camera apps can now control the areas that the camera uses for focus and when metering white
@@ -334,7 +339,7 @@ these properties from changing.</li>
<h4>Camera Broadcast Intents</h4>
<ul>
-<li>{@link android.hardware.Camera#ACTION_NEW_PICTURE Camera.ACTION_NEW_PICTURE}
+<li>{@link android.hardware.Camera#ACTION_NEW_PICTURE Camera.ACTION_NEW_PICTURE}
This indicates that the user has captured a new photo. The built-in camera app invokes this
broadcast after a photo is captured and third-party camera apps should also broadcast this intent
after capturing a photo.</li>
@@ -344,10 +349,10 @@ broadcast after a video is recorded and third-party camera apps should also broa
after capturing a video.</li>
</ul>
-
-
-
+
+
+
<h3 id="Multimedia">Multimedia</h3>
<p>Android 4.0 adds several new APIs for applications that interact with media such as photos,
@@ -476,7 +481,7 @@ image (usually done by calling the {@link android.opengl.GLES20#glTexImage2D glT
function). You may provide multiple mipmap levels. If the output texture has not been bound to a
texture image, it will be automatically bound by the effect as a {@link
android.opengl.GLES20#GL_TEXTURE_2D}. It will contain one mipmap level (0), which will have the same
-size as the input.</p>
+size as the input.</p>
@@ -844,9 +849,9 @@ accessibility information about the view, and its child views.
onInitializeAccessibilityEvent()} - This method collects information about the view. If your
application has specific requirements for accessibility, you should extend this method to add that
information to the {@link android.view.accessibility.AccessibilityEvent}.</li>
-
+
<li>{@link
-
+
android.view.View#dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
dispatchPopulateAccessibilityEvent()} is called by the framework to request text information for
this view and its children. This method calls {@link
@@ -890,7 +895,7 @@ call
the {@link android.view.accessibility.AccessibilityEvent#getRecord(int)
AccessibilityEvent.getRecord()} to retrieve new accessibility information about the state of the
view.</li>
-<li>From the {@link android.view.accessibility.AccessibilityRecord}, call {@link
+<li>From the {@link android.view.accessibility.AccessibilityRecord}, call {@link
android.view.accessibility.AccessibilityRecord#getSource() getSource()} to retrieve a {@link
android.view.accessibility.AccessibilityNodeInfo} object.</li>
<li>With the {@link android.view.accessibility.AccessibilityNodeInfo}, call {@link
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 333efa2..dab5b29 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,8 +1,8 @@
page.title=ADT Plugin for Eclipse
adt.zip.version=14.0.0
adt.zip.download=ADT-14.0.0.zip
-adt.zip.bytes=6745584
-adt.zip.checksum=a645330d90fd9dae6187662bb1c3c644
+adt.zip.bytes=6745047
+adt.zip.checksum=014312e1553e3b8da55cb6a24e33e432
@jd:body
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 065f41b..67be5c9 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -2,20 +2,20 @@ page.title=Android SDK
sdk.redirect=0
sdk.win_installer=installer_r14-windows.exe
-sdk.win_installer_bytes=33860145
-sdk.win_installer_checksum=7a563491bf4671d09b9da0dcde85f212
+sdk.win_installer_bytes=33860326
+sdk.win_installer_checksum=6d4f76385daaee766ad901699cdae6cc
sdk.win_download=android-sdk_r14-windows.zip
-sdk.win_bytes=33852972
-sdk.win_checksum=d1381a0cc8e6f9358174aa6d051ba379
+sdk.win_bytes=33853090
+sdk.win_checksum=0c39628e296d6176ed928cc64498ba04
sdk.mac_download=android-sdk_r14-macosx.zip
-sdk.mac_bytes=30426052
-sdk.mac_checksum=df0a5c5b5327ffcaf256ce735998e12a
+sdk.mac_bytes=30426431
+sdk.mac_checksum=189ce3e26dfb46298a7def21d3bdf271
sdk.linux_download=android-sdk_r14-linux.tgz
-sdk.linux_bytes=26083315
-sdk.linux_checksum=2049d5c1a164fcae47a5e93c52200752
+sdk.linux_bytes=26082867
+sdk.linux_checksum=500483f8acd0d3cae94c68c3dcefbb98
@jd:body
diff --git a/docs/html/sdk/oem-usb.jd b/docs/html/sdk/oem-usb.jd
index ad3be4a..88bf008 100644
--- a/docs/html/sdk/oem-usb.jd
+++ b/docs/html/sdk/oem-usb.jd
@@ -55,6 +55,14 @@ href="http://drivers.cmcs.com.tw/">http://drivers.cmcs.com.tw/</a></td>
</tr>
<tr>
<td>
+ Fujitsu
+ </td>
+ <td><a
+href="http://www.fmworld.net/product/phone/sp/android/develop/">http://www.fmworld.net/product/phone/sp/android/develop/</a>
+ </td>
+ </tr>
+ <tr>
+ <td>
Fujitsu Toshiba
</td>
<td><a
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index 2d044ed..6cb246c 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -66,7 +66,12 @@ padding: .25em 1em;
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
width="9px" />SDK Tools, Revision 14</a> <em>(October 2011)</em>
+
<div class="toggleme">
+ <p class="note"><strong>Important:</strong> To download the new Android
+ 4.0 system components from the Android SDK Manager, you must first update the
+ SDK tools to revision 14 and restart the Android SDK Manager. If you do not,
+ the Android 4.0 system components will not be available for download.</p>
<dl>
<dt>Dependencies:</dt>
<dd>
@@ -81,6 +86,9 @@ padding: .25em 1em;
<dt>General notes:</dt>
<dd>
<ul>
+ <li>Added webcam support to Android 4.0 or later platforms to emulate rear-facing cameras when one webcam is present,
+ and to emulate both rear-facing and front-facing cameras when two webcams are present. Webcam suport is for Windows and Linux only.
+ Mac support will come in a later release.</li>
<li>Changed <code>default.properties</code> to <code>project.properties</code> and
<code>build.properties</code> to <code>ant.properties</code>. Any existing
projects that you build with Ant must be updated with the <code>android update project</code>
@@ -90,7 +98,6 @@ padding: .25em 1em;
commands, see the
<a href="{@docRoot}guide/developing/building/building-cmdline.html#AntReference">Ant Command
Reference</a>.</li>
-
<li>Changed how library projects are built.</a></li>
<li>Improved incremental builds, so that resource compilation runs less frequently. Builds no
longer run when you edit strings or layouts (unless you add a new <code>id</code>) and no longer
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index de0fabc..50964d5 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -285,7 +285,7 @@ public class GradientDrawable extends Drawable {
of the fill (if any) without worrying about blending artifacts.
*/
final boolean useLayer = haveStroke && haveFill && st.mShape != LINE &&
- currStrokeAlpha < 255;
+ currStrokeAlpha < 255 && (mAlpha < 255 || mColorFilter != null);
/* Drawing with a layer is slower than direct drawing, but it
allows us to apply paint effects like alpha and colorfilter to
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
index d227244..5d490ed 100644
--- a/include/binder/CursorWindow.h
+++ b/include/binder/CursorWindow.h
@@ -21,18 +21,8 @@
#include <stddef.h>
#include <stdint.h>
-#include <binder/IMemory.h>
-#include <utils/RefBase.h>
-
-#define DEFAULT_WINDOW_SIZE 4096
-#define WINDOW_ALLOCATION_SIZE 4096
-
-#define ROW_SLOT_CHUNK_NUM_ROWS 16
-
-// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
-// with an offset after the rows that points to the next chunk
-#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
-
+#include <binder/Parcel.h>
+#include <utils/String8.h>
#if LOG_NDEBUG
@@ -46,176 +36,157 @@
#endif
-
-// When defined to true strings are stored as UTF8, otherwise they're UTF16
-#define WINDOW_STORAGE_UTF8 1
-
-// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
-#define WINDOW_STORAGE_INLINE_NUMERICS 1
-
namespace android {
-typedef struct
-{
- uint32_t numRows;
- uint32_t numColumns;
-} window_header_t;
-
-typedef struct
-{
- uint32_t offset;
-} row_slot_t;
-
-typedef struct
-{
- uint8_t type;
- union {
- double d;
- int64_t l;
- struct {
- uint32_t offset;
- uint32_t size;
- } buffer;
- } data;
-} __attribute__((packed)) field_slot_t;
-
-#define FIELD_TYPE_NULL 0
-#define FIELD_TYPE_INTEGER 1
-#define FIELD_TYPE_FLOAT 2
-#define FIELD_TYPE_STRING 3
-#define FIELD_TYPE_BLOB 4
-
/**
* This class stores a set of rows from a database in a buffer. The begining of the
- * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
+ * window has first chunk of RowSlots, which are offsets to the row directory, followed by
+ * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
* the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * field_slot_t per column, which has the size, offset, and type of the data for that field.
+ * FieldSlot per column, which has the size, offset, and type of the data for that field.
* Note that the data types come from sqlite3.h.
+ *
+ * Strings are stored in UTF-8.
*/
-class CursorWindow
-{
+class CursorWindow {
+ CursorWindow(const String8& name, int ashmemFd,
+ void* data, size_t size, bool readOnly);
+
public:
- CursorWindow(size_t maxSize);
- CursorWindow(){}
- bool setMemory(const sp<IMemory>&);
- ~CursorWindow();
-
- bool initBuffer(bool localOnly);
- sp<IMemory> getMemory() {return mMemory;}
-
- size_t size() {return mSize;}
- uint8_t * data() {return mData;}
- uint32_t getNumRows() {return mHeader->numRows;}
- uint32_t getNumColumns() {return mHeader->numColumns;}
- void freeLastRow() {
- if (mHeader->numRows > 0) {
- mHeader->numRows--;
- }
- }
- bool setNumColumns(uint32_t numColumns)
- {
- uint32_t cur = mHeader->numColumns;
- if (cur > 0 && cur != numColumns) {
- LOGE("Trying to go from %d columns to %d", cur, numColumns);
- return false;
- }
- mHeader->numColumns = numColumns;
- return true;
- }
-
- int32_t freeSpace();
-
- void clear();
-
- /**
- * Allocate a row slot and its directory. The returned
- * pointer points to the begining of the row's directory
- * or NULL if there wasn't room. The directory is
- * initialied with NULL entries for each field.
- */
- field_slot_t * allocRow();
-
- /**
- * Allocate a portion of the window. Returns the offset
- * of the allocation, or 0 if there isn't enough space.
- * If aligned is true, the allocation gets 4 byte alignment.
- */
- uint32_t alloc(size_t size, bool aligned = false);
-
- /**
- * Copy data into the window at the given offset.
- */
- void copyIn(uint32_t offset, uint8_t const * data, size_t size);
- void copyIn(uint32_t offset, int64_t data);
- void copyIn(uint32_t offset, double data);
-
- void copyOut(uint32_t offset, uint8_t * data, size_t size);
- int64_t copyOutLong(uint32_t offset);
- double copyOutDouble(uint32_t offset);
-
- bool putLong(unsigned int row, unsigned int col, int64_t value);
- bool putDouble(unsigned int row, unsigned int col, double value);
- bool putNull(unsigned int row, unsigned int col);
-
- bool getLong(unsigned int row, unsigned int col, int64_t * valueOut);
- bool getDouble(unsigned int row, unsigned int col, double * valueOut);
- bool getNull(unsigned int row, unsigned int col, bool * valueOut);
-
- uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;}
-
- row_slot_t * allocRowSlot();
-
- row_slot_t * getRowSlot(int row);
-
- /**
- * return NULL if Failed to find rowSlot or
- * Invalid rowSlot
- */
- field_slot_t * getFieldSlotWithCheck(int row, int column);
- field_slot_t * getFieldSlot(int row, int column)
- {
- int fieldDirOffset = getRowSlot(row)->offset;
- return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
- }
-
- int64_t getFieldSlotValueLong(field_slot_t* fieldSlot) {
-#if WINDOW_STORAGE_INLINE_NUMERICS
+ /* Field types. */
+ enum {
+ FIELD_TYPE_NULL = 0,
+ FIELD_TYPE_INTEGER = 1,
+ FIELD_TYPE_FLOAT = 2,
+ FIELD_TYPE_STRING = 3,
+ FIELD_TYPE_BLOB = 4,
+ };
+
+ /* Opaque type that describes a field slot. */
+ struct FieldSlot {
+ private:
+ int32_t type;
+ union {
+ double d;
+ int64_t l;
+ struct {
+ uint32_t offset;
+ uint32_t size;
+ } buffer;
+ } data;
+
+ friend class CursorWindow;
+ } __attribute((packed));
+
+ ~CursorWindow();
+
+ static status_t create(const String8& name, size_t size, bool localOnly,
+ CursorWindow** outCursorWindow);
+ static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
+
+ status_t writeToParcel(Parcel* parcel);
+
+ inline String8 name() { return mName; }
+ inline size_t size() { return mSize; }
+ inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
+ inline uint32_t getNumRows() { return mHeader->numRows; }
+ inline uint32_t getNumColumns() { return mHeader->numColumns; }
+
+ status_t clear();
+ status_t setNumColumns(uint32_t numColumns);
+
+ /**
+ * Allocate a row slot and its directory.
+ * The row is initialized will null entries for each field.
+ */
+ status_t allocRow();
+ status_t freeLastRow();
+
+ status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
+ status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
+ status_t putLong(uint32_t row, uint32_t column, int64_t value);
+ status_t putDouble(uint32_t row, uint32_t column, double value);
+ status_t putNull(uint32_t row, uint32_t column);
+
+ /**
+ * Gets the field slot at the specified row and column.
+ * Returns null if the requested row or column is not in the window.
+ */
+ FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
+
+ inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
+ return fieldSlot->type;
+ }
+
+ inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
return fieldSlot->data.l;
-#else
- return copyOutLong(fieldSlot->data.buffer.offset);
-#endif
}
- double getFieldSlotValueDouble(field_slot_t* fieldSlot) {
-#if WINDOW_STORAGE_INLINE_NUMERICS
+ inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
return fieldSlot->data.d;
-#else
- return copyOutDouble(fieldSlot->data.buffer.offset);
-#endif
}
-#if WINDOW_STORAGE_UTF8
- char* getFieldSlotValueString(field_slot_t* fieldSlot) {
- return reinterpret_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
+ inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
+ size_t* outSizeIncludingNull) {
+ *outSizeIncludingNull = fieldSlot->data.buffer.size;
+ return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
}
-#else
- char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) {
- return reinterpret_cast<char16_t*>(offsetToPtr(fieldSlot->data.buffer.offset));
+
+ inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
+ *outSize = fieldSlot->data.buffer.size;
+ return offsetToPtr(fieldSlot->data.buffer.offset);
}
-#endif
private:
- uint8_t * mData;
+ static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
+
+ struct Header {
+ // Offset of the lowest unused byte in the window.
+ uint32_t freeOffset;
+
+ // Offset of the first row slot chunk.
+ uint32_t firstChunkOffset;
+
+ uint32_t numRows;
+ uint32_t numColumns;
+ };
+
+ struct RowSlot {
+ uint32_t offset;
+ };
+
+ struct RowSlotChunk {
+ RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
+ uint32_t nextChunkOffset;
+ };
+
+ String8 mName;
+ int mAshmemFd;
+ void* mData;
size_t mSize;
- size_t mMaxSize;
- window_header_t * mHeader;
- sp<IMemory> mMemory;
+ bool mReadOnly;
+ Header* mHeader;
+
+ inline void* offsetToPtr(uint32_t offset) {
+ return static_cast<uint8_t*>(mData) + offset;
+ }
+
+ inline uint32_t offsetFromPtr(void* ptr) {
+ return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
+ }
/**
- * Offset of the lowest unused data byte in the array.
+ * Allocate a portion of the window. Returns the offset
+ * of the allocation, or 0 if there isn't enough space.
+ * If aligned is true, the allocation gets 4 byte alignment.
*/
- uint32_t mFreeOffset;
+ uint32_t alloc(size_t size, bool aligned = false);
+
+ RowSlot* getRowSlot(uint32_t row);
+ RowSlot* allocRowSlot();
+
+ status_t putBlobOrString(uint32_t row, uint32_t column,
+ const void* value, size_t size, int32_t type);
};
}; // namespace android
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index a520a6a..ef4cf5c 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -283,7 +283,8 @@ public:
// outside the current field of view, even when using zoom.
//
// Focus area only has effect if the current focus mode is FOCUS_MODE_AUTO,
- // FOCUS_MODE_MACRO, or FOCUS_MODE_CONTINOUS_VIDEO.
+ // FOCUS_MODE_MACRO, FOCUS_MODE_CONTINUOUS_VIDEO, or
+ // FOCUS_MODE_CONTINUOUS_PICTURE.
// Example value: "(-10,-10,0,0,300),(0,0,10,10,700)". Read/write.
static const char KEY_FOCUS_AREAS[];
// Focal length in millimeter.
@@ -504,6 +505,25 @@ public:
// Example value: "true" or "false". Read only.
static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
+ // The state of the video stabilization. If set to true, both the
+ // preview stream and the recorded video stream are stabilized by
+ // the camera. Only valid to set if KEY_VIDEO_STABILIZATION_SUPPORTED is
+ // set to true.
+ //
+ // The value of this key can be changed any time the camera is
+ // open. If preview or recording is active, it is acceptable for
+ // there to be a slight video glitch when video stabilization is
+ // toggled on and off.
+ //
+ // This only stabilizes video streams (between-frames stabilization), and
+ // has no effect on still image capture.
+ static const char KEY_VIDEO_STABILIZATION[];
+
+ // Returns true if video stabilization is supported. That is, applications
+ // can set KEY_VIDEO_STABILIZATION to true and have a stabilized preview
+ // stream and record stabilized videos.
+ static const char KEY_VIDEO_STABILIZATION_SUPPORTED[];
+
// Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
static const char TRUE[];
static const char FALSE[];
@@ -610,19 +630,29 @@ public:
// recording because the focus changes smoothly . Applications still can
// call CameraHardwareInterface.takePicture in this mode but the subject may
// not be in focus. Auto focus starts when the parameter is set.
- // Applications should not call CameraHardwareInterface.autoFocus in this
- // mode. To stop continuous focus, applications should change the focus mode
- // to other modes.
+ //
+ // Applications can call CameraHardwareInterface.autoFocus in this mode. The
+ // focus callback will immediately return with a boolean that indicates
+ // whether the focus is sharp or not. The focus position is locked after
+ // autoFocus call. If applications want to resume the continuous focus,
+ // cancelAutoFocus must be called. Restarting the preview will not resume
+ // the continuous autofocus. To stop continuous focus, applications should
+ // change the focus mode to other modes.
static const char FOCUS_MODE_CONTINUOUS_VIDEO[];
// Continuous auto focus mode intended for taking pictures. The camera
// continuously tries to focus. The speed of focus change is more aggressive
// than FOCUS_MODE_CONTINUOUS_VIDEO. Auto focus starts when the parameter is
- // set. If applications call autoFocus in this mode, the focus callback will
- // immediately return with a boolean that indicates the focus is sharp or
- // not. The apps can then decide if they want to take a picture immediately
- // or to change the focus mode to auto, and run a full autofocus cycle. To
- // stop continuous focus, applications should change the focus mode to other
- // modes.
+ // set.
+ //
+ // If applications call CameraHardwareInterface.autoFocus in this mode, the
+ // focus callback will immediately return with a boolean that indicates
+ // whether the focus is sharp or not. The apps can then decide if they want
+ // to take a picture immediately or to change the focus mode to auto, and
+ // run a full autofocus cycle. The focus position is locked after autoFocus
+ // call. If applications want to resume the continuous focus,
+ // cancelAutoFocus must be called. Restarting the preview will not resume
+ // the continuous autofocus. To stop continuous focus, applications should
+ // change the focus mode to other modes.
static const char FOCUS_MODE_CONTINUOUS_PICTURE[];
private:
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index e0f4cf9..ea022a6 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -80,6 +80,7 @@ public:
eOrientation90 = 1,
eOrientation180 = 2,
eOrientation270 = 3,
+ eOrientationUnchanged = 4,
eOrientationSwapMask = 0x01
};
@@ -101,15 +102,8 @@ public:
virtual sp<IMemoryHeap> getCblk() const = 0;
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual void setTransactionState(const Vector<ComposerState>& state) = 0;
-
- /* [un]freeze display. requires ACCESS_SURFACE_FLINGER permission */
- virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) = 0;
- virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags) = 0;
-
- /* Set display orientation. requires ACCESS_SURFACE_FLINGER permission
- * No flags are currently defined. Set flags to 0. */
- virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags) = 0;
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ int orientation) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index ace0735..14e5b23 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -195,4 +195,3 @@ public:
}; // namespace android
#endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
-
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
index b02374f..1b85a71 100644
--- a/libs/binder/CursorWindow.cpp
+++ b/libs/binder/CursorWindow.cpp
@@ -19,8 +19,9 @@
#include <utils/Log.h>
#include <binder/CursorWindow.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
+
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
#include <assert.h>
#include <string.h>
@@ -28,350 +29,325 @@
namespace android {
-CursorWindow::CursorWindow(size_t maxSize) :
- mMaxSize(maxSize)
-{
+CursorWindow::CursorWindow(const String8& name, int ashmemFd,
+ void* data, size_t size, bool readOnly) :
+ mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
+ mHeader = static_cast<Header*>(mData);
}
-bool CursorWindow::setMemory(const sp<IMemory>& memory)
-{
- mMemory = memory;
- mData = (uint8_t *) memory->pointer();
- if (mData == NULL) {
- return false;
- }
- mHeader = (window_header_t *) mData;
-
- // Make the window read-only
- ssize_t size = memory->size();
- mSize = size;
- mMaxSize = size;
- mFreeOffset = size;
-LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
- return true;
+CursorWindow::~CursorWindow() {
+ ::munmap(mData, mSize);
+ ::close(mAshmemFd);
}
-bool CursorWindow::initBuffer(bool localOnly)
-{
- //TODO Use a non-memory dealer mmap region for localOnly
-
- sp<MemoryHeapBase> heap;
- heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
- if (heap != NULL) {
- mMemory = new MemoryBase(heap, 0, mMaxSize);
- if (mMemory != NULL) {
- mData = (uint8_t *) mMemory->pointer();
- if (mData) {
- mHeader = (window_header_t *) mData;
- mSize = mMaxSize;
-
- // Put the window into a clean state
- clear();
- LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
- return true;
+status_t CursorWindow::create(const String8& name, size_t size, bool localOnly,
+ CursorWindow** outCursorWindow) {
+ String8 ashmemName("CursorWindow: ");
+ ashmemName.append(name);
+ ashmemName.append(localOnly ? " (local)" : " (remote)");
+
+ status_t result;
+ int ashmemFd = ashmem_create_region(ashmemName.string(), size);
+ if (ashmemFd < 0) {
+ result = -errno;
+ } else {
+ result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
+ if (result >= 0) {
+ void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+ if (data == MAP_FAILED) {
+ result = -errno;
+ } else {
+ result = ashmem_set_prot_region(ashmemFd, PROT_READ);
+ if (result >= 0) {
+ CursorWindow* window = new CursorWindow(name, ashmemFd,
+ data, size, false /*readOnly*/);
+ result = window->clear();
+ if (!result) {
+ LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+ }
+ delete window;
+ }
}
- }
- LOGE("CursorWindow heap allocation failed");
- return false;
+ ::munmap(data, size);
+ }
+ ::close(ashmemFd);
+ }
+ *outCursorWindow = NULL;
+ return result;
+}
+
+status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
+ String8 name = parcel->readString8();
+
+ status_t result;
+ int ashmemFd = parcel->readFileDescriptor();
+ if (ashmemFd == int(BAD_TYPE)) {
+ result = BAD_TYPE;
} else {
- LOGE("failed to create the CursorWindow heap");
- return false;
+ ssize_t size = ashmem_get_size_region(ashmemFd);
+ if (size < 0) {
+ result = UNKNOWN_ERROR;
+ } else {
+ int dupAshmemFd = ::dup(ashmemFd);
+ if (dupAshmemFd < 0) {
+ result = -errno;
+ } else {
+ void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
+ if (data == MAP_FAILED) {
+ result = -errno;
+ } else {
+ CursorWindow* window = new CursorWindow(name, dupAshmemFd,
+ data, size, true /*readOnly*/);
+ LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
+ "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
+ window->mHeader->freeOffset,
+ window->mHeader->numRows,
+ window->mHeader->numColumns,
+ window->mSize, window->mData);
+ *outCursorWindow = window;
+ return OK;
+ }
+ ::close(dupAshmemFd);
+ }
+ }
}
+ *outCursorWindow = NULL;
+ return result;
}
-CursorWindow::~CursorWindow()
-{
- // Everything that matters is a smart pointer
+status_t CursorWindow::writeToParcel(Parcel* parcel) {
+ status_t status = parcel->writeString8(mName);
+ if (!status) {
+ status = parcel->writeDupFileDescriptor(mAshmemFd);
+ }
+ return status;
}
-void CursorWindow::clear()
-{
+status_t CursorWindow::clear() {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
+ }
+
+ mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
+ mHeader->firstChunkOffset = sizeof(Header);
mHeader->numRows = 0;
mHeader->numColumns = 0;
- mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
- // Mark the first chunk's next 'pointer' as null
- *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
+
+ RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
+ firstChunk->nextChunkOffset = 0;
+ return OK;
}
-int32_t CursorWindow::freeSpace()
-{
- int32_t freeSpace = mSize - mFreeOffset;
- if (freeSpace < 0) {
- freeSpace = 0;
+status_t CursorWindow::setNumColumns(uint32_t numColumns) {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
+ }
+
+ uint32_t cur = mHeader->numColumns;
+ if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
+ LOGE("Trying to go from %d columns to %d", cur, numColumns);
+ return INVALID_OPERATION;
}
- return freeSpace;
+ mHeader->numColumns = numColumns;
+ return OK;
}
-field_slot_t * CursorWindow::allocRow()
-{
+status_t CursorWindow::allocRow() {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
+ }
+
// Fill in the row slot
- row_slot_t * rowSlot = allocRowSlot();
+ RowSlot* rowSlot = allocRowSlot();
if (rowSlot == NULL) {
- return NULL;
+ return NO_MEMORY;
}
// Allocate the slots for the field directory
- size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
- uint32_t fieldDirOffset = alloc(fieldDirSize);
+ size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
+ uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
if (!fieldDirOffset) {
mHeader->numRows--;
- LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
- return NULL;
+ LOG_WINDOW("The row failed, so back out the new row accounting "
+ "from allocRowSlot %d", mHeader->numRows);
+ return NO_MEMORY;
}
- field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
- memset(fieldDir, 0x0, fieldDirSize);
+ FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
+ memset(fieldDir, 0, fieldDirSize);
-LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
+ LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
+ mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
rowSlot->offset = fieldDirOffset;
+ return OK;
+}
+
+status_t CursorWindow::freeLastRow() {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
+ }
- return fieldDir;
+ if (mHeader->numRows > 0) {
+ mHeader->numRows--;
+ }
+ return OK;
}
-uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
-{
- int32_t size;
+uint32_t CursorWindow::alloc(size_t size, bool aligned) {
uint32_t padding;
if (aligned) {
// 4 byte alignment
- padding = 4 - (mFreeOffset & 0x3);
+ padding = (~mHeader->freeOffset + 1) & 3;
} else {
padding = 0;
}
- size = requestedSize + padding;
-
- if (size > freeSpace()) {
- LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size,
- freeSpace(), mHeader->numRows);
- // Only grow the window if the first row doesn't fit
- if (mHeader->numRows > 1) {
- LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows,
- mMaxSize);
- return 0;
- }
-
- // Find a new size that will fit the allocation
- int allocated = mSize - freeSpace();
- int newSize = mSize + WINDOW_ALLOCATION_SIZE;
- while (size > (newSize - allocated)) {
- newSize += WINDOW_ALLOCATION_SIZE;
- if (newSize > mMaxSize) {
- LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
- return 0;
- }
- }
-LOG_WINDOW("found size %d", newSize);
- mSize = newSize;
+ uint32_t offset = mHeader->freeOffset + padding;
+ uint32_t nextFreeOffset = offset + size;
+ if (nextFreeOffset > mSize) {
+ LOGE("Window is full: requested allocation %d bytes, "
+ "free space %d bytes, window size %d bytes",
+ size, freeSpace(), mSize);
+ return 0;
}
- uint32_t offset = mFreeOffset + padding;
- mFreeOffset += size;
+ mHeader->freeOffset = nextFreeOffset;
return offset;
}
-row_slot_t * CursorWindow::getRowSlot(int row)
-{
- LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
- int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
- int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
- int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
- uint8_t * rowChunk = mData + sizeof(window_header_t);
- for (int i = 0; i < chunkNum; i++) {
- rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
- chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
+ uint32_t chunkPos = row;
+ RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
+ offsetToPtr(mHeader->firstChunkOffset));
+ while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
+ chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
+ chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
}
- return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
- LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);
+ return &chunk->slots[chunkPos];
}
-row_slot_t * CursorWindow::allocRowSlot()
-{
- int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
- int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
- int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
- uint8_t * rowChunk = mData + sizeof(window_header_t);
-LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
- for (int i = 0; i < chunkNum; i++) {
- uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
-LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
- if (nextChunkOffset == 0) {
- // Allocate a new row chunk
- nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
- if (nextChunkOffset == 0) {
+CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
+ uint32_t chunkPos = mHeader->numRows;
+ RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
+ offsetToPtr(mHeader->firstChunkOffset));
+ while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
+ chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
+ chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
+ }
+ if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
+ if (!chunk->nextChunkOffset) {
+ chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
+ if (!chunk->nextChunkOffset) {
return NULL;
}
- rowChunk = offsetToPtr(nextChunkOffset);
-LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
- *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
- // Mark the new chunk's next 'pointer' as null
- *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
- } else {
-LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
- rowChunk = offsetToPtr(nextChunkOffset);
- chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
}
+ chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
+ chunk->nextChunkOffset = 0;
+ chunkPos = 0;
}
- mHeader->numRows++;
-
- return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
-}
-
-field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
-{
- if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
- LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.",
- row, column, mHeader->numRows, mHeader->numColumns);
- return NULL;
- }
- row_slot_t * rowSlot = getRowSlot(row);
- if (!rowSlot) {
- LOGE("Failed to find rowSlot for row %d", row);
- return NULL;
- }
- if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
- LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
- return NULL;
- }
- int fieldDirOffset = rowSlot->offset;
- return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
+ mHeader->numRows += 1;
+ return &chunk->slots[chunkPos];
}
-void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
-{
- assert(offset + size <= mSize);
- memcpy(mData + offset, data, size);
-}
-
-void CursorWindow::copyIn(uint32_t offset, int64_t data)
-{
- assert(offset + sizeof(int64_t) <= mSize);
- memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
-}
-
-void CursorWindow::copyIn(uint32_t offset, double data)
-{
- assert(offset + sizeof(double) <= mSize);
- memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
+CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
+ if (row >= mHeader->numRows || column >= mHeader->numColumns) {
+ LOGE("Failed to read row %d, column %d from a CursorWindow which "
+ "has %d rows, %d columns.",
+ row, column, mHeader->numRows, mHeader->numColumns);
+ return NULL;
+ }
+ RowSlot* rowSlot = getRowSlot(row);
+ if (!rowSlot) {
+ LOGE("Failed to find rowSlot for row %d.", row);
+ return NULL;
+ }
+ FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
+ return &fieldDir[column];
}
-void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
-{
- assert(offset + size <= mSize);
- memcpy(data, mData + offset, size);
+status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
+ return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
}
-int64_t CursorWindow::copyOutLong(uint32_t offset)
-{
- int64_t value;
- assert(offset + sizeof(int64_t) <= mSize);
- memcpy(&value, mData + offset, sizeof(int64_t));
- return value;
+status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
+ size_t sizeIncludingNull) {
+ return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
}
-double CursorWindow::copyOutDouble(uint32_t offset)
-{
- double value;
- assert(offset + sizeof(double) <= mSize);
- memcpy(&value, mData + offset, sizeof(double));
- return value;
-}
+status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
+ const void* value, size_t size, int32_t type) {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
+ }
-bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ FieldSlot* fieldSlot = getFieldSlot(row, column);
if (!fieldSlot) {
- return false;
+ return BAD_VALUE;
}
-#if WINDOW_STORAGE_INLINE_NUMERICS
- fieldSlot->data.l = value;
-#else
- int offset = alloc(sizeof(int64_t));
+ uint32_t offset = alloc(size);
if (!offset) {
- return false;
+ return NO_MEMORY;
}
- copyIn(offset, value);
+ memcpy(offsetToPtr(offset), value, size);
+ fieldSlot->type = type;
fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = sizeof(int64_t);
-#endif
- fieldSlot->type = FIELD_TYPE_INTEGER;
- return true;
+ fieldSlot->data.buffer.size = size;
+ return OK;
}
-bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
- if (!fieldSlot) {
- return false;
+status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
}
-#if WINDOW_STORAGE_INLINE_NUMERICS
- fieldSlot->data.d = value;
-#else
- int offset = alloc(sizeof(int64_t));
- if (!offset) {
- return false;
+ FieldSlot* fieldSlot = getFieldSlot(row, column);
+ if (!fieldSlot) {
+ return BAD_VALUE;
}
- copyIn(offset, value);
-
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = sizeof(double);
-#endif
- fieldSlot->type = FIELD_TYPE_FLOAT;
- return true;
+ fieldSlot->type = FIELD_TYPE_INTEGER;
+ fieldSlot->data.l = value;
+ return OK;
}
-bool CursorWindow::putNull(unsigned int row, unsigned int col)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
- if (!fieldSlot) {
- return false;
+status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
}
- fieldSlot->type = FIELD_TYPE_NULL;
- fieldSlot->data.buffer.offset = 0;
- fieldSlot->data.buffer.size = 0;
- return true;
-}
-
-bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
- if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
- return false;
+ FieldSlot* fieldSlot = getFieldSlot(row, column);
+ if (!fieldSlot) {
+ return BAD_VALUE;
}
- *valueOut = getFieldSlotValueLong(fieldSlot);
- return true;
+ fieldSlot->type = FIELD_TYPE_FLOAT;
+ fieldSlot->data.d = value;
+ return OK;
}
-bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
- if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
- return false;
+status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
+ if (mReadOnly) {
+ return INVALID_OPERATION;
}
- *valueOut = getFieldSlotValueDouble(fieldSlot);
- return true;
-}
-
-bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
-{
- field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ FieldSlot* fieldSlot = getFieldSlot(row, column);
if (!fieldSlot) {
- return false;
- }
-
- if (fieldSlot->type != FIELD_TYPE_NULL) {
- *valueOut = false;
- } else {
- *valueOut = true;
+ return BAD_VALUE;
}
- return true;
+
+ fieldSlot->type = FIELD_TYPE_NULL;
+ fieldSlot->data.buffer.offset = 0;
+ fieldSlot->data.buffer.size = 0;
+ return OK;
}
}; // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 608877e..c7180ce 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -752,7 +752,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
if (result < 0) {
- status = -result;
+ status = result;
} else {
void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
@@ -760,7 +760,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
} else {
result = ashmem_set_prot_region(fd, PROT_READ);
if (result < 0) {
- status = -result;
+ status = result;
} else {
status = writeInt32(1);
if (!status) {
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 0dcab6b..c6087b4 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -88,6 +88,8 @@ const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected
const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
+const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
+const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
const char CameraParameters::TRUE[] = "true";
const char CameraParameters::FALSE[] = "false";
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 030a83e..eb90147 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -78,7 +78,8 @@ public:
return interface_cast<IMemoryHeap>(reply.readStrongBinder());
}
- virtual void setTransactionState(const Vector<ComposerState>& state)
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ int orientation)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -88,38 +89,8 @@ public:
for ( ; b != e ; ++b ) {
b->write(data);
}
- remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
- }
-
- virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeInt32(dpy);
- data.writeInt32(flags);
- remote()->transact(BnSurfaceComposer::FREEZE_DISPLAY, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeInt32(dpy);
- data.writeInt32(flags);
- remote()->transact(BnSurfaceComposer::UNFREEZE_DISPLAY, data, &reply);
- return reply.readInt32();
- }
-
- virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeInt32(dpy);
data.writeInt32(orientation);
- data.writeInt32(flags);
- remote()->transact(BnSurfaceComposer::SET_ORIENTATION, data, &reply);
- return reply.readInt32();
+ remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
virtual void bootFinished()
@@ -232,26 +203,8 @@ status_t BnSurfaceComposer::onTransact(
s.read(data);
state.add(s);
}
- setTransactionState(state);
- } break;
- case SET_ORIENTATION: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- DisplayID dpy = data.readInt32();
int orientation = data.readInt32();
- uint32_t flags = data.readInt32();
- reply->writeInt32( setOrientation(dpy, orientation, flags) );
- } break;
- case FREEZE_DISPLAY: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- DisplayID dpy = data.readInt32();
- uint32_t flags = data.readInt32();
- reply->writeInt32( freezeDisplay(dpy, flags) );
- } break;
- case UNFREEZE_DISPLAY: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- DisplayID dpy = data.readInt32();
- uint32_t flags = data.readInt32();
- reply->writeInt32( unfreezeDisplay(dpy, flags) );
+ setTransactionState(state, orientation);
} break;
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 00a4bf6..5f3d608 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -91,8 +91,10 @@ class Composer : public Singleton<Composer>
mutable Mutex mLock;
SortedVector<ComposerState> mStates;
+ int mOrientation;
- Composer() : Singleton<Composer>() { }
+ Composer() : Singleton<Composer>(),
+ mOrientation(ISurfaceComposer::eOrientationUnchanged) { }
void closeGlobalTransactionImpl();
@@ -119,6 +121,7 @@ public:
status_t setFreezeTint(
const sp<SurfaceComposerClient>& client, SurfaceID id,
uint32_t tint);
+ status_t setOrientation(int orientation);
static void closeGlobalTransaction() {
Composer::getInstance().closeGlobalTransactionImpl();
@@ -133,14 +136,18 @@ void Composer::closeGlobalTransactionImpl() {
sp<ISurfaceComposer> sm(getComposerService());
Vector<ComposerState> transaction;
+ int orientation;
{ // scope for the lock
Mutex::Autolock _l(mLock);
transaction = mStates;
mStates.clear();
+
+ orientation = mOrientation;
+ mOrientation = ISurfaceComposer::eOrientationUnchanged;
}
- sm->setTransactionState(transaction);
+ sm->setTransactionState(transaction, orientation);
}
layer_state_t* Composer::getLayerStateLocked(
@@ -260,6 +267,12 @@ status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client,
return NO_ERROR;
}
+status_t Composer::setOrientation(int orientation) {
+ Mutex::Autolock _l(mLock);
+ mOrientation = orientation;
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
SurfaceComposerClient::SurfaceComposerClient()
@@ -427,6 +440,12 @@ status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx,
return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
}
+status_t SurfaceComposerClient::setOrientation(DisplayID dpy,
+ int orientation, uint32_t flags)
+{
+ return Composer::getInstance().setOrientation(orientation);
+}
+
// ----------------------------------------------------------------------------
status_t SurfaceComposerClient::getDisplayInfo(
@@ -491,21 +510,14 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays()
status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
{
- sp<ISurfaceComposer> sm(getComposerService());
- return sm->freezeDisplay(dpy, flags);
+ // This has been made a no-op because it can cause Gralloc buffer deadlocks.
+ return NO_ERROR;
}
status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags)
{
- sp<ISurfaceComposer> sm(getComposerService());
- return sm->unfreezeDisplay(dpy, flags);
-}
-
-int SurfaceComposerClient::setOrientation(DisplayID dpy,
- int orientation, uint32_t flags)
-{
- sp<ISurfaceComposer> sm(getComposerService());
- return sm->setOrientation(dpy, orientation, flags);
+ // This has been made a no-op because it can cause Gralloc buffer deadlocks.
+ return NO_ERROR;
}
// ----------------------------------------------------------------------------
@@ -572,4 +584,3 @@ size_t ScreenshotClient::getSize() const {
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 53d4970..2d51208 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -319,8 +319,12 @@ void Context::destroyWorkerThreadResources() {
void Context::printWatchdogInfo(void *ctx) {
Context *rsc = (Context *)ctx;
- LOGE("RS watchdog timeout: %i %s line %i %s", rsc->watchdog.inRoot,
- rsc->watchdog.command, rsc->watchdog.line, rsc->watchdog.file);
+ if (rsc->watchdog.command && rsc->watchdog.file) {
+ LOGE("RS watchdog timeout: %i %s line %i %s", rsc->watchdog.inRoot,
+ rsc->watchdog.command, rsc->watchdog.line, rsc->watchdog.file);
+ } else {
+ LOGE("RS watchdog timeout: %i", rsc->watchdog.inRoot);
+ }
}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 198ae4c..77acfe6 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -35,14 +35,27 @@ import android.util.Log;
import java.lang.IllegalArgumentException;
/**
- * TODO javadoc update for ComponentName - PendingIntent change
* RemoteControlClient enables exposing information meant to be consumed by remote controls
* capable of displaying metadata, artwork and media transport control buttons.
- * A remote control client object is associated with a media button event receiver. This
+ *
+ * <p>A remote control client object is associated with a media button event receiver. This
* event receiver must have been previously registered with
* {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the
* RemoteControlClient can be registered through
* {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
+ *
+ * <p>Here is an example of creating a RemoteControlClient instance after registering a media
+ * button event receiver:
+ * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName());
+ * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver);
+ * // build the PendingIntent for the remote control client
+ * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ * mediaButtonIntent.setComponent(myEventReceiver);
+ * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
+ * // create and register the remote control client
+ * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
+ * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
*/
public class RemoteControlClient
{
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
index 2de82f2..c5cc2ca 100755
--- a/media/java/android/media/videoeditor/AudioTrack.java
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -147,14 +147,16 @@ public class AudioTrack {
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage() + " : " + filename);
}
- switch (mMANativeHelper.getFileType(properties.fileType)) {
+ int fileType = mMANativeHelper.getFileType(properties.fileType);
+ switch (fileType) {
case MediaProperties.FILE_3GP:
case MediaProperties.FILE_MP4:
case MediaProperties.FILE_MP3:
+ case MediaProperties.FILE_AMR:
break;
default: {
- throw new IllegalArgumentException("Unsupported input file type");
+ throw new IllegalArgumentException("Unsupported input file type: " + fileType);
}
}
switch (mMANativeHelper.getAudioCodecType(properties.audioFormat)) {
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index d7b8eaa..03ae62a 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -3239,6 +3239,9 @@ class MediaArtistNativeHelper {
case FileType.M4V:
retValue = MediaProperties.FILE_M4V;
break;
+ case FileType.AMR:
+ retValue = MediaProperties.FILE_AMR;
+ break;
default:
retValue = -1;
diff --git a/media/java/android/media/videoeditor/MediaProperties.java b/media/java/android/media/videoeditor/MediaProperties.java
index fd034ab..cf518a5 100755
--- a/media/java/android/media/videoeditor/MediaProperties.java
+++ b/media/java/android/media/videoeditor/MediaProperties.java
@@ -286,7 +286,7 @@ public class MediaProperties {
*/
public static final int FILE_3GP = 0;
public static final int FILE_MP4 = 1;
- // 2 is for AMRNB
+ public static final int FILE_AMR = 2;
public static final int FILE_MP3 = 3;
// 4 is for PCM
public static final int FILE_JPEG = 5;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index fa9417a..1165af5 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -2005,6 +2005,11 @@ status_t AwesomePlayer::finishSetDataSource_l() {
mConnectingDataSource->setUID(mUID);
}
+ String8 cacheConfig;
+ bool disconnectAtHighwatermark;
+ NuCachedSource2::RemoveCacheSpecificHeaders(
+ &mUriHeaders, &cacheConfig, &disconnectAtHighwatermark);
+
mLock.unlock();
status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
mLock.lock();
@@ -2024,7 +2029,10 @@ status_t AwesomePlayer::finishSetDataSource_l() {
new ThrottledSource(
mConnectingDataSource, 50 * 1024 /* bytes/sec */));
#else
- mCachedSource = new NuCachedSource2(mConnectingDataSource);
+ mCachedSource = new NuCachedSource2(
+ mConnectingDataSource,
+ cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
+ disconnectAtHighwatermark);
#endif
dataSource = mCachedSource;
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 9adb841..4f183f5 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -177,7 +177,10 @@ void PageCache::copy(size_t from, void *data, size_t size) {
////////////////////////////////////////////////////////////////////////////////
-NuCachedSource2::NuCachedSource2(const sp<DataSource> &source)
+NuCachedSource2::NuCachedSource2(
+ const sp<DataSource> &source,
+ const char *cacheConfig,
+ bool disconnectAtHighwatermark)
: mSource(source),
mReflector(new AHandlerReflector<NuCachedSource2>(this)),
mLooper(new ALooper),
@@ -190,9 +193,24 @@ NuCachedSource2::NuCachedSource2(const sp<DataSource> &source)
mNumRetriesLeft(kMaxNumRetries),
mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
mLowwaterThresholdBytes(kDefaultLowWaterThreshold),
- mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs) {
+ mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs),
+ mDisconnectAtHighwatermark(disconnectAtHighwatermark) {
+ // We are NOT going to support disconnect-at-highwatermark indefinitely
+ // and we are not guaranteeing support for client-specified cache
+ // parameters. Both of these are temporary measures to solve a specific
+ // problem that will be solved in a better way going forward.
+
updateCacheParamsFromSystemProperty();
+ if (cacheConfig != NULL) {
+ updateCacheParamsFromString(cacheConfig);
+ }
+
+ if (mDisconnectAtHighwatermark) {
+ // Makes no sense to disconnect and do keep-alives...
+ mKeepAliveIntervalUs = 0;
+ }
+
mLooper->setName("NuCachedSource2");
mLooper->registerHandler(mReflector);
mLooper->start();
@@ -339,6 +357,12 @@ void NuCachedSource2::onFetch() {
if (mFetching && mCache->totalSize() >= mHighwaterThresholdBytes) {
LOGI("Cache full, done prefetching for now");
mFetching = false;
+
+ if (mDisconnectAtHighwatermark
+ && (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
+ LOGV("Disconnecting at high watermark");
+ static_cast<HTTPBase *>(mSource.get())->disconnect();
+ }
}
} else {
Mutex::Autolock autoLock(mLock);
@@ -637,4 +661,34 @@ void NuCachedSource2::updateCacheParamsFromString(const char *s) {
mKeepAliveIntervalUs);
}
+// static
+void NuCachedSource2::RemoveCacheSpecificHeaders(
+ KeyedVector<String8, String8> *headers,
+ String8 *cacheConfig,
+ bool *disconnectAtHighwatermark) {
+ *cacheConfig = String8();
+ *disconnectAtHighwatermark = false;
+
+ if (headers == NULL) {
+ return;
+ }
+
+ ssize_t index;
+ if ((index = headers->indexOfKey(String8("x-cache-config"))) >= 0) {
+ *cacheConfig = headers->valueAt(index);
+
+ headers->removeItemsAt(index);
+
+ LOGV("Using special cache config '%s'", cacheConfig->string());
+ }
+
+ if ((index = headers->indexOfKey(
+ String8("x-disconnect-at-highwatermark"))) >= 0) {
+ *disconnectAtHighwatermark = true;
+ headers->removeItemsAt(index);
+
+ LOGV("Client requested disconnection at highwater mark");
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index f04c566..7a03e7e 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -28,7 +28,10 @@ struct ALooper;
struct PageCache;
struct NuCachedSource2 : public DataSource {
- NuCachedSource2(const sp<DataSource> &source);
+ NuCachedSource2(
+ const sp<DataSource> &source,
+ const char *cacheConfig = NULL,
+ bool disconnectAtHighwatermark = false);
virtual status_t initCheck() const;
@@ -56,6 +59,11 @@ struct NuCachedSource2 : public DataSource {
status_t getEstimatedBandwidthKbps(int32_t *kbps);
status_t setCacheStatCollectFreq(int32_t freqMs);
+ static void RemoveCacheSpecificHeaders(
+ KeyedVector<String8, String8> *headers,
+ String8 *cacheConfig,
+ bool *disconnectAtHighwatermark);
+
protected:
virtual ~NuCachedSource2();
@@ -105,6 +113,8 @@ private:
// If the keep-alive interval is 0, keep-alives are disabled.
int64_t mKeepAliveIntervalUs;
+ bool mDisconnectAtHighwatermark;
+
void onMessageReceived(const sp<AMessage> &msg);
void onFetch();
void onRead(const sp<AMessage> &msg);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index e13464e..72f1282 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -724,8 +724,10 @@ void ATSParser::Stream::onPayloadData(
const uint8_t *data, size_t size) {
LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
- CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
- int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
+ int64_t timeUs = 0ll; // no presentation timestamp available.
+ if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+ timeUs = mProgram->convertPTSToTimestamp(PTS);
+ }
status_t err = mQueue->appendData(data, size, timeUs);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f8a1d84..a56da36 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -444,6 +444,10 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) {
}
}
+ if (timeUs == 0ll) {
+ LOGV("Returning 0 timestamp");
+ }
+
return timeUs;
}
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index a9b539b..dc6c011 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -947,7 +947,12 @@ MtpResponseCode MtpServer::doSendObject() {
if (mSendObjectFileSize - initialData > 0) {
mfr.offset = initialData;
- mfr.length = mSendObjectFileSize - initialData;
+ if (mSendObjectFileSize == 0xFFFFFFFF) {
+ // tell driver to read until it receives a short packet
+ mfr.length = 0xFFFFFFFF;
+ } else {
+ mfr.length = mSendObjectFileSize - initialData;
+ }
LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
// transfer the file
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a2452c4..64c54d9 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -101,5 +101,21 @@
android:taskAffinity="com.android.systemui.net"
android:excludeFromRecents="true" />
+ <!-- started from ... somewhere -->
+ <activity
+ android:name=".Nyandroid"
+ android:exported="true"
+ android:label="Nyandroid"
+ android:icon="@drawable/nyandroid04"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:hardwareAccelerated="true"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DREAM" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index 3adcbec..4a1d37e 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
index d7a591c..9378fac 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png
index 277dcb8..6e84546 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_inout.png
index edc1760..c56905e 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png
index fbc6b99..11ffbde 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png
index fb938e8..2bb923e 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png
index 2d35517..783ad175 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png
index fe68c3c..e499f9d 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
index 49411bd..39e3df0 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
index 77924f0..b4920c3 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png
index 000e98b..31c0936 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png
index 62b940a..7e9b752 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png
index 5beb543..3209234 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png
index f70d315..95c56ed 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png
index be9953f..11b9a93 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png
index de20bdd..0f85ca0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid00.png b/packages/SystemUI/res/drawable-nodpi/nyandroid00.png
new file mode 100644
index 0000000..6cea873
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid00.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid01.png b/packages/SystemUI/res/drawable-nodpi/nyandroid01.png
new file mode 100644
index 0000000..82b8a21
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid01.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid02.png b/packages/SystemUI/res/drawable-nodpi/nyandroid02.png
new file mode 100644
index 0000000..fde0033
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid02.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid03.png b/packages/SystemUI/res/drawable-nodpi/nyandroid03.png
new file mode 100644
index 0000000..54c5f46
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid03.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid04.png b/packages/SystemUI/res/drawable-nodpi/nyandroid04.png
new file mode 100644
index 0000000..35e5ab5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid04.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid05.png b/packages/SystemUI/res/drawable-nodpi/nyandroid05.png
new file mode 100644
index 0000000..d3eaace
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid05.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid06.png b/packages/SystemUI/res/drawable-nodpi/nyandroid06.png
new file mode 100644
index 0000000..0e0d3b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid06.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid07.png b/packages/SystemUI/res/drawable-nodpi/nyandroid07.png
new file mode 100644
index 0000000..edb0b17
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid07.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid08.png b/packages/SystemUI/res/drawable-nodpi/nyandroid08.png
new file mode 100644
index 0000000..10fc4f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid08.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid09.png b/packages/SystemUI/res/drawable-nodpi/nyandroid09.png
new file mode 100644
index 0000000..57ade54
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid09.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid10.png b/packages/SystemUI/res/drawable-nodpi/nyandroid10.png
new file mode 100644
index 0000000..36feb2f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid11.png b/packages/SystemUI/res/drawable-nodpi/nyandroid11.png
new file mode 100644
index 0000000..125935b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/nyandroid11.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star0.png b/packages/SystemUI/res/drawable-nodpi/star0.png
new file mode 100644
index 0000000..f2ca960
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star1.png b/packages/SystemUI/res/drawable-nodpi/star1.png
new file mode 100644
index 0000000..69ef4da
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star2.png b/packages/SystemUI/res/drawable-nodpi/star2.png
new file mode 100644
index 0000000..b95968a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star3.png b/packages/SystemUI/res/drawable-nodpi/star3.png
new file mode 100644
index 0000000..ad0f589
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star4.png b/packages/SystemUI/res/drawable-nodpi/star4.png
new file mode 100644
index 0000000..934c45b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star5.png b/packages/SystemUI/res/drawable-nodpi/star5.png
new file mode 100644
index 0000000..46a4435
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/star5.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_in.png
index 8a3d90c..3d67766 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_inout.png
index 45dda51..b74e070 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_out.png
index 18e019c..24485e1 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_in.png
index cb8ed3a..390d500 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_inout.png
index ab4ad05..78998f9 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_out.png
index 956b6c1..c539615 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_in.png
index 9d95f17..5c38d45 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_inout.png
index e68d57d..6a79695 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_out.png
index 4ac361d..99dbe1b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_in.png
index 5e7ecdc..6a73a89 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_inout.png
index 462fad4..7042f2b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_out.png
index d284c02..3da781e 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_in.png
index 4a5e701..cf63e24 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_inout.png
index 9a08949..8f68e1f 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_out.png
index 314f422..894c63b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_in.png
index 4e0a48a..1ec5b49 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_inout.png
index 4eeae1d..9ca3ca8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_out.png
index 1a6f1ef..74241e0 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
index d853993..faeee29 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
index 2e6e3ac..f7e7102 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_in.png
index 2864ec3..cc9c49f 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_in.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_inout.png
index 0bb0c72..5a313c5 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_inout.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_out.png
index f23dd60..373a4a4 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_out.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_in.png
index b1c3168..d299daf 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_in.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_in.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_inout.png
index 5e41470..dcfdb7b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_inout.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_inout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_out.png
index 639842b..fb8125a 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_out.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_out.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/nyandroid_anim.xml b/packages/SystemUI/res/drawable/nyandroid_anim.xml
new file mode 100644
index 0000000..855a0c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/nyandroid_anim.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<animation-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item android:drawable="@drawable/nyandroid00" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid01" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid02" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid03" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid04" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid05" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid06" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid07" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid08" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid09" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid10" android:duration="80" />
+ <item android:drawable="@drawable/nyandroid11" android:duration="80" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/drawable/star_anim.xml b/packages/SystemUI/res/drawable/star_anim.xml
new file mode 100644
index 0000000..d7f2d8f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/star_anim.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<animation-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item android:drawable="@drawable/star0" android:duration="200" />
+ <item android:drawable="@drawable/star1" android:duration="200" />
+ <item android:drawable="@drawable/star2" android:duration="200" />
+ <item android:drawable="@drawable/star3" android:duration="200" />
+ <item android:drawable="@drawable/star4" android:duration="200" />
+ <item android:drawable="@drawable/star5" android:duration="200" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e971896..65d5138 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -305,6 +305,9 @@
<!-- Content description of the ringer silent icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ringer_silent">Ringer silent.</string>
+ <!-- Content description to tell the user an application has been removed from recents -->
+ <string name="accessibility_recents_item_dismissed"><xliff:g id="app" example="Calendar">%s</xliff:g> dismissed.</string>
+
<!-- Title of dialog shown when 2G-3G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
<string name="data_usage_disabled_dialog_3g_title">2G-3G data disabled</string>
<!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
diff --git a/packages/SystemUI/src/com/android/systemui/Nyandroid.java b/packages/SystemUI/src/com/android/systemui/Nyandroid.java
new file mode 100644
index 0000000..6f168ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/Nyandroid.java
@@ -0,0 +1,253 @@
+/*);
+ * Copyright (C) 2011 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.systemui;
+
+import android.animation.AnimatorSet;
+import android.animation.PropertyValuesHolder;
+import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import java.util.HashMap;
+import java.util.Random;
+
+public class Nyandroid extends Activity {
+ final static boolean DEBUG = false;
+
+ public static class Board extends FrameLayout
+ {
+ public static final boolean FIXED_STARS = true;
+ public static final int NUM_CATS = 20;
+
+ static Random sRNG = new Random();
+
+ static float lerp(float a, float b, float f) {
+ return (b-a)*f + a;
+ }
+
+ static float randfrange(float a, float b) {
+ return lerp(a, b, sRNG.nextFloat());
+ }
+
+ static int randsign() {
+ return sRNG.nextBoolean() ? 1 : -1;
+ }
+
+ static <E> E pick(E[] array) {
+ if (array.length == 0) return null;
+ return array[sRNG.nextInt(array.length)];
+ }
+
+ public class FlyingCat extends ImageView {
+ public static final float VMAX = 1000.0f;
+ public static final float VMIN = 100.0f;
+
+ public float v, vr;
+
+ public float dist;
+ public float z;
+
+ public ComponentName component;
+
+ public FlyingCat(Context context, AttributeSet as) {
+ super(context, as);
+ setImageResource(R.drawable.nyandroid_anim); // @@@
+
+ if (DEBUG) setBackgroundColor(0x80FF0000);
+ }
+
+ public String toString() {
+ return String.format("<cat (%.1f, %.1f) (%d x %d)>",
+ getX(), getY(), getWidth(), getHeight());
+ }
+
+ public void reset() {
+ final float scale = lerp(0.1f,2f,z);
+ setScaleX(scale); setScaleY(scale);
+
+ setX(-scale*getWidth()+1);
+ setY(randfrange(0, Board.this.getHeight()-scale*getHeight()));
+ v = lerp(VMIN, VMAX, z);
+
+ dist = 0;
+
+// android.util.Log.d("Nyandroid", "reset cat: " + this);
+ }
+
+ public void update(float dt) {
+ dist += v * dt;
+ setX(getX() + v * dt);
+ }
+ }
+
+ TimeAnimator mAnim;
+
+ public Board(Context context, AttributeSet as) {
+ super(context, as);
+
+ setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ setBackgroundColor(0xFF003366);
+ }
+
+ private void reset() {
+// android.util.Log.d("Nyandroid", "board reset");
+ removeAllViews();
+
+ final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ if (FIXED_STARS) {
+ for(int i=0; i<20; i++) {
+ ImageView fixedStar = new ImageView(getContext(), null);
+ if (DEBUG) fixedStar.setBackgroundColor(0x8000FF80);
+ fixedStar.setImageResource(R.drawable.star_anim); // @@@
+ addView(fixedStar, wrap);
+ final float scale = randfrange(0.1f, 1f);
+ fixedStar.setScaleX(scale); fixedStar.setScaleY(scale);
+ fixedStar.setX(randfrange(0, getWidth()));
+ fixedStar.setY(randfrange(0, getHeight()));
+ final AnimationDrawable anim = (AnimationDrawable) fixedStar.getDrawable();
+ postDelayed(new Runnable() {
+ public void run() {
+ anim.start();
+ }}, (int) randfrange(0, 1000));
+ }
+ }
+
+ for(int i=0; i<NUM_CATS; i++) {
+ FlyingCat nv = new FlyingCat(getContext(), null);
+ addView(nv, wrap);
+ nv.z = ((float)i/NUM_CATS);
+ nv.z *= nv.z;
+ nv.reset();
+ nv.setX(randfrange(0,Board.this.getWidth()));
+ final AnimationDrawable anim = (AnimationDrawable) nv.getDrawable();
+ postDelayed(new Runnable() {
+ public void run() {
+ anim.start();
+ }}, (int) randfrange(0, 1000));
+ }
+
+ if (mAnim != null) {
+ mAnim.cancel();
+ }
+ mAnim = new TimeAnimator();
+ mAnim.setTimeListener(new TimeAnimator.TimeListener() {
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ // setRotation(totalTime * 0.01f); // not as cool as you would think
+// android.util.Log.d("Nyandroid", "t=" + totalTime);
+
+ for (int i=0; i<getChildCount(); i++) {
+ View v = getChildAt(i);
+ if (!(v instanceof FlyingCat)) continue;
+ FlyingCat nv = (FlyingCat) v;
+ nv.update(deltaTime / 1000f);
+ final float catWidth = nv.getWidth() * nv.getScaleX();
+ final float catHeight = nv.getHeight() * nv.getScaleY();
+ if ( nv.getX() + catWidth < -2
+ || nv.getX() > getWidth() + 2
+ || nv.getY() + catHeight < -2
+ || nv.getY() > getHeight() + 2)
+ {
+ nv.reset();
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onSizeChanged (int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w,h,oldw,oldh);
+// android.util.Log.d("Nyandroid", "resized: " + w + "x" + h);
+ post(new Runnable() { public void run() {
+ reset();
+ mAnim.start();
+ } });
+ }
+
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mAnim.cancel();
+ }
+
+ @Override
+ public boolean isOpaque() {
+ return true;
+ }
+ }
+
+ private Board mBoard;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ );
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mBoard = new Board(this, null);
+ setContentView(mBoard);
+
+ mBoard.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+ @Override
+ public void onSystemUiVisibilityChange(int vis) {
+ if (0 == (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) {
+ Nyandroid.this.finish();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onUserInteraction() {
+// android.util.Log.d("Nyandroid", "finishing on user interaction");
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index fbf00d2..886a14d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -121,6 +121,7 @@ import android.view.View;
createAnimation(appearing);
mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mContentView.buildLayer();
mContentAnim.start();
mVisible = appearing;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
new file mode 100644
index 0000000..47aa849
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.tablet.TabletStatusBar;
+
+public class RecentTasksLoader {
+ static final String TAG = "RecentTasksLoader";
+ static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
+
+ private static final int DISPLAY_TASKS = 20;
+ private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
+
+ private Context mContext;
+ private RecentsPanelView mRecentsPanel;
+
+ private AsyncTask<Void, Integer, Void> mThumbnailLoader;
+ private final Handler mHandler;
+
+ private int mIconDpi;
+ private Bitmap mDefaultThumbnailBackground;
+
+ public RecentTasksLoader(Context context) {
+ mContext = context;
+
+ final Resources res = context.getResources();
+
+ // get the icon size we want -- on tablets, we use bigger icons
+ boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
+ int density = res.getDisplayMetrics().densityDpi;
+ if (isTablet) {
+ if (density == DisplayMetrics.DENSITY_LOW) {
+ mIconDpi = DisplayMetrics.DENSITY_MEDIUM;
+ } else if (density == DisplayMetrics.DENSITY_MEDIUM) {
+ mIconDpi = DisplayMetrics.DENSITY_HIGH;
+ } else if (density == DisplayMetrics.DENSITY_HIGH) {
+ mIconDpi = DisplayMetrics.DENSITY_XHIGH;
+ } else if (density == DisplayMetrics.DENSITY_XHIGH) {
+ // We'll need to use a denser icon, or some sort of a mipmap
+ mIconDpi = DisplayMetrics.DENSITY_XHIGH;
+ }
+ } else {
+ mIconDpi = res.getDisplayMetrics().densityDpi;
+ }
+ mIconDpi = isTablet ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
+
+ // Render the default thumbnail background
+ int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
+ int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
+ int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
+
+ mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(mDefaultThumbnailBackground);
+ c.drawColor(color);
+
+ // If we're using the cache, begin listening to the activity manager for
+ // updated thumbnails
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
+ mHandler = new Handler();
+ }
+
+ public void setRecentsPanel(RecentsPanelView recentsPanel) {
+ mRecentsPanel = recentsPanel;
+ }
+
+ // Create an TaskDescription, returning null if the title or icon is null, or if it's the
+ // home activity
+ TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
+ ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) {
+ Intent intent = new Intent(baseIntent);
+ if (origActivity != null) {
+ intent.setComponent(origActivity);
+ }
+ final PackageManager pm = mContext.getPackageManager();
+ if (homeInfo == null) {
+ homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+ .resolveActivityInfo(pm, 0);
+ }
+
+ intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+ if (resolveInfo != null) {
+ final ActivityInfo info = resolveInfo.activityInfo;
+ final String title = info.loadLabel(pm).toString();
+ Drawable icon = getFullResIcon(resolveInfo, pm);
+
+ if (title != null && title.length() > 0 && icon != null) {
+ if (DEBUG) Log.v(TAG, "creating activity desc for id="
+ + persistentTaskId + ", label=" + title);
+
+ TaskDescription item = new TaskDescription(taskId,
+ persistentTaskId, resolveInfo, baseIntent, info.packageName,
+ description);
+ item.setLabel(title);
+ item.setIcon(icon);
+
+ // Don't load the current home activity.
+ if (homeInfo != null
+ && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+ && homeInfo.name.equals(intent.getComponent().getClassName())) {
+ return null;
+ }
+
+ return item;
+ } else {
+ if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
+ }
+ }
+ return null;
+ }
+
+ void loadThumbnail(TaskDescription td) {
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
+
+ if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
+ + td + ": " + thumbs.mainThumbnail);
+ synchronized (td) {
+ if (thumbs != null && thumbs.mainThumbnail != null) {
+ td.setThumbnail(thumbs.mainThumbnail);
+ } else {
+ td.setThumbnail(mDefaultThumbnailBackground);
+ }
+ }
+ }
+
+ Drawable getFullResDefaultActivityIcon() {
+ return getFullResIcon(Resources.getSystem(),
+ com.android.internal.R.mipmap.sym_def_app_icon);
+ }
+
+ Drawable getFullResIcon(Resources resources, int iconId) {
+ try {
+ return resources.getDrawableForDensity(iconId, mIconDpi);
+ } catch (Resources.NotFoundException e) {
+ return getFullResDefaultActivityIcon();
+ }
+ }
+
+ private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
+ Resources resources;
+ try {
+ resources = packageManager.getResourcesForApplication(
+ info.activityInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ resources = null;
+ }
+ if (resources != null) {
+ int iconId = info.activityInfo.getIconResource();
+ if (iconId != 0) {
+ return getFullResIcon(resources, iconId);
+ }
+ }
+ return getFullResDefaultActivityIcon();
+ }
+
+ public void cancelLoadingThumbnails() {
+ if (mThumbnailLoader != null) {
+ mThumbnailLoader.cancel(false);
+ mThumbnailLoader = null;
+ }
+ }
+
+ // return a snapshot of the current list of recent apps
+ ArrayList<TaskDescription> getRecentTasks() {
+ cancelLoadingThumbnails();
+
+ ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
+ final PackageManager pm = mContext.getPackageManager();
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
+ final List<ActivityManager.RecentTaskInfo> recentTasks =
+ am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+ ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+ .resolveActivityInfo(pm, 0);
+
+ HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
+ int numTasks = recentTasks.size();
+
+ // skip the first task - assume it's either the home screen or the current activity.
+ final int first = 1;
+ recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
+ for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+ final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+
+ TaskDescription item = createTaskDescription(recentInfo.id,
+ recentInfo.persistentId, recentInfo.baseIntent,
+ recentInfo.origActivity, recentInfo.description, homeInfo);
+
+ if (item != null) {
+ tasks.add(item);
+ ++index;
+ }
+ }
+
+ // when we're not using the TaskDescription cache, we load the thumbnails in the
+ // background
+ loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
+ return tasks;
+ }
+
+ private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
+ if (descriptions.size() > 0) {
+ if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
+ loadThumbnail(descriptions.get(0));
+ if (descriptions.size() > 1) {
+ mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ final TaskDescription td = descriptions.get(values[0]);
+ if (!isCancelled()) {
+ mRecentsPanel.onTaskThumbnailLoaded(td);
+ }
+ // This is to prevent the loader thread from getting ahead
+ // of our UI updates.
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ synchronized (td) {
+ td.notifyAll();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ final int origPri = Process.getThreadPriority(Process.myTid());
+ Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ long nextTime = SystemClock.uptimeMillis();
+ for (int i=1; i<descriptions.size(); i++) {
+ TaskDescription td = descriptions.get(i);
+ loadThumbnail(td);
+ long now = SystemClock.uptimeMillis();
+ nextTime += 150;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (isCancelled()) {
+ break;
+ }
+ synchronized (td) {
+ publishProgress(i);
+ try {
+ td.wait(500);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ Process.setThreadPriority(origPri);
+ return null;
+ }
+ };
+ mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 58af255..f971d2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -32,14 +32,14 @@ import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
+import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
public class RecentsHorizontalScrollView extends HorizontalScrollView
implements SwipeHelper.Callback {
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
private LinearLayout mLinearLayout;
- private ActivityDescriptionAdapter mAdapter;
+ private TaskDescriptionAdapter mAdapter;
private RecentsCallback mCallback;
protected int mLastScrollPosition;
private SwipeHelper mSwipeHelper;
@@ -304,7 +304,7 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView
}
}
- public void setAdapter(ActivityDescriptionAdapter adapter) {
+ public void setAdapter(TaskDescriptionAdapter adapter) {
mAdapter = adapter;
mAdapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index e3f5cdb..8d5c33f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -17,32 +17,20 @@
package com.android.systemui.recent;
import java.util.ArrayList;
-import java.util.List;
import android.animation.Animator;
import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Process;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -50,6 +38,7 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
@@ -72,66 +61,24 @@ public class RecentsPanelView extends RelativeLayout
implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
- private static final int DISPLAY_TASKS = 20;
- private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
+ private Context mContext;
private StatusBar mBar;
- private ArrayList<ActivityDescription> mActivityDescriptions;
- private AsyncTask<Void, Integer, Void> mThumbnailLoader;
- private int mIconDpi;
private View mRecentsScrim;
private View mRecentsGlowView;
private View mRecentsNoApps;
private ViewGroup mRecentsContainer;
- private Bitmap mDefaultThumbnailBackground;
private boolean mShowing;
private Choreographer mChoreo;
private View mRecentsDismissButton;
- private ActivityDescriptionAdapter mListAdapter;
- private final Handler mHandler = new Handler();
-
- /* package */ final class ActivityDescription {
- final ActivityManager.RecentTaskInfo recentTaskInfo;
- final ResolveInfo resolveInfo;
- final int taskId; // application task id for curating apps
- final int persistentTaskId; // persistent id
- final Intent intent; // launch intent for application
- final String packageName; // used to override animations (see onClick())
- final int position; // position in list
-
- Matrix matrix; // arbitrary rotation matrix to correct orientation
-
- private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
- private Drawable mIcon; // application package icon
- private CharSequence mLabel; // application package label
-
- public ActivityDescription(ActivityManager.RecentTaskInfo _recentInfo,
- ResolveInfo _resolveInfo, Intent _intent,
- int _pos, String _packageName) {
- recentTaskInfo = _recentInfo;
- resolveInfo = _resolveInfo;
- intent = _intent;
- taskId = _recentInfo.id;
- persistentTaskId = _recentInfo.persistentId;
- position = _pos;
- packageName = _packageName;
- }
-
- public CharSequence getLabel() {
- return mLabel;
- }
-
- public Drawable getIcon() {
- return mIcon;
- }
- public void setThumbnail(Bitmap thumbnail) {
- mThumbnail = thumbnail;
- }
+ private RecentTasksLoader mRecentTasksLoader;
+ private ArrayList<TaskDescription> mRecentTaskDescriptions;
+ private TaskDescriptionAdapter mListAdapter;
+ private int mThumbnailWidth;
- public Bitmap getThumbnail() {
- return mThumbnail;
- }
+ public void setRecentTasksLoader(RecentTasksLoader loader) {
+ mRecentTasksLoader = loader;
}
private final class OnLongClickDelegate implements View.OnLongClickListener {
@@ -150,18 +97,18 @@ public class RecentsPanelView extends RelativeLayout
ImageView iconView;
TextView labelView;
TextView descriptionView;
- ActivityDescription activityDescription;
+ TaskDescription taskDescription;
}
- /* package */ final class ActivityDescriptionAdapter extends BaseAdapter {
+ /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
private LayoutInflater mInflater;
- public ActivityDescriptionAdapter(Context context) {
+ public TaskDescriptionAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
public int getCount() {
- return mActivityDescriptions != null ? mActivityDescriptions.size() : 0;
+ return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0;
}
public Object getItem(int position) {
@@ -189,18 +136,15 @@ public class RecentsPanelView extends RelativeLayout
holder = (ViewHolder) convertView.getTag();
}
- // activityId is reverse since most recent appears at the bottom...
- final int activityId = mActivityDescriptions.size() - position - 1;
+ // index is reverse since most recent appears at the bottom...
+ final int index = mRecentTaskDescriptions.size() - position - 1;
+
+ final TaskDescription taskDescription = mRecentTaskDescriptions.get(index);
+ applyTaskDescription(holder, taskDescription, false);
- final ActivityDescription activityDescription = mActivityDescriptions.get(activityId);
- holder.thumbnailViewImage.setImageBitmap(activityDescription.getThumbnail());
- holder.iconView.setImageDrawable(activityDescription.getIcon());
- holder.labelView.setText(activityDescription.getLabel());
- holder.descriptionView.setText(activityDescription.recentTaskInfo.description);
- holder.thumbnailView.setTag(activityDescription);
+ holder.thumbnailView.setTag(taskDescription);
holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
- holder.thumbnailView.setContentDescription(activityDescription.getLabel());
- holder.activityDescription = activityDescription;
+ holder.taskDescription = taskDescription;
return convertView;
}
@@ -226,14 +170,19 @@ public class RecentsPanelView extends RelativeLayout
}
public void show(boolean show, boolean animate) {
+ show(show, animate, null);
+ }
+
+ public void show(boolean show, boolean animate,
+ ArrayList<TaskDescription> recentTaskDescriptions) {
if (show) {
// Need to update list of recent apps before we set visibility so this view's
// content description is updated before it gets focus for TalkBack mode
- refreshApplicationList();
+ refreshRecentTasksList(recentTaskDescriptions);
// if there are no apps, either bring up a "No recent apps" message, or just
// quit early
- boolean noApps = (mActivityDescriptions.size() == 0);
+ boolean noApps = (mRecentTaskDescriptions.size() == 0);
if (mRecentsNoApps != null) { // doesn't exist on large devices
mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
} else {
@@ -242,6 +191,8 @@ public class RecentsPanelView extends RelativeLayout
return;
}
}
+ } else {
+ mRecentTasksLoader.cancelLoadingThumbnails();
}
if (animate) {
if (mShowing != show) {
@@ -255,6 +206,7 @@ public class RecentsPanelView extends RelativeLayout
mShowing = show;
setVisibility(show ? View.VISIBLE : View.GONE);
mChoreo.jumpTo(show);
+ onAnimationEnd(null);
}
if (show) {
setFocusable(true);
@@ -298,6 +250,9 @@ public class RecentsPanelView extends RelativeLayout
createCustomAnimations(transitioner);
} else {
((ViewGroup)mRecentsContainer).setLayoutTransition(null);
+ // Clear memory used by screenshots
+ mRecentTaskDescriptions.clear();
+ mListAdapter.notifyDataSetInvalidated();
}
}
@@ -349,21 +304,13 @@ public class RecentsPanelView extends RelativeLayout
public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mContext = context;
+ updateValuesFromResources();
+ }
- Resources res = context.getResources();
- boolean xlarge = (res.getConfiguration().screenLayout
- & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
-
- mIconDpi = xlarge ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
-
- int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
- int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
- int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
-
- // Render the default thumbnail background
- mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(mDefaultThumbnailBackground);
- c.drawColor(color);
+ public void updateValuesFromResources() {
+ mThumbnailWidth =
+ (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_thumbnail_width);
}
@Override
@@ -371,7 +318,7 @@ public class RecentsPanelView extends RelativeLayout
super.onFinishInflate();
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
- mListAdapter = new ActivityDescriptionAdapter(mContext);
+ mListAdapter = new TaskDescriptionAdapter(mContext);
if (mRecentsContainer instanceof RecentsHorizontalScrollView){
RecentsHorizontalScrollView scrollView
= (RecentsHorizontalScrollView) mRecentsContainer;
@@ -427,122 +374,50 @@ public class RecentsPanelView extends RelativeLayout
}
}
- Drawable getFullResDefaultActivityIcon() {
- return getFullResIcon(Resources.getSystem(),
- com.android.internal.R.mipmap.sym_def_app_icon);
- }
-
- Drawable getFullResIcon(Resources resources, int iconId) {
- try {
- return resources.getDrawableForDensity(iconId, mIconDpi);
- } catch (Resources.NotFoundException e) {
- return getFullResDefaultActivityIcon();
- }
- }
- private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
- Resources resources;
- try {
- resources = packageManager.getResourcesForApplication(
- info.activityInfo.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- resources = null;
- }
- if (resources != null) {
- int iconId = info.activityInfo.getIconResource();
- if (iconId != 0) {
- return getFullResIcon(resources, iconId);
- }
- }
- return getFullResDefaultActivityIcon();
- }
-
- private ArrayList<ActivityDescription> getRecentTasks() {
- ArrayList<ActivityDescription> activityDescriptions = new ArrayList<ActivityDescription>();
- final PackageManager pm = mContext.getPackageManager();
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
- final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
-
- ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm, 0);
-
- int numTasks = recentTasks.size();
-
- // skip the first activity - assume it's either the home screen or the current app.
- final int first = 1;
- for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
- final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
-
- Intent intent = new Intent(recentInfo.baseIntent);
- if (recentInfo.origActivity != null) {
- intent.setComponent(recentInfo.origActivity);
+ void applyTaskDescription(ViewHolder h, TaskDescription td, boolean anim) {
+ h.iconView.setImageDrawable(td.getIcon());
+ if (h.iconView.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.iconView.setAnimation(AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
-
- // Skip the current home activity.
- if (homeInfo != null
- && homeInfo.packageName.equals(intent.getComponent().getPackageName())
- && homeInfo.name.equals(intent.getComponent().getClassName())) {
- continue;
+ h.iconView.setVisibility(View.VISIBLE);
+ }
+ h.labelView.setText(td.getLabel());
+ h.thumbnailView.setContentDescription(td.getLabel());
+ if (h.labelView.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.labelView.setAnimation(AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
-
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
- if (resolveInfo != null) {
- final ActivityInfo info = resolveInfo.activityInfo;
- final String title = info.loadLabel(pm).toString();
- Drawable icon = getFullResIcon(resolveInfo, pm);
- if (title != null && title.length() > 0 && icon != null) {
- if (DEBUG) Log.v(TAG, "creating activity desc for id="
- + recentInfo.id + ", label=" + title);
- ActivityDescription item = new ActivityDescription(recentInfo,
- resolveInfo, intent, index, info.packageName);
- activityDescriptions.add(item);
- ++index;
- } else {
- if (DEBUG) Log.v(TAG, "SKIPPING item " + recentInfo.id);
+ h.labelView.setVisibility(View.VISIBLE);
+ }
+ Bitmap thumbnail = td.getThumbnail();
+ if (thumbnail != null) {
+ // Should remove the default image in the frame
+ // that this now covers, to improve scrolling speed.
+ // That can't be done until the anim is complete though.
+ h.thumbnailViewImage.setImageBitmap(thumbnail);
+ // scale to fill up the full width
+ Matrix scaleMatrix = new Matrix();
+ float scale = mThumbnailWidth / (float) thumbnail.getWidth();
+ scaleMatrix.setScale(scale, scale);
+ h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
+ h.thumbnailViewImage.setImageMatrix(scaleMatrix);
+ if (h.thumbnailViewImage.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.thumbnailViewImage.setAnimation(
+ AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
+ h.thumbnailViewImage.setVisibility(View.VISIBLE);
}
}
- return activityDescriptions;
+ //h.descriptionView.setText(ad.description);
}
- ActivityDescription findActivityDescription(int id)
- {
- ActivityDescription desc = null;
- for (int i = 0; i < mActivityDescriptions.size(); i++) {
- ActivityDescription item = mActivityDescriptions.get(i);
- if (item != null && item.taskId == id) {
- desc = item;
- break;
- }
- }
- return desc;
- }
-
- void loadActivityDescription(ActivityDescription ad, int index) {
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- final PackageManager pm = mContext.getPackageManager();
- ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(
- ad.recentTaskInfo.persistentId);
- CharSequence label = ad.resolveInfo.activityInfo.loadLabel(pm);
- Drawable icon = getFullResIcon(ad.resolveInfo, pm);
- if (DEBUG) Log.v(TAG, "Loaded bitmap for #" + index + " in "
- + ad + ": " + thumbs.mainThumbnail);
- synchronized (ad) {
- ad.mLabel = label;
- ad.mIcon = icon;
- if (thumbs != null && thumbs.mainThumbnail != null) {
- ad.setThumbnail(thumbs.mainThumbnail);
- }
- }
- }
-
- void applyActivityDescription(ActivityDescription ad, int index, boolean anim) {
+ void onTaskThumbnailLoaded(TaskDescription ad) {
synchronized (ad) {
if (mRecentsContainer != null) {
ViewGroup container = mRecentsContainer;
@@ -556,44 +431,8 @@ public class RecentsPanelView extends RelativeLayout
View v = container.getChildAt(i);
if (v.getTag() instanceof ViewHolder) {
ViewHolder h = (ViewHolder)v.getTag();
- if (h.activityDescription == ad) {
- if (DEBUG) Log.v(TAG, "Updating thumbnail #" + index + " in "
- + h.activityDescription
- + ": " + ad.getThumbnail());
- h.iconView.setImageDrawable(ad.getIcon());
- if (anim) {
- h.iconView.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.iconView.setVisibility(View.VISIBLE);
- h.labelView.setText(ad.getLabel());
- h.thumbnailView.setContentDescription(ad.getLabel());
- if (anim) {
- h.labelView.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.labelView.setVisibility(View.VISIBLE);
- Bitmap thumbnail = ad.getThumbnail();
- if (thumbnail != null) {
- // Should remove the default image in the frame
- // that this now covers, to improve scrolling speed.
- // That can't be done until the anim is complete though.
- h.thumbnailViewImage.setImageBitmap(thumbnail);
-
- // scale to fill up the full width
- Matrix scaleMatrix = new Matrix();
- float thumbnailViewWidth = h.thumbnailViewImage.getWidth();
- float scale = thumbnailViewWidth / thumbnail.getWidth();
- scaleMatrix.setScale(scale, scale);
- h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
- h.thumbnailViewImage.setImageMatrix(scaleMatrix);
-
- if (anim) {
- h.thumbnailViewImage.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.thumbnailViewImage.setVisibility(View.VISIBLE);
- }
+ if (h.taskDescription == ad) {
+ applyTaskDescription(h, ad, true);
}
}
}
@@ -601,14 +440,28 @@ public class RecentsPanelView extends RelativeLayout
}
}
- private void refreshApplicationList() {
- if (mThumbnailLoader != null) {
- mThumbnailLoader.cancel(false);
- mThumbnailLoader = null;
+ private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+ if (recentTasksList != null) {
+ mRecentTaskDescriptions = recentTasksList;
+ } else {
+ mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
}
+ mListAdapter.notifyDataSetInvalidated();
+ updateUiElements(getResources().getConfiguration());
+ }
+
+ public ArrayList<TaskDescription> getRecentTasksList() {
+ return mRecentTaskDescriptions;
+ }
+
+ private void updateUiElements(Configuration config) {
+ final int items = mRecentTaskDescriptions.size();
+
+ mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
+ mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
- mActivityDescriptions = getRecentTasks();
- int numRecentApps = mActivityDescriptions.size();
+ // Set description for accessibility
+ int numRecentApps = mRecentTaskDescriptions.size();
String recentAppsAccessibilityDescription;
if (numRecentApps == 0) {
recentAppsAccessibilityDescription =
@@ -618,81 +471,10 @@ public class RecentsPanelView extends RelativeLayout
R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps);
}
setContentDescription(recentAppsAccessibilityDescription);
- for (ActivityDescription ad : mActivityDescriptions) {
- ad.setThumbnail(mDefaultThumbnailBackground);
- }
- mListAdapter.notifyDataSetInvalidated();
- if (mActivityDescriptions.size() > 0) {
- if (DEBUG) Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps");
- updateUiElements(getResources().getConfiguration());
- final ArrayList<ActivityDescription> descriptions =
- new ArrayList<ActivityDescription>(mActivityDescriptions);
- loadActivityDescription(descriptions.get(0), 0);
- applyActivityDescription(descriptions.get(0), 0, false);
- if (descriptions.size() > 1) {
- mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
- @Override
- protected void onProgressUpdate(Integer... values) {
- final ActivityDescription ad = descriptions.get(values[0]);
- if (!isCancelled()) {
- applyActivityDescription(ad, values[0], true);
- }
- // This is to prevent the loader thread from getting ahead
- // of our UI updates.
- mHandler.post(new Runnable() {
- @Override public void run() {
- synchronized (ad) {
- ad.notifyAll();
- }
- }
- });
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- final int origPri = Process.getThreadPriority(Process.myTid());
- Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
- long nextTime = SystemClock.uptimeMillis();
- for (int i=1; i<descriptions.size(); i++) {
- ActivityDescription ad = descriptions.get(i);
- loadActivityDescription(ad, i);
- long now = SystemClock.uptimeMillis();
- nextTime += 150;
- if (nextTime > now) {
- try {
- Thread.sleep(nextTime-now);
- } catch (InterruptedException e) {
- }
- }
- if (isCancelled()) {
- break;
- }
- synchronized (ad) {
- publishProgress(i);
- try {
- ad.wait(500);
- } catch (InterruptedException e) {
- }
- }
- }
- Process.setThreadPriority(origPri);
- return null;
- }
- };
- mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
- }
-
- private void updateUiElements(Configuration config) {
- final int items = mActivityDescriptions.size();
-
- mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
- mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
}
public void handleOnClick(View view) {
- ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
@@ -702,7 +484,8 @@ public class RecentsPanelView extends RelativeLayout
} else {
Intent intent = ad.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
context.startActivity(intent);
}
@@ -714,14 +497,14 @@ public class RecentsPanelView extends RelativeLayout
}
public void handleSwipe(View view) {
- ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel());
- mActivityDescriptions.remove(ad);
+ mRecentTaskDescriptions.remove(ad);
// Handled by widget containers to enable LayoutTransitions properly
// mListAdapter.notifyDataSetChanged();
- if (mActivityDescriptions.size() == 0) {
+ if (mRecentTaskDescriptions.size() == 0) {
hide(false);
}
@@ -730,6 +513,12 @@ public class RecentsPanelView extends RelativeLayout
final ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+
+ // Accessibility feedback
+ setContentDescription(
+ mContext.getString(R.string.accessibility_recents_item_dismissed, ad.getLabel()));
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ setContentDescription(null);
}
private void startApplicationDetailsActivity(String packageName) {
@@ -751,7 +540,7 @@ public class RecentsPanelView extends RelativeLayout
} else if (item.getItemId() == R.id.recent_inspect_item) {
ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
if (viewHolder != null) {
- final ActivityDescription ad = viewHolder.activityDescription;
+ final TaskDescription ad = viewHolder.taskDescription;
startApplicationDetailsActivity(ad.packageName);
mBar.animateCollapse();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 7853402..dc13092 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -31,13 +31,13 @@ import android.widget.ScrollView;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
+import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
private LinearLayout mLinearLayout;
- private ActivityDescriptionAdapter mAdapter;
+ private TaskDescriptionAdapter mAdapter;
private RecentsCallback mCallback;
protected int mLastScrollPosition;
private SwipeHelper mSwipeHelper;
@@ -319,7 +319,7 @@ public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper
}
}
- public void setAdapter(ActivityDescriptionAdapter adapter) {
+ public void setAdapter(TaskDescriptionAdapter adapter) {
mAdapter = adapter;
mAdapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
new file mode 100644
index 0000000..dcfd6d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+public final class TaskDescription {
+ final ResolveInfo resolveInfo;
+ final int taskId; // application task id for curating apps
+ final int persistentTaskId; // persistent id
+ final Intent intent; // launch intent for application
+ final String packageName; // used to override animations (see onClick())
+ final CharSequence description;
+
+ private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
+ private Drawable mIcon; // application package icon
+ private CharSequence mLabel; // application package label
+
+ public TaskDescription(int _taskId, int _persistentTaskId,
+ ResolveInfo _resolveInfo, Intent _intent,
+ String _packageName, CharSequence _description) {
+ resolveInfo = _resolveInfo;
+ intent = _intent;
+ taskId = _taskId;
+ persistentTaskId = _persistentTaskId;
+
+ description = _description;
+ packageName = _packageName;
+ }
+
+ // mark all these as locked?
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(CharSequence label) {
+ mLabel = label;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ public void setThumbnail(Bitmap thumbnail) {
+ mThumbnail = thumbnail;
+ }
+
+ public Bitmap getThumbnail() {
+ return mThumbnail;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index e3a64a8..694da20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -116,15 +116,13 @@ public class NavigationBarView extends LinearLayout {
mDisabledFlags = disabledFlags;
- final boolean disableNavigation = ((disabledFlags & View.STATUS_BAR_DISABLE_NAVIGATION) != 0);
+ final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
+ final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0);
getBackButton() .setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
- getHomeButton() .setVisibility(disableNavigation ? View.INVISIBLE : View.VISIBLE);
- getRecentsButton().setVisibility(disableNavigation ? View.INVISIBLE : View.VISIBLE);
-
- getMenuButton() .setVisibility((disableNavigation || !mShowMenu)
- ? View.INVISIBLE : View.VISIBLE);
+ getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
+ getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
}
public void setMenuVisibility(final boolean show) {
@@ -136,9 +134,7 @@ public class NavigationBarView extends LinearLayout {
mShowMenu = show;
- getMenuButton().setVisibility(
- (0 != (mDisabledFlags & View.STATUS_BAR_DISABLE_NAVIGATION) || !mShowMenu)
- ? View.INVISIBLE : View.VISIBLE);
+ getMenuButton().setVisibility(mShowMenu ? View.VISIBLE : View.INVISIBLE);
}
public void setLowProfile(final boolean lightsOut) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 09ea6ad..9c8c229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,7 +74,9 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
+import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsPanelView;
+import com.android.systemui.recent.TaskDescription;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBar;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -193,6 +195,7 @@ public class PhoneStatusBar extends StatusBar {
// Recent apps
private RecentsPanelView mRecentsPanel;
+ private RecentTasksLoader mRecentTasksLoader;
// Tracking finger for opening/closing.
int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
@@ -297,18 +300,6 @@ public class PhoneStatusBar extends StatusBar {
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
mNavigationBarView.setDisabledFlags(mDisabled);
-
- sb.setOnSystemUiVisibilityChangeListener(
- new View.OnSystemUiVisibilityChangeListener() {
- @Override
- public void onSystemUiVisibilityChange(int visibility) {
- if (DEBUG) {
- Slog.d(TAG, "systemUi: " + visibility);
- }
- boolean hide = (0 != (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
- mNavigationBarView.setHidden(hide);
- }
- });
}
} catch (Resources.NotFoundException ex) {
// no nav bar for you
@@ -363,6 +354,7 @@ public class PhoneStatusBar extends StatusBar {
signalCluster.setNetworkController(mNetworkController);
// Recents Panel
+ mRecentTasksLoader = new RecentTasksLoader(context);
updateRecentsPanel();
// receive broadcasts
@@ -399,16 +391,21 @@ public class PhoneStatusBar extends StatusBar {
protected void updateRecentsPanel() {
// Recents Panel
boolean visible = false;
+ ArrayList<TaskDescription> recentTasksList = null;
if (mRecentsPanel != null) {
visible = mRecentsPanel.isShowing();
WindowManagerImpl.getDefault().removeView(mRecentsPanel);
+ if (visible) {
+ recentTasksList = mRecentsPanel.getRecentTasksList();
+ }
}
// Provide RecentsPanelView with a temporary parent to allow layout params to work.
LinearLayout tmpRoot = new LinearLayout(mContext);
mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_recent_panel, tmpRoot, false);
-
+ mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
+ mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
mRecentsPanel));
mRecentsPanel.setVisibility(View.GONE);
@@ -417,7 +414,7 @@ public class PhoneStatusBar extends StatusBar {
WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
mRecentsPanel.setBar(this);
if (visible) {
- mRecentsPanel.show(true, false);
+ mRecentsPanel.show(true, false, recentTasksList);
}
}
@@ -1055,10 +1052,12 @@ public class PhoneStatusBar extends StatusBar {
flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
- flagdbg.append(((state & StatusBarManager.DISABLE_NAVIGATION) != 0) ? "NAVIGATION" : "navigation");
- flagdbg.append(((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
+ flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
+ flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
+ flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
+ flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
flagdbg.append(">");
@@ -1074,11 +1073,13 @@ public class PhoneStatusBar extends StatusBar {
}
}
- if ((diff & (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK)) != 0) {
- // the nav bar will take care of DISABLE_NAVIGATION and DISABLE_BACK
+ if ((diff & (StatusBarManager.DISABLE_HOME
+ | StatusBarManager.DISABLE_RECENT
+ | StatusBarManager.DISABLE_BACK)) != 0) {
+ // the nav bar will take care of these
if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
- if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
+ if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
// close recents if it's visible
mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index ba52fb8..415a9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -77,6 +77,7 @@ import com.android.systemui.statusbar.policy.CompatModeButton;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
+import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsPanelView;
public class TabletStatusBar extends StatusBar implements
@@ -179,6 +180,7 @@ public class TabletStatusBar extends StatusBar implements
int mDisabled = 0;
private RecentsPanelView mRecentsPanel;
+ private RecentTasksLoader mRecentTasksLoader;
private InputMethodsPanel mInputMethodsPanel;
private CompatModePanel mCompatModePanel;
@@ -299,12 +301,15 @@ public class TabletStatusBar extends StatusBar implements
}
// Recents Panel
+ mRecentTasksLoader = new RecentTasksLoader(context);
mRecentsPanel = (RecentsPanelView) View.inflate(context,
R.layout.status_bar_recent_panel, null);
mRecentsPanel.setVisibility(View.GONE);
mRecentsPanel.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
mRecentsPanel));
+ mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
+ mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
lp = new WindowManager.LayoutParams(
@@ -392,6 +397,7 @@ public class TabletStatusBar extends StatusBar implements
mNotificationPanelParams.height = getNotificationPanelHeight();
WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel,
mNotificationPanelParams);
+ mRecentsPanel.updateValuesFromResources();
}
protected void loadDimens() {
@@ -958,34 +964,24 @@ public class TabletStatusBar extends StatusBar implements
mTicker.halt();
}
}
- if ((diff & (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK)) != 0) {
- setNavigationVisibility(state &
- (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK));
+ if ((diff & (StatusBarManager.DISABLE_RECENT
+ | StatusBarManager.DISABLE_BACK
+ | StatusBarManager.DISABLE_HOME)) != 0) {
+ setNavigationVisibility(state);
}
}
private void setNavigationVisibility(int visibility) {
- boolean disableNavigation = ((visibility & StatusBarManager.DISABLE_NAVIGATION) != 0);
+ boolean disableHome = ((visibility & StatusBarManager.DISABLE_HOME) != 0);
+ boolean disableRecent = ((visibility & StatusBarManager.DISABLE_RECENT) != 0);
boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0);
- Slog.i(TAG, "DISABLE_BACK: " + (disableBack ? "yes" : "no"));
- Slog.i(TAG, "DISABLE_NAVIGATION: " + (disableNavigation ? "yes" : "no"));
+ mBackButton.setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
+ mHomeButton.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
+ mRecentButton.setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
- if (disableNavigation && disableBack) {
- mNavigationArea.setVisibility(View.INVISIBLE);
- } else {
- int backVisiblity = (disableBack ? View.INVISIBLE : View.VISIBLE);
- int navVisibility = (disableNavigation ? View.INVISIBLE : View.VISIBLE);
-
- mBackButton.setVisibility(backVisiblity);
- mHomeButton.setVisibility(navVisibility);
- mRecentButton.setVisibility(navVisibility);
- // don't change menu button visibility here
-
- mNavigationArea.setVisibility(View.VISIBLE);
- }
-
- mInputMethodSwitchButton.setScreenLocked(disableNavigation);
+ mInputMethodSwitchButton.setScreenLocked(
+ (visibility & StatusBarManager.DISABLE_SYSTEM_INFO) != 0);
}
private boolean hasTicker(Notification n) {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 8569143..11b6c15 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -18,7 +18,6 @@ package com.android.internal.policy.impl;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -57,8 +56,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private static final String TAG = "GlobalActions";
- private StatusBarManager mStatusBar;
-
private final Context mContext;
private final AudioManager mAudioManager;
@@ -103,13 +100,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog == null) {
- mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
mDialog = createDialog();
}
prepareDialog();
- mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
mDialog.show();
+ mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
/**
@@ -249,7 +245,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
/** {@inheritDoc} */
public void onDismiss(DialogInterface dialog) {
- mStatusBar.disable(StatusBarManager.DISABLE_NONE);
}
/** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index 86807ad..6614d79 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -186,6 +186,9 @@ class KeyguardStatusViewManager implements OnClickListener {
mTransientTextManager = new TransientTextManager(mCarrierView);
+ mUpdateMonitor.registerInfoCallback(mInfoCallback);
+ mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
+
resetStatusInfo();
refreshDate();
updateOwnerInfo();
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 303a3b5..2d8185b 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -226,7 +226,7 @@ public class KeyguardUpdateMonitor {
// take a guess to start
mSimState = IccCard.State.READY;
- mBatteryStatus = BATTERY_STATUS_FULL;
+ mBatteryStatus = BATTERY_STATUS_UNKNOWN;
mBatteryLevel = 100;
mTelephonyPlmn = getDefaultPlmn();
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
index 59b546d..de156c9 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -50,8 +50,6 @@ public abstract class KeyguardViewBase extends FrameLayout {
public KeyguardViewBase(Context context) {
super(context);
- setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
-
// This is a faster way to draw the background on devices without hardware acceleration
setBackgroundDrawable(new Drawable() {
@Override
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index 90972da..2fd165a 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -171,6 +171,17 @@ public class KeyguardViewManager implements KeyguardWindowController {
}
}
+ // Disable aspects of the system/status/navigation bars that are not appropriate or
+ // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities.
+ // Other disabled bits are handled by the KeyguardViewMediator talking directly to the
+ // status bar service.
+ int visFlags =
+ ( View.STATUS_BAR_DISABLE_BACK
+ | View.STATUS_BAR_DISABLE_HOME
+ | View.STATUS_BAR_DISABLE_CLOCK
+ );
+ mKeyguardHost.setSystemUiVisibility(visFlags);
+
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
mKeyguardHost.setVisibility(View.VISIBLE);
mKeyguardView.requestFocus();
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index f1b6ef9..0471dfe 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -1188,19 +1188,12 @@ public class KeyguardViewMediator implements KeyguardViewCallback,
}
}
+ // Disable aspects of the system/status/navigation bars that must not be re-enabled by
+ // windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (mShowing) {
- // disable navigation status bar components if lock screen is up
- flags |= StatusBarManager.DISABLE_NAVIGATION;
- if (!mHidden) {
- // showing lockscreen exclusively (no activities in front of it)
- // disable back button too
- flags |= StatusBarManager.DISABLE_BACK;
- if (mUpdateMonitor.isClockVisible()) {
- // lockscreen showing a clock, so hide statusbar clock
- flags |= StatusBarManager.DISABLE_CLOCK;
- }
- }
+ // disable navigation status bar components (home, recents) if lock screen is up
+ flags |= StatusBarManager.DISABLE_RECENT;
if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
// showing secure lockscreen; disable expanding.
flags |= StatusBarManager.DISABLE_EXPAND;
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 65cdd32..d1bb8d1 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -98,7 +98,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
private View mLockScreen;
private View mUnlockScreen;
- private boolean mScreenOn = false;
+ private volatile boolean mScreenOn = false;
+ private volatile boolean mWindowFocused = false;
private boolean mEnableFallback = false; // assume no fallback UI until we know better
private boolean mShowLockBeforeUnlock = false;
@@ -110,14 +111,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
private boolean mFaceLockServiceRunning = false;
private final Object mFaceLockServiceRunningLock = new Object();
+ private final Object mFaceLockStartupLock = new Object();
private Handler mHandler;
private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0;
private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1;
- // Long enough to stay black while dialer comes up
- // Short enough to not be black if the user goes back immediately
- private final int FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT = 1000;
+ // Long enough to stay visible while dialer comes up
+ // Short enough to not be visible if the user goes back immediately
+ private final int FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT = 1000;
+
+ // Long enough to stay visible while the service starts
+ // Short enough to not have to wait long for backup if service fails to start or crashes
+ // The service can take a couple of seconds to start on the first try after boot
+ private final int FACELOCK_VIEW_AREA_SERVICE_TIMEOUT = 3000;
/**
* The current {@link KeyguardScreen} will use this to communicate back to us.
@@ -126,6 +133,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
private boolean mRequiresSim;
+ private volatile boolean mEmergencyCall;
/**
@@ -266,6 +274,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
mUpdateMonitor = updateMonitor;
mLockPatternUtils = lockPatternUtils;
mWindowController = controller;
+ mEmergencyCall = false;
mUpdateMonitor.registerInfoCallback(this);
@@ -320,12 +329,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
}
public void takeEmergencyCallAction() {
+ mEmergencyCall = true;
// FaceLock must be stopped if it is running when emergency call is pressed
stopAndUnbindFromFaceLock();
- // Delay hiding FaceLock area so unlock doesn't display while dialer is coming up
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW,
- FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT);
+ // Continue showing FaceLock area until dialer comes up
+ showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT);
pokeWakelock(EMERGENCY_CALL_TIMEOUT);
if (TelephonyManager.getDefault().getCallState()
@@ -504,6 +513,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
public void onScreenTurnedOff() {
mScreenOn = false;
mForgotPattern = false;
+ mEmergencyCall = false;
if (mMode == Mode.LockScreen) {
((KeyguardScreen) mLockScreen).onPause();
} else {
@@ -514,32 +524,56 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
stopAndUnbindFromFaceLock();
}
- @Override
- public void onScreenTurnedOn() {
- mScreenOn = true;
- show();
-
- // When screen is turned on, need to bind to FaceLock service if we are using FaceLock
- // But only if not dealing with a call
+ /** When screen is turned on and focused, need to bind to FaceLock service if we are using
+ * FaceLock, but only if we're not dealing with a call
+ */
+ private void activateFaceLockIfAble() {
final boolean transportInvisible = mTransportControlView == null ? true :
mTransportControlView.getVisibility() != View.VISIBLE;
if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
&& transportInvisible) {
bindToFaceLock();
- //Eliminate the black background so that the lockpattern will be visible
- //If FaceUnlock is cancelled
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, 4000);
+ // Show FaceLock area, but only for a little bit so lockpattern will become visible if
+ // FaceLock fails to start or crashes
+ showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT);
} else {
- mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+ hideFaceLockArea();
}
}
- /** Unbind from facelock if something covers this window (such as an alarm) */
+ @Override
+ public void onScreenTurnedOn() {
+ boolean runFaceLock = false;
+ //Make sure to start facelock iff the screen is both on and focused
+ synchronized(mFaceLockStartupLock) {
+ mScreenOn = true;
+ runFaceLock = mWindowFocused;
+ }
+ show();
+ if(runFaceLock) activateFaceLockIfAble();
+ }
+
+ /** Unbind from facelock if something covers this window (such as an alarm)
+ * bind to facelock if the lockscreen window just came into focus, and the screen is on
+ */
@Override
public void onWindowFocusChanged (boolean hasWindowFocus) {
+ if(DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused");
+ boolean runFaceLock = false;
+ //Make sure to start facelock iff the screen is both on and focused
+ synchronized(mFaceLockStartupLock) {
+ if(mScreenOn && !mWindowFocused) runFaceLock = hasWindowFocus;
+ mWindowFocused = hasWindowFocus;
+ }
if(!hasWindowFocus) {
stopAndUnbindFromFaceLock();
- mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+ hideFaceLockArea();
+ } else if (runFaceLock) {
+ //Don't activate facelock while the user is calling 911!
+ if(mEmergencyCall) mEmergencyCall = false;
+ else {
+ activateFaceLockIfAble();
+ }
}
}
@@ -553,9 +587,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
if (mLockPatternUtils.usingBiometricWeak() &&
mLockPatternUtils.isBiometricWeakInstalled()) {
- mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
+ showFaceLockArea();
} else {
- mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+ hideFaceLockArea();
}
}
@@ -621,9 +655,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
//We need to stop faceunlock when a phonecall comes in
@Override
public void onPhoneStateChanged(int phoneState) {
+ if (DEBUG) Log.d(TAG, "phone state: " + phoneState);
if(phoneState == TelephonyManager.CALL_STATE_RINGING) {
stopAndUnbindFromFaceLock();
- mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+ hideFaceLockArea();
}
}
@@ -1056,6 +1091,32 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
return true;
}
+ // Removes show and hide messages from the message queue
+ private void removeFaceLockAreaDisplayMessages() {
+ mHandler.removeMessages(MSG_SHOW_FACELOCK_AREA_VIEW);
+ mHandler.removeMessages(MSG_HIDE_FACELOCK_AREA_VIEW);
+ }
+
+ // Shows the FaceLock area immediately
+ private void showFaceLockArea() {
+ // Remove messages to prevent a delayed hide message from undo-ing the show
+ removeFaceLockAreaDisplayMessages();
+ mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
+ }
+
+ // Hides the FaceLock area immediately
+ private void hideFaceLockArea() {
+ // Remove messages to prevent a delayed show message from undo-ing the hide
+ removeFaceLockAreaDisplayMessages();
+ mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+ }
+
+ // Shows the FaceLock area for a period of time
+ private void showFaceLockAreaWithTimeout(long timeoutMillis) {
+ showFaceLockArea();
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, timeoutMillis);
+ }
+
// Binds to FaceLock service, but does not tell it to start
public void bindToFaceLock() {
if (mLockPatternUtils.usingBiometricWeak() &&
@@ -1101,7 +1162,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
try {
mFaceLockService.registerCallback(mFaceLockCallback);
} catch (RemoteException e) {
- throw new RuntimeException("Remote exception");
+ Log.e(TAG, "Caught exception connecting to FaceLock: " + e.toString());
+ mFaceLockService = null;
+ mBoundToFaceLockService = false;
+ return;
}
if (mFaceLockAreaView != null) {
@@ -1118,6 +1182,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
mFaceLockService = null;
mFaceLockServiceRunning = false;
}
+ mBoundToFaceLockService = false;
Log.w(TAG, "Unexpected disconnect from FaceLock service");
}
};
@@ -1133,7 +1198,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
try {
mFaceLockService.startUi(windowToken, x, y, h, w);
} catch (RemoteException e) {
- throw new RuntimeException("Remote exception");
+ Log.e(TAG, "Caught exception starting FaceLock: " + e.toString());
+ return;
}
mFaceLockServiceRunning = true;
} else {
@@ -1157,7 +1223,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
if (DEBUG) Log.d(TAG, "Stopping FaceLock");
mFaceLockService.stopUi();
} catch (RemoteException e) {
- throw new RuntimeException("Remote exception");
+ Log.e(TAG, "Caught exception stopping FaceLock: " + e.toString());
}
mFaceLockServiceRunning = false;
}
@@ -1172,7 +1238,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
@Override
public void unlock() {
if (DEBUG) Log.d(TAG, "FaceLock unlock()");
- mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW); // Keep fallback covered
+ showFaceLockArea(); // Keep fallback covered
stopFaceLock();
mKeyguardScreenCallback.keyguardDone(true);
@@ -1184,7 +1250,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
@Override
public void cancel() {
if (DEBUG) Log.d(TAG, "FaceLock cancel()");
- mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); // Expose fallback
+ hideFaceLockArea(); // Expose fallback
stopFaceLock();
}
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index ec0072c..3ad716b 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -28,6 +28,7 @@ import com.android.internal.widget.PasswordEntryKeyboardView;
import android.os.CountDownTimer;
import android.os.SystemClock;
+import android.provider.Settings;
import android.security.KeyStore;
import android.text.Editable;
import android.text.InputType;
@@ -109,6 +110,10 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen
mPasswordEntry.setOnEditorActionListener(this);
mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false);
+ mKeyboardHelper.setEnableHaptics(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, 0)
+ != 0);
if (mIsAlpha) {
// We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
@@ -150,9 +155,6 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen
//KeyguardStatusViewManager.LOCK_ICON);
}
- mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
- com.android.internal.R.array.config_virtualKeyVibePattern : 0);
-
// Poke the wakelock any time the text is selected or modified
mPasswordEntry.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2cd6eab..af86ae9 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -349,8 +349,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
// Already prepared (isPrepared will be reset to false later)
- if (st.isPrepared)
+ if (st.isPrepared) {
return true;
+ }
if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
// Another Panel is prepared and possibly open, so close it
@@ -800,14 +801,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
closePanel(st, true);
} else if (st.isPrepared) {
+ boolean show = true;
+ if (st.refreshMenuContent) {
+ // Something may have invalidated the menu since we prepared it.
+ // Re-prepare it to refresh.
+ st.isPrepared = false;
+ show = preparePanel(st, event);
+ }
- // Write 'menu opened' to event log
- EventLog.writeEvent(50001, 0);
+ if (show) {
+ // Write 'menu opened' to event log
+ EventLog.writeEvent(50001, 0);
- // Show menu
- openPanel(st, event);
+ // Show menu
+ openPanel(st, event);
- playSoundEffect = true;
+ playSoundEffect = true;
+ }
}
}
@@ -1084,8 +1094,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
MenuView menuView = st.isInListMode()
- ? st.getListMenuView(mPanelMenuPresenterCallback)
- : st.getIconMenuView(mPanelMenuPresenterCallback);
+ ? st.getListMenuView(getContext(), mPanelMenuPresenterCallback)
+ : st.getIconMenuView(getContext(), mPanelMenuPresenterCallback);
st.shownPanelView = (View) menuView;
@@ -3251,11 +3261,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
- MenuView getListMenuView(MenuPresenter.Callback cb) {
+ MenuView getListMenuView(Context context, MenuPresenter.Callback cb) {
if (menu == null) return null;
if (!isCompact) {
- getIconMenuView(cb); // Need this initialized to know where our offset goes
+ getIconMenuView(context, cb); // Need this initialized to know where our offset goes
}
if (listMenuPresenter == null) {
@@ -3275,11 +3285,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return result;
}
- MenuView getIconMenuView(MenuPresenter.Callback cb) {
+ MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) {
if (menu == null) return null;
if (iconMenuPresenter == null) {
- iconMenuPresenter = new IconMenuPresenter();
+ iconMenuPresenter = new IconMenuPresenter(context);
iconMenuPresenter.setCallback(cb);
iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter);
menu.addMenuPresenter(iconMenuPresenter);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 487063d..aac3209 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -128,7 +128,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import android.view.KeyCharacterMap.FallbackAction;
-import android.view.WindowManagerPolicy.WindowManagerFuncs;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -180,26 +179,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int PRIORITY_PHONE_LAYER = 7;
// like the ANR / app crashed dialogs
static final int SYSTEM_ALERT_LAYER = 8;
- // system-level error dialogs
- static final int SYSTEM_ERROR_LAYER = 9;
// on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_LAYER = 10;
+ static final int INPUT_METHOD_LAYER = 9;
// on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_DIALOG_LAYER = 11;
+ static final int INPUT_METHOD_DIALOG_LAYER = 10;
// the keyguard; nothing on top of these can take focus, since they are
// responsible for power management when displayed.
- static final int KEYGUARD_LAYER = 12;
- static final int KEYGUARD_DIALOG_LAYER = 13;
- static final int STATUS_BAR_SUB_PANEL_LAYER = 14;
- static final int STATUS_BAR_LAYER = 15;
- static final int STATUS_BAR_PANEL_LAYER = 16;
+ static final int KEYGUARD_LAYER = 11;
+ static final int KEYGUARD_DIALOG_LAYER = 12;
+ static final int STATUS_BAR_SUB_PANEL_LAYER = 13;
+ static final int STATUS_BAR_LAYER = 14;
+ static final int STATUS_BAR_PANEL_LAYER = 15;
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- static final int VOLUME_OVERLAY_LAYER = 17;
+ static final int VOLUME_OVERLAY_LAYER = 16;
// things in here CAN NOT take focus, but are shown on top of everything else.
- static final int SYSTEM_OVERLAY_LAYER = 18;
+ static final int SYSTEM_OVERLAY_LAYER = 17;
// the navigation bar, if available, shows atop most things
- static final int NAVIGATION_BAR_LAYER = 19;
+ static final int NAVIGATION_BAR_LAYER = 18;
+ // system-level error dialogs
+ static final int SYSTEM_ERROR_LAYER = 19;
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
static final int DRAG_LAYER = 20;
@@ -267,7 +266,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mKeyguard = null;
KeyguardViewMediator mKeyguardMediator;
GlobalActions mGlobalActions;
- volatile boolean mPowerKeyHandled;
+ volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread
+ boolean mPendingPowerKeyUpCanceled;
RecentApplicationsDialog mRecentAppsDialog;
Handler mHandler;
@@ -403,8 +403,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private int mLongPressOnHomeBehavior = -1;
// Screenshot trigger states
- private boolean mVolumeDownTriggered;
- private boolean mPowerDownTriggered;
+ // Time to volume and power must be pressed within this interval of each other.
+ private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
+ private boolean mVolumeDownKeyTriggered;
+ private long mVolumeDownKeyTime;
+ private boolean mVolumeDownKeyConsumedByScreenshotChord;
+ private boolean mVolumeUpKeyTriggered;
+ private boolean mPowerKeyTriggered;
+ private long mPowerKeyTime;
ShortcutManager mShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
@@ -552,40 +558,67 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mPowerKeyHandled) {
mHandler.removeCallbacks(mPowerLongPress);
return !canceled;
- } else {
- mPowerKeyHandled = true;
- return false;
}
+ return false;
+ }
+
+ private void cancelPendingPowerKeyAction() {
+ if (!mPowerKeyHandled) {
+ mHandler.removeCallbacks(mPowerLongPress);
+ }
+ mPendingPowerKeyUpCanceled = true;
+ }
+
+ private void interceptScreenshotChord() {
+ if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
+ final long now = SystemClock.uptimeMillis();
+ if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
+ && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
+ mVolumeDownKeyConsumedByScreenshotChord = true;
+ cancelPendingPowerKeyAction();
+
+ mHandler.postDelayed(mScreenshotChordLongPress,
+ ViewConfiguration.getGlobalActionKeyTimeout());
+ }
+ }
+ }
+
+ private void cancelPendingScreenshotChordAction() {
+ mHandler.removeCallbacks(mScreenshotChordLongPress);
}
private final Runnable mPowerLongPress = new Runnable() {
public void run() {
- if (!mPowerKeyHandled) {
- // The context isn't read
- if (mLongPressOnPowerBehavior < 0) {
- mLongPressOnPowerBehavior = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_longPressOnPowerBehavior);
- }
- switch (mLongPressOnPowerBehavior) {
- case LONG_PRESS_POWER_NOTHING:
- break;
- case LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
- showGlobalActionsDialog();
- break;
- case LONG_PRESS_POWER_SHUT_OFF:
- mPowerKeyHandled = true;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
- ShutdownThread.shutdown(mContext, true);
- break;
- }
+ // The context isn't read
+ if (mLongPressOnPowerBehavior < 0) {
+ mLongPressOnPowerBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerBehavior);
+ }
+ switch (mLongPressOnPowerBehavior) {
+ case LONG_PRESS_POWER_NOTHING:
+ break;
+ case LONG_PRESS_POWER_GLOBAL_ACTIONS:
+ mPowerKeyHandled = true;
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+ showGlobalActionsDialog();
+ break;
+ case LONG_PRESS_POWER_SHUT_OFF:
+ mPowerKeyHandled = true;
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+ ShutdownThread.shutdown(mContext, true);
+ break;
}
}
};
+ private final Runnable mScreenshotChordLongPress = new Runnable() {
+ public void run() {
+ takeScreenshot();
+ }
+ };
+
void showGlobalActionsDialog() {
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext);
@@ -1381,11 +1414,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
+ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
+ final int flags = event.getFlags();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
@@ -1394,6 +1428,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
}
+ // If we think we might have a volume down & power key chord on the way
+ // but we're not sure, then tell the dispatcher to wait a little while and
+ // try again later before dispatching.
+ if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+ if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) {
+ final long now = SystemClock.uptimeMillis();
+ final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
+ if (now < timeoutTime) {
+ return timeoutTime - now;
+ }
+ }
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ && mVolumeDownKeyConsumedByScreenshotChord) {
+ if (!down) {
+ mVolumeDownKeyConsumedByScreenshotChord = false;
+ }
+ return -1;
+ }
+ }
+
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
@@ -1425,7 +1479,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
Log.i(TAG, "Ignoring HOME; event canceled.");
}
- return true;
+ return -1;
}
// If a system window has focus, then it doesn't make sense
@@ -1436,13 +1490,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
// the "app" is keyguard, so give it the key
- return false;
+ return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for (int i=0; i<typeCount; i++) {
if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
// don't do anything, but also don't pass it to the app
- return true;
+ return -1;
}
}
}
@@ -1456,7 +1510,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
}
- return true;
+ return -1;
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
@@ -1465,7 +1519,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
mContext.sendOrderedBroadcast(intent, null);
- return true;
+ return -1;
} else if (SHOW_PROCESSES_ON_ALT_MENU &&
(metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
Intent service = new Intent();
@@ -1480,7 +1534,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
Settings.System.putInt(
res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
- return true;
+ return -1;
}
}
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
@@ -1493,15 +1547,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mShortcutKeyPressed = -1;
if (mConsumeShortcutKeyUp) {
mConsumeShortcutKeyUp = false;
- return true;
+ return -1;
}
}
- return false;
+ return 0;
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
if (down && repeatCount == 0) {
showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
}
- return true;
+ return -1;
}
// Shortcuts are invoked through Search+key, so intercept those here
@@ -1531,11 +1585,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ "+" + KeyEvent.keyCodeToString(keyCode));
}
}
- return true;
+ return -1;
}
}
- return false;
+ return 0;
}
/** {@inheritDoc} */
@@ -1606,7 +1660,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
flags, event.getSource(), null);
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true);
if ((actions & ACTION_PASS_TO_USER) != 0) {
- if (!interceptKeyBeforeDispatching(win, fallbackEvent, policyFlags)) {
+ long delayMillis = interceptKeyBeforeDispatching(
+ win, fallbackEvent, policyFlags);
+ if (delayMillis == 0) {
if (DEBUG_FALLBACK) {
Slog.d(TAG, "Performing fallback.");
}
@@ -2472,76 +2528,65 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
- Runnable mScreenshotTimeout = null;
- void finishScreenshotLSS(ServiceConnection conn) {
- if (mScreenshotConnection == conn) {
- mContext.unbindService(conn);
- mScreenshotConnection = null;
- if (mScreenshotTimeout != null) {
- mHandler.removeCallbacks(mScreenshotTimeout);
- mScreenshotTimeout = null;
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
}
}
- }
+ };
+ // Assume this is called from the Handler thread.
private void takeScreenshot() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- return;
- }
- ComponentName cn = new ComponentName("com.android.systemui",
- "com.android.systemui.screenshot.TakeScreenshotService");
- Intent intent = new Intent();
- intent.setComponent(cn);
- ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != this) {
- return;
- }
- Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, 1);
- final ServiceConnection myConn = this;
- Handler h = new Handler(mHandler.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- synchronized (mScreenshotLock) {
- finishScreenshotLSS(myConn);
- }
- }
- };
- msg.replyTo = new Messenger(h);
- try {
- messenger.send(msg);
- } catch (RemoteException e) {
- }
- }
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ ComponentName cn = new ComponentName("com.android.systemui",
+ "com.android.systemui.screenshot.TakeScreenshotService");
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
}
- @Override
- public void onServiceDisconnected(ComponentName name) {}
- };
- if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
- mScreenshotConnection = conn;
- mScreenshotTimeout = new Runnable() {
- @Override public void run() {
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, 1);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(mHandler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- finishScreenshotLSS(mScreenshotConnection);
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mHandler.removeCallbacks(mScreenshotTimeout);
}
}
}
-
};
- mHandler.postDelayed(mScreenshotTimeout, 10000);
+ msg.replyTo = new Messenger(h);
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ }
}
}
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+ if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+ mScreenshotConnection = conn;
+ mHandler.postDelayed(mScreenshotTimeout, 10000);
}
- });
+ }
}
/** {@inheritDoc} */
@@ -2609,28 +2654,35 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
- if (down) {
- if (isScreenOn) {
- // If the power key down was already triggered, take the screenshot
- if (mPowerDownTriggered) {
- // Dismiss the power-key longpress
- mHandler.removeCallbacks(mPowerLongPress);
- mPowerKeyHandled = true;
-
- // Take the screenshot
- takeScreenshot();
-
- // Prevent the event from being passed through to the current activity
- result &= ~ACTION_PASS_TO_USER;
- break;
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_MUTE: {
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ if (down) {
+ if (isScreenOn && !mVolumeDownKeyTriggered
+ && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ mVolumeDownKeyTriggered = true;
+ mVolumeDownKeyTime = event.getDownTime();
+ mVolumeDownKeyConsumedByScreenshotChord = false;
+ cancelPendingPowerKeyAction();
+ interceptScreenshotChord();
+ }
+ } else {
+ mVolumeDownKeyTriggered = false;
+ cancelPendingScreenshotChordAction();
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ if (down) {
+ if (isScreenOn && !mVolumeUpKeyTriggered
+ && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ mVolumeUpKeyTriggered = true;
+ cancelPendingPowerKeyAction();
+ cancelPendingScreenshotChordAction();
}
- mVolumeDownTriggered = true;
+ } else {
+ mVolumeUpKeyTriggered = false;
+ cancelPendingScreenshotChordAction();
}
- } else {
- mVolumeDownTriggered = false;
}
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (down) {
ITelephony telephonyService = getTelephonyService();
if (telephonyService != null) {
@@ -2709,17 +2761,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
- if (isScreenOn) {
- // If the volume down key has been triggered, then just take the screenshot
- if (mVolumeDownTriggered) {
- // Take the screenshot
- takeScreenshot();
- mPowerKeyHandled = true;
-
- // Prevent the event from being passed through to the current activity
- break;
- }
- mPowerDownTriggered = true;
+ if (isScreenOn && !mPowerKeyTriggered
+ && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ mPowerKeyTriggered = true;
+ mPowerKeyTime = event.getDownTime();
+ interceptScreenshotChord();
}
ITelephony telephonyService = getTelephonyService();
@@ -2741,12 +2787,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Log.w(TAG, "ITelephony threw RemoteException", ex);
}
}
- interceptPowerKeyDown(!isScreenOn || hungUp);
+ interceptPowerKeyDown(!isScreenOn || hungUp
+ || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
} else {
- mPowerDownTriggered = false;
- if (interceptPowerKeyUp(canceled)) {
+ mPowerKeyTriggered = false;
+ cancelPendingScreenshotChordAction();
+ if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
}
+ mPendingPowerKeyUpCanceled = false;
}
break;
}
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 1eb5f0e..04b4855 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -804,6 +804,18 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
logOutboundKeyDetailsLocked("dispatchKey - ", entry);
}
+ // Handle case where the policy asked us to try again later last time.
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
+ if (currentTime < entry->interceptKeyWakeupTime) {
+ if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
+ *nextWakeupTime = entry->interceptKeyWakeupTime;
+ }
+ return false; // wait until next wakeup
+ }
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ entry->interceptKeyWakeupTime = 0;
+ }
+
// Give the policy a chance to intercept the key.
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
@@ -3827,14 +3839,19 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
mLock.unlock();
- bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
+ nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
&event, entry->policyFlags);
mLock.lock();
- entry->interceptKeyResult = consumed
- ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
- : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ if (delay < 0) {
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+ } else if (!delay) {
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ } else {
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+ entry->interceptKeyWakeupTime = now() + delay;
+ }
entry->release();
}
@@ -4156,7 +4173,8 @@ InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime,
deviceId(deviceId), source(source), action(action), flags(flags),
keyCode(keyCode), scanCode(scanCode), metaState(metaState),
repeatCount(repeatCount), downTime(downTime),
- syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+ interceptKeyWakeupTime(0) {
}
InputDispatcher::KeyEntry::~KeyEntry() {
@@ -4168,6 +4186,7 @@ void InputDispatcher::KeyEntry::recycle() {
dispatchInProgress = false;
syntheticRepeat = false;
interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ interceptKeyWakeupTime = 0;
}
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index e78f7bd..8ae5a56 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -242,7 +242,7 @@ public:
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
- virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
@@ -481,8 +481,10 @@ private:
INTERCEPT_KEY_RESULT_UNKNOWN,
INTERCEPT_KEY_RESULT_SKIP,
INTERCEPT_KEY_RESULT_CONTINUE,
+ INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
};
InterceptKeyResult interceptKeyResult; // set based on the interception result
+ nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
KeyEntry(nsecs_t eventTime,
int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 8dfb44b..961566f 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -75,9 +75,9 @@ private:
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
}
- virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
const KeyEvent* keyEvent, uint32_t policyFlags) {
- return false;
+ return 0;
}
virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index dd649e7..eb75ebc 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -16,24 +16,6 @@
package com.android.server;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.commons.logging.impl.SimpleLog;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@@ -42,9 +24,9 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -58,7 +40,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
@@ -68,20 +49,37 @@ import android.util.Slog;
import android.util.TypedValue;
import android.util.Xml;
import android.widget.RemoteViews;
-import android.widget.RemoteViewsService;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
class AppWidgetService extends IAppWidgetService.Stub
{
private static final String TAG = "AppWidgetService";
private static final String SETTINGS_FILENAME = "appwidgets.xml";
- private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
/*
@@ -1159,70 +1157,46 @@ class AppWidgetService extends IAppWidgetService.Stub
// only call from initialization -- it assumes that the data structures are all empty
void loadStateLocked() {
- File temp = savedStateTempFile();
- File real = savedStateRealFile();
-
- // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
- // real one. if there is both a real file and a temp one, assume that the temp one isn't
- // fully written and delete it.
- if (real.exists()) {
- readStateFromFileLocked(real);
- if (temp.exists()) {
- //noinspection ResultOfMethodCallIgnored
- temp.delete();
+ AtomicFile file = savedStateFile();
+ try {
+ FileInputStream stream = file.openRead();
+ readStateFromFileLocked(stream);
+
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close state FileInputStream " + e);
+ }
}
- } else if (temp.exists()) {
- readStateFromFileLocked(temp);
- //noinspection ResultOfMethodCallIgnored
- temp.renameTo(real);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Failed to read state: " + e);
}
}
-
+
void saveStateLocked() {
- File temp = savedStateTempFile();
- File real = savedStateRealFile();
-
- if (!real.exists()) {
- // If the real one doesn't exist, it's either because this is the first time
- // or because something went wrong while copying them. In this case, we can't
- // trust anything that's in temp. In order to have the loadState code not
- // use the temporary one until it's fully written, create an empty file
- // for real, which will we'll shortly delete.
- try {
- //noinspection ResultOfMethodCallIgnored
- real.createNewFile();
- } catch (IOException e) {
- // Ignore
+ AtomicFile file = savedStateFile();
+ FileOutputStream stream;
+ try {
+ stream = file.startWrite();
+ if (writeStateToFileLocked(stream)) {
+ file.finishWrite(stream);
+ } else {
+ file.failWrite(stream);
+ Slog.w(TAG, "Failed to save state, restoring backup.");
}
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed open state file for write: " + e);
}
-
- if (temp.exists()) {
- //noinspection ResultOfMethodCallIgnored
- temp.delete();
- }
-
- if (!writeStateToFileLocked(temp)) {
- Slog.w(TAG, "Failed to persist new settings");
- return;
- }
-
- //noinspection ResultOfMethodCallIgnored
- real.delete();
- //noinspection ResultOfMethodCallIgnored
- temp.renameTo(real);
}
- boolean writeStateToFileLocked(File file) {
- FileOutputStream stream = null;
+ boolean writeStateToFileLocked(FileOutputStream stream) {
int N;
try {
- stream = new FileOutputStream(file, false);
XmlSerializer out = new FastXmlSerializer();
out.setOutput(stream, "utf-8");
out.startDocument(null, true);
-
-
out.startTag(null, "gs");
int providerIndex = 0;
@@ -1264,31 +1238,17 @@ class AppWidgetService extends IAppWidgetService.Stub
out.endTag(null, "gs");
out.endDocument();
- stream.close();
return true;
} catch (IOException e) {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException ex) {
- // Ignore
- }
- if (file.exists()) {
- //noinspection ResultOfMethodCallIgnored
- file.delete();
- }
+ Slog.w(TAG, "Failed to write state: " + e);
return false;
}
}
- void readStateFromFileLocked(File file) {
- FileInputStream stream = null;
-
+ void readStateFromFileLocked(FileInputStream stream) {
boolean success = false;
try {
- stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
@@ -1390,22 +1350,15 @@ class AppWidgetService extends IAppWidgetService.Stub
} while (type != XmlPullParser.END_DOCUMENT);
success = true;
} catch (NullPointerException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, "failed parsing " + e);
} catch (NumberFormatException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, "failed parsing " + e);
} catch (XmlPullParserException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, "failed parsing " + e);
} catch (IOException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, "failed parsing " + e);
} catch (IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + file, e);
- }
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- // Ignore
+ Slog.w(TAG, "failed parsing " + e);
}
if (success) {
@@ -1416,6 +1369,8 @@ class AppWidgetService extends IAppWidgetService.Stub
}
} else {
// failed reading, clean up
+ Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
mAppWidgetIds.clear();
mHosts.clear();
final int N = mInstalledProviders.size();
@@ -1425,14 +1380,8 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- File savedStateTempFile() {
- return new File("/data/system/" + SETTINGS_TMP_FILENAME);
- //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
- }
-
- File savedStateRealFile() {
- return new File("/data/system/" + SETTINGS_FILENAME);
- //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
+ AtomicFile savedStateFile() {
+ return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
}
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 5758954..f9f5458 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1966,6 +1966,9 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized (mQueueLock) {
mBackupRunning = false;
if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ // Make sure we back up everything and perform the one-time init
+ clearMetadata();
+ if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
backupNow();
}
}
@@ -1975,6 +1978,12 @@ class BackupManagerService extends IBackupManager.Stub {
mWakelock.release();
}
+ // Remove the PM metadata state. This will generate an init on the next pass.
+ void clearMetadata() {
+ final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
+ if (pmState.exists()) pmState.delete();
+ }
+
// Invoke an agent's doBackup() and start a timeout message spinning on the main
// handler in case it doesn't get back to us.
int invokeAgentForBackup(String packageName, IBackupAgent agent,
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 78dbbd6..994201b 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1677,6 +1677,18 @@ public class PowerManagerService extends IPowerManager.Stub
e.fillInStackTrace();
Slog.i(TAG, "Set screen state: " + on, e);
}
+ if (on) {
+ if ((mPowerState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
+ // If we are turning the screen state on, but the screen
+ // light is currently off, then make sure that we set the
+ // light at this point to 0. This is the case where we are
+ // turning on the screen and waiting for the UI to be drawn
+ // before showing it to the user. We want the light off
+ // until it is ready to be shown to the user, not it using
+ // whatever the last value it had.
+ mScreenBrightness.forceValueLocked(Power.BRIGHTNESS_OFF);
+ }
+ }
int err = Power.setScreenState(on);
if (err == 0) {
mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
@@ -1686,11 +1698,6 @@ public class PowerManagerService extends IPowerManager.Stub
// make sure button and key backlights are off too
mButtonLight.turnOff();
mKeyboardLight.turnOff();
- // clear current value so we will update based on the new conditions
- // when the sensor is reenabled.
- mLightSensorValue = -1;
- // reset our highest light sensor value when the screen turns off
- mHighestLightSensorValue = -1;
}
}
}
@@ -2029,8 +2036,6 @@ public class PowerManagerService extends IPowerManager.Stub
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Slog.i(TAG, "Setting screen brightness: " + brightness, e);
- mScreenBrightness.setTargetLocked(brightness, steps,
- INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
}
}
@@ -2103,6 +2108,15 @@ public class PowerManagerService extends IPowerManager.Stub
+ " delta=" + delta);
}
+ void forceValueLocked(int value) {
+ targetValue = -1;
+ curValue = value;
+ setLightBrightness(mask, value);
+ if (animating) {
+ finishAnimationLocked(false, value);
+ }
+ }
+
void setTargetLocked(int target, int stepsToTarget, int initialValue,
int nominalCurrentValue) {
if (!initialized) {
@@ -2453,6 +2467,7 @@ public class PowerManagerService extends IPowerManager.Stub
synchronized (mLocks) {
mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
if (mIsDocked) {
+ // allow brightness to decrease when docked
mHighestLightSensorValue = -1;
}
if ((mPowerState & SCREEN_ON_BIT) != 0) {
@@ -3028,11 +3043,21 @@ public class PowerManagerService extends IPowerManager.Stub
long identity = Binder.clearCallingIdentity();
try {
if (enable) {
+ // reset our highest value when reenabling
+ mHighestLightSensorValue = -1;
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ handleLightSensorValue(value);
+ }
mSensorManager.registerListener(mLightListener, mLightSensor,
SensorManager.SENSOR_DELAY_NORMAL);
} else {
mSensorManager.unregisterListener(mLightListener);
mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3084,43 +3109,45 @@ public class PowerManagerService extends IPowerManager.Stub
}
};
+ private void handleLightSensorValue(int value) {
+ long milliseconds = SystemClock.elapsedRealtime();
+ if (mLightSensorValue == -1 ||
+ milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
+ // process the value immediately if screen has just turned on
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
+ lightSensorChangedLocked(value);
+ } else {
+ if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
+ (value < mLightSensorValue && mLightSensorPendingIncrease) ||
+ (value == mLightSensorValue) ||
+ (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
+ // delay processing to debounce the sensor
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = (value < mLightSensorValue);
+ mLightSensorPendingIncrease = (value > mLightSensorValue);
+ if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+ mLightSensorPendingValue = value;
+ mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
+ }
+ } else {
+ mLightSensorPendingValue = value;
+ }
+ }
+ }
+
SensorEventListener mLightListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
+ if (mDebugLightSensor) {
+ Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]);
+ }
synchronized (mLocks) {
// ignore light sensor while screen is turning off
if (isScreenTurningOffLocked()) {
return;
}
-
- int value = (int)event.values[0];
- long milliseconds = SystemClock.elapsedRealtime();
- if (mDebugLightSensor) {
- Slog.d(TAG, "onSensorChanged: light value: " + value);
- }
- if (mLightSensorValue == -1 ||
- milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
- // process the value immediately if screen has just turned on
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value);
- } else {
- if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
- (value < mLightSensorValue && mLightSensorPendingIncrease) ||
- (value == mLightSensorValue) ||
- (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
- // delay processing to debounce the sensor
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = (value < mLightSensorValue);
- mLightSensorPendingIncrease = (value > mLightSensorValue);
- if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
- mLightSensorPendingValue = value;
- mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
- }
- } else {
- mLightSensorPendingValue = value;
- }
- }
+ handleLightSensorValue((int)event.values[0]);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5006de7..3ae62ad 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -163,6 +163,11 @@ class ServerThread extends Thread {
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
+ boolean firstBoot = false;
+ try {
+ firstBoot = pm.isFirstBoot();
+ } catch (RemoteException e) {
+ }
ActivityManagerService.setSystemProcess();
@@ -208,7 +213,8 @@ class ServerThread extends Thread {
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
- factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
+ factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
+ !firstBoot);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ActivityManagerService.self().setWindowManager(wm);
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 5875ee3..3c6b416 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -457,6 +457,9 @@ public class TouchExplorer implements Explorer {
case MotionEvent.ACTION_MOVE: {
final int activePointerCount = mPointerTracker.getActivePointerCount();
switch (activePointerCount) {
+ case 1: {
+ // do nothing
+ } break;
case 2: {
if (isDraggingGesture(event)) {
// If still dragging send a drag event.
@@ -484,10 +487,12 @@ public class TouchExplorer implements Explorer {
}
} break;
case MotionEvent.ACTION_POINTER_UP: {
- mCurrentState = STATE_TOUCH_EXPLORING;
// Send an event to the end of the drag gesture.
sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
} break;
+ case MotionEvent.ACTION_UP: {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ } break;
case MotionEvent.ACTION_CANCEL: {
clear();
} break;
@@ -500,7 +505,7 @@ public class TouchExplorer implements Explorer {
* @param event The event to be handled.
* @param policyFlags The policy flags associated with the event.
*/
- public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
throw new IllegalStateException("Delegating state can only be reached if "
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d038d76..55fb371 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3427,8 +3427,10 @@ public final class ActivityManagerService extends ActivityManagerNative
ac.removePackage(name);
}
}
- mMainStack.resumeTopActivityLocked(null);
- mMainStack.scheduleIdleLocked();
+ if (mBooted) {
+ mMainStack.resumeTopActivityLocked(null);
+ mMainStack.scheduleIdleLocked();
+ }
}
return didSomething;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 7bc19ab..1aed7fe 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -589,8 +589,8 @@ final class ActivityStack {
}
}
app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
- System.identityHashCode(r),
- r.info, r.compat, r.icicle, results, newIntents, !andResume,
+ System.identityHashCode(r), r.info, mService.mConfiguration,
+ r.compat, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward(), profileFile, profileFd,
profileAutoStop);
@@ -2067,7 +2067,7 @@ final class ActivityStack {
* task starting at a specified index.
*/
private final void performClearTaskAtIndexLocked(int taskId, int i) {
- while (i < (mHistory.size()-1)) {
+ while (i < mHistory.size()) {
ActivityRecord r = mHistory.get(i);
if (r.task.taskId != taskId) {
// Whoops hit the end.
@@ -4035,7 +4035,18 @@ final class ActivityStack {
// But then we need to figure out how it needs to deal with that.
Configuration oldConfig = r.configuration;
r.configuration = newConfig;
-
+
+ // Determine what has changed. May be nothing, if this is a config
+ // that has come back from the app after going idle. In that case
+ // we just want to leave the official config object now in the
+ // activity and do nothing else.
+ final int changes = oldConfig.diff(newConfig);
+ if (changes == 0 && !r.forceNewConfig) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration no differences in " + r);
+ return true;
+ }
+
// If the activity isn't currently running, just leave the new
// configuration and it will pick that up next time it starts.
if (r.app == null || r.app.thread == null) {
@@ -4046,8 +4057,7 @@ final class ActivityStack {
return true;
}
- // Figure out what has changed between the two configurations.
- int changes = oldConfig.diff(newConfig);
+ // Figure out how to handle the changes between the configurations.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ Integer.toHexString(changes) + ", handles=0x"
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index a769c05..57e11cf 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -24,6 +24,7 @@ import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.Slog;
+import android.view.WindowManager;
class AppErrorDialog extends BaseErrorDialog {
private final static String TAG = "AppErrorDialog";
@@ -73,6 +74,9 @@ class AppErrorDialog extends BaseErrorDialog {
setTitle(res.getText(com.android.internal.R.string.aerr_title));
getWindow().addFlags(FLAG_SYSTEM_ERROR);
getWindow().setTitle("Application Error: " + app.info.processName);
+ if (app.persistent) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ }
// After the timeout, pretend the user clicked the quit button
mHandler.sendMessageDelayed(
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 0da5cc6..e610782 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -85,7 +85,6 @@ import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
-import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Binder;
import android.os.Environment;
@@ -489,7 +488,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long end = currentTime;
final long totalBytes = getTotalBytes(policy.template, start, end);
- if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) {
+ if (policy.isOverLimit(totalBytes)) {
if (policy.lastSnooze >= start) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
@@ -574,7 +573,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final CharSequence title = res.getText(R.string.data_usage_warning_title);
final CharSequence body = res.getString(R.string.data_usage_warning_body);
- builder.setSmallIcon(R.drawable.ic_menu_info_details);
+ builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
@@ -606,7 +605,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
break;
}
- builder.setSmallIcon(com.android.internal.R.drawable.ic_menu_block);
+ builder.setSmallIcon(R.drawable.stat_notify_disabled);
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
@@ -640,7 +639,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
break;
}
- builder.setSmallIcon(R.drawable.ic_menu_info_details);
+ builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
@@ -677,7 +676,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
builder.setOnlyAlertOnce(true);
builder.setOngoing(true);
- builder.setSmallIcon(R.drawable.ic_menu_info_details);
+ builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
@@ -750,8 +749,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long totalBytes = getTotalBytes(policy.template, start, end);
// disable data connection when over limit and not snoozed
- final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
- && totalBytes > policy.limitBytes && policy.lastSnooze < start;
+ final boolean overLimit = policy.isOverLimit(totalBytes) && policy.lastSnooze < start;
final boolean enabled = !overLimit;
setNetworkTemplateEnabled(policy.template, enabled);
@@ -1535,10 +1533,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private long getTotalBytes(NetworkTemplate template, long start, long end) {
try {
- final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- return entry.rxBytes + entry.txBytes;
+ return mNetworkStats.getSummaryForNetwork(template, start, end).getTotalBytes();
} catch (RemoteException e) {
// ignored; service lives in system_server
return 0;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 19dd606..0e9f64c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1157,6 +1157,10 @@ public class PackageManagerService extends IPackageManager.Stub {
} // synchronized (mInstallLock)
}
+ public boolean isFirstBoot() {
+ return !mRestoredSettings;
+ }
+
private String getRequiredVerifierLPr() {
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
@@ -2983,12 +2987,14 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (pkgs != null) {
for (int i=0; i<pkgs.size(); i++) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(
- com.android.internal.R.string.android_upgrading_apk,
- i+1, pkgs.size()), true);
- } catch (RemoteException e) {
+ if (!isFirstBoot()) {
+ try {
+ ActivityManagerNative.getDefault().showBootMessage(
+ mContext.getResources().getString(
+ com.android.internal.R.string.android_upgrading_apk,
+ i+1, pkgs.size()), true);
+ } catch (RemoteException e) {
+ }
}
PackageParser.Package p = pkgs.get(i);
synchronized (mInstallLock) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 7cdb5b1..bfe6613 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1238,9 +1238,8 @@ final class Settings {
mReadMessages.append("No start tag found in settings file\n");
PackageManagerService.reportSettingsProblem(Log.WARN,
"No start tag found in package manager settings");
- Log
- .wtf(PackageManagerService.TAG,
- "No start tag found in package manager settings");
+ Log.wtf(PackageManagerService.TAG,
+ "No start tag found in package manager settings");
return false;
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 25cc259..73cd64e 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -274,7 +274,8 @@ class DragState {
final int myPid = Process.myPid();
// Move the surface to the given touch
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
Surface.openTransaction();
try {
mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
@@ -283,7 +284,8 @@ class DragState {
(int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
} finally {
Surface.closeTransaction();
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
}
// Tell the affected window
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 60333a3..df7e0e1 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -575,7 +575,7 @@ public class InputManager implements Watchdog.Monitor {
}
@SuppressWarnings("unused")
- public boolean interceptKeyBeforeDispatching(InputWindowHandle focus,
+ public long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(
focus, event, policyFlags);
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 9a559e0..fb74d27 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -288,7 +288,7 @@ final class InputMonitor {
/* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch. */
- public boolean interceptKeyBeforeDispatching(
+ public long interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index e25638f..91576e7 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -81,7 +81,7 @@ class ScreenRotationAnimation {
mOriginalHeight = originalHeight;
if (!inTransaction) {
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation");
Surface.openTransaction();
}
@@ -117,7 +117,7 @@ class ScreenRotationAnimation {
mSurface = null;
return;
}
-
+
Paint paint = new Paint(0);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
c.drawBitmap(screenshot, 0, 0, paint);
@@ -127,7 +127,7 @@ class ScreenRotationAnimation {
} finally {
if (!inTransaction) {
Surface.closeTransaction();
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
"<<< CLOSE TRANSACTION ScreenRotationAnimation");
}
@@ -254,7 +254,7 @@ class ScreenRotationAnimation {
mEnterAnimation.restrictDuration(maxAnimationDuration);
mEnterAnimation.scaleCurrentDuration(animationScale);
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
Surface.openTransaction();
@@ -266,7 +266,7 @@ class ScreenRotationAnimation {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
Surface.closeTransaction();
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss");
}
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 10882f9..03b7546 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -278,7 +278,8 @@ final class Session extends IWindowSession.Stub
// Make the surface visible at the proper location
final Surface surface = mService.mDragState.mSurface;
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
Surface.openTransaction();
try {
surface.setPosition(touchX - thumbCenterX,
@@ -288,7 +289,8 @@ final class Session extends IWindowSession.Stub
surface.show();
} finally {
Surface.closeTransaction();
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 73a9601..68f0e66 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -167,8 +167,10 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_DRAG = false;
static final boolean DEBUG_SCREEN_ON = false;
static final boolean DEBUG_SCREENSHOT = false;
+ static final boolean DEBUG_BOOT = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
+ static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean PROFILE_ORIENTATION = false;
@@ -297,6 +299,8 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean mHaveInputMethods;
+ final boolean mAllowBootMessages;
+
final boolean mLimitedAlphaCompositing;
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
@@ -633,8 +637,8 @@ public class WindowManagerService extends IWindowManager.Stub
float mCompatibleScreenScale;
public static WindowManagerService main(Context context,
- PowerManagerService pm, boolean haveInputMethods) {
- WMThread thr = new WMThread(context, pm, haveInputMethods);
+ PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs) {
+ WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs);
thr.start();
synchronized (thr) {
@@ -654,19 +658,21 @@ public class WindowManagerService extends IWindowManager.Stub
private final Context mContext;
private final PowerManagerService mPM;
private final boolean mHaveInputMethods;
+ private final boolean mAllowBootMessages;
public WMThread(Context context, PowerManagerService pm,
- boolean haveInputMethods) {
+ boolean haveInputMethods, boolean allowBootMsgs) {
super("WindowManager");
mContext = context;
mPM = pm;
mHaveInputMethods = haveInputMethods;
+ mAllowBootMessages = allowBootMsgs;
}
public void run() {
Looper.prepare();
WindowManagerService s = new WindowManagerService(mContext, mPM,
- mHaveInputMethods);
+ mHaveInputMethods, mAllowBootMessages);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
android.os.Process.setCanSelfBackground(false);
@@ -728,9 +734,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
private WindowManagerService(Context context, PowerManagerService pm,
- boolean haveInputMethods) {
+ boolean haveInputMethods, boolean showBootMsgs) {
mContext = context;
mHaveInputMethods = haveInputMethods;
+ mAllowBootMessages = showBootMsgs;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
@@ -2364,7 +2371,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client, false);
if ((w != null) && (w.mSurface != null)) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION setTransparentRegion");
Surface.openTransaction();
try {
@@ -2373,7 +2380,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.mSurface.setTransparentRegionHint(region);
} finally {
Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setTransparentRegion");
}
}
@@ -4722,6 +4729,14 @@ public class WindowManagerService extends IWindowManager.Stub
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
+ if (DEBUG_BOOT) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
+ + " mForceDisplayEnabled=" + mForceDisplayEnabled
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted, here);
+ }
if (mSystemBooted) {
return;
}
@@ -4739,6 +4754,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
void enableScreenIfNeededLocked() {
+ if (DEBUG_BOOT) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "enableScreenIfNeededLocked: mDisplayEnabled=" + mDisplayEnabled
+ + " mForceDisplayEnabled=" + mForceDisplayEnabled
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted, here);
+ }
if (mDisplayEnabled) {
return;
}
@@ -4761,6 +4784,14 @@ public class WindowManagerService extends IWindowManager.Stub
public void performEnableScreen() {
synchronized(mWindowMap) {
+ if (DEBUG_BOOT) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
+ + " mForceDisplayEnabled=" + mForceDisplayEnabled
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted, here);
+ }
if (mDisplayEnabled) {
return;
}
@@ -4774,10 +4805,22 @@ public class WindowManagerService extends IWindowManager.Stub
boolean haveBootMsg = false;
boolean haveApp = false;
boolean haveWallpaper = false;
- boolean haveKeyguard = false;
+ boolean haveKeyguard = true;
final int N = mWindows.size();
for (int i=0; i<N; i++) {
WindowState w = mWindows.get(i);
+ if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
+ // Only if there is a keyguard attached to the window manager
+ // will we consider ourselves as having a keyguard. If it
+ // isn't attached, we don't know if it wants to be shown or
+ // hidden. If it is attached, we will say we have a keyguard
+ // if the window doesn't want to be visible, because in that
+ // case it explicitly doesn't want to be shown so we should
+ // not delay turning the screen on for it.
+ boolean vis = w.mViewVisibility == View.VISIBLE
+ && w.mPolicyVisibility;
+ haveKeyguard = !vis;
+ }
if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
return;
}
@@ -4794,7 +4837,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- if (DEBUG_SCREEN_ON) {
+ if (DEBUG_SCREEN_ON || DEBUG_BOOT) {
Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
+ " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+ " haveWall=" + haveWallpaper + " haveKeyguard=" + haveKeyguard);
@@ -4815,7 +4858,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
mDisplayEnabled = true;
- if (DEBUG_SCREEN_ON) Slog.i(TAG, "******************** ENABLING SCREEN!");
+ if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG, "******************** ENABLING SCREEN!");
if (false) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -4846,6 +4889,17 @@ public class WindowManagerService extends IWindowManager.Stub
public void showBootMessage(final CharSequence msg, final boolean always) {
boolean first = false;
synchronized(mWindowMap) {
+ if (DEBUG_BOOT) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "showBootMessage: msg=" + msg + " always=" + always
+ + " mAllowBootMessages=" + mAllowBootMessages
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted, here);
+ }
+ if (!mAllowBootMessages) {
+ return;
+ }
if (!mShowingBootMessages) {
if (!always) {
return;
@@ -4864,6 +4918,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void hideBootMessagesLocked() {
+ if (DEBUG_BOOT) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.i(TAG, "hideBootMessagesLocked: mDisplayEnabled=" + mDisplayEnabled
+ + " mForceDisplayEnabled=" + mForceDisplayEnabled
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted, here);
+ }
if (mShowingBootMessages) {
mShowingBootMessages = false;
mPolicy.hideBootMessages();
@@ -4897,7 +4959,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION showStrictModeViolation");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION showStrictModeViolation");
Surface.openTransaction();
try {
if (mStrictModeFlash == null) {
@@ -4906,7 +4969,8 @@ public class WindowManagerService extends IWindowManager.Stub
mStrictModeFlash.setVisibility(on);
} finally {
Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION showStrictModeViolation");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION showStrictModeViolation");
}
}
}
@@ -5222,32 +5286,27 @@ public class WindowManagerService extends IWindowManager.Stub
startFreezingDisplayLocked(inTransaction);
mInputManager.setDisplayOrientation(0, rotation);
- // NOTE: We disable the rotation in the emulator because
- // it doesn't support hardware OpenGL emulation yet.
- if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
- && mScreenRotationAnimation.hasScreenshot()) {
- Surface.freezeDisplay(0);
- if (!inTransaction) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION setRotationUnchecked");
- Surface.openTransaction();
- }
- try {
- if (mScreenRotationAnimation != null) {
- mScreenRotationAnimation.setRotation(rotation);
- }
- } finally {
- if (!inTransaction) {
- Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setRotationUnchecked");
- }
+ if (!inTransaction) {
+ if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION setRotationUnchecked");
+ Surface.openTransaction();
+ }
+ try {
+ // NOTE: We disable the rotation in the emulator because
+ // it doesn't support hardware OpenGL emulation yet.
+ if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
+ && mScreenRotationAnimation.hasScreenshot()) {
+ mScreenRotationAnimation.setRotation(rotation);
}
Surface.setOrientation(0, rotation);
- Surface.unfreezeDisplay(0);
- } else {
- Surface.setOrientation(0, rotation);
+ } finally {
+ if (!inTransaction) {
+ Surface.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setRotationUnchecked");
+ }
}
+
rebuildBlackFrame(inTransaction);
for (int i=mWindows.size()-1; i>=0; i--) {
@@ -5840,6 +5899,10 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayMetrics dm = mDisplayMetrics;
mAppDisplayWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
mAppDisplayHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
+ if (false) {
+ Slog.i(TAG, "Set app display size: " + mAppDisplayWidth
+ + " x " + mAppDisplayHeight);
+ }
mDisplay.getMetricsWithSize(dm, mAppDisplayWidth, mAppDisplayHeight);
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,
@@ -5857,8 +5920,8 @@ public class WindowManagerService extends IWindowManager.Stub
// Compute the screen layout size class.
int screenLayout;
- int longSize = dw;
- int shortSize = dh;
+ int longSize = mAppDisplayWidth;
+ int shortSize = mAppDisplayHeight;
if (longSize < shortSize) {
int tmp = longSize;
longSize = shortSize;
@@ -6844,7 +6907,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void rebuildBlackFrame(boolean inTransaction) {
if (!inTransaction) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION rebuildBlackFrame");
Surface.openTransaction();
}
@@ -6879,7 +6942,7 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
if (!inTransaction) {
Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION rebuildBlackFrame");
}
}
@@ -7351,7 +7414,8 @@ public class WindowManagerService extends IWindowManager.Stub
createWatermark = true;
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
Surface.openTransaction();
@@ -8460,7 +8524,8 @@ public class WindowManagerService extends IWindowManager.Stub
Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
if (mWatermark != null) {
mWatermark.drawIfNeeded();
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 47f74fb..e921818 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -57,6 +57,7 @@ import java.util.ArrayList;
final class WindowState implements WindowManagerPolicy.WindowState {
static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
+ static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
final WindowManagerService mService;
@@ -671,7 +672,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
WindowManagerService.TAG, "Got surface: " + mSurface
+ ", set left=" + mFrame.left + " top=" + mFrame.top
+ ", animLayer=" + mAnimLayer);
- if (SHOW_TRANSACTIONS) {
+ if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left
+ "," + mFrame.top + ") (" +
@@ -700,7 +701,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mLastHidden = true;
} finally {
Surface.closeTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION createSurfaceLocked");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ "<<< CLOSE TRANSACTION createSurfaceLocked");
}
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Created surface " + this);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index f976301..7e9fba8 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -149,6 +149,12 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl
}
}
+enum {
+ WM_ACTION_PASS_TO_USER = 1,
+ WM_ACTION_POKE_USER_ACTIVITY = 2,
+ WM_ACTION_GO_TO_SLEEP = 4,
+};
+
// --- NativeInputManager ---
@@ -199,7 +205,8 @@ public:
virtual bool isKeyRepeatEnabled();
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
- virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual nsecs_t interceptKeyBeforeDispatching(
+ const sp<InputWindowHandle>& inputWindowHandle,
const KeyEvent* keyEvent, uint32_t policyFlags);
virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
@@ -819,12 +826,6 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
uint32_t& policyFlags) {
- enum {
- WM_ACTION_PASS_TO_USER = 1,
- WM_ACTION_POKE_USER_ACTIVITY = 2,
- WM_ACTION_GO_TO_SLEEP = 4,
- };
-
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("handleInterceptActions: Going to sleep.");
@@ -848,14 +849,14 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
}
}
-bool NativeInputManager::interceptKeyBeforeDispatching(
+nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
const sp<InputWindowHandle>& inputWindowHandle,
const KeyEvent* keyEvent, uint32_t policyFlags) {
// Policy:
// - Ignore untrusted events and pass them along.
// - Filter normal events and trusted injected events through the window manager policy to
// handle the HOME key and the like.
- bool result = false;
+ nsecs_t result = 0;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
@@ -863,13 +864,19 @@ bool NativeInputManager::interceptKeyBeforeDispatching(
jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
- jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ jlong delayMillis = env->CallLongMethod(mCallbacksObj,
gCallbacksClassInfo.interceptKeyBeforeDispatching,
inputWindowHandleObj, keyEventObj, policyFlags);
bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
- result = consumed && !error;
+ if (!error) {
+ if (delayMillis < 0) {
+ result = -1;
+ } else if (delayMillis > 0) {
+ result = milliseconds_to_nanoseconds(delayMillis);
+ }
+ }
} else {
LOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
}
@@ -1433,7 +1440,7 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
- "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z");
+ "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)J");
GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, clazz,
"dispatchUnhandledKey",
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 51eb0a3..dab0705 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -22,6 +22,9 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
ifeq ($(TARGET_BOARD_PLATFORM), omap3)
LOCAL_CFLAGS += -DNO_RGBX_8888
endif
+ifeq ($(TARGET_BOARD_PLATFORM), omap4)
+ LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+endif
ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
endif
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index 59b7e5a..3b7c09e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -122,7 +122,7 @@ status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
uint32_t displayIndex)
- : mCanDraw(true), mScreenAcquired(true)
+ : mScreenAcquired(true)
{
mDisplayEventThread = new DisplayEventThread(flinger);
}
@@ -133,14 +133,9 @@ DisplayHardwareBase::~DisplayHardwareBase()
mDisplayEventThread->requestExitAndWait();
}
-void DisplayHardwareBase::setCanDraw(bool canDraw)
-{
- mCanDraw = canDraw;
-}
-
bool DisplayHardwareBase::canDraw() const
{
- return mCanDraw && mScreenAcquired;
+ return mScreenAcquired;
}
void DisplayHardwareBase::releaseScreen() const
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index 3ebc7b6..ef2df43 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -43,7 +43,6 @@ public:
bool isScreenAcquired() const;
bool canDraw() const;
- void setCanDraw(bool canDraw);
private:
@@ -74,7 +73,6 @@ private:
};
sp<DisplayEventThreadBase> mDisplayEventThread;
- mutable int mCanDraw;
mutable int mScreenAcquired;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b01a6a3..1441a54 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -84,7 +84,6 @@ SurfaceFlinger::SurfaceFlinger()
mBootTime(systemTime()),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
- mDeferReleaseConsole(false),
mFreezeDisplay(false),
mElectronBeamAnimationMode(0),
mFreezeCount(0),
@@ -503,17 +502,9 @@ void SurfaceFlinger::handleConsoleEvents()
SurfaceFlinger::turnElectronBeamOn(mElectronBeamAnimationMode);
}
- if (mDeferReleaseConsole && hw.isScreenAcquired()) {
- // We got the release signal before the acquire signal
- mDeferReleaseConsole = false;
- hw.releaseScreen();
- }
-
if (what & eConsoleReleased) {
if (hw.isScreenAcquired()) {
hw.releaseScreen();
- } else {
- mDeferReleaseConsole = true;
}
}
@@ -1243,10 +1234,22 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
}
-void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state) {
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
+ int orientation) {
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
+ if (mCurrentState.orientation != orientation) {
+ if (uint32_t(orientation)<=eOrientation270 || orientation==42) {
+ mCurrentState.orientation = orientation;
+ flags |= eTransactionNeeded;
+ mResizeTransationPending = true;
+ } else if (orientation != eOrientationUnchanged) {
+ LOGW("setTransactionState: ignoring unrecognized orientation: %d",
+ orientation);
+ }
+ }
+
const size_t count = state.size();
for (size_t i=0 ; i<count ; i++) {
const ComposerState& s(state[i]);
@@ -1814,6 +1817,8 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy,
// redraw the screen entirely...
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
@@ -1845,7 +1850,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t hw_w = hw.getWidth();
const uint32_t hw_h = hw.getHeight();
- const Region screenBounds(hw.bounds());
+ const Region screenBounds(hw.getBounds());
GLfloat u, v;
GLuint tname;
@@ -1855,7 +1860,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
}
GLfloat vtx[8];
- const GLfloat texCoords[4][2] = { {0,1}, {0,1-v}, {u,1-v}, {u,1} };
+ const GLfloat texCoords[4][2] = { {0,0}, {0,v}, {u,v}, {u,0} };
glBindTexture(GL_TEXTURE_2D, tname);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -1941,6 +1946,12 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
s_curve_interpolator itb(nbFrames, 8.5f);
v_stretch vverts(hw_w, hw_h);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
for (int i=0 ; i<nbFrames ; i++) {
@@ -2169,7 +2180,6 @@ status_t SurfaceFlinger::turnElectronBeamOffImplLocked(int32_t mode)
glEnable(GL_SCISSOR_TEST);
hw.flip( Region(hw.bounds()) );
- hw.setCanDraw(false);
return NO_ERROR;
}
@@ -2218,7 +2228,6 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode)
if (mode & ISurfaceComposer::eElectronBeamAnimationOn) {
electronBeamOnAnimationImplLocked();
}
- hw.setCanDraw(true);
// make sure to redraw the whole screen when the animation is done
mDirtyRegion.set(hw.bounds());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 92b265e..0e642c1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -167,7 +167,8 @@ public:
virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
- virtual void setTransactionState(const Vector<ComposerState>& state);
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ int orientation);
virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags);
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
@@ -363,7 +364,6 @@ private:
Region mWormholeRegion;
bool mVisibleRegionsDirty;
bool mHwWorkListDirty;
- bool mDeferReleaseConsole;
bool mFreezeDisplay;
int32_t mElectronBeamAnimationMode;
int32_t mFreezeCount;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index e4c6028..a42a267 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -518,6 +518,7 @@ public abstract class SMSDispatcher extends Handler {
* @param address the originating address
* @param referenceNumber distinguishes concatenated messages from the same sender
* @param sequenceNumber the order of this segment in the message
+ * (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages).
* @param messageCount the number of segments in the message
* @param timestamp the service center timestamp in millis
* @param destPort the destination port for the message, or -1 for no destination port
@@ -583,7 +584,11 @@ public abstract class SMSDispatcher extends Handler {
for (int i = 0; i < cursorCount; i++) {
cursor.moveToNext();
int cursorSequence = cursor.getInt(SEQUENCE_COLUMN);
- pdus[cursorSequence - 1] = HexDump.hexStringToByteArray(
+ // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
+ if (!isCdmaWapPush) {
+ cursorSequence--;
+ }
+ pdus[cursorSequence] = HexDump.hexStringToByteArray(
cursor.getString(PDU_COLUMN));
// Read the destination port from the first segment (needed for CDMA WAP PDU).
@@ -593,7 +598,12 @@ public abstract class SMSDispatcher extends Handler {
}
}
// This one isn't in the DB, so add it
- pdus[sequenceNumber - 1] = pdu;
+ // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
+ if (isCdmaWapPush) {
+ pdus[sequenceNumber] = pdu;
+ } else {
+ pdus[sequenceNumber - 1] = pdu;
+ }
// Remove the parts from the database
mResolver.delete(mRawUri, where, whereArgs);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index c2b9e4f..4f50d24 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -18,13 +18,17 @@ package com.android.internal.telephony.cdma;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.database.SQLException;
import android.net.Uri;
+import android.os.AsyncResult;
import android.os.Message;
+import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.util.Log;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.PhoneProxy;
@@ -41,6 +45,18 @@ public class CDMALTEPhone extends CDMAPhone {
/** Secondary SMSDispatcher for 3GPP format messages. */
SMSDispatcher m3gppSMS;
+ /**
+ * Small container class used to hold information relevant to
+ * the carrier selection process. operatorNumeric can be ""
+ * if we are looking for automatic selection. operatorAlphaLong is the
+ * corresponding operator name.
+ */
+ private static class NetworkSelectMessage {
+ public Message message;
+ public String operatorNumeric;
+ public String operatorAlphaLong;
+ }
+
// Constructors
public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
super(context, ci, notifier, false);
@@ -48,6 +64,20 @@ public class CDMALTEPhone extends CDMAPhone {
}
@Override
+ public void handleMessage (Message msg) {
+ AsyncResult ar;
+ Message onComplete;
+ switch (msg.what) {
+ // handle the select network completion callbacks.
+ case EVENT_SET_NETWORK_MANUAL_COMPLETE:
+ handleSetSelectNetwork((AsyncResult) msg.obj);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ @Override
protected void initSstIcc() {
mSST = new CdmaLteServiceStateTracker(this);
mIccRecords = new CdmaLteUiccRecords(this);
@@ -109,6 +139,58 @@ public class CDMALTEPhone extends CDMAPhone {
}
@Override
+ public void
+ selectNetworkManually(OperatorInfo network,
+ Message response) {
+ // wrap the response message in our own message along with
+ // the operator's id.
+ NetworkSelectMessage nsm = new NetworkSelectMessage();
+ nsm.message = response;
+ nsm.operatorNumeric = network.getOperatorNumeric();
+ nsm.operatorAlphaLong = network.getOperatorAlphaLong();
+
+ // get the message
+ Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
+
+ mCM.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg);
+ }
+
+ /**
+ * Used to track the settings upon completion of the network change.
+ */
+ private void handleSetSelectNetwork(AsyncResult ar) {
+ // look for our wrapper within the asyncresult, skip the rest if it
+ // is null.
+ if (!(ar.userObj instanceof NetworkSelectMessage)) {
+ if (DBG) Log.d(LOG_TAG, "unexpected result from user object.");
+ return;
+ }
+
+ NetworkSelectMessage nsm = (NetworkSelectMessage) ar.userObj;
+
+ // found the object, now we send off the message we had originally
+ // attached to the request.
+ if (nsm.message != null) {
+ if (DBG) Log.d(LOG_TAG, "sending original message to recipient");
+ AsyncResult.forMessage(nsm.message, ar.result, ar.exception);
+ nsm.message.sendToTarget();
+ }
+
+ // open the shared preferences editor, and write the value.
+ // nsm.operatorNumeric is "" if we're in automatic.selection.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(NETWORK_SELECTION_KEY, nsm.operatorNumeric);
+ editor.putString(NETWORK_SELECTION_NAME_KEY, nsm.operatorAlphaLong);
+
+ // commit and log the result.
+ if (! editor.commit()) {
+ Log.e(LOG_TAG, "failed to commit network selection preference");
+ }
+
+ }
+
+ @Override
public boolean updateCurrentCarrierInProvider() {
if (mIccRecords != null) {
try {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index dded39e..ca8d9ae 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -88,6 +88,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
PendingIntent intent = tracker.mDeliveryIntent;
Intent fillIn = new Intent();
fillIn.putExtra("pdu", sms.getPdu());
+ fillIn.putExtra("format", android.telephony.SmsMessage.FORMAT_3GPP2);
try {
intent.send(mContext, Activity.RESULT_OK, fillIn);
} catch (CanceledException ex) {}
@@ -197,13 +198,18 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
int index = 0;
- int msgType = pdu[index++];
+ int msgType = (0xFF & pdu[index++]);
if (msgType != 0) {
Log.w(TAG, "Received a WAP SMS which is not WDP. Discard.");
return Intents.RESULT_SMS_HANDLED;
}
- int totalSegments = pdu[index++]; // >= 1
- int segment = pdu[index++]; // >= 0
+ int totalSegments = (0xFF & pdu[index++]); // >= 1
+ int segment = (0xFF & pdu[index++]); // >= 0
+
+ if (segment >= totalSegments) {
+ Log.e(TAG, "WDP bad segment #" + segment + " expecting 0-" + (totalSegments - 1));
+ return Intents.RESULT_SMS_HANDLED;
+ }
// Only the first segment contains sourcePort and destination Port
int sourcePort = 0;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 8f5a2eb..3d6cd68 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -726,7 +726,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker {
isPrlLoaded = false;
}
if (!isPrlLoaded) {
- newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
+ newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
} else if (!isSidsAllZeros()) {
if (!namMatch && !mIsInPrl) {
// Use default
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 4e1cc9a..c1553d8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -122,6 +122,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
PendingIntent intent = tracker.mDeliveryIntent;
Intent fillIn = new Intent();
fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
+ fillIn.putExtra("format", android.telephony.SmsMessage.FORMAT_3GPP);
try {
intent.send(mContext, Activity.RESULT_OK, fillIn);
} catch (CanceledException ex) {}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index ea030e6..2da9642 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -300,8 +300,8 @@ public class SmsMessage extends SmsMessageBase {
return null;
}
// TP-Data-Coding-Scheme
- // Class 3, UCS-2 encoding, uncompressed
- bo.write(0x0b);
+ // UCS-2 encoding, uncompressed
+ bo.write(0x08);
}
// (no TP-Validity-Period)
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index b63ff3d..e0ce322 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -21,16 +21,12 @@ import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
-import android.content.EntityIterator;
import android.content.IContentProvider;
import android.content.OperationApplicationException;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -55,84 +51,75 @@ public class MockContentProvider extends ContentProvider {
* IContentProvider that directs all calls to this MockContentProvider.
*/
private class InversionIContentProvider implements IContentProvider {
- @SuppressWarnings("unused")
+ @Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(operations);
}
- @SuppressWarnings("unused")
+ @Override
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
- @SuppressWarnings("unused")
- public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, IContentObserver observer,
- CursorWindow window) throws RemoteException {
- throw new UnsupportedOperationException("Must not come here");
- }
-
- @SuppressWarnings("unused")
+ @Override
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return MockContentProvider.this.delete(url, selection, selectionArgs);
}
- @SuppressWarnings("unused")
+ @Override
public String getType(Uri url) throws RemoteException {
return MockContentProvider.this.getType(url);
}
- @SuppressWarnings("unused")
+ @Override
public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
return MockContentProvider.this.insert(url, initialValues);
}
- @SuppressWarnings("unused")
+ @Override
public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException,
FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
- @SuppressWarnings("unused")
+ @Override
public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException,
FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
- @SuppressWarnings("unused")
+ @Override
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
String sortOrder) throws RemoteException {
return MockContentProvider.this.query(url, projection, selection,
selectionArgs, sortOrder);
}
- @SuppressWarnings("unused")
+ @Override
public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
throws RemoteException {
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
- /**
- * @hide
- */
- @SuppressWarnings("unused")
+ @Override
public Bundle call(String method, String request, Bundle args)
throws RemoteException {
return MockContentProvider.this.call(method, request, args);
}
+ @Override
public IBinder asBinder() {
throw new UnsupportedOperationException();
}
- @SuppressWarnings("unused")
+ @Override
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
return MockContentProvider.this.getStreamTypes(url, mimeTypeFilter);
}
- @SuppressWarnings("unused")
+ @Override
public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 183be41..b7733a4 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -23,9 +23,6 @@ import android.content.EntityIterator;
import android.content.IContentProvider;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -47,12 +44,6 @@ public class MockIContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, IContentObserver observer,
- CursorWindow window) {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
@SuppressWarnings("unused")
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 7c94c2d..94ad620 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -114,9 +114,9 @@ public class StatusBarTest extends TestActivity
// v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
// }
// },
- new Test("systemUiVisibility: STATUS_BAR_DISABLE_NAVIGATION") {
+ new Test("systemUiVisibility: STATUS_BAR_DISABLE_HOME") {
public void run() {
- mListView.setSystemUiVisibility(View.STATUS_BAR_DISABLE_NAVIGATION);
+ mListView.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
}
},
new Test("Double Remove") {
@@ -227,16 +227,21 @@ public class StatusBarTest extends TestActivity
}, 3000);
}
},
- new Test("Disable Navigation") {
+ new Test("Disable Home (StatusBarManager)") {
public void run() {
- mStatusBarManager.disable(StatusBarManager.DISABLE_NAVIGATION);
+ mStatusBarManager.disable(StatusBarManager.DISABLE_HOME);
}
},
- new Test("Disable Back") {
+ new Test("Disable Back (StatusBarManager)") {
public void run() {
mStatusBarManager.disable(StatusBarManager.DISABLE_BACK);
}
},
+ new Test("Disable Recent (StatusBarManager)") {
+ public void run() {
+ mStatusBarManager.disable(StatusBarManager.DISABLE_RECENT);
+ }
+ },
new Test("Disable Clock") {
public void run() {
mStatusBarManager.disable(StatusBarManager.DISABLE_CLOCK);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 3835378..c91a3bf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -23,9 +23,6 @@ import android.content.IContentProvider;
import android.content.OperationApplicationException;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -41,78 +38,84 @@ import java.util.ArrayList;
* TODO: never return null when the method is not supposed to. Return fake data instead.
*/
public final class BridgeContentProvider implements IContentProvider {
-
+ @Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
throws RemoteException, OperationApplicationException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
- public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
- String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
+ @Override
public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public String getType(Uri arg0) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
}
+ @Override
public String[] getStreamTypes(Uri arg0, String arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public AssetFileDescriptor openTypedAssetFile(Uri arg0, String arg1, Bundle arg2)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub