summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--api/current.xml835
-rw-r--r--cmds/stagefright/Android.mk7
-rw-r--r--cmds/stagefright/stagefright.cpp17
-rw-r--r--core/java/android/animation/AnimatorSet.java32
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java33
-rw-r--r--core/java/android/app/Activity.java26
-rw-r--r--core/java/android/app/ActivityManager.java26
-rw-r--r--core/java/android/app/ActivityManagerNative.java142
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/ContextImpl.java13
-rw-r--r--core/java/android/app/DownloadManager.java6
-rw-r--r--core/java/android/app/IActivityManager.java15
-rw-r--r--core/java/android/app/Instrumentation.java42
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/app/PendingIntent.java68
-rw-r--r--core/java/android/app/SearchDialog.java3
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/Intent.java24
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java42
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java16
-rw-r--r--core/java/android/os/Debug.java44
-rw-r--r--core/java/android/os/Looper.java69
-rw-r--r--core/java/android/os/StrictMode.java79
-rw-r--r--core/java/android/preference/PreferenceFrameLayout.java84
-rw-r--r--core/java/android/provider/ContactsContract.java7
-rw-r--r--core/java/android/provider/Downloads.java21
-rw-r--r--core/java/android/provider/MediaStore.java7
-rw-r--r--core/java/android/provider/Ptp.java (renamed from core/java/android/provider/Mtp.java)28
-rw-r--r--core/java/android/util/PrefixPrinter.java50
-rw-r--r--core/java/android/util/Singleton.java39
-rw-r--r--core/java/android/util/TimeUtils.java5
-rw-r--r--core/java/android/view/IWindowManager.aidl11
-rw-r--r--core/java/android/view/View.java163
-rw-r--r--core/java/android/view/ViewConfiguration.java33
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java60
-rw-r--r--core/java/android/webkit/BrowserFrame.java111
-rw-r--r--core/java/android/webkit/OverScrollGlow.java223
-rw-r--r--core/java/android/webkit/WebChromeClient.java10
-rw-r--r--core/java/android/webkit/WebSettings.java26
-rw-r--r--core/java/android/webkit/WebTextView.java100
-rw-r--r--core/java/android/webkit/WebView.java377
-rw-r--r--core/java/android/widget/AbsListView.java694
-rw-r--r--core/java/android/widget/EdgeGlow.java331
-rw-r--r--core/java/android/widget/GridView.java7
-rw-r--r--core/java/android/widget/HorizontalScrollView.java193
-rw-r--r--core/java/android/widget/ListView.java166
-rw-r--r--core/java/android/widget/OverScroller.java888
-rw-r--r--core/java/android/widget/ScrollView.java259
-rw-r--r--core/java/android/widget/Scroller.java44
-rw-r--r--core/java/android/widget/TextView.java51
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java2
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl4
-rw-r--r--core/res/res/anim/activity_close_enter.xml12
-rw-r--r--core/res/res/anim/activity_close_exit.xml14
-rw-r--r--core/res/res/anim/activity_open_enter.xml14
-rw-r--r--core/res/res/anim/activity_open_exit.xml13
-rw-r--r--core/res/res/anim/dialog_enter.xml8
-rw-r--r--core/res/res/anim/dialog_exit.xml12
-rw-r--r--core/res/res/anim/fragment_close_enter.xml25
-rw-r--r--core/res/res/anim/fragment_close_exit.xml29
-rw-r--r--core/res/res/anim/fragment_next_enter.xml12
-rw-r--r--core/res/res/anim/fragment_next_exit.xml12
-rw-r--r--core/res/res/anim/fragment_open_enter.xml26
-rw-r--r--core/res/res/anim/fragment_open_exit.xml21
-rw-r--r--core/res/res/anim/fragment_prev_enter.xml12
-rw-r--r--core/res/res/anim/fragment_prev_exit.xml12
-rw-r--r--core/res/res/anim/grow_fade_in.xml15
-rw-r--r--core/res/res/anim/grow_fade_in_center.xml15
-rw-r--r--core/res/res/anim/grow_fade_in_from_bottom.xml15
-rw-r--r--core/res/res/anim/input_method_enter.xml6
-rw-r--r--core/res/res/anim/input_method_exit.xml6
-rw-r--r--core/res/res/anim/lock_screen_behind_enter.xml4
-rw-r--r--core/res/res/anim/lock_screen_enter.xml4
-rw-r--r--core/res/res/anim/lock_screen_exit.xml4
-rw-r--r--core/res/res/anim/shrink_fade_out.xml18
-rw-r--r--core/res/res/anim/shrink_fade_out_center.xml18
-rw-r--r--core/res/res/anim/shrink_fade_out_from_bottom.xml18
-rw-r--r--core/res/res/anim/task_close_enter.xml18
-rw-r--r--core/res/res/anim/task_close_exit.xml20
-rw-r--r--core/res/res/anim/task_open_enter.xml12
-rw-r--r--core/res/res/anim/task_open_exit.xml16
-rw-r--r--core/res/res/anim/wallpaper_close_enter.xml62
-rw-r--r--core/res/res/anim/wallpaper_close_exit.xml69
-rw-r--r--core/res/res/anim/wallpaper_intra_close_enter.xml17
-rw-r--r--core/res/res/anim/wallpaper_intra_close_exit.xml14
-rw-r--r--core/res/res/anim/wallpaper_intra_open_enter.xml14
-rw-r--r--core/res/res/anim/wallpaper_intra_open_exit.xml15
-rw-r--r--core/res/res/anim/wallpaper_open_enter.xml70
-rw-r--r--core/res/res/anim/wallpaper_open_exit.xml66
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_error.pngbin704 -> 508 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sdcard.pngbin552 -> 375 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.pngbin1046 -> 476 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sdcard_usb.pngbin781 -> 415 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_wifi_in_range.pngbin1075 -> 768 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_adb.pngbin679 -> 281 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_0.pngbin392 -> 374 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_15.pngbin252 -> 253 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.pngbin443 -> 440 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.pngbin429 -> 430 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.pngbin443 -> 448 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.pngbin450 -> 446 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.pngbin452 -> 448 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.pngbin450 -> 452 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.pngbin444 -> 452 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.pngbin430 -> 440 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_unknown.pngbin449 -> 461 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim0.pngbin645 -> 396 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim1.pngbin645 -> 464 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim2.pngbin653 -> 494 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim3.pngbin659 -> 497 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim4.pngbin645 -> 520 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim5.pngbin626 -> 490 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim0.pngbin657 -> 345 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim1.pngbin653 -> 413 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim2.pngbin666 -> 445 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim3.pngbin659 -> 451 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim4.pngbin641 -> 488 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim5.pngbin641 -> 436 bytes
-rw-r--r--core/res/res/layout/alert_dialog.xml3
-rw-r--r--core/res/res/layout/preference_dialog_edittext.xml3
-rw-r--r--core/res/res/layout/preference_list_content.xml4
-rw-r--r--core/res/res/layout/preference_list_fragment.xml4
-rw-r--r--core/res/res/layout/select_dialog.xml3
-rw-r--r--core/res/res/values-xlarge/config.xml4
-rwxr-xr-xcore/res/res/values/attrs.xml27
-rw-r--r--core/res/res/values/config.xml18
-rw-r--r--core/res/res/values/public.xml3
-rwxr-xr-xcore/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/styles.xml15
-rw-r--r--core/res/res/values/themes.xml10
-rw-r--r--core/tests/ConnectivityManagerTest/AndroidManifest.xml9
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java62
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java122
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java89
-rw-r--r--core/tests/coretests/src/android/content/SyncOperationTest.java93
-rw-r--r--core/tests/coretests/src/android/util/TimeUtilsTest.java18
-rw-r--r--data/keyboards/Android.mk19
-rw-r--r--data/keyboards/Generic.kcm26
-rw-r--r--data/keyboards/Virtual.kcm26
-rw-r--r--data/keyboards/common.mk30
-rw-r--r--data/keyboards/keyboards.mk12
-rw-r--r--data/keyboards/qwerty.kcm508
-rw-r--r--data/keyboards/qwerty.kl112
-rw-r--r--data/keyboards/qwerty2.kcm505
-rw-r--r--data/keyboards/qwerty2.kl109
-rwxr-xr-xdata/sounds/AudioPackage5.mk72
-rwxr-xr-xdata/sounds/notifications/Aldebaran.oggbin0 -> 7593 bytes
-rw-r--r--data/sounds/notifications/Altair.oggbin0 -> 7021 bytes
-rwxr-xr-xdata/sounds/notifications/Antares.oggbin0 -> 10375 bytes
-rwxr-xr-xdata/sounds/notifications/Betelgeuse.oggbin0 -> 16108 bytes
-rwxr-xr-xdata/sounds/notifications/Canopus.oggbin0 -> 11948 bytes
-rw-r--r--data/sounds/notifications/Capella.oggbin0 -> 13162 bytes
-rw-r--r--data/sounds/notifications/Castor.oggbin0 -> 14648 bytes
-rwxr-xr-xdata/sounds/notifications/CetiAlpha.oggbin0 -> 26158 bytes
-rwxr-xr-xdata/sounds/notifications/CetiAlpha.wavbin0 -> 507500 bytes
-rw-r--r--data/sounds/notifications/Deneb.oggbin0 -> 14416 bytes
-rw-r--r--data/sounds/notifications/Electra.oggbin0 -> 15199 bytes
-rw-r--r--data/sounds/notifications/Fomalhaut.oggbin0 -> 22003 bytes
-rwxr-xr-xdata/sounds/notifications/Merope.oggbin0 -> 18854 bytes
-rw-r--r--data/sounds/notifications/Polaris.oggbin0 -> 20567 bytes
-rw-r--r--data/sounds/notifications/Pollux.oggbin0 -> 23397 bytes
-rw-r--r--data/sounds/notifications/Procyon.oggbin0 -> 22380 bytes
-rwxr-xr-xdata/sounds/notifications/Sirrah.oggbin0 -> 20976 bytes
-rw-r--r--data/sounds/notifications/arcturus.oggbin0 -> 7908 bytes
-rw-r--r--data/sounds/notifications/regulus.oggbin0 -> 27852 bytes
-rw-r--r--data/sounds/notifications/sirius.oggbin0 -> 26612 bytes
-rw-r--r--data/sounds/notifications/vega.oggbin0 -> 29476 bytes
-rw-r--r--data/sounds/ringtones/ANDROMEDA.oggbin0 -> 25014 bytes
-rw-r--r--data/sounds/ringtones/Aquila.oggbin0 -> 21509 bytes
-rw-r--r--data/sounds/ringtones/ArgoNavis.oggbin0 -> 89534 bytes
-rw-r--r--data/sounds/ringtones/BOOTES.oggbin0 -> 26310 bytes
-rw-r--r--data/sounds/ringtones/CANISMAJOR.oggbin0 -> 31170 bytes
-rw-r--r--data/sounds/ringtones/CASSIOPEIA.oggbin0 -> 31941 bytes
-rw-r--r--data/sounds/ringtones/Carina.oggbin0 -> 15462 bytes
-rwxr-xr-xdata/sounds/ringtones/Carina.wavbin0 -> 264848 bytes
-rw-r--r--data/sounds/ringtones/Centaurus.oggbin0 -> 27681 bytes
-rw-r--r--data/sounds/ringtones/Cygnus.oggbin0 -> 29844 bytes
-rw-r--r--data/sounds/ringtones/Draco.oggbin0 -> 31819 bytes
-rw-r--r--data/sounds/ringtones/Eridani.oggbin0 -> 36585 bytes
-rw-r--r--data/sounds/ringtones/Lyra.oggbin0 -> 42540 bytes
-rw-r--r--data/sounds/ringtones/Machina.oggbin0 -> 38077 bytes
-rw-r--r--data/sounds/ringtones/Orion.oggbin0 -> 54456 bytes
-rw-r--r--data/sounds/ringtones/PERSEUS.oggbin0 -> 112981 bytes
-rw-r--r--data/sounds/ringtones/Pegasus.oggbin0 -> 86731 bytes
-rw-r--r--data/sounds/ringtones/Pyxis.oggbin0 -> 16653 bytes
-rw-r--r--data/sounds/ringtones/Rigel.oggbin0 -> 32129 bytes
-rw-r--r--data/sounds/ringtones/Scarabaeus.oggbin0 -> 108968 bytes
-rw-r--r--data/sounds/ringtones/Sceptrum.oggbin0 -> 294019 bytes
-rw-r--r--data/sounds/ringtones/Solarium.oggbin0 -> 60201 bytes
-rw-r--r--data/sounds/ringtones/Testudo.oggbin0 -> 72078 bytes
-rw-r--r--data/sounds/ringtones/URSAMINOR.oggbin0 -> 144112 bytes
-rw-r--r--data/sounds/ringtones/Vespa.oggbin0 -> 18043 bytes
-rw-r--r--data/sounds/ringtones/hydra.oggbin0 -> 22962 bytes
-rw-r--r--docs/html/guide/guide_toc.cs3
-rw-r--r--docs/html/guide/topics/fragments/index.jd2
-rw-r--r--docs/html/guide/topics/fundamentals.jd55
-rw-r--r--docs/html/guide/topics/ui/actionbar.jd332
-rw-r--r--docs/html/guide/topics/ui/declaring-layout.jd4
-rw-r--r--docs/html/guide/topics/ui/dialogs.jd22
-rw-r--r--docs/html/guide/topics/ui/menus.jd96
-rw-r--r--docs/html/images/ui/actionbar-item-withtext.pngbin0 -> 6582 bytes
-rw-r--r--docs/html/images/ui/actionbar-logo.pngbin0 -> 8616 bytes
-rw-r--r--docs/html/images/ui/actionbar.pngbin0 -> 7661 bytes
-rw-r--r--drm/common/Android.mk1
-rw-r--r--drm/common/DrmEngineBase.cpp10
-rw-r--r--drm/common/DrmMetadata.cpp117
-rw-r--r--drm/common/IDrmManagerService.cpp86
-rw-r--r--drm/common/ReadWriteUtils.cpp4
-rw-r--r--drm/drmserver/DrmManager.cpp16
-rw-r--r--drm/drmserver/DrmManagerService.cpp55
-rw-r--r--drm/java/android/drm/DrmErrorEvent.java5
-rw-r--r--drm/java/android/drm/DrmEvent.java6
-rw-r--r--drm/java/android/drm/DrmManagerClient.java97
-rw-r--r--drm/jni/android_drm_DrmManagerClient.cpp41
-rw-r--r--drm/libdrmframework/DrmManagerClient.cpp10
-rw-r--r--drm/libdrmframework/DrmManagerClientImpl.cpp14
-rw-r--r--drm/libdrmframework/include/DrmManager.h9
-rw-r--r--drm/libdrmframework/include/DrmManagerClientImpl.h18
-rw-r--r--drm/libdrmframework/include/DrmManagerService.h8
-rw-r--r--drm/libdrmframework/include/IDrmManagerService.h18
-rw-r--r--drm/libdrmframework/plugins/common/include/DrmEngineBase.h26
-rw-r--r--drm/libdrmframework/plugins/common/include/IDrmEngine.h19
-rw-r--r--drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h8
-rw-r--r--drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp11
-rw-r--r--graphics/java/android/graphics/ComposeShader.java4
-rw-r--r--graphics/java/android/renderscript/RenderScript.java6
-rw-r--r--include/drm/DrmManagerClient.h18
-rw-r--r--include/drm/DrmMetadata.h111
-rw-r--r--include/media/stagefright/ColorConverter.h42
-rw-r--r--include/media/stagefright/MetaData.h19
-rw-r--r--libs/hwui/ResourceCache.cpp5
-rw-r--r--libs/hwui/ResourceCache.h1
-rw-r--r--libs/rs/RenderScript.h15
-rw-r--r--libs/rs/rs.spec5
-rw-r--r--libs/rs/rsContext.cpp45
-rw-r--r--libs/rs/rsContext.h12
-rw-r--r--libs/rs/rsElement.cpp2
-rw-r--r--libs/rs/rsProgramFragment.cpp6
-rw-r--r--libs/rs/rsProgramVertex.cpp44
-rw-r--r--libs/rs/rsProgramVertex.h2
-rw-r--r--libs/rs/rsShaderCache.cpp2
-rw-r--r--libs/rs/rsType.cpp2
-rw-r--r--libs/ui/Android.mk43
-rw-r--r--libs/ui/KeyCharacterMap.cpp1
-rw-r--r--libs/ui/Overlay.cpp9
-rw-r--r--media/java/android/media/AudioManager.java7
-rw-r--r--media/java/android/media/MtpDatabase.java4
-rw-r--r--media/java/android/media/PtpClient.java (renamed from media/java/android/media/MtpClient.java)12
-rw-r--r--media/java/android/media/PtpCursor.java (renamed from media/java/android/media/MtpCursor.java)78
-rwxr-xr-xmedia/java/android/media/videoeditor/MediaImageItem.java35
-rw-r--r--media/jni/Android.mk4
-rw-r--r--media/jni/android_media_MediaPlayer.cpp12
-rw-r--r--media/jni/android_media_MtpDatabase.cpp9
-rw-r--r--media/jni/android_media_PtpClient.cpp (renamed from media/jni/android_media_MtpClient.cpp)48
-rw-r--r--media/jni/android_media_PtpCursor.cpp (renamed from media/jni/android_media_MtpCursor.cpp)42
-rw-r--r--media/libmediaplayerservice/Android.mk1
-rw-r--r--media/libstagefright/Android.mk2
-rw-r--r--media/libstagefright/AwesomePlayer.cpp262
-rw-r--r--media/libstagefright/MetaData.cpp35
-rw-r--r--media/libstagefright/OMXCodec.cpp455
-rw-r--r--media/libstagefright/SampleTable.cpp4
-rw-r--r--media/libstagefright/StagefrightMetadataRetriever.cpp29
-rw-r--r--media/libstagefright/codecs/avc/dec/AVCDecoder.cpp27
-rw-r--r--media/libstagefright/colorconversion/Android.mk16
-rw-r--r--media/libstagefright/colorconversion/ColorConverter.cpp192
-rw-r--r--media/libstagefright/colorconversion/SoftwareRenderer.cpp147
-rw-r--r--media/libstagefright/include/AwesomePlayer.h33
-rw-r--r--media/libstagefright/include/SoftwareRenderer.h13
-rw-r--r--media/libstagefright/omx/Android.mk1
-rw-r--r--media/mtp/Android.mk31
-rw-r--r--media/mtp/MtpPacket.cpp4
-rw-r--r--media/mtp/MtpProperty.cpp4
-rw-r--r--media/mtp/MtpProperty.h2
-rw-r--r--media/mtp/MtpServer.cpp46
-rw-r--r--media/mtp/MtpServer.h1
-rw-r--r--media/mtp/PtpCursor.cpp (renamed from media/mtp/MtpCursor.cpp)46
-rw-r--r--media/mtp/PtpCursor.h (renamed from media/mtp/MtpCursor.h)12
-rw-r--r--media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java10
-rw-r--r--media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java10
-rw-r--r--media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java38
-rw-r--r--media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java8
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.pngbin359 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.pngbin459 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.pngbin381 -> 409 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.pngbin381 -> 409 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.pngbin654 -> 431 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.pngbin639 -> 431 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.pngbin0 -> 234 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.pngbin741 -> 458 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.pngbin734 -> 458 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.pngbin789 -> 450 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.pngbin763 -> 450 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.pngbin0 -> 258 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.pngbin794 -> 424 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.pngbin806 -> 424 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.pngbin0 -> 237 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.pngbin745 -> 517 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.pngbin0 -> 234 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.pngbin0 -> 234 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.pngbin0 -> 236 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.pngbin3343 -> 554 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.pngbin0 -> 554 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.pngbin771 -> 492 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.pngbin1096 -> 492 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.pngbin3734 -> 554 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.pngbin3744 -> 554 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.pngbin3827 -> 616 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.pngbin3814 -> 616 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.pngbin3967 -> 497 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.pngbin3922 -> 497 bytes
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar.xml13
-rw-r--r--packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java4
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java40
-rw-r--r--policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java11
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java11
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java196
-rw-r--r--services/java/com/android/server/StrictModeFlash.java114
-rw-r--r--services/java/com/android/server/UsbObserver.java6
-rw-r--r--services/java/com/android/server/WindowManagerService.java105
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java126
-rw-r--r--services/java/com/android/server/am/ActivityStack.java275
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java39
-rw-r--r--services/java/com/android/server/am/TaskRecord.java10
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java267
-rw-r--r--telephony/java/android/telephony/SmsManager.java62
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl26
-rw-r--r--telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java8
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java19
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java5
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java12
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java166
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java139
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java59
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java302
-rw-r--r--test-runner/src/android/test/mock/MockContext.java5
-rw-r--r--tests/StatusBar/AndroidManifest.xml6
-rw-r--r--tests/StatusBar/res/drawable-mdpi/emo_im_kissing.pngbin0 -> 3492 bytes
-rw-r--r--tests/StatusBar/res/layout/notification_builder_test.xml819
-rw-r--r--tests/StatusBar/res/values/styles.xml63
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java392
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java2
-rw-r--r--tools/aapt/Package.cpp29
-rw-r--r--tools/aapt/Resource.cpp27
-rw-r--r--tools/aapt/ResourceTable.cpp23
-rw-r--r--tools/aapt/ResourceTable.h6
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java6
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java45
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java2
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java225
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java32
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java109
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java73
-rw-r--r--tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java1
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java1
-rw-r--r--tools/validatekeymaps/Android.mk34
-rw-r--r--tools/validatekeymaps/Main.cpp110
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java2
366 files changed, 12909 insertions, 2685 deletions
diff --git a/Android.mk b/Android.mk
index 4e01b97..7a24663 100644
--- a/Android.mk
+++ b/Android.mk
@@ -444,7 +444,7 @@ web_docs_sample_code_flags := \
## SDK version identifiers used in the published docs
# major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=2.2
+framework_docs_SDK_VERSION:=2.3
# release version (ie "Release x") (full releases only)
framework_docs_SDK_REL_ID:=1
# flag to build offline docs for a preview release
diff --git a/api/current.xml b/api/current.xml
index 3d5a265..7624086 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6774,6 +6774,39 @@
visibility="public"
>
</field>
+<field name="overScrollFooter"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843459"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="overScrollHeader"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843458"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="overScrollMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843457"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="padding"
type="int"
transient="false"
@@ -12904,6 +12937,23 @@
>
</field>
</class>
+<class name="R.fraction"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="R.fraction"
+ type="android.R.fraction"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
<class name="R.id"
extends="java.lang.Object"
abstract="false"
@@ -18944,7 +18994,7 @@
visibility="public"
>
<method name="after"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
abstract="false"
native="false"
synchronized="false"
@@ -18957,7 +19007,7 @@
</parameter>
</method>
<method name="after"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
abstract="false"
native="false"
synchronized="false"
@@ -18970,7 +19020,7 @@
</parameter>
</method>
<method name="before"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
abstract="false"
native="false"
synchronized="false"
@@ -18983,7 +19033,7 @@
</parameter>
</method>
<method name="with"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
abstract="false"
native="false"
synchronized="false"
@@ -23927,6 +23977,21 @@
<parameter name="packageName" type="java.lang.String">
</parameter>
</method>
+<method name="moveTaskToFront"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="taskId" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="restartPackage"
return="void"
abstract="false"
@@ -23940,6 +24005,17 @@
<parameter name="packageName" type="java.lang.String">
</parameter>
</method>
+<field name="MOVE_TASK_WITH_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="RECENT_WITH_EXCLUDED"
type="int"
transient="false"
@@ -33066,6 +33142,25 @@
visibility="public"
>
</method>
+<method name="getActivities"
+ return="android.app.PendingIntent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="getActivity"
return="android.app.PendingIntent"
abstract="false"
@@ -47145,6 +47240,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="true"
@@ -48602,6 +48710,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="false"
@@ -52554,6 +52675,17 @@
visibility="public"
>
</field>
+<field name="FLAG_ACTIVITY_CLEAR_TASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32768"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_ACTIVITY_CLEAR_TOP"
type="int"
transient="false"
@@ -52708,6 +52840,17 @@
visibility="public"
>
</field>
+<field name="FLAG_ACTIVITY_TASK_ON_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_DEBUG_LOG_RESOLUTION"
type="int"
transient="false"
@@ -70609,17 +70752,6 @@
<parameter name="message" type="java.lang.String">
</parameter>
</constructor>
-<field name="TYPE_DRM_INFO_ACQUISITION_FAILED"
- type="int"
- transient="false"
- volatile="false"
- value="2008"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TYPE_NOT_SUPPORTED"
type="int"
transient="false"
@@ -70753,17 +70885,6 @@
visibility="public"
>
</method>
-<field name="DRM_INFO_OBJECT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;drm_info_object&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="DRM_INFO_STATUS_OBJECT"
type="java.lang.String"
transient="false"
@@ -70786,17 +70907,6 @@
visibility="public"
>
</field>
-<field name="TYPE_DRM_INFO_ACQUIRED"
- type="int"
- transient="false"
- volatile="false"
- value="1003"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TYPE_DRM_INFO_PROCESSED"
type="int"
transient="false"
@@ -71272,6 +71382,19 @@
</parameter>
</constructor>
<method name="acquireDrmInfo"
+ return="android.drm.DrmInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmInfoRequest" type="android.drm.DrmInfoRequest">
+</parameter>
+</method>
+<method name="acquireRights"
return="int"
abstract="false"
native="false"
@@ -71469,6 +71592,32 @@
<parameter name="mimeType" type="java.lang.String">
</parameter>
</method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
<method name="getOriginalMimeType"
return="java.lang.String"
abstract="false"
@@ -98978,6 +99127,17 @@
visibility="public"
>
</field>
+<field name="MODE_IN_COMMUNICATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MODE_NORMAL"
type="int"
transient="false"
@@ -144138,6 +144298,17 @@
visibility="public"
>
</method>
+<method name="penaltyFlashScreen"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="penaltyLog"
return="android.os.StrictMode.ThreadPolicy.Builder"
abstract="false"
@@ -155992,6 +156163,17 @@
visibility="public"
>
</field>
+<field name="GROUP_IS_READ_ONLY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;group_is_read_only&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="GROUP_VISIBLE"
type="java.lang.String"
transient="false"
@@ -157824,6 +158006,17 @@
visibility="public"
>
</field>
+<field name="ACTION_MTP_SESSION_END"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.provider.action.MTP_SESSION_END&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_VIDEO_CAPTURE"
type="java.lang.String"
transient="false"
@@ -174449,6 +174642,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="false"
@@ -205520,6 +205726,17 @@
visibility="public"
>
</method>
+<method name="getOverScrollMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPaddingBottom"
return="int"
abstract="false"
@@ -206851,6 +207068,25 @@
<parameter name="heightMeasureSpec" type="int">
</parameter>
</method>
+<method name="onOverScrolled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="scrollX" type="int">
+</parameter>
+<parameter name="scrollY" type="int">
+</parameter>
+<parameter name="clampedX" type="boolean">
+</parameter>
+<parameter name="clampedY" type="boolean">
+</parameter>
+</method>
<method name="onRestoreInstanceState"
return="void"
abstract="false"
@@ -207004,6 +207240,35 @@
<parameter name="visibility" type="int">
</parameter>
</method>
+<method name="overScrollBy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="deltaX" type="int">
+</parameter>
+<parameter name="deltaY" type="int">
+</parameter>
+<parameter name="scrollX" type="int">
+</parameter>
+<parameter name="scrollY" type="int">
+</parameter>
+<parameter name="scrollRangeX" type="int">
+</parameter>
+<parameter name="scrollRangeY" type="int">
+</parameter>
+<parameter name="maxOverScrollX" type="int">
+</parameter>
+<parameter name="maxOverScrollY" type="int">
+</parameter>
+<parameter name="isTouchEvent" type="boolean">
+</parameter>
+</method>
<method name="performClick"
return="boolean"
abstract="false"
@@ -207921,6 +208186,19 @@
<parameter name="l" type="android.view.View.OnTouchListener">
</parameter>
</method>
+<method name="setOverScrollMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overScrollMode" type="int">
+</parameter>
+</method>
<method name="setPadding"
return="void"
abstract="false"
@@ -208755,6 +209033,39 @@
visibility="public"
>
</field>
+<field name="OVER_SCROLL_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVER_SCROLL_IF_CONTENT_SCROLLS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVER_SCROLL_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
type="int[]"
transient="false"
@@ -209635,6 +209946,28 @@
visibility="public"
>
</method>
+<method name="getScaledOverflingDistance"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaledOverscrollDistance"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getScaledPagingTouchSlop"
return="int"
abstract="false"
@@ -220872,6 +221205,17 @@
visibility="public"
>
</method>
+<method name="getShortcutInputMethodsAndSubtypes"
+ return="java.util.Map&lt;android.view.inputmethod.InputMethodInfo, java.util.List&lt;android.view.inputmethod.InputMethodSubtype&gt;&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="hideSoftInputFromInputMethod"
return="void"
abstract="false"
@@ -221049,6 +221393,23 @@
<parameter name="id" type="java.lang.String">
</parameter>
</method>
+<method name="setInputMethodAndSubtype"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="android.os.IBinder">
+</parameter>
+<parameter name="id" type="java.lang.String">
+</parameter>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
<method name="showInputMethodAndSubtypeEnabler"
return="void"
abstract="false"
@@ -224474,6 +224835,17 @@
visibility="public"
>
</method>
+<method name="getUseWebViewBackgroundForOverscrollBackground"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getUseWideViewPort"
return="boolean"
abstract="false"
@@ -225079,6 +225451,19 @@
<parameter name="use" type="boolean">
</parameter>
</method>
+<method name="setUseWebViewBackgroundForOverscrollBackground"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="boolean">
+</parameter>
+</method>
<method name="setUseWideViewPort"
return="void"
abstract="false"
@@ -237520,6 +237905,28 @@
visibility="public"
>
</method>
+<method name="getOverscrollFooter"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOverscrollHeader"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="removeFooterView"
return="boolean"
abstract="false"
@@ -237611,6 +238018,32 @@
<parameter name="itemsCanFocus" type="boolean">
</parameter>
</method>
+<method name="setOverscrollFooter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="footer" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setOverscrollHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -238162,6 +238595,334 @@
</parameter>
</method>
</interface>
+<class name="OverScroller"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="interpolator" type="android.graphics.Interpolator">
+</parameter>
+<parameter name="bounceCoefficientX" type="float">
+</parameter>
+<parameter name="bounceCoefficientY" type="float">
+</parameter>
+<parameter name="flywheel" type="boolean">
+</parameter>
+</constructor>
+<method name="abortAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="computeScrollOffset"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="forceFinished"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="finished" type="boolean">
+</parameter>
+</method>
+<method name="getCurrX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFinished"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isOverScrolled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="notifyHorizontalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="finalX" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+</method>
+<method name="notifyVerticalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="finalY" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="setFriction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="friction" type="float">
+</parameter>
+</method>
+<method name="springBack"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+</class>
<class name="PopupMenu"
extends="java.lang.Object"
abstract="false"
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 654d9dc..93baefd 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -7,13 +7,16 @@ LOCAL_SRC_FILES:= \
SineSource.cpp
LOCAL_SHARED_LIBRARIES := \
- libstagefright libmedia libutils libbinder libstagefright_foundation
+ libstagefright libmedia libutils libbinder libstagefright_foundation \
+ libskia
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
frameworks/base/media/libstagefright \
frameworks/base/media/libstagefright/include \
- $(TOP)/frameworks/base/include/media/stagefright/openmax
+ $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ external/skia/include/core \
+ external/skia/include/images \
LOCAL_CFLAGS += -Wno-multichar
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 579d8c3..7e7f6d1 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -49,6 +49,10 @@
#include <media/stagefright/MPEG2TSWriter.h>
#include <media/stagefright/MPEG4Writer.h>
+#include <private/media/VideoFrame.h>
+#include <SkBitmap.h>
+#include <SkImageEncoder.h>
+
#include <fcntl.h>
using namespace android;
@@ -681,6 +685,19 @@ int main(int argc, char **argv) {
if (mem != NULL) {
printf("captureFrame(%s) => OK\n", filename);
+
+ VideoFrame *frame = (VideoFrame *)mem->pointer();
+
+ SkBitmap bitmap;
+ bitmap.setConfig(
+ SkBitmap::kRGB_565_Config, frame->mWidth, frame->mHeight);
+
+ bitmap.setPixels((uint8_t *)frame + sizeof(VideoFrame));
+
+ CHECK(SkImageEncoder::EncodeFile(
+ "/sdcard/out.jpg", bitmap,
+ SkImageEncoder::kJPEG_Type,
+ SkImageEncoder::kDefaultQuality));
} else {
mem = retriever->extractAlbumArt();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 9ba9388..f5420d1 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -341,6 +341,20 @@ public final class AnimatorSet extends Animator {
return this;
}
+ @Override
+ public void setupStartValues() {
+ for (Node node : mNodes) {
+ node.animation.setupStartValues();
+ }
+ }
+
+ @Override
+ public void setupEndValues() {
+ for (Node node : mNodes) {
+ node.animation.setupEndValues();
+ }
+ }
+
/**
* {@inheritDoc}
*
@@ -401,6 +415,7 @@ public final class AnimatorSet extends Animator {
}
}
});
+ delayAnim.start();
}
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
@@ -408,6 +423,11 @@ public final class AnimatorSet extends Animator {
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
+ if (mNodes.size() == 0) {
+ // Handle unusual case where empty AnimatorSet is started - should send out
+ // end event immediately since the event will not be sent out at all otherwise
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
}
}
}
@@ -894,7 +914,7 @@ public final class AnimatorSet extends Animator {
* @param anim The animation that will play when the animation supplied to the
* {@link AnimatorSet#play(Animator)} method starts.
*/
- public void with(Animator anim) {
+ public Builder with(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -903,6 +923,7 @@ public final class AnimatorSet extends Animator {
}
Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
node.addDependency(dependency);
+ return this;
}
/**
@@ -913,7 +934,7 @@ public final class AnimatorSet extends Animator {
* @param anim The animation that will play when the animation supplied to the
* {@link AnimatorSet#play(Animator)} method ends.
*/
- public void before(Animator anim) {
+ public Builder before(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -922,6 +943,7 @@ public final class AnimatorSet extends Animator {
}
Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
node.addDependency(dependency);
+ return this;
}
/**
@@ -932,7 +954,7 @@ public final class AnimatorSet extends Animator {
* @param anim The animation whose end will cause the animation supplied to the
* {@link AnimatorSet#play(Animator)} method to play.
*/
- public void after(Animator anim) {
+ public Builder after(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -941,6 +963,7 @@ public final class AnimatorSet extends Animator {
}
Dependency dependency = new Dependency(node, Dependency.AFTER);
mCurrentNode.addDependency(dependency);
+ return this;
}
/**
@@ -951,11 +974,12 @@ public final class AnimatorSet extends Animator {
* @param delay The number of milliseconds that should elapse before the
* animation starts.
*/
- public void after(long delay) {
+ public Builder after(long delay) {
// setup dummy ValueAnimator just to run the clock
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(delay);
after(anim);
+ return this;
}
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index b021e75..e192067 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -19,6 +19,7 @@ package android.animation;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.AndroidRuntimeException;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
@@ -860,21 +861,22 @@ public class ValueAnimator extends Animator {
/**
* Start the animation playing. This version of start() takes a boolean flag that indicates
* whether the animation should play in reverse. The flag is usually false, but may be set
- * to true if called from the reverse() method/
+ * to true if called from the reverse() method.
+ *
+ * <p>The animation started by calling this method will be run on the thread that called
+ * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+ * this is not the case). Also, if the animation will animate
+ * properties of objects in the view hierarchy, then the calling thread should be the UI
+ * thread for that view hierarchy.</p>
*
* @param playBackwards Whether the ValueAnimator should start playing in reverse.
*/
private void start(boolean playBackwards) {
- mPlayingBackwards = playBackwards;
- Looper looper = Looper.getMainLooper();
- final boolean isUiThread;
- if (looper != null) {
- isUiThread = Thread.currentThread() == looper.getThread();
- } else {
- // ignore check if we don't have a Looper (this isn't an Activity)
- isUiThread = true;
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
- if ((mStartDelay == 0) && isUiThread) {
+ mPlayingBackwards = playBackwards;
+ if (mStartDelay == 0) {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
@@ -912,9 +914,14 @@ public class ValueAnimator extends Animator {
listener.onAnimationCancel(this);
}
}
- // Just set the CANCELED flag - this causes the animation to end the next time a frame
- // is processed.
- mPlayingState = CANCELED;
+ // Only cancel if the animation is actually running or has been started and is about
+ // to run
+ if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
+ sDelayedAnims.get().contains(this)) {
+ // Just set the CANCELED flag - this causes the animation to end the next time a frame
+ // is processed.
+ mPlayingState = CANCELED;
+ }
}
@Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5174f19..d69a179 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3135,6 +3135,30 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Launch a new activity. You will not receive any information about when
+ * the activity exits. This implementation overrides the base version,
+ * providing information about
+ * the activity performing the launch. Because of this additional
+ * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
+ * required; if not specified, the new activity will be added to the
+ * task of the caller.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param intents The intents to start.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see #startActivityForResult
+ */
+ @Override
+ public void startActivities(Intent[] intents) {
+ mInstrumentation.execStartActivities(this, mMainThread.getApplicationThread(),
+ mToken, this, intents);
+ }
+
+ /**
* Like {@link #startActivity(Intent)}, but taking a IntentSender
* to start; see
* {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
@@ -3616,7 +3640,7 @@ public class Activity extends ContextThemeWrapper
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
mParent == null ? mToken : mParent.mToken,
- mEmbeddedID, requestCode, data, null, flags);
+ mEmbeddedID, requestCode, new Intent[] { data }, null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fe1e7d7..e168034 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -334,6 +334,32 @@ public class ActivityManager {
}
/**
+ * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
+ * activity along with the task, so it is positioned immediately behind
+ * the task.
+ */
+ public static final int MOVE_TASK_WITH_HOME = 0x00000001;
+
+ /**
+ * Ask that the task associated with a given task ID be moved to the
+ * front of the stack, so it is now visible to the user. Requires that
+ * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
+ * or a SecurityException will be thrown.
+ *
+ * @param taskId The identifier of the task to be moved, as found in
+ * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+ * @param flags Additional operational flags, 0 or more of
+ * {@link #MOVE_TASK_WITH_HOME}.
+ */
+ public void moveTaskToFront(int taskId, int flags) {
+ try {
+ ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ }
+ }
+
+ /**
* Information you can retrieve about a particular Service that is
* currently running in the system.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 8cc6428..f3cc4ee 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -41,6 +41,7 @@ import android.os.StrictMode;
import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
+import android.util.Singleton;
import java.util.ArrayList;
import java.util.List;
@@ -52,8 +53,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*/
- static public IActivityManager asInterface(IBinder obj)
- {
+ static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
@@ -62,27 +62,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
if (in != null) {
return in;
}
-
+
return new ActivityManagerProxy(obj);
}
-
+
/**
* Retrieve the system's default/global activity manager.
*/
- static public IActivityManager getDefault()
- {
- if (gDefault != null) {
- //if (Config.LOGV) Log.v(
- // "ActivityManager", "returning cur default = " + gDefault);
- return gDefault;
- }
- IBinder b = ServiceManager.getService("activity");
- if (Config.LOGV) Log.v(
- "ActivityManager", "default service binder = " + b);
- gDefault = asInterface(b);
- if (Config.LOGV) Log.v(
- "ActivityManager", "default service = " + gDefault);
- return gDefault;
+ static public IActivityManager getDefault() {
+ return gDefault.get();
}
/**
@@ -95,13 +83,12 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return sSystemReady;
}
static boolean sSystemReady = false;
-
+
/**
* Convenience for sending a sticky broadcast. For internal use only.
* If you don't care about permission, use null.
*/
- static public void broadcastStickyIntent(Intent intent, String permission)
- {
+ static public void broadcastStickyIntent(Intent intent, String permission) {
try {
getDefault().broadcastIntent(
null, intent, null, null, Activity.RESULT_OK, null, null,
@@ -117,8 +104,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
}
}
- public ActivityManagerNative()
- {
+ public ActivityManagerNative() {
attachInterface(this, descriptor);
}
@@ -492,7 +478,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case MOVE_TASK_TO_FRONT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int task = data.readInt();
- moveTaskToFront(task);
+ int fl = data.readInt();
+ moveTaskToFront(task, fl);
reply.writeNoException();
return true;
}
@@ -791,13 +778,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
IBinder token = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
- Intent requestIntent = data.readInt() != 0
- ? Intent.CREATOR.createFromParcel(data) : null;
- String requestResolvedType = data.readString();
+ Intent[] requestIntents;
+ String[] requestResolvedTypes;
+ if (data.readInt() != 0) {
+ requestIntents = data.createTypedArray(Intent.CREATOR);
+ requestResolvedTypes = data.createStringArray();
+ } else {
+ requestIntents = null;
+ requestResolvedTypes = null;
+ }
int fl = data.readInt();
IIntentSender res = getIntentSender(type, packageName, token,
- resultWho, requestCode, requestIntent,
- requestResolvedType, fl);
+ resultWho, requestCode, requestIntents,
+ requestResolvedTypes, fl);
reply.writeNoException();
reply.writeStrongBinder(res != null ? res.asBinder() : null);
return true;
@@ -1355,17 +1348,55 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case START_ACTIVITIES_IN_PACKAGE_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int uid = data.readInt();
+ Intent[] intents = data.createTypedArray(Intent.CREATOR);
+ String[] resolvedTypes = data.createStringArray();
+ IBinder resultTo = data.readStrongBinder();
+ int result = startActivitiesInPackage(uid, intents, resolvedTypes, resultTo);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
+ case START_ACTIVITIES_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent[] intents = data.createTypedArray(Intent.CREATOR);
+ String[] resolvedTypes = data.createStringArray();
+ IBinder resultTo = data.readStrongBinder();
+ int result = startActivities(app, intents, resolvedTypes, resultTo);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
}
-
+
+ }
+
return super.onTransact(code, data, reply, flags);
}
- public IBinder asBinder()
- {
+ public IBinder asBinder() {
return this;
}
- private static IActivityManager gDefault;
+ private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
+ protected IActivityManager create() {
+ IBinder b = ServiceManager.getService("activity");
+ if (Config.LOGV) {
+ Log.v("ActivityManager", "default service binder = " + b);
+ }
+ IActivityManager am = asInterface(b);
+ if (Config.LOGV) {
+ Log.v("ActivityManager", "default service = " + am);
+ }
+ return am;
+ }
+ };
}
class ActivityManagerProxy implements IActivityManager
@@ -1829,12 +1860,13 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public void moveTaskToFront(int task) throws RemoteException
+ public void moveTaskToFront(int task, int flags) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(task);
+ data.writeInt(flags);
mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -2283,7 +2315,7 @@ class ActivityManagerProxy implements IActivityManager
}
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags)
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2293,13 +2325,13 @@ class ActivityManagerProxy implements IActivityManager
data.writeStrongBinder(token);
data.writeString(resultWho);
data.writeInt(requestCode);
- if (intent != null) {
+ if (intents != null) {
data.writeInt(1);
- intent.writeToParcel(data, 0);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
} else {
data.writeInt(0);
}
- data.writeString(resolvedType);
data.writeInt(flags);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
@@ -3026,5 +3058,39 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
+ public int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
+ data.writeStrongBinder(resultTo);
+ mRemote.transact(START_ACTIVITIES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
+ public int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
+ data.writeStrongBinder(resultTo);
+ mRemote.transact(START_ACTIVITIES_IN_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c0714e3..5c4f57a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -870,6 +870,12 @@ public final class ActivityThread {
(dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ",
(dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ",
dbStats.cache, dbStats.dbName);
+ if (dbStats.dataDump != null) {
+ int size = dbStats.dataDump.size();
+ for (int dumpIndex = 0; dumpIndex < size; dumpIndex++) {
+ printRow(pw, "%s", dbStats.dataDump.get(dumpIndex));
+ }
+ }
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 82b3f75..d2de382 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -844,6 +844,19 @@ class ContextImpl extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ throw new AndroidRuntimeException(
+ "Calling startActivities() from outside of an Activity "
+ + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
+ + " Is this really what you want?");
+ }
+ mMainThread.getInstrumentation().execStartActivities(
+ getOuterContext(), mMainThread.getApplicationThread(), null,
+ (Activity)null, intents);
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 09a21f8..4aa4d77 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -841,6 +841,12 @@ public class DownloadManager {
}
ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_DELETED, 1);
+ // if only one id is passed in, then include it in the uri itself.
+ // this will eliminate a full database scan in the download service.
+ if (ids.length == 1) {
+ return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values,
+ null, null);
+ }
return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
getWhereArgsForIds(ids));
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c9d5448..dc18083 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -134,7 +134,7 @@ public interface IActivityManager extends IInterface {
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
- public void moveTaskToFront(int task) throws RemoteException;
+ public void moveTaskToFront(int task, int flags) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
@@ -199,7 +199,8 @@ public interface IActivityManager extends IInterface {
public static final int INTENT_SENDER_SERVICE = 4;
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) throws RemoteException;
+ int requestCode, Intent[] intents, String[] resolvedTypes,
+ int flags) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer) throws RemoteException;
@@ -208,7 +209,8 @@ public interface IActivityManager extends IInterface {
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
- public void setProcessForeground(IBinder token, int pid, boolean isForeground) throws RemoteException;
+ public void setProcessForeground(IBinder token, int pid,
+ boolean isForeground) throws RemoteException;
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
@@ -332,6 +334,11 @@ public interface IActivityManager extends IInterface {
public boolean dumpHeap(String process, boolean managed, String path,
ParcelFileDescriptor fd) throws RemoteException;
+ public int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+ public int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -544,4 +551,6 @@ public interface IActivityManager extends IInterface {
int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
+ int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
+ int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 1deb9fb..ad811d8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1354,8 +1354,8 @@ public class Instrumentation {
* {@hide}
*/
public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Activity target,
- Intent intent, int requestCode) {
+ Context who, IBinder contextThread, IBinder token, Activity target,
+ Intent intent, int requestCode) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
@@ -1386,6 +1386,44 @@ public class Instrumentation {
/**
* Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+ * but accepts an array of activities to be started. Note that active
+ * {@link ActivityMonitor} objects only match against the first activity in
+ * the array.
+ *
+ * {@hide}
+ */
+ public void execStartActivities(Context who, IBinder contextThread,
+ IBinder token, Activity target, Intent[] intents) {
+ IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ if (am.match(who, null, intents[0])) {
+ am.mHits++;
+ if (am.isBlocking()) {
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+ try {
+ String[] resolvedTypes = new String[intents.length];
+ for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
+ }
+ int result = ActivityManagerNative.getDefault()
+ .startActivities(whoThread, intents, resolvedTypes, token);
+ checkStartActivityResult(result, intents[0]);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
* but for calls from a {#link Fragment}.
*
* @param who The Context from which the activity is being started.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 920e457..f5a46c5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -742,7 +742,6 @@ public class Notification implements Parcelable
mLedArgb = argb;
mLedOnMs = onMs;
mLedOffMs = offMs;
- mFlags |= FLAG_SHOW_LIGHTS;
return this;
}
@@ -763,10 +762,6 @@ public class Notification implements Parcelable
public Builder setDefaults(int defaults) {
mDefaults = defaults;
- int moreFlags = 0;
- if ((defaults & DEFAULT_LIGHTS) != 0) {
- moreFlags |= FLAG_SHOW_LIGHTS;
- }
return this;
}
@@ -851,6 +846,12 @@ public class Notification implements Parcelable
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
+ if (mLedOnMs != 0 && mLedOffMs != 0) {
+ n.flags |= FLAG_SHOW_LIGHTS;
+ }
+ if ((mDefaults & DEFAULT_LIGHTS) != 0) {
+ n.flags |= FLAG_SHOW_LIGHTS;
+ }
return n;
}
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index be1dc4a..5b43b65 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -195,7 +195,67 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Like {@link #getActivity(Context, int, Intent, int)}, but allows an
+ * array of Intents to be supplied. The first Intent in the array is
+ * taken as the primary key for the PendingIntent, like the single Intent
+ * given to {@link #getActivity(Context, int, Intent, int)}. Upon sending
+ * the resulting PendingIntent, all of the Intents are started in the same
+ * way as they would be by passing them to {@link Context#startActivities(Intent[])}.
+ *
+ * <p class="note">
+ * The <em>first</em> intent in the array will be started outside of the context of an
+ * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
+ * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent. (Activities after
+ * the first in the array are started in the context of the previous activity
+ * in the array, so FLAG_ACTIVITY_NEW_TASK is not needed nor desired for them.)
+ * </p>
+ *
+ * <p class="note">
+ * The <em>last</em> intent in the array represents the key for the
+ * PendingIntent. In other words, it is the significant element for matching
+ * (as done with the single intent given to {@link #getActivity(Context, int, Intent, int)},
+ * its content will be the subject of replacement by
+ * {@link #send(Context, int, Intent)} and {@link #FLAG_UPDATE_CURRENT}, etc.
+ * This is because it is the most specific of the supplied intents, and the
+ * UI the user actually sees when the intents are started.
+ * </p>
+ *
+ * @param context The Context in which this PendingIntent should start
+ * the activity.
+ * @param requestCode Private request code for the sender (currently
+ * not used).
+ * @param intents Array of Intents of the activities to be launched.
+ * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+ * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if {@link #FLAG_NO_CREATE} has been
+ * supplied.
+ */
+ public static PendingIntent getActivities(Context context, int requestCode,
+ Intent[] intents, int flags) {
+ String packageName = context.getPackageName();
+ String[] resolvedTypes = new String[intents.length];
+ for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
+ }
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ null, null, requestCode, intents, resolvedTypes, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -230,7 +290,8 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_BROADCAST, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -266,7 +327,8 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_SERVICE, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 18602df..0c280f9 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1109,6 +1109,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @return true if a successful launch, false if could not (e.g. bad position).
*/
protected boolean launchSuggestion(int position, int actionKey, String actionMsg) {
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
Cursor c = mSuggestionsAdapter.getCursor();
if ((c != null) && c.moveToPosition(position)) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a463afc..27be51a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -725,6 +725,28 @@ public abstract class Context {
public abstract void startActivity(Intent intent);
/**
+ * Launch multiple new activities. This is generally the same as calling
+ * {@link #startActivity(Intent)} for the first Intent in the array,
+ * that activity during its creation calling {@link #startActivity(Intent)}
+ * for the second entry, etc. Note that unlike that approach, generally
+ * none of the activities except the last in the array will be created
+ * at this point, but rather will be created when the user first visits
+ * them (due to pressing back from the activity on top).
+ *
+ * <p>This method throws {@link ActivityNotFoundException}
+ * if there was no Activity found for <em>any</em> given Intent. In this
+ * case the state of the activity stack is undefined (some Intents in the
+ * list may be on it, some not), so you probably want to avoid such situations.
+ *
+ * @param intents An array of Intents to be started.
+ *
+ * @throws ActivityNotFoundException
+ *
+ * @see PackageManager#resolveActivity
+ */
+ public abstract void startActivities(Intent[] intents);
+
+ /**
* Like {@link #startActivity(Intent)}, but taking a IntentSender
* to start. If the IntentSender is for an activity, that activity will be started
* as if you had called the regular {@link #startActivity(Intent)}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3f5d215..f8928e4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -266,6 +266,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ mBase.startActivities(intents);
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4b6333e..84cb2fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2614,13 +2614,29 @@ public class Intent implements Parcelable, Cloneable {
* animation to go to the next activity state. This doesn't mean an
* animation will never run -- if another activity change happens that doesn't
* specify this flag before the activity started here is displayed, then
- * that transition will be used. This this flag can be put to good use
+ * that transition will be used. This flag can be put to good use
* when you are going to do a series of activity operations but the
* animation seen by the user shouldn't be driven by the first activity
* change but rather a later one.
*/
public static final int FLAG_ACTIVITY_NO_ANIMATION = 0X00010000;
/**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause any existing task that would be associated with the
+ * activity to be cleared before the activity is started. That is, the activity
+ * becomes the new root of an otherwise empty task, and any old activities
+ * are finished. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+ */
+ public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;
+ /**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause a newly launching task to be placed on top of the current
+ * home activity task (if there is one). That is, pressing back from the task
+ * will always return the user to home even if that was not the last activity they
+ * saw. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+ */
+ public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
+ /**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
@@ -4871,18 +4887,22 @@ public class Intent implements Parcelable, Cloneable {
* @see #FLAG_DEBUG_LOG_RESOLUTION
* @see #FLAG_FROM_BACKGROUND
* @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
- * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
+ * @see #FLAG_ACTIVITY_CLEAR_TASK
* @see #FLAG_ACTIVITY_CLEAR_TOP
+ * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
* @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
* @see #FLAG_ACTIVITY_FORWARD_RESULT
* @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
* @see #FLAG_ACTIVITY_MULTIPLE_TASK
* @see #FLAG_ACTIVITY_NEW_TASK
+ * @see #FLAG_ACTIVITY_NO_ANIMATION
* @see #FLAG_ACTIVITY_NO_HISTORY
* @see #FLAG_ACTIVITY_NO_USER_ACTION
* @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP
* @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ * @see #FLAG_ACTIVITY_REORDER_TO_FRONT
* @see #FLAG_ACTIVITY_SINGLE_TOP
+ * @see #FLAG_ACTIVITY_TASK_ON_HOME
* @see #FLAG_RECEIVER_REGISTERED_ONLY
*/
public Intent setFlags(int flags) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 7efb7fd..87f55d2 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2507,7 +2507,7 @@ public class SQLiteDatabase extends SQLiteClosable {
if (pageCount > 0) {
dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
- db.getCachesize()));
+ db.getCachesize(), getDataDump(db)));
}
}
// if there are pooled connections, return the cache stats for them also.
@@ -2518,7 +2518,7 @@ public class SQLiteDatabase extends SQLiteClosable {
for (SQLiteDatabase pDb : connPool.getConnectionList()) {
dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+ lastnode, 0, 0, 0, pDb.getCacheHitNum(),
- pDb.getCacheMissNum(), pDb.getCachesize()));
+ pDb.getCacheMissNum(), pDb.getCachesize(), null));
}
}
} catch (SQLiteException e) {
@@ -2529,6 +2529,44 @@ public class SQLiteDatabase extends SQLiteClosable {
return dbStatsList;
}
+ private static ArrayList<String> getDataDump(SQLiteDatabase db) {
+ // create database dump of certain data from certain databases for debugging purposes
+ if (db.getPath().equalsIgnoreCase(
+ "/data/data/com.android.providers.downloads/databases/downloads.db")) {
+ String sql =
+ "select * from downloads " +
+ " where notificationpackage = 'com.google.android.gsf'" +
+ " or status >= 400";
+ Cursor cursor = db.rawQuery(sql, null);
+ try {
+ int count = cursor.getCount();
+ if (count == 0) {
+ return null;
+ }
+ ArrayList<String> buff = new ArrayList<String>();
+ buff.add(" Data from downloads.db");
+ int columnCount = cursor.getColumnCount();
+ for (int i =0; i < count && cursor.moveToNext(); i++) {
+ buff.add(" Row#" + i + "");
+ for (int j = 0; j < columnCount; j++) {
+ String colName = cursor.getColumnName(j);
+ String value = cursor.getString(j);
+ buff.add(" " + colName + " = " + value);
+ }
+ }
+ for (String s : buff) Log.i("vnoritag", s);
+ return buff;
+ } catch (SQLiteException e) {
+ Log.w(TAG, "exception in executing the sql: " + sql, e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Returns list of full pathnames of all attached databases including the main database
* by executing 'pragma database_list' on the database.
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 9496079..72377f0 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -121,27 +121,31 @@ public final class SQLiteDebug {
*/
public static class DbStats {
/** name of the database */
- public String dbName;
+ public final String dbName;
/** the page size for the database */
- public long pageSize;
+ public final long pageSize;
/** the database size */
- public long dbSize;
+ public final long dbSize;
/** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
- public int lookaside;
+ public final int lookaside;
/** statement cache stats: hits/misses/cachesize */
- public String cache;
+ public final String cache;
+
+ /** database dump of 'useful info for debugging only */
+ public final ArrayList<String> dataDump;
public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
- int hits, int misses, int cachesize) {
+ int hits, int misses, int cachesize, ArrayList<String> data) {
this.dbName = dbName;
this.pageSize = pageSize / 1024;
dbSize = (pageCount * pageSize) / 1024;
this.lookaside = lookaside;
this.cache = hits + "/" + misses + "/" + cachesize;
+ this.dataDump = data;
}
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2b4f39a..1c295a7 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -513,21 +513,27 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
}
/**
- * Count the number and aggregate size of memory allocations between
- * two points.
+ * Start counting the number and aggregate size of memory allocations.
*
- * The "start" function resets the counts and enables counting. The
- * "stop" function disables the counting so that the analysis code
- * doesn't cause additional allocations. The "get" function returns
- * the specified value.
+ * <p>The {@link #startAllocCounting() start} function resets the counts and enables counting.
+ * The {@link #stopAllocCounting() stop} function disables the counting so that the analysis
+ * code doesn't cause additional allocations. The various <code>get</code> functions return
+ * the specified value. And the various <code>reset</code> functions reset the specified
+ * count.</p>
*
- * Counts are kept for the system as a whole and for each thread.
+ * <p>Counts are kept for the system as a whole and for each thread.
* The per-thread counts for threads other than the current thread
- * are not cleared by the "reset" or "start" calls.
+ * are not cleared by the "reset" or "start" calls.</p>
*/
public static void startAllocCounting() {
VMDebug.startAllocCounting();
}
+
+ /**
+ * Stop counting the number and aggregate size of memory allocations.
+ *
+ * @see #startAllocCounting()
+ */
public static void stopAllocCounting() {
VMDebug.stopAllocCounting();
}
@@ -671,11 +677,11 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* for catching regressions in code that is expected to operate
* without causing any allocations.
*
- * Pass in the maximum number of allowed allocations. Use -1 to disable
- * the limit. Returns the previous limit.
- *
- * The preferred way to use this is:
+ * <p>Pass in the maximum number of allowed allocations. Use -1 to disable
+ * the limit. Returns the previous limit.</p>
*
+ * <p>The preferred way to use this is:
+ * <pre>
* int prevLimit = -1;
* try {
* prevLimit = Debug.setAllocationLimit(0);
@@ -683,16 +689,16 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* } finally {
* Debug.setAllocationLimit(prevLimit);
* }
- *
+ * </pre>
* This allows limits to be nested. The try/finally ensures that the
- * limit is reset if something fails.
+ * limit is reset if something fails.</p>
*
- * Exceeding the limit causes a dalvik.system.AllocationLimitError to
+ * <p>Exceeding the limit causes a dalvik.system.AllocationLimitError to
* be thrown from a memory allocation call. The limit is reset to -1
- * when this happens.
+ * when this happens.</p>
*
- * The feature may be disabled in the VM configuration. If so, this
- * call has no effect, and always returns -1.
+ * <p>The feature may be disabled in the VM configuration. If so, this
+ * call has no effect, and always returns -1.</p>
*/
public static int setAllocationLimit(int limit) {
return VMDebug.setAllocationLimit(limit);
@@ -846,6 +852,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* API for gathering and querying instruction counts.
*
* Example usage:
+ * <pre>
* Debug.InstructionCount icount = new Debug.InstructionCount();
* icount.resetAndStart();
* [... do lots of stuff ...]
@@ -855,6 +862,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* System.out.println("Method invocations: "
* + icount.globalMethodInvocations());
* }
+ * </pre>
*/
public static class InstructionCount {
private static final int NUM_INSTR = 256;
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index d360140..898c642 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -17,7 +17,9 @@
package android.os;
import android.util.Config;
+import android.util.Log;
import android.util.Printer;
+import android.util.PrefixPrinter;
/**
* Class used to run a message loop for a thread. Threads by default do
@@ -31,37 +33,38 @@ import android.util.Printer;
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
- *
+ *
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
- *
+ *
* public void run() {
* Looper.prepare();
- *
+ *
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
- *
+ *
* Looper.loop();
* }
* }</pre>
*/
public class Looper {
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final String TAG = "Looper";
+ private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
// sThreadLocal.get() will return null unless you've called prepare().
- private static final ThreadLocal sThreadLocal = new ThreadLocal();
+ private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
+ final Thread mThread;
volatile boolean mRun;
- Thread mThread;
+
private Printer mLogging = null;
- private static Looper mMainLooper = null;
-
+ private static Looper mMainLooper = null; // guarded by Looper.class
+
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
@@ -74,13 +77,13 @@ public class Looper {
}
sThreadLocal.set(new Looper());
}
-
- /** Initialize the current thread as a looper, marking it as an application's main
- * looper. The main looper for your application is created by the Android environment,
- * so you should never need to call this function yourself.
- * {@link #prepare()}
+
+ /**
+ * Initialize the current thread as a looper, marking it as an
+ * application's main looper. The main looper for your application
+ * is created by the Android environment, so you should never need
+ * to call this function yourself. See also: {@link #prepare()}
*/
-
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
@@ -92,7 +95,7 @@ public class Looper {
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
-
+
/** Returns the application's main looper, which lives in the main thread of the application.
*/
public synchronized static final Looper getMainLooper() {
@@ -100,28 +103,28 @@ public class Looper {
}
/**
- * Run the message queue in this thread. Be sure to call
+ * Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
Looper me = myLooper();
+ if (me == null) {
+ throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
+ }
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
- //if (!me.mRun) {
- // break;
- //}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
- if (me.mLogging!= null) me.mLogging.println(
+ if (me.mLogging != null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
+ if (me.mLogging != null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
@@ -134,7 +137,7 @@ public class Looper {
* null if the calling thread is not associated with a Looper.
*/
public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
+ return sThreadLocal.get();
}
/**
@@ -179,28 +182,29 @@ public class Looper {
public Thread getThread() {
return mThread;
}
-
+
/** @hide */
public MessageQueue getQueue() {
return mQueue;
}
-
+
public void dump(Printer pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mRun=" + mRun);
- pw.println(prefix + "mThread=" + mThread);
- pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
+ pw = PrefixPrinter.create(pw, prefix);
+ pw.println(this.toString());
+ pw.println("mRun=" + mRun);
+ pw.println("mThread=" + mThread);
+ pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null"));
if (mQueue != null) {
synchronized (mQueue) {
long now = SystemClock.uptimeMillis();
Message msg = mQueue.mMessages;
int n = 0;
while (msg != null) {
- pw.println(prefix + " Message " + n + ": " + msg.toString(now));
+ pw.println(" Message " + n + ": " + msg.toString(now));
n++;
msg = msg.next;
}
- pw.println(prefix + "(Total messages: " + n + ")");
+ pw.println("(Total messages: " + n + ")");
}
}
}
@@ -226,4 +230,3 @@ public class Looper {
}
}
}
-
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 4facc39..854428f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -19,9 +19,12 @@ import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.app.IActivityManager;
import android.content.Intent;
import android.util.Log;
import android.util.Printer;
+import android.util.Singleton;
+import android.view.IWindowManager;
import com.android.internal.os.RuntimeInit;
@@ -108,6 +111,7 @@ public final class StrictMode {
private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
+ private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
// Only log a duplicate stack trace to the logs every second.
private static final long MIN_LOG_INTERVAL_MS = 1000;
@@ -180,6 +184,13 @@ public final class StrictMode {
public static final int PENALTY_DEATH_ON_NETWORK = 0x200;
/**
+ * Flash the screen during violations.
+ *
+ * @hide
+ */
+ public static final int PENALTY_FLASH = 0x800;
+
+ /**
* @hide
*/
public static final int PENALTY_DROPBOX = 0x80;
@@ -202,7 +213,7 @@ public final class StrictMode {
*/
private static final int PENALTY_MASK =
PENALTY_LOG | PENALTY_DIALOG | PENALTY_DEATH | PENALTY_DROPBOX | PENALTY_GATHER |
- PENALTY_DEATH_ON_NETWORK;
+ PENALTY_DEATH_ON_NETWORK | PENALTY_FLASH;
/**
* The current VmPolicy in effect.
@@ -377,6 +388,13 @@ public final class StrictMode {
}
/**
+ * Flash the screen during a violation.
+ */
+ public Builder penaltyFlashScreen() {
+ return enable(PENALTY_FLASH);
+ }
+
+ /**
* Log detected violations to the system log.
*/
public Builder penaltyLog() {
@@ -710,7 +728,9 @@ public final class StrictMode {
StrictMode.DETECT_DISK_WRITE |
StrictMode.DETECT_DISK_READ |
StrictMode.DETECT_NETWORK |
- StrictMode.PENALTY_DROPBOX);
+ StrictMode.PENALTY_DROPBOX |
+ (IS_ENG_BUILD ? StrictMode.PENALTY_FLASH : 0)
+ );
sVmPolicyMask = StrictMode.DETECT_VM_CURSOR_LEAKS |
StrictMode.DETECT_VM_CLOSABLE_LEAKS |
StrictMode.PENALTY_DROPBOX;
@@ -904,6 +924,15 @@ public final class StrictMode {
return;
}
+ final IWindowManager windowManager = (info.policy & PENALTY_FLASH) != 0 ?
+ sWindowManager.get() : null;
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(true);
+ } catch (RemoteException unused) {
+ }
+ }
+
queue.addIdleHandler(new MessageQueue.IdleHandler() {
public boolean queueIdle() {
long loopFinishTime = SystemClock.uptimeMillis();
@@ -915,6 +944,12 @@ public final class StrictMode {
handleViolation(v);
}
records.clear();
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(false);
+ } catch (RemoteException unused) {
+ }
+ }
return false; // remove this idle handler from the array
}
});
@@ -953,7 +988,7 @@ public final class StrictMode {
}
// Not perfect, but fast and good enough for dup suppression.
- Integer crashFingerprint = info.crashInfo.stackTrace.hashCode();
+ Integer crashFingerprint = info.hashCode();
long lastViolationTime = 0;
if (mLastViolationTime.containsKey(crashFingerprint)) {
lastViolationTime = mLastViolationTime.get(crashFingerprint);
@@ -1057,11 +1092,15 @@ public final class StrictMode {
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
- ActivityManagerNative.getDefault().
- handleApplicationStrictModeViolation(
- RuntimeInit.getApplicationObject(),
- violationMaskSubset,
- info);
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am == null) {
+ Log.d(TAG, "No activity manager; failed to Dropbox violation.");
+ } else {
+ am.handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(),
+ violationMaskSubset,
+ info);
+ }
} catch (RemoteException e) {
Log.e(TAG, "RemoteException handling StrictMode violation", e);
}
@@ -1369,6 +1408,12 @@ public final class StrictMode {
}
};
+ private static Singleton<IWindowManager> sWindowManager = new Singleton<IWindowManager>() {
+ protected IWindowManager create() {
+ return IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ }
+ };
+
/**
* Enter a named critical span (e.g. an animation)
*
@@ -1510,6 +1555,24 @@ public final class StrictMode {
}
}
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 37 * result + crashInfo.stackTrace.hashCode();
+ if (numAnimationsRunning != 0) {
+ result *= 37;
+ }
+ if (broadcastIntentAction != null) {
+ result = 37 * result + broadcastIntentAction.hashCode();
+ }
+ if (tags != null) {
+ for (String tag : tags) {
+ result = 37 * result + tag.hashCode();
+ }
+ }
+ return result;
+ }
+
/**
* Create an instance of ViolationInfo initialized from a Parcel.
*/
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
new file mode 100644
index 0000000..426abf0
--- /dev/null
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * @hide
+ */
+public class PreferenceFrameLayout extends FrameLayout {
+ private static final int DEFAULT_TOP_PADDING = 0;
+ private static final int DEFAULT_BOTTOM_PADDING = 0;
+ private final int mTopPadding;
+ private final int mBottomPadding;
+ private boolean mPaddingApplied = false;
+
+ public PreferenceFrameLayout(Context context) {
+ this(context, null);
+ }
+
+ public PreferenceFrameLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.preferenceFrameLayoutStyle);
+ }
+
+ public PreferenceFrameLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);
+
+ mTopPadding = (int) a.getDimension(
+ com.android.internal.R.styleable.PreferenceFrameLayout_topPadding,
+ DEFAULT_TOP_PADDING);
+ mBottomPadding = (int) a.getDimension(
+ com.android.internal.R.styleable.PreferenceFrameLayout_bottomPadding,
+ DEFAULT_BOTTOM_PADDING);
+
+ a.recycle();
+ }
+
+ @Override
+ public void addView(View child) {
+ int topPadding = getPaddingTop();
+ int bottomPadding = getPaddingBottom();
+ // Check on the id of the child before adding it.
+ if (child != null && child.getId() != com.android.internal.R.id.default_preference_layout) {
+ // Add the padding to the view group after determining if the padding already exists.
+ if (!mPaddingApplied) {
+ topPadding += mTopPadding;
+ bottomPadding += mBottomPadding;
+ mPaddingApplied = true;
+ }
+ } else {
+ if (mPaddingApplied) {
+ topPadding -= mTopPadding;
+ bottomPadding -= mBottomPadding;
+ mPaddingApplied = false;
+ }
+ }
+ int previousTop = getPaddingTop();
+ int previousBottom = getPaddingBottom();
+ if (previousTop != topPadding || previousBottom != bottomPadding) {
+ setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+ }
+ super.addView(child);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 2d2f205..d1ca0c9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5669,6 +5669,13 @@ public final class ContactsContract {
* Type: INTEGER (boolean)
*/
public static final String FAVORITES = "favorites";
+
+ /**
+ * The "read-only" flag: "0" by default, "1" if the row cannot be modified or
+ * deleted except by a sync adapter. See {@link ContactsContract#CALLER_IS_SYNCADAPTER}.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String GROUP_IS_READ_ONLY = "group_is_read_only";
}
/**
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index ff769ad..683e603 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -382,6 +382,27 @@ public final class Downloads {
*/
public static final String COLUMN_ERROR_MSG = "errorMsg";
+ /**
+ * This column stores the source of the last update to this row.
+ * This column is only for internal use.
+ * Valid values are indicated by LAST_UPDATESRC_* constants.
+ * <P>Type: INT</P>
+ */
+ public static final String COLUMN_LAST_UPDATESRC = "lastUpdateSrc";
+
+ /**
+ * default value for {@link #COLUMN_LAST_UPDATESRC}.
+ * This value is used when this column's value is not relevant.
+ */
+ public static final int LAST_UPDATESRC_NOT_RELEVANT = 0;
+
+ /**
+ * One of the values taken by {@link #COLUMN_LAST_UPDATESRC}.
+ * This value is used when the update is NOT to be relayed to the DownloadService
+ * (and thus spare DownloadService from scanning the database when this change occurs)
+ */
+ public static final int LAST_UPDATESRC_DONT_NOTIFY_DOWNLOADSVC = 1;
+
/*
* Lists the destinations that an application can specify for a download.
*/
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f111ef2..fb4bed7 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -53,6 +53,13 @@ public final class MediaStore {
private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
+ /**
+ * Broadcast Action: A broadcast to indicate the end of an MTP session with the host.
+ * This broadcast is only sent if MTP activity has modified the media database during the
+ * most recent MTP session.
+ */
+ public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
+
/**
* Activity Action: Launch a music player.
* The activity should be able to play, browse, or manipulate music files stored on the device.
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Ptp.java
index 78110ef..2c54370 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Ptp.java
@@ -22,28 +22,20 @@ import android.util.Log;
/**
- * The MTP provider supports accessing content on MTP and PTP devices.
+ * The PTP provider supports accessing content on PTP devices.
* @hide
*/
-public final class Mtp
+public final class Ptp
{
- private final static String TAG = "Mtp";
+ private final static String TAG = "Ptp";
- public static final String AUTHORITY = "mtp";
+ public static final String AUTHORITY = "ptp";
private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
private static final String CONTENT_AUTHORITY_DEVICE_SLASH = "content://" + AUTHORITY + "/device/";
-
- /**
- * Broadcast Action: A broadcast to indicate the end of an MTP session with the host.
- * This broadcast is only sent if MTP activity has modified the media database during the
- * most recent MTP session
- */
- public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
-
/**
- * Contains list of all MTP/PTP devices
+ * Contains list of all PTP devices
*/
public static final class Device implements BaseColumns {
@@ -67,7 +59,7 @@ public final class Mtp
}
/**
- * Contains list of storage units for an MTP/PTP device
+ * Contains list of storage units for an PTP device
*/
public static final class Storage implements BaseColumns {
@@ -93,7 +85,7 @@ public final class Mtp
}
/**
- * Contains list of objects on an MTP/PTP device
+ * Contains list of objects on an PTP device
*/
public static final class Object implements BaseColumns {
@@ -133,7 +125,7 @@ public final class Mtp
/**
* The following columns correspond to the fields in the ObjectInfo dataset
- * as described in the MTP specification.
+ * as described in the PTP specification.
*/
/**
@@ -144,7 +136,7 @@ public final class Mtp
/**
* The object's format. Can be one of the FORMAT_* symbols below,
- * or any of the valid MTP object formats as defined in the MTP specification.
+ * or any of the valid PTP object formats as defined in the PTP specification.
* <P>Type: INTEGER</P>
*/
public static final String FORMAT = "format";
@@ -163,7 +155,7 @@ public final class Mtp
/**
* The object's thumbnail format. Can be one of the FORMAT_* symbols below,
- * or any of the valid MTP object formats as defined in the MTP specification.
+ * or any of the valid PTP object formats as defined in the PTP specification.
* <P>Type: INTEGER</P>
*/
public static final String THUMB_FORMAT = "thumb_format";
diff --git a/core/java/android/util/PrefixPrinter.java b/core/java/android/util/PrefixPrinter.java
new file mode 100644
index 0000000..62f7da1
--- /dev/null
+++ b/core/java/android/util/PrefixPrinter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * PrefixPrinter is a Printer which prefixes all lines with a given
+ * prefix.
+ *
+ * @hide
+ */
+public class PrefixPrinter implements Printer {
+ private final Printer mPrinter;
+ private final String mPrefix;
+
+ /**
+ * Creates a new PrefixPrinter.
+ *
+ * <p>If prefix is null or empty, the provided printer is returned, rather
+ * than making a prefixing printer.
+ */
+ public static Printer create(Printer printer, String prefix) {
+ if (prefix == null || prefix.equals("")) {
+ return printer;
+ }
+ return new PrefixPrinter(printer, prefix);
+ }
+
+ private PrefixPrinter(Printer printer, String prefix) {
+ mPrinter = printer;
+ mPrefix = prefix;
+ }
+
+ public void println(String str) {
+ mPrinter.println(mPrefix + str);
+ }
+}
diff --git a/core/java/android/util/Singleton.java b/core/java/android/util/Singleton.java
new file mode 100644
index 0000000..8a38bdb
--- /dev/null
+++ b/core/java/android/util/Singleton.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Singleton helper class for lazily initialization.
+ *
+ * Modeled after frameworks/base/include/utils/Singleton.h
+ *
+ * @hide
+ */
+public abstract class Singleton<T> {
+ private T mInstance;
+
+ protected abstract T create();
+
+ public final T get() {
+ synchronized (this) {
+ if (mInstance == null) {
+ mInstance = create();
+ }
+ return mInstance;
+ }
+ }
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 60ca384..85ce5e1 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -158,18 +158,17 @@ public class TimeUtils {
static private int printField(char[] formatStr, int amt, char suffix, int pos,
boolean always, int zeropad) {
if (always || amt > 0) {
+ final int startPos = pos;
if ((always && zeropad >= 3) || amt > 99) {
int dig = amt/100;
formatStr[pos] = (char)(dig + '0');
pos++;
- always = true;
amt -= (dig*100);
}
- if ((always && zeropad >= 2) || amt > 9) {
+ if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
int dig = amt/10;
formatStr[pos] = (char)(dig + '0');
pos++;
- always = true;
amt -= (dig*10);
}
formatStr[pos] = (char)(amt + '0');
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index dd0f477..d964e2f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -122,7 +122,7 @@ interface IWindowManager
int getTrackballKeycodeState(int sw);
int getDPadKeycodeState(int sw);
InputChannel monitorInput(String inputChannelName);
-
+
// Report whether the hardware supports the given keys; returns true if successful
boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
@@ -132,7 +132,14 @@ interface IWindowManager
// For testing
void setInTouchMode(boolean showFocus);
-
+
+ // For StrictMode flashing a red border on violations from the UI
+ // thread. The uid/pid is implicit from the Binder call, and the Window
+ // Manager uses that to determine whether or not the red border should
+ // actually be shown. (it will be ignored that pid doesn't have windows
+ // on screen)
+ void showStrictModeViolation(boolean on);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Change the current screen rotation, constants as per
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e6eb46e..011ad77 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1627,6 +1627,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
static final int ACTIVATED = 0x40000000;
/**
+ * Always allow a user to over-scroll this view, provided it is a
+ * view that can scroll.
+ *
+ * @see #getOverScrollMode()
+ * @see #setOverScrollMode(int)
+ */
+ public static final int OVER_SCROLL_ALWAYS = 0;
+
+ /**
+ * Allow a user to over-scroll this view only if the content is large
+ * enough to meaningfully scroll, provided it is a view that can scroll.
+ *
+ * @see #getOverScrollMode()
+ * @see #setOverScrollMode(int)
+ */
+ public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
+
+ /**
+ * Never allow a user to over-scroll this view.
+ *
+ * @see #getOverScrollMode()
+ * @see #setOverScrollMode(int)
+ */
+ public static final int OVER_SCROLL_NEVER = 2;
+
+ /**
+ * Controls the over-scroll mode for this view.
+ * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
+ * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
+ * and {@link #OVER_SCROLL_NEVER}.
+ */
+ private int mOverScrollMode;
+
+ /**
* The parent this view is attached to.
* {@hide}
*
@@ -2057,6 +2091,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mResources = context != null ? context.getResources() : null;
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
}
/**
@@ -2122,6 +2157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
+ int overScrollMode = mOverScrollMode;
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
@@ -2327,9 +2363,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
});
}
break;
+ case R.styleable.View_overScrollMode:
+ overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
+ break;
}
}
+ setOverScrollMode(overScrollMode);
+
if (background != null) {
setBackgroundDrawable(background);
}
@@ -10131,6 +10172,128 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Scroll the view with standard behavior for scrolling beyond the normal
+ * content boundaries. Views that call this method should override
+ * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
+ * results of an over-scroll operation.
+ *
+ * Views can use this method to handle any touch or fling-based scrolling.
+ *
+ * @param deltaX Change in X in pixels
+ * @param deltaY Change in Y in pixels
+ * @param scrollX Current X scroll value in pixels before applying deltaX
+ * @param scrollY Current Y scroll value in pixels before applying deltaY
+ * @param scrollRangeX Maximum content scroll range along the X axis
+ * @param scrollRangeY Maximum content scroll range along the Y axis
+ * @param maxOverScrollX Number of pixels to overscroll by in either direction
+ * along the X axis.
+ * @param maxOverScrollY Number of pixels to overscroll by in either direction
+ * along the Y axis.
+ * @param isTouchEvent true if this scroll operation is the result of a touch event.
+ * @return true if scrolling was clamped to an over-scroll boundary along either
+ * axis, false otherwise.
+ */
+ protected boolean overScrollBy(int deltaX, int deltaY,
+ int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY,
+ int maxOverScrollX, int maxOverScrollY,
+ boolean isTouchEvent) {
+ final int overScrollMode = mOverScrollMode;
+ final boolean canScrollHorizontal =
+ computeHorizontalScrollRange() > computeHorizontalScrollExtent();
+ final boolean canScrollVertical =
+ computeVerticalScrollRange() > computeVerticalScrollExtent();
+ final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
+ (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
+ final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
+ (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
+
+ int newScrollX = scrollX + deltaX;
+ if (!overScrollHorizontal) {
+ maxOverScrollX = 0;
+ }
+
+ int newScrollY = scrollY + deltaY;
+ if (!overScrollVertical) {
+ maxOverScrollY = 0;
+ }
+
+ // Clamp values if at the limits and record
+ final int left = -maxOverScrollX;
+ final int right = maxOverScrollX + scrollRangeX;
+ final int top = -maxOverScrollY;
+ final int bottom = maxOverScrollY + scrollRangeY;
+
+ boolean clampedX = false;
+ if (newScrollX > right) {
+ newScrollX = right;
+ clampedX = true;
+ } else if (newScrollX < left) {
+ newScrollX = left;
+ clampedX = true;
+ }
+
+ boolean clampedY = false;
+ if (newScrollY > bottom) {
+ newScrollY = bottom;
+ clampedY = true;
+ } else if (newScrollY < top) {
+ newScrollY = top;
+ clampedY = true;
+ }
+
+ onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
+
+ return clampedX || clampedY;
+ }
+
+ /**
+ * Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to
+ * respond to the results of an over-scroll operation.
+ *
+ * @param scrollX New X scroll value in pixels
+ * @param scrollY New Y scroll value in pixels
+ * @param clampedX True if scrollX was clamped to an over-scroll boundary
+ * @param clampedY True if scrollY was clamped to an over-scroll boundary
+ */
+ protected void onOverScrolled(int scrollX, int scrollY,
+ boolean clampedX, boolean clampedY) {
+ // Intentionally empty.
+ }
+
+ /**
+ * Returns the over-scroll mode for this view. The result will be
+ * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * (allow over-scrolling only if the view content is larger than the container),
+ * or {@link #OVER_SCROLL_NEVER}.
+ *
+ * @return This view's over-scroll mode.
+ */
+ public int getOverScrollMode() {
+ return mOverScrollMode;
+ }
+
+ /**
+ * Set the over-scroll mode for this view. Valid over-scroll modes are
+ * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * (allow over-scrolling only if the view content is larger than the container),
+ * or {@link #OVER_SCROLL_NEVER}.
+ *
+ * Setting the over-scroll mode of a view will have an effect only if the
+ * view is capable of scrolling.
+ *
+ * @param overScrollMode The new over-scroll mode for this view.
+ */
+ public void setOverScrollMode(int overScrollMode) {
+ if (overScrollMode != OVER_SCROLL_ALWAYS &&
+ overScrollMode != OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ overScrollMode != OVER_SCROLL_NEVER) {
+ throw new IllegalArgumentException("Invalid overscroll mode " + overScrollMode);
+ }
+ mOverScrollMode = overScrollMode;
+ }
+
+ /**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height.
* A MeasureSpec is comprised of a size and a mode. There are three possible
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 85981d2..bb85894 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -144,7 +144,7 @@ public class ViewConfiguration {
/**
* Maximum velocity to initiate a fling, as measured in pixels per second
*/
- private static final int MAXIMUM_FLING_VELOCITY = 4000;
+ private static final int MAXIMUM_FLING_VELOCITY = 8000;
/**
* The maximum size of View's drawing cache, expressed in bytes. This size
@@ -158,6 +158,16 @@ public class ViewConfiguration {
*/
private static float SCROLL_FRICTION = 0.015f;
+ /**
+ * Max distance to overscroll for edge effects
+ */
+ private static final int OVERSCROLL_DISTANCE = 0;
+
+ /**
+ * Max distance to overfling for edge effects
+ */
+ private static final int OVERFLING_DISTANCE = 4;
+
private final int mEdgeSlop;
private final int mFadingEdgeLength;
private final int mMinimumFlingVelocity;
@@ -168,6 +178,8 @@ public class ViewConfiguration {
private final int mDoubleTapSlop;
private final int mWindowTouchSlop;
private final int mMaximumDrawingCacheSize;
+ private final int mOverscrollDistance;
+ private final int mOverflingDistance;
private static final SparseArray<ViewConfiguration> sConfigurations =
new SparseArray<ViewConfiguration>(2);
@@ -188,6 +200,8 @@ public class ViewConfiguration {
mWindowTouchSlop = WINDOW_TOUCH_SLOP;
//noinspection deprecation
mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
+ mOverscrollDistance = OVERSCROLL_DISTANCE;
+ mOverflingDistance = OVERFLING_DISTANCE;
}
/**
@@ -216,6 +230,9 @@ public class ViewConfiguration {
// Size of the screen in bytes, in ARGB_8888 format
mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
+
+ mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);
+ mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);
}
/**
@@ -473,6 +490,20 @@ public class ViewConfiguration {
}
/**
+ * @return The maximum distance a View should overscroll by when showing edge effects.
+ */
+ public int getScaledOverscrollDistance() {
+ return mOverscrollDistance;
+ }
+
+ /**
+ * @return The maximum distance a View should overfling by when showing edge effects.
+ */
+ public int getScaledOverflingDistance() {
+ return mOverflingDistance;
+ }
+
+ /**
* The amount of time that the zoom controls should be
* displayed on the screen expressed in milliseconds.
*
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fe55f34..9bc1c22 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -47,7 +47,10 @@ import com.android.internal.view.InputBindResult;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -1275,10 +1278,10 @@ public final class InputMethodManager {
}
}
}
-
+
/**
- * Force switch to a new input method component. This can only be called
- * from the currently active input method, as validated by the given token.
+ * Force switch to a new input method component. This can only be called
+ * from an application or a service which has a token of the currently active input method.
* @param token Supplies the identifying token given to an input method
* when it was started, which allows it to perform this operation on
* itself.
@@ -1291,7 +1294,24 @@ public final class InputMethodManager {
throw new RuntimeException(e);
}
}
-
+
+ /**
+ * Force switch to a new input method and subtype. This can only be called
+ * from an application or a service which has a token of the currently active input method.
+ * @param token Supplies the identifying token given to an input method
+ * when it was started, which allows it to perform this operation on
+ * itself.
+ * @param id The unique identifier for the new input method to be switched to.
+ * @param subtype The new subtype of the new input method to be switched to.
+ */
+ public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+ try {
+ mService.setInputMethodAndSubtype(token, id, subtype);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Close/hide the input method's soft input area, so the user no longer
* sees it or can interact with it. This can only be called
@@ -1452,6 +1472,38 @@ public final class InputMethodManager {
}
}
+ public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
+ synchronized (mH) {
+ HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
+ new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+ try {
+ // TODO: We should change the return type from List<Object> to List<Parcelable>
+ List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
+ // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
+ ArrayList<InputMethodSubtype> subtypes = null;
+ final int N = info.size();
+ if (info != null && N > 0) {
+ for (int i = 0; i < N; ++i) {
+ Object o = info.get(i);
+ if (o instanceof InputMethodInfo) {
+ if (ret.containsKey(o)) {
+ Log.e(TAG, "IMI list already contains the same InputMethod.");
+ break;
+ }
+ subtypes = new ArrayList<InputMethodSubtype>();
+ ret.put((InputMethodInfo)o, subtypes);
+ } else if (subtypes != null && o instanceof InputMethodSubtype) {
+ subtypes.add((InputMethodSubtype)o);
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ return ret;
+ }
+ }
+
public boolean switchToLastInputMethod(IBinder imeToken) {
synchronized (mH) {
try {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 61a30ab..9568e4f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -780,50 +780,11 @@ class BrowserFrame extends Handler {
if (cacheMode == WebSettings.LOAD_NORMAL) {
cacheMode = WebSettings.LOAD_NO_CACHE;
}
- if (mSettings.getSavePassword() && hasPasswordField()) {
- try {
- if (DebugFlags.BROWSER_FRAME) {
- Assert.assertNotNull(mCallbackProxy.getBackForwardList()
- .getCurrentItem());
- }
- WebAddress uri = new WebAddress(mCallbackProxy
- .getBackForwardList().getCurrentItem().getUrl());
- String schemePlusHost = uri.getScheme() + uri.getHost();
- String[] ret = getUsernamePassword();
- // Has the user entered a username/password pair and is
- // there some POST data
- if (ret != null && postData != null &&
- ret[0].length() > 0 && ret[1].length() > 0) {
- // Check to see if the username & password appear in
- // the post data (there could be another form on the
- // page and that was posted instead.
- String postString = new String(postData);
- if (postString.contains(URLEncoder.encode(ret[0])) &&
- postString.contains(URLEncoder.encode(ret[1]))) {
- String[] saved = mDatabase.getUsernamePassword(
- schemePlusHost);
- if (saved != null) {
- // null username implies that user has chosen not to
- // save password
- if (saved[0] != null) {
- // non-null username implies that user has
- // chosen to save password, so update the
- // recorded password
- mDatabase.setUsernamePassword(
- schemePlusHost, ret[0], ret[1]);
- }
- } else {
- // CallbackProxy will handle creating the resume
- // message
- mCallbackProxy.onSavePassword(schemePlusHost, ret[0],
- ret[1], null);
- }
- }
- }
- } catch (ParseException ex) {
- // if it is bad uri, don't save its password
- }
-
+ String[] ret = getUsernamePassword();
+ if (ret != null) {
+ String domUsername = ret[0];
+ String domPassword = ret[1];
+ maybeSavePassword(postData, domUsername, domPassword);
}
}
@@ -874,6 +835,68 @@ class BrowserFrame extends Handler {
return !synchronous ? loadListener : null;
}
+ /**
+ * If this looks like a POST request (form submission) containing a username
+ * and password, give the user the option of saving them. Will either do
+ * nothing, or block until the UI interaction is complete.
+ *
+ * Called by startLoadingResource when using the Apache HTTP stack.
+ * Called directly by WebKit when using the Chrome HTTP stack.
+ *
+ * @param postData The data about to be sent as the body of a POST request.
+ * @param username The username entered by the user (sniffed from the DOM).
+ * @param password The password entered by the user (sniffed from the DOM).
+ */
+ private void maybeSavePassword(
+ byte[] postData, String username, String password) {
+ if (postData == null
+ || username == null || username.isEmpty()
+ || password == null || password.isEmpty()) {
+ return; // No password to save.
+ }
+
+ if (!mSettings.getSavePassword()) {
+ return; // User doesn't want to save passwords.
+ }
+
+ try {
+ if (DebugFlags.BROWSER_FRAME) {
+ Assert.assertNotNull(mCallbackProxy.getBackForwardList()
+ .getCurrentItem());
+ }
+ WebAddress uri = new WebAddress(mCallbackProxy
+ .getBackForwardList().getCurrentItem().getUrl());
+ String schemePlusHost = uri.getScheme() + uri.getHost();
+ // Check to see if the username & password appear in
+ // the post data (there could be another form on the
+ // page and that was posted instead.
+ String postString = new String(postData);
+ if (postString.contains(URLEncoder.encode(username)) &&
+ postString.contains(URLEncoder.encode(password))) {
+ String[] saved = mDatabase.getUsernamePassword(
+ schemePlusHost);
+ if (saved != null) {
+ // null username implies that user has chosen not to
+ // save password
+ if (saved[0] != null) {
+ // non-null username implies that user has
+ // chosen to save password, so update the
+ // recorded password
+ mDatabase.setUsernamePassword(
+ schemePlusHost, username, password);
+ }
+ } else {
+ // CallbackProxy will handle creating the resume
+ // message
+ mCallbackProxy.onSavePassword(schemePlusHost, username,
+ password, null);
+ }
+ }
+ } catch (ParseException ex) {
+ // if it is bad uri, don't save its password
+ }
+ }
+
// Called by jni from the chrome network stack.
private WebResourceResponse shouldInterceptRequest(String url) {
InputStream androidResource = inputStreamForAndroidResource(url);
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
new file mode 100644
index 0000000..53600f6
--- /dev/null
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.EdgeGlow;
+
+/**
+ * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
+ * @hide
+ */
+public class OverScrollGlow {
+ private WebView mHostView;
+
+ private EdgeGlow mEdgeGlowTop;
+ private EdgeGlow mEdgeGlowBottom;
+ private EdgeGlow mEdgeGlowLeft;
+ private EdgeGlow mEdgeGlowRight;
+
+ private int mOverScrollDeltaX;
+ private int mOverScrollDeltaY;
+
+ public OverScrollGlow(WebView host) {
+ mHostView = host;
+ final Resources res = host.getContext().getResources();
+ final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+ final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+ mEdgeGlowTop = new EdgeGlow(edge, glow);
+ mEdgeGlowBottom = new EdgeGlow(edge, glow);
+ mEdgeGlowLeft = new EdgeGlow(edge, glow);
+ mEdgeGlowRight = new EdgeGlow(edge, glow);
+ }
+
+ /**
+ * Pull leftover touch scroll distance into one of the edge glows as appropriate.
+ *
+ * @param x Current X scroll offset
+ * @param y Current Y scroll offset
+ * @param oldX Old X scroll offset
+ * @param oldY Old Y scroll offset
+ * @param maxX Maximum range for horizontal scrolling
+ * @param maxY Maximum range for vertical scrolling
+ */
+ public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) {
+ // Only show overscroll bars if there was no movement in any direction
+ // as a result of scrolling.
+ if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) {
+ // Don't show left/right glows if we fit the whole content.
+ // Also don't show if there was vertical movement.
+ if (maxX > 0) {
+ final int pulledToX = oldX + mOverScrollDeltaX;
+ if (pulledToX < 0) {
+ mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
+ if (!mEdgeGlowRight.isFinished()) {
+ mEdgeGlowRight.onRelease();
+ }
+ } else if (pulledToX > maxX) {
+ mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
+ if (!mEdgeGlowLeft.isFinished()) {
+ mEdgeGlowLeft.onRelease();
+ }
+ }
+ mOverScrollDeltaX = 0;
+ }
+
+ if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+ final int pulledToY = oldY + mOverScrollDeltaY;
+ if (pulledToY < 0) {
+ mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (pulledToY > maxY) {
+ mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ mOverScrollDeltaY = 0;
+ }
+ }
+ }
+
+ /**
+ * Set touch delta values indicating the current amount of overscroll.
+ *
+ * @param deltaX
+ * @param deltaY
+ */
+ public void setOverScrollDeltas(int deltaX, int deltaY) {
+ mOverScrollDeltaX = deltaX;
+ mOverScrollDeltaY = deltaY;
+ }
+
+ /**
+ * Absorb leftover fling velocity into one of the edge glows as appropriate.
+ *
+ * @param x Current X scroll offset
+ * @param y Current Y scroll offset
+ * @param oldX Old X scroll offset
+ * @param oldY Old Y scroll offset
+ * @param rangeX Maximum range for horizontal scrolling
+ * @param rangeY Maximum range for vertical scrolling
+ */
+ public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) {
+ if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+ if (y < 0 && oldY >= 0) {
+ mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (y > rangeY && oldY <= rangeY) {
+ mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ }
+
+ if (rangeX > 0) {
+ if (x < 0 && oldX >= 0) {
+ mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+ if (!mEdgeGlowRight.isFinished()) {
+ mEdgeGlowRight.onRelease();
+ }
+ } else if (x > rangeX && oldX <= rangeX) {
+ mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+ if (!mEdgeGlowLeft.isFinished()) {
+ mEdgeGlowLeft.onRelease();
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null.
+ *
+ * @param canvas Canvas to draw into, transformed into view coordinates.
+ * @return true if glow effects are still animating and the view should invalidate again.
+ */
+ public boolean drawEdgeGlows(Canvas canvas) {
+ final int scrollX = mHostView.getScrollX();
+ final int scrollY = mHostView.getScrollY();
+ final int width = mHostView.getWidth();
+ int height = mHostView.getHeight();
+
+ boolean invalidateForGlow = false;
+ if (!mEdgeGlowTop.isFinished()) {
+ final int restoreCount = canvas.save();
+
+ canvas.translate(-width / 2 + scrollX, Math.min(0, scrollY));
+ mEdgeGlowTop.setSize(width * 2, height);
+ invalidateForGlow |= mEdgeGlowTop.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowBottom.isFinished()) {
+ final int restoreCount = canvas.save();
+
+ canvas.translate(-width / 2 + scrollX,
+ Math.max(mHostView.computeMaxScrollY(), scrollY) + height);
+ canvas.rotate(180, width, 0);
+ mEdgeGlowBottom.setSize(width * 2, height);
+ invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowLeft.isFinished()) {
+ final int restoreCount = canvas.save();
+
+ canvas.rotate(270);
+ canvas.translate(-height * 1.5f - scrollY, Math.min(0, scrollX));
+ mEdgeGlowLeft.setSize(height * 2, width);
+ invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowRight.isFinished()) {
+ final int restoreCount = canvas.save();
+
+ canvas.rotate(90);
+ canvas.translate(-height / 2 + scrollY,
+ -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width));
+ mEdgeGlowRight.setSize(height * 2, width);
+ invalidateForGlow |= mEdgeGlowRight.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ return invalidateForGlow;
+ }
+
+ /**
+ * @return True if any glow is still animating
+ */
+ public boolean isAnimating() {
+ return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() ||
+ !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished());
+ }
+
+ /**
+ * Release all glows from any touch pulls in progress.
+ */
+ public void releaseAll() {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ mEdgeGlowLeft.onRelease();
+ mEdgeGlowRight.onRelease();
+ }
+}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index b2ba7e2..755366c 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -330,4 +330,14 @@ public class WebChromeClient {
*/
public void setInstallableWebApp() { }
+ /**
+ * Tell the client that the page being viewed has an autofillable
+ * form and the user would like to set a profile up.
+ * @param msg A Message to send once the user has successfully
+ * set up a profile and to inform the WebTextView it should
+ * now autofill using that new profile.
+ * @hide
+ */
+ public void setupAutoFill(Message msg) { }
+
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f4caa74..2e69d99 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -268,6 +268,8 @@ public class WebSettings {
private AutoFillProfile mAutoFillProfile;
+ private boolean mUseWebViewBackgroundForOverscroll = true;
+
// private WebSettings, not accessible by the host activity
static private int mDoubleTapToastCount = 3;
@@ -631,6 +633,23 @@ public class WebSettings {
}
/**
+ * Set whether the WebView uses its background for over scroll background.
+ * If true, it will use the WebView's background. If false, it will use an
+ * internal pattern. Default is true.
+ */
+ public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+ mUseWebViewBackgroundForOverscroll = view;
+ }
+
+ /**
+ * Returns true if this WebView uses WebView's background instead of
+ * internal pattern for over scroll background.
+ */
+ public boolean getUseWebViewBackgroundForOverscrollBackground() {
+ return mUseWebViewBackgroundForOverscroll;
+ }
+
+ /**
* Store whether the WebView is saving form data.
*/
public void setSaveFormData(boolean save) {
@@ -1626,6 +1645,13 @@ public class WebSettings {
}
}
+ /**
+ * @hide
+ */
+ public synchronized AutoFillProfile getAutoFillProfile() {
+ return mAutoFillProfile;
+ }
+
int getDoubleTapToastCount() {
return mDoubleTapToastCount;
}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 1caa707..e1a5c2d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -26,9 +26,14 @@ import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.text.BoringLayout.Metrics;
+import android.text.DynamicLayout;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Layout;
+import android.text.Layout.Alignment;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextPaint;
@@ -126,6 +131,7 @@ import junit.framework.Assert;
private boolean mAutoFillable; // Is this textview part of an autofillable form?
private int mQueryId;
+ private boolean mAutoFillProfileIsSet;
// Types used with setType. Keep in sync with CachedInput.h
private static final int NORMAL_TEXT_FIELD = 0;
@@ -137,6 +143,9 @@ import junit.framework.Assert;
private static final int TELEPHONE = 6;
private static final int URL = 7;
+ private static final int AUTOFILL_FORM = 100;
+ private Handler mHandler;
+
/**
* Create a new WebTextView.
* @param context The Context for this WebTextView.
@@ -160,6 +169,18 @@ import junit.framework.Assert;
setTextColor(DebugFlags.DRAW_WEBTEXTVIEW ? Color.RED : Color.BLACK);
// This helps to align the text better with the text in the web page.
setIncludeFontPadding(false);
+
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AUTOFILL_FORM:
+ mWebView.autoFillForm(mQueryId);
+ break;
+ }
+ }
+ };
+
}
public void setAutoFillable(int queryId) {
@@ -359,6 +380,68 @@ import junit.framework.Assert;
}
}
+ @Override
+ protected void makeNewLayout(int w, int hintWidth, Metrics boring,
+ Metrics hintBoring, int ellipsisWidth, boolean bringIntoView) {
+ // Necessary to get a Layout to work with, and to do the other work that
+ // makeNewLayout does.
+ super.makeNewLayout(w, hintWidth, boring, hintBoring, ellipsisWidth,
+ bringIntoView);
+
+ // For fields that do not draw, create a layout which is altered so that
+ // the text lines up.
+ if (DebugFlags.DRAW_WEBTEXTVIEW || willNotDraw()) {
+ float lineHeight = -1;
+ if (mWebView != null) {
+ float height = mWebView.nativeFocusCandidateLineHeight();
+ if (height != -1) {
+ lineHeight = height * mWebView.getScale();
+ }
+ }
+ CharSequence text = getText();
+ // Copy from the existing Layout.
+ mLayout = new WebTextViewLayout(text, text, getPaint(), w,
+ mLayout.getAlignment(), mLayout.getSpacingMultiplier(),
+ mLayout.getSpacingAdd(), false, null, ellipsisWidth,
+ lineHeight);
+ }
+ }
+
+ /**
+ * Custom layout which figures out its line spacing. If -1 is passed in for
+ * the height, it will use the ascent and descent from the paint to
+ * determine the line spacing. Otherwise it will use the spacing provided.
+ */
+ private static class WebTextViewLayout extends DynamicLayout {
+ private float mLineHeight;
+ private float mDifference;
+ public WebTextViewLayout(CharSequence base, CharSequence display,
+ TextPaint paint,
+ int width, Alignment align,
+ float spacingMult, float spacingAdd,
+ boolean includepad,
+ TextUtils.TruncateAt ellipsize, int ellipsizedWidth,
+ float lineHeight) {
+ super(base, display, paint, width, align, spacingMult, spacingAdd,
+ includepad, ellipsize, ellipsizedWidth);
+ float paintLineHeight = paint.descent() - paint.ascent();
+ if (lineHeight == -1f) {
+ mLineHeight = paintLineHeight;
+ mDifference = 0f;
+ } else {
+ mLineHeight = lineHeight;
+ // Through trial and error, I found this calculation to improve
+ // the accuracy of line placement.
+ mDifference = (lineHeight - paintLineHeight) / 2;
+ }
+ }
+
+ @Override
+ public int getLineTop(int line) {
+ return Math.round(mLineHeight * line - mDifference);
+ }
+ }
+
@Override public InputConnection onCreateInputConnection(
EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
@@ -736,8 +819,17 @@ import junit.framework.Assert;
if (id == 0 && position == 0) {
// Blank out the text box while we wait for WebCore to fill the form.
replaceText("");
- // Call a webview method to tell WebCore to autofill the form.
- mWebView.autoFillForm(mQueryId);
+ WebSettings settings = mWebView.getSettings();
+ if (mAutoFillProfileIsSet) {
+ // Call a webview method to tell WebCore to autofill the form.
+ mWebView.autoFillForm(mQueryId);
+ } else {
+ // There is no autofill profile setup yet and the user has
+ // elected to try and set one up. Call through to the
+ // embedder to action that.
+ mWebView.getWebChromeClient().setupAutoFill(
+ mHandler.obtainMessage(AUTOFILL_FORM));
+ }
}
}
});
@@ -1059,4 +1151,8 @@ import junit.framework.Assert;
/* package */ void updateCachedTextfield() {
mWebView.updateCachedTextfield(getText().toString());
}
+
+ /* package */ void setAutoFillProfileIsSet(boolean autoFillProfileIsSet) {
+ mAutoFillProfileIsSet = autoFillProfileIsSet;
+ }
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5bf2ad4..a0ee765 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,19 +16,25 @@
package android.webkit;
+import com.android.internal.R;
+
import android.annotation.Widget;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.IntentFilter;
import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
@@ -40,6 +46,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.http.SslCertificate;
@@ -81,13 +88,12 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
+import android.widget.EdgeGlow;
import android.widget.LinearLayout;
import android.widget.ListView;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import android.widget.Toast;
-import junit.framework.Assert;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -102,6 +108,8 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import junit.framework.Assert;
+
/**
* <p>A View that displays web pages. This class is the basis upon which you
* can roll your own web browser or simply display some online content within your Activity.
@@ -529,7 +537,13 @@ public class WebView extends AbsoluteLayout
// time for the longest scroll animation
private static final int MAX_DURATION = 750; // milliseconds
private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
- private Scroller mScroller;
+
+ // Used by OverScrollGlow
+ OverScroller mScroller;
+
+ private boolean mInOverScrollMode = false;
+ private static Paint mOverScrollBackground;
+ private static Paint mOverScrollBorder;
private boolean mWrapContent;
private static final int MOTIONLESS_FALSE = 0;
@@ -734,6 +748,20 @@ public class WebView extends AbsoluteLayout
// variable to cache the above pattern in case accessibility is enabled.
private Pattern mMatchAxsUrlParameterPattern;
+ /**
+ * Max distance to overscroll by in pixels.
+ * This how far content can be pulled beyond its normal bounds by the user.
+ */
+ private int mOverscrollDistance;
+
+ /**
+ * Max distance to overfling by in pixels.
+ * This is how far flinged content can move beyond the end of its normal bounds.
+ */
+ private int mOverflingDistance;
+
+ private OverScrollGlow mOverScrollGlow;
+
// Used to match key downs and key ups
private boolean mGotKeyDown;
@@ -906,9 +934,10 @@ public class WebView extends AbsoluteLayout
mCallbackProxy = new CallbackProxy(context, this);
mViewManager = new ViewManager(this);
+ L10nUtils.loadStrings(context);
mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
mDatabase = WebViewDatabase.getInstance(context);
- mScroller = new Scroller(context);
+ mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
mZoomManager = new ZoomManager(this, mCallbackProxy);
/* The init method must follow the creation of certain member variables,
@@ -922,7 +951,6 @@ public class WebView extends AbsoluteLayout
startPrivateBrowsing();
}
- L10nUtils.loadStrings(context);
mAutoFillData = new WebViewCore.AutoFillData();
}
@@ -1044,6 +1072,9 @@ public class WebView extends AbsoluteLayout
// Compute the inverse of the density squared.
DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
+
+ mOverscrollDistance = configuration.getScaledOverscrollDistance();
+ mOverflingDistance = configuration.getScaledOverflingDistance();
}
/**
@@ -1066,6 +1097,18 @@ public class WebView extends AbsoluteLayout
new TextToSpeech(getContext(), null));
}
+ @Override
+ public void setOverScrollMode(int mode) {
+ super.setOverScrollMode(mode);
+ if (mode != OVER_SCROLL_NEVER) {
+ if (mOverScrollGlow == null) {
+ mOverScrollGlow = new OverScrollGlow(this);
+ }
+ } else {
+ mOverScrollGlow = null;
+ }
+ }
+
/* package */void updateDefaultZoomDensity(int zoomDensity) {
final float density = mContext.getResources().getDisplayMetrics().density
* 100 / zoomDensity;
@@ -1197,7 +1240,8 @@ public class WebView extends AbsoluteLayout
* @hide
*/
public int getVisibleTitleHeight() {
- return Math.max(getTitleHeight() - mScrollY, 0);
+ // need to restrict mScrollY due to over scroll
+ return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
}
/*
@@ -1946,7 +1990,7 @@ public class WebView extends AbsoluteLayout
}
nativeClearCursor(); // start next trackball movement from page edge
if (bottom) {
- return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
+ return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
}
// Page down.
int h = getHeight();
@@ -2171,13 +2215,15 @@ public class WebView extends AbsoluteLayout
// Expects x in view coordinates
int pinLocX(int x) {
- return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
+ if (mInOverScrollMode) return x;
+ return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
}
// Expects y in view coordinates
int pinLocY(int y) {
+ if (mInOverScrollMode) return y;
return pinLoc(y, getViewHeightWithTitle(),
- computeVerticalScrollRange() + getTitleHeight());
+ computeRealVerticalScrollRange() + getTitleHeight());
}
/**
@@ -2412,7 +2458,7 @@ public class WebView extends AbsoluteLayout
// Sets r to be our visible rectangle in content coordinates
private void calcOurContentVisibleRect(Rect r) {
calcOurVisibleRect(r);
- // pin the rect to the bounds of the content
+ // since we might overscroll, pin the rect to the bounds of the content
r.left = Math.max(viewToContentX(r.left), 0);
// viewToContentY will remove the total height of the title bar. Add
// the visible height back in to account for the fact that if the title
@@ -2497,8 +2543,7 @@ public class WebView extends AbsoluteLayout
return false;
}
- @Override
- protected int computeHorizontalScrollRange() {
+ private int computeRealHorizontalScrollRange() {
if (mDrawHistory) {
return mHistoryWidth;
} else if (mHorizontalScrollBarMode == SCROLLBAR_ALWAYSOFF
@@ -2512,7 +2557,27 @@ public class WebView extends AbsoluteLayout
}
@Override
- protected int computeVerticalScrollRange() {
+ protected int computeHorizontalScrollRange() {
+ int range = computeRealHorizontalScrollRange();
+
+ // Adjust reported range if overscrolled to compress the scroll bars
+ final int scrollX = mScrollX;
+ final int overscrollRight = computeMaxScrollX();
+ if (scrollX < 0) {
+ range -= scrollX;
+ } else if (scrollX > overscrollRight) {
+ range += scrollX - overscrollRight;
+ }
+
+ return range;
+ }
+
+ @Override
+ protected int computeHorizontalScrollOffset() {
+ return Math.max(mScrollX, 0);
+ }
+
+ private int computeRealVerticalScrollRange() {
if (mDrawHistory) {
return mHistoryHeight;
} else if (mVerticalScrollBarMode == SCROLLBAR_ALWAYSOFF
@@ -2526,6 +2591,22 @@ public class WebView extends AbsoluteLayout
}
@Override
+ protected int computeVerticalScrollRange() {
+ int range = computeRealVerticalScrollRange();
+
+ // Adjust reported range if overscrolled to compress the scroll bars
+ final int scrollY = mScrollY;
+ final int overscrollBottom = computeMaxScrollY();
+ if (scrollY < 0) {
+ range -= scrollY;
+ } else if (scrollY > overscrollBottom) {
+ range += scrollY - overscrollBottom;
+ }
+
+ return range;
+ }
+
+ @Override
protected int computeVerticalScrollOffset() {
return Math.max(mScrollY - getTitleHeight(), 0);
}
@@ -2540,10 +2621,39 @@ public class WebView extends AbsoluteLayout
protected void onDrawVerticalScrollBar(Canvas canvas,
Drawable scrollBar,
int l, int t, int r, int b) {
+ if (mScrollY < 0) {
+ t -= mScrollY;
+ }
scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
scrollBar.draw(canvas);
}
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
+ boolean clampedY) {
+ mInOverScrollMode = false;
+ int maxX = computeMaxScrollX();
+ int maxY = computeMaxScrollY();
+ if (maxX == 0) {
+ // do not over scroll x if the page just fits the screen
+ scrollX = pinLocX(scrollX);
+ } else if (scrollX < 0 || scrollX > maxX) {
+ mInOverScrollMode = true;
+ }
+ if (scrollY < 0 || scrollY > maxY) {
+ mInOverScrollMode = true;
+ }
+
+ int oldX = mScrollX;
+ int oldY = mScrollY;
+
+ super.scrollTo(scrollX, scrollY);
+
+ if (mOverScrollGlow != null) {
+ mOverScrollGlow.pullGlow(mScrollX, mScrollY, oldX, oldY, maxX, maxY);
+ }
+ }
+
/**
* Get the url for the current page. This is not always the same as the url
* passed to WebViewClient.onPageStarted because although the load for
@@ -2923,13 +3033,23 @@ public class WebView extends AbsoluteLayout
if (mScroller.computeScrollOffset()) {
int oldX = mScrollX;
int oldY = mScrollY;
- mScrollX = mScroller.getCurrX();
- mScrollY = mScroller.getCurrY();
- postInvalidate(); // So we draw again
- if (oldX != mScrollX || oldY != mScrollY) {
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
- } else if (mScroller.getStartX() != mScrollX
- || mScroller.getStartY() != mScrollY) {
+ int x = mScroller.getCurrX();
+ int y = mScroller.getCurrY();
+ invalidate(); // So we draw again
+
+ if (!mScroller.isFinished()) {
+ final int rangeX = computeMaxScrollX();
+ final int rangeY = computeMaxScrollY();
+ overScrollBy(x - oldX, y - oldY, oldX, oldY,
+ rangeX, rangeY,
+ mOverflingDistance, mOverflingDistance, false);
+
+ if (mOverScrollGlow != null) {
+ mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
+ }
+ } else {
+ mScrollX = x;
+ mScrollY = y;
abortAnimation();
mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
WebViewCore.resumePriority();
@@ -3436,6 +3556,40 @@ public class WebView extends AbsoluteLayout
drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
}
+ /**
+ * Draw the background when beyond bounds
+ * @param canvas Canvas to draw into
+ */
+ private void drawOverScrollBackground(Canvas canvas) {
+ if (mOverScrollBackground == null) {
+ mOverScrollBackground = new Paint();
+ Bitmap bm = BitmapFactory.decodeResource(
+ mContext.getResources(),
+ com.android.internal.R.drawable.status_bar_background);
+ mOverScrollBackground.setShader(new BitmapShader(bm,
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ mOverScrollBorder = new Paint();
+ mOverScrollBorder.setStyle(Paint.Style.STROKE);
+ mOverScrollBorder.setStrokeWidth(0);
+ mOverScrollBorder.setColor(0xffbbbbbb);
+ }
+
+ int top = 0;
+ int right = computeRealHorizontalScrollRange();
+ int bottom = top + computeRealVerticalScrollRange();
+ // first draw the background and anchor to the top of the view
+ canvas.save();
+ canvas.translate(mScrollX, mScrollY);
+ canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
+ - mScrollY, Region.Op.DIFFERENCE);
+ canvas.drawPaint(mOverScrollBackground);
+ canvas.restore();
+ // then draw the border
+ canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
+ // next clip the region for the content
+ canvas.clipRect(0, top, right, bottom);
+ }
+
@Override
protected void onDraw(Canvas canvas) {
// if mNativeClass is 0, the WebView has been destroyed. Do nothing.
@@ -3452,6 +3606,10 @@ public class WebView extends AbsoluteLayout
}
int saveCount = canvas.save();
+ if (mInOverScrollMode && !getSettings()
+ .getUseWebViewBackgroundForOverscrollBackground()) {
+ drawOverScrollBackground(canvas);
+ }
if (mTitleBar != null) {
canvas.translate(0, (int) mTitleBar.getHeight());
}
@@ -3466,6 +3624,10 @@ public class WebView extends AbsoluteLayout
}
mWebViewCore.signalRepaintDone();
+ if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
+ invalidate();
+ }
+
// paint the highlight in the end
if (!mTouchHighlightRegion.isEmpty()) {
if (mTouchHightlightPaint == null) {
@@ -3549,6 +3711,15 @@ public class WebView extends AbsoluteLayout
* click action, look for a word under the click. If one is found,
* animate the text selection into view.
* FIXME: no animation code yet */
+ return selectText();
+ }
+
+ /**
+ * Select the word at the last click point.
+ *
+ * @hide pending API council approval
+ */
+ public boolean selectText() {
int x = viewToContentX((int) mLastTouchX + mScrollX);
int y = viewToContentY((int) mLastTouchY + mScrollY);
setUpSelect();
@@ -3561,6 +3732,16 @@ public class WebView extends AbsoluteLayout
return false;
}
+ private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mSelectingText && mOrientation != newConfig.orientation) {
+ selectionDone();
+ }
+ mOrientation = newConfig.orientation;
+ }
+
/**
* Keep track of the Callback so we can end its ActionMode or remove its
* titlebar.
@@ -4042,10 +4223,20 @@ public class WebView extends AbsoluteLayout
// Note that code inside the adapter click handler in WebTextView depends
// on the AutoFill item being at the top of the drop down list. If you change
// the order, make sure to do it there too!
- pastEntries.add(getResources().getText(
- com.android.internal.R.string.autofill_this_form).toString() +
- " " +
- mAutoFillData.getPreviewString());
+ WebSettings settings = getSettings();
+ if (settings != null && settings.getAutoFillProfile() != null) {
+ pastEntries.add(getResources().getText(
+ com.android.internal.R.string.autofill_this_form).toString() +
+ " " +
+ mAutoFillData.getPreviewString());
+ mWebTextView.setAutoFillProfileIsSet(true);
+ } else {
+ // There is no autofill profile set up yet, so add an option that
+ // will invite the user to set their profile up.
+ pastEntries.add(getResources().getText(
+ com.android.internal.R.string.setup_autofill).toString());
+ mWebTextView.setAutoFillProfileIsSet(false);
+ }
}
pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
@@ -4688,12 +4879,14 @@ public class WebView extends AbsoluteLayout
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
- sendOurVisibleRect();
- // update WebKit if visible title bar height changed. The logic is same
- // as getVisibleTitleHeight.
- int titleHeight = getTitleHeight();
- if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
- sendViewSizeZoom(false);
+ if (!mInOverScrollMode) {
+ sendOurVisibleRect();
+ // update WebKit if visible title bar height changed. The logic is same
+ // as getVisibleTitleHeight.
+ int titleHeight = getTitleHeight();
+ if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
+ sendViewSizeZoom(false);
+ }
}
}
@@ -4820,7 +5013,7 @@ public class WebView extends AbsoluteLayout
final ScaleGestureDetector detector =
mZoomManager.getMultiTouchGestureDetector();
- boolean skipScaleGesture = false;
+ boolean isScrollGesture = false;
// Set to the mid-point of a two-finger gesture used to detect if the
// user has touched a layer.
float gestureX = x;
@@ -4848,7 +5041,7 @@ public class WebView extends AbsoluteLayout
}
action = ev.getActionMasked();
if (dist < DRAG_LAYER_FINGER_DISTANCE) {
- skipScaleGesture = true;
+ isScrollGesture = true;
} else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
// Fingers moved too far apart while dragging, the user
// might be trying to zoom.
@@ -4857,9 +5050,13 @@ public class WebView extends AbsoluteLayout
}
}
- // If the page disallows zoom, pass multi-pointer events to webkit.
- if (!skipScaleGesture && ev.getPointerCount() > 1
- && (mZoomManager.isZoomScaleFixed() || mDeferMultitouch)) {
+ // If the page disallows zoom, pass multi-touch events to webkit.
+ // mDeferMultitouch is a hack for layout tests, where it is used to
+ // force passing multi-touch events to webkit.
+ // FIXME: always pass multi-touch events to webkit and remove everything
+ // related to mDeferMultitouch.
+ if (ev.getPointerCount() > 1 &&
+ (mDeferMultitouch || (!isScrollGesture && mZoomManager.isZoomScaleFixed()))) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit");
}
@@ -4868,7 +5065,7 @@ public class WebView extends AbsoluteLayout
}
if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
- mTouchMode != TOUCH_DRAG_LAYER_MODE && !skipScaleGesture) {
+ mTouchMode != TOUCH_DRAG_LAYER_MODE && !isScrollGesture) {
if (!detector.isInProgress() &&
ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
// Insert a fake pointer down event in order to start
@@ -5141,27 +5338,13 @@ public class WebView extends AbsoluteLayout
deltaX = 0;
deltaY = 0;
- if (skipScaleGesture) {
+ if (isScrollGesture) {
startScrollingLayer(gestureX, gestureY);
}
startDrag();
}
// do pan
- if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
- int newScrollX = pinLocX(mScrollX + deltaX);
- int newDeltaX = newScrollX - mScrollX;
- if (deltaX != newDeltaX) {
- deltaX = newDeltaX;
- fDeltaX = (float) newDeltaX;
- }
- int newScrollY = pinLocY(mScrollY + deltaY);
- int newDeltaY = newScrollY - mScrollY;
- if (deltaY != newDeltaY) {
- deltaY = newDeltaY;
- fDeltaY = (float) newDeltaY;
- }
- }
boolean done = false;
boolean keepScrollBarsVisible = false;
if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -5351,6 +5534,12 @@ public class WebView extends AbsoluteLayout
mHeldMotionless = MOTIONLESS_IGNORE;
doFling();
break;
+ } else {
+ if (mScroller.springBack(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0,
+ computeMaxScrollY())) {
+ invalidate();
+ }
}
// redraw in high-quality, as we're done dragging
mHeldMotionless = MOTIONLESS_TRUE;
@@ -5371,6 +5560,8 @@ public class WebView extends AbsoluteLayout
}
case MotionEvent.ACTION_CANCEL: {
if (mTouchMode == TOUCH_DRAG_MODE) {
+ mScroller.springBack(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0, computeMaxScrollY());
invalidate();
}
cancelWebCoreTouchEvent(contentX, contentY, false);
@@ -5446,7 +5637,22 @@ public class WebView extends AbsoluteLayout
}
return;
}
- scrollBy(deltaX, deltaY);
+
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
+ final int rangeX = computeMaxScrollX();
+ final int rangeY = computeMaxScrollY();
+
+ if (mOverScrollGlow != null) {
+ mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
+ }
+
+ overScrollBy(deltaX, deltaY, oldX, oldY,
+ rangeX, rangeY,
+ mOverscrollDistance, mOverscrollDistance, true);
+ if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
+ invalidate();
+ }
}
mZoomManager.keepZoomPickerVisible();
}
@@ -5459,6 +5665,11 @@ public class WebView extends AbsoluteLayout
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+
+ // Release any pulled glows
+ if (mOverScrollGlow != null) {
+ mOverScrollGlow.releaseAll();
+ }
}
private void cancelTouch() {
@@ -5469,6 +5680,7 @@ public class WebView extends AbsoluteLayout
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+
if (mTouchMode == TOUCH_DRAG_MODE ||
mTouchMode == TOUCH_DRAG_LAYER_MODE) {
WebViewCore.resumePriority();
@@ -5780,12 +5992,20 @@ public class WebView extends AbsoluteLayout
}
}
- private int computeMaxScrollX() {
- return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ /**
+ * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
+ * @return Maximum horizontal scroll position within real content
+ */
+ int computeMaxScrollX() {
+ return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
}
- private int computeMaxScrollY() {
- return Math.max(computeVerticalScrollRange() + getTitleHeight()
+ /**
+ * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
+ * @return Maximum vertical scroll position within real content
+ */
+ int computeMaxScrollY() {
+ return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
- getViewHeightWithTitle(), 0);
}
@@ -5804,7 +6024,7 @@ public class WebView extends AbsoluteLayout
public void flingScroll(int vx, int vy) {
mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
- computeMaxScrollY());
+ computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
invalidate();
}
@@ -5834,6 +6054,10 @@ public class WebView extends AbsoluteLayout
if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
WebViewCore.resumePriority();
WebViewCore.resumeUpdatePicture(mWebViewCore);
+ if (mScroller.springBack(mScrollX, mScrollY, 0, computeMaxScrollX(),
+ 0, computeMaxScrollY())) {
+ invalidate();
+ }
return;
}
float currentVelocity = mScroller.getCurrVelocity();
@@ -5860,13 +6084,37 @@ public class WebView extends AbsoluteLayout
+ " maxX=" + maxX + " maxY=" + maxY
+ " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
}
+
+ // Allow sloppy flings without overscrolling at the edges.
+ if ((mScrollX == 0 || mScrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
+ vx = 0;
+ }
+ if ((mScrollY == 0 || mScrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
+ vy = 0;
+ }
+
+ if (mOverscrollDistance < mOverflingDistance) {
+ if (mScrollX == -mOverscrollDistance || mScrollX == maxX + mOverscrollDistance) {
+ vx = 0;
+ }
+ if (mScrollY == -mOverscrollDistance || mScrollY == maxY + mOverscrollDistance) {
+ vy = 0;
+ }
+ }
+
mLastVelX = vx;
mLastVelY = vy;
mLastVelocity = velocity;
- mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
+ // no horizontal overscroll if the content just fits
+ mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
+ maxX == 0 ? 0 : mOverflingDistance, mOverflingDistance);
+ // Duration is calculated based on velocity. With range boundaries and overscroll
+ // we may not know how long the final animation will take. (Hence the deprecation
+ // warning on the call below.) It's not a big deal for scroll bars but if webcore
+ // resumes during this effect we will take a performance hit. See computeScroll;
+ // we resume webcore there when the animation is finished.
final int time = mScroller.getDuration();
- mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
awakenScrollBars(time);
invalidate();
}
@@ -6665,6 +6913,10 @@ public class WebView extends AbsoluteLayout
case MotionEvent.ACTION_CANCEL:
if (mDeferTouchMode == TOUCH_DRAG_MODE) {
// no fling in defer process
+ mScroller.springBack(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0,
+ computeMaxScrollY());
+ invalidate();
WebViewCore.resumePriority();
WebViewCore.resumeUpdatePicture(mWebViewCore);
}
@@ -7461,6 +7713,7 @@ public class WebView extends AbsoluteLayout
/* package */ native int nativeFocusCandidatePointer();
private native String nativeFocusCandidateText();
/* package */ native float nativeFocusCandidateTextSize();
+ /* package */ native int nativeFocusCandidateLineHeight();
/**
* Returns an integer corresponding to WebView.cpp::type.
* See WebTextView.setType()
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 70cfee9..423a788 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,6 +20,7 @@ import com.android.internal.R;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -39,6 +40,7 @@ import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
import android.util.StateSet;
import android.view.ActionMode;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -52,7 +54,6 @@ import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -138,6 +139,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
static final int TOUCH_MODE_FLING = 4;
/**
+ * Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end.
+ */
+ static final int TOUCH_MODE_OVERSCROLL = 5;
+
+ /**
+ * Indicates the view is being flung outside of normal content bounds
+ * and will spring back.
+ */
+ static final int TOUCH_MODE_OVERFLING = 6;
+
+ /**
* Regular layout - usually an unsolicited layout from the view system
*/
static final int LAYOUT_NORMAL = 0;
@@ -446,6 +458,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private ContextMenuInfo mContextMenuInfo = null;
/**
+ * Maximum distance to record overscroll
+ */
+ int mOverscrollMax;
+
+ /**
+ * Content height divided by this is the overscroll limit.
+ */
+ static final int OVERSCROLL_LIMIT_DIVISOR = 3;
+
+ /**
* Used to request a layout when we changed touch mode
*/
private static final int TOUCH_MODE_UNKNOWN = -1;
@@ -548,6 +570,48 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private static final int INVALID_POINTER = -1;
/**
+ * Maximum distance to overscroll by during edge effects
+ */
+ int mOverscrollDistance;
+
+ /**
+ * Maximum distance to overfling during edge effects
+ */
+ int mOverflingDistance;
+
+ // These two EdgeGlows are always set and used together.
+ // Checking one for null is as good as checking both.
+
+ /**
+ * Tracks the state of the top edge glow.
+ */
+ private EdgeGlow mEdgeGlowTop;
+
+ /**
+ * Tracks the state of the bottom edge glow.
+ */
+ private EdgeGlow mEdgeGlowBottom;
+
+ /**
+ * An estimate of how many pixels are between the top of the list and
+ * the top of the first position in the adapter, based on the last time
+ * we saw it. Used to hint where to draw edge glows.
+ */
+ private int mFirstPositionDistanceGuess;
+
+ /**
+ * An estimate of how many pixels are between the bottom of the list and
+ * the bottom of the last position in the adapter, based on the last time
+ * we saw it. Used to hint where to draw edge glows.
+ */
+ private int mLastPositionDistanceGuess;
+
+ /**
+ * Used for determining when to cancel out of overscroll.
+ */
+ private int mDirection = 0;
+
+ /**
* Interface definition for a callback to be invoked when the list or grid
* has been scrolled.
*/
@@ -690,9 +754,29 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mOverscrollDistance = configuration.getScaledOverscrollDistance();
+ mOverflingDistance = configuration.getScaledOverflingDistance();
+
mDensityScale = getContext().getResources().getDisplayMetrics().density;
}
+ @Override
+ public void setOverScrollMode(int mode) {
+ if (mode != OVER_SCROLL_NEVER) {
+ if (mEdgeGlowTop == null) {
+ final Resources res = getContext().getResources();
+ final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+ final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+ mEdgeGlowTop = new EdgeGlow(edge, glow);
+ mEdgeGlowBottom = new EdgeGlow(edge, glow);
+ }
+ } else {
+ mEdgeGlowTop = null;
+ mEdgeGlowBottom = null;
+ }
+ super.setOverScrollMode(mode);
+ }
+
/**
* {@inheritDoc}
*/
@@ -1003,6 +1087,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
+ * @return true if all list content currently fits within the view boundaries
+ */
+ private boolean contentFits() {
+ final int childCount = getChildCount();
+ if (childCount != mItemCount) {
+ return false;
+ }
+
+ return getChildAt(0).getTop() >= 0 && getChildAt(childCount - 1).getBottom() <= mBottom;
+ }
+
+ /**
* Enables fast scrolling by letting the user quickly scroll through lists by
* dragging the fast scroll thumb. The adapter attached to the list may want
* to implement {@link SectionIndexer} if it wishes to display alphabet preview and
@@ -1540,6 +1636,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
int result;
if (mSmoothScrollbarEnabled) {
result = Math.max(mItemCount * 100, 0);
+ if (mScrollY != 0) {
+ // Compensate for overscroll
+ result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100));
+ }
} else {
result = mItemCount;
}
@@ -1612,6 +1712,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
layoutChildren();
mInLayout = false;
+
+ mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
}
/**
@@ -2126,6 +2228,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mFlingRunnable.endFling();
if (mScrollY != 0) {
mScrollY = 0;
+ finishGlows();
invalidate();
}
}
@@ -2445,9 +2548,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Check if we have moved far enough that it looks more like a
// scroll than a tap
final int distance = Math.abs(deltaY);
- if (distance > mTouchSlop) {
+ final boolean overscroll = mScrollY != 0;
+ if (overscroll || distance > mTouchSlop) {
createScrollingCache();
- mTouchMode = TOUCH_MODE_SCROLL;
+ mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
mMotionCorrection = deltaY;
final Handler handler = getHandler();
// Handler should not be null unless the AbsListView is not attached to a
@@ -2483,6 +2587,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// touch mode). Force an initial layout to get rid of the selection.
layoutChildren();
}
+ } else {
+ int touchMode = mTouchMode;
+ if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
+ if (mFlingRunnable != null) {
+ mFlingRunnable.endFling();
+ }
+
+ if (mScrollY != 0) {
+ mScrollY = 0;
+ finishGlows();
+ invalidate();
+ }
+ }
}
}
@@ -2513,49 +2630,63 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
- mActivePointerId = ev.getPointerId(0);
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- int motionPosition = pointToPosition(x, y);
- if (!mDataChanged) {
- if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
- && (getAdapter().isEnabled(motionPosition))) {
- // User clicked on an actual view (and was not stopping a fling). It might be a
- // click or a scroll. Assume it is a click until proven otherwise
- mTouchMode = TOUCH_MODE_DOWN;
- // FIXME Debounce
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- } else {
- if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
- // If we couldn't find a view to click on, but the down event was touching
- // the edge, we will bail out and try again. This allows the edge correcting
- // code in ViewRoot to try to find a nearby view to select
- return false;
- }
+ switch (mTouchMode) {
+ case TOUCH_MODE_OVERFLING: {
+ mFlingRunnable.endFling();
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ mMotionY = mLastY = (int) ev.getY();
+ mMotionCorrection = 0;
+ mActivePointerId = ev.getPointerId(0);
+ break;
+ }
- if (mTouchMode == TOUCH_MODE_FLING) {
- // Stopped a fling. It is a scroll.
- createScrollingCache();
- mTouchMode = TOUCH_MODE_SCROLL;
- mMotionCorrection = 0;
- motionPosition = findMotionRow(y);
- mFlingRunnable.flywheelTouch();
+ default: {
+ mActivePointerId = ev.getPointerId(0);
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ int motionPosition = pointToPosition(x, y);
+ if (!mDataChanged) {
+ if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
+ && (getAdapter().isEnabled(motionPosition))) {
+ // User clicked on an actual view (and was not stopping a fling). It might be a
+ // click or a scroll. Assume it is a click until proven otherwise
+ mTouchMode = TOUCH_MODE_DOWN;
+ // FIXME Debounce
+ if (mPendingCheckForTap == null) {
+ mPendingCheckForTap = new CheckForTap();
+ }
+ postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+ } else {
+ if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
+ // If we couldn't find a view to click on, but the down event was touching
+ // the edge, we will bail out and try again. This allows the edge correcting
+ // code in ViewRoot to try to find a nearby view to select
+ return false;
+ }
+
+ if (mTouchMode == TOUCH_MODE_FLING) {
+ // Stopped a fling. It is a scroll.
+ createScrollingCache();
+ mTouchMode = TOUCH_MODE_SCROLL;
+ mMotionCorrection = 0;
+ motionPosition = findMotionRow(y);
+ mFlingRunnable.flywheelTouch();
+ }
}
}
- }
- if (motionPosition >= 0) {
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
+ if (motionPosition >= 0) {
+ // Remember where the motion event started
+ v = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = v.getTop();
+ }
+ mMotionX = x;
+ mMotionY = y;
+ mMotionPosition = motionPosition;
+ mLastY = Integer.MIN_VALUE;
+ break;
+ }
}
- mMotionX = x;
- mMotionY = y;
- mMotionPosition = motionPosition;
- mLastY = Integer.MIN_VALUE;
break;
}
@@ -2593,9 +2724,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
requestDisallowInterceptTouchEvent(true);
}
+ final int rawDeltaY = deltaY;
deltaY -= mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ final int motionIndex;
+ if (mMotionPosition >= 0) {
+ motionIndex = mMotionPosition - mFirstPosition;
+ } else {
+ // If we don't have a motion position that we can reliably track,
+ // pick something in the middle to make a best guess at things below.
+ motionIndex = getChildCount() / 2;
+ }
+
+ int motionViewPrevTop = 0;
+ View motionView = this.getChildAt(motionIndex);
+ if (motionView != null) {
+ motionViewPrevTop = motionView.getTop();
+ }
+
// No need to do all this work if we're not going to move anyway
boolean atEdge = false;
if (incrementalDeltaY != 0) {
@@ -2603,23 +2750,117 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
// Check to see if we have bumped into the scroll limit
- if (atEdge && getChildCount() > 0) {
- // Treat this like we're starting a new scroll from the current
- // position. This will let the user start scrolling back into
- // content immediately rather than needing to scroll back to the
- // point where they hit the limit first.
- int motionPosition = findMotionRow(y);
- if (motionPosition >= 0) {
- final View motionView = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = motionView.getTop();
+ motionView = this.getChildAt(motionIndex);
+ if (motionView != null) {
+ // Check if the top of the motion view is where it is
+ // supposed to be
+ final int motionViewRealTop = motionView.getTop();
+ if (atEdge) {
+ // Apply overscroll
+
+ int overscroll = -incrementalDeltaY -
+ (motionViewRealTop - motionViewPrevTop);
+ overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
+ 0, mOverscrollDistance, true);
+ if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+ // Don't allow overfling if we're at the edge.
+ mVelocityTracker.clear();
+ }
+
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ !contentFits())) {
+ mDirection = 0; // Reset when entering overscroll.
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ if (rawDeltaY > 0) {
+ mEdgeGlowTop.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (rawDeltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ }
}
mMotionY = y;
- mMotionPosition = motionPosition;
invalidate();
}
mLastY = y;
}
break;
+
+ case TOUCH_MODE_OVERSCROLL:
+ if (y != mLastY) {
+ final int rawDeltaY = deltaY;
+ deltaY -= mMotionCorrection;
+ int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+
+ final int oldScroll = mScrollY;
+ final int newScroll = oldScroll - incrementalDeltaY;
+ int newDirection = y > mLastY ? 1 : -1;
+
+ if (mDirection == 0) {
+ mDirection = newDirection;
+ }
+
+ if (mDirection != newDirection) {
+ // Coming back to 'real' list scrolling
+ incrementalDeltaY = -newScroll;
+ mScrollY = 0;
+
+ // No need to do all this work if we're not going to move anyway
+ if (incrementalDeltaY != 0) {
+ trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+ }
+
+ // Check to see if we are back in
+ View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null) {
+ mTouchMode = TOUCH_MODE_SCROLL;
+
+ // We did not scroll the full amount. Treat this essentially like the
+ // start of a new touch scroll
+ final int motionPosition = findClosestMotionRow(y);
+
+ mMotionCorrection = 0;
+ motionView = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = motionView.getTop();
+ mMotionY = y;
+ mMotionPosition = motionPosition;
+ }
+ } else {
+ overScrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
+ 0, mOverscrollDistance, true);
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ !contentFits())) {
+ if (rawDeltaY > 0) {
+ mEdgeGlowTop.onPull((float) -incrementalDeltaY / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (rawDeltaY < 0) {
+ mEdgeGlowBottom.onPull((float) -incrementalDeltaY / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ invalidate();
+ }
+ if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+ // Don't allow overfling if we're at the edge.
+ mVelocityTracker.clear();
+ }
+ }
+ mLastY = y;
+ mDirection = newDirection;
+ }
+ break;
}
break;
@@ -2693,19 +2934,30 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
case TOUCH_MODE_SCROLL:
final int childCount = getChildCount();
if (childCount > 0) {
- if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
+ final int firstChildTop = getChildAt(0).getTop();
+ final int lastChildBottom = getChildAt(childCount - 1).getBottom();
+ final int contentTop = mListPadding.top;
+ final int contentBottom = getHeight() - mListPadding.bottom;
+ if (mFirstPosition == 0 && firstChildTop >= contentTop &&
mFirstPosition + childCount < mItemCount &&
- getChildAt(childCount - 1).getBottom() <=
- getHeight() - mListPadding.bottom) {
+ lastChildBottom <= getHeight() - contentBottom) {
mTouchMode = TOUCH_MODE_REST;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
} else {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+
final int initialVelocity = (int)
(velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);
-
- if (Math.abs(initialVelocity) > mMinimumVelocity) {
+ // Fling if we have enough velocity and we aren't at a boundary.
+ // Since we can potentially overfling more than we can overscroll, don't
+ // allow the weird behavior where you can scroll to a boundary then
+ // fling further.
+ if (Math.abs(initialVelocity) > mMinimumVelocity &&
+ !((mFirstPosition == 0 &&
+ firstChildTop == contentTop - mOverscrollDistance) ||
+ (mFirstPosition + childCount == mItemCount &&
+ lastChildBottom == contentBottom + mOverscrollDistance))) {
if (mFlingRunnable == null) {
mFlingRunnable = new FlingRunnable();
}
@@ -2725,10 +2977,32 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
}
break;
+
+ case TOUCH_MODE_OVERSCROLL:
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ if (Math.abs(initialVelocity) > mMinimumVelocity) {
+ mFlingRunnable.startOverfling(-initialVelocity);
+ } else {
+ mFlingRunnable.startSpringback();
+ }
+
+ break;
}
setPressed(false);
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
+
// Need to redraw since we probably aren't drawing the selector anymore
invalidate();
@@ -2759,24 +3033,42 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
case MotionEvent.ACTION_CANCEL: {
- mTouchMode = TOUCH_MODE_REST;
- setPressed(false);
- View motionView = getChildAt(mMotionPosition - mFirstPosition);
- if (motionView != null) {
- motionView.setPressed(false);
- }
- clearScrollingCache();
+ switch (mTouchMode) {
+ case TOUCH_MODE_OVERSCROLL:
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ mFlingRunnable.startSpringback();
+ break;
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
+ case TOUCH_MODE_OVERFLING:
+ // Do nothing - let it play out.
+ break;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
+ default:
+ mTouchMode = TOUCH_MODE_REST;
+ setPressed(false);
+ View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+ if (motionView != null) {
+ motionView.setPressed(false);
+ }
+ clearScrollingCache();
+
+ final Handler handler = getHandler();
+ if (handler != null) {
+ handler.removeCallbacks(mPendingCheckForLongPress);
+ }
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
}
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
mActivePointerId = INVALID_POINTER;
break;
}
@@ -2801,10 +3093,61 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ protected void onOverScrolled(int scrollX, int scrollY,
+ boolean clampedX, boolean clampedY) {
+ mScrollY = scrollY;
+
+ if (clampedY) {
+ // Velocity is broken by hitting the limit; don't start a fling off of this.
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ awakenScrollBars();
+ }
+
+ @Override
public void draw(Canvas canvas) {
super.draw(canvas);
+ if (mEdgeGlowTop != null) {
+ final int scrollY = mScrollY;
+ if (!mEdgeGlowTop.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+
+ canvas.translate(0, Math.min(0, scrollY + mFirstPositionDistanceGuess));
+ mEdgeGlowTop.setSize(width, getHeight());
+ if (mEdgeGlowTop.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowBottom.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight();
+
+ canvas.translate(-width, 0);
+ canvas.rotate(-180, width, 0);
+ canvas.translate(0, -height);
+ mEdgeGlowBottom.setSize(width, height);
+ if (mEdgeGlowBottom.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ }
if (mFastScroller != null) {
- mFastScroller.draw(canvas);
+ final int scrollY = mScrollY;
+ if (scrollY != 0) {
+ // Pin to the top/bottom during overscroll
+ int restoreCount = canvas.save();
+ canvas.translate(0, (float) scrollY);
+ mFastScroller.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ } else {
+ mFastScroller.draw(canvas);
+ }
}
}
@@ -2823,6 +3166,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
int touchMode = mTouchMode;
+ if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
+ mMotionCorrection = 0;
+ return true;
+ }
final int x = (int) ev.getX();
final int y = (int) ev.getY();
@@ -2887,6 +3234,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mMotionX = (int) ev.getX(newPointerIndex);
mMotionY = (int) ev.getY(newPointerIndex);
+ mMotionCorrection = 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
@@ -2942,7 +3290,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
/**
* Tracks the decay of a fling scroll
*/
- private final Scroller mScroller;
+ private final OverScroller mScroller;
/**
* Y value reported by mScroller on the previous fling
@@ -2953,7 +3301,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public void run() {
final int activeId = mActivePointerId;
final VelocityTracker vt = mVelocityTracker;
- final Scroller scroller = mScroller;
+ final OverScroller scroller = mScroller;
if (vt == null || activeId == INVALID_POINTER) {
return;
}
@@ -2975,7 +3323,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private static final int FLYWHEEL_TIMEOUT = 40; // milliseconds
FlingRunnable() {
- mScroller = new Scroller(getContext());
+ mScroller = new OverScroller(getContext());
}
void start(int initialVelocity) {
@@ -2998,6 +3346,42 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+ void startSpringback() {
+ if (mScroller.springBack(0, mScrollY, 0, 0, 0, 0)) {
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ invalidate();
+ post(this);
+ } else {
+ mTouchMode = TOUCH_MODE_REST;
+ }
+ }
+
+ void startOverfling(int initialVelocity) {
+ final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0;
+ final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE;
+ mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight());
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ invalidate();
+ post(this);
+ }
+
+ void edgeReached(int delta) {
+ mScroller.notifyVerticalEdgeReached(mScrollY, 0, mOverflingDistance);
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) {
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ final int vel = (int) mScroller.getCurrVelocity();
+ if (delta > 0) {
+ mEdgeGlowTop.onAbsorb(vel);
+ } else {
+ mEdgeGlowBottom.onAbsorb(vel);
+ }
+ }
+ invalidate();
+ post(this);
+ }
+
void startScroll(int distance, int duration) {
int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
@@ -3040,58 +3424,100 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return;
}
// Fall through
- case TOUCH_MODE_FLING:
+ case TOUCH_MODE_FLING: {
if (mItemCount == 0 || getChildCount() == 0) {
endFling();
return;
}
- break;
- }
- final Scroller scroller = mScroller;
- boolean more = scroller.computeScrollOffset();
- final int y = scroller.getCurrY();
- // Flip sign to convert finger direction to list items direction
- // (e.g. finger moving down means list is moving towards the top)
- int delta = mLastFlingY - y;
+ final OverScroller scroller = mScroller;
+ boolean more = scroller.computeScrollOffset();
+ final int y = scroller.getCurrY();
- // Pretend that each frame of a fling scroll is a touch scroll
- if (delta > 0) {
- // List is moving towards the top. Use first view as mMotionPosition
- mMotionPosition = mFirstPosition;
- final View firstView = getChildAt(0);
- mMotionViewOriginalTop = firstView.getTop();
+ // Flip sign to convert finger direction to list items direction
+ // (e.g. finger moving down means list is moving towards the top)
+ int delta = mLastFlingY - y;
- // Don't fling more than 1 screen
- delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);
- } else {
- // List is moving towards the bottom. Use last view as mMotionPosition
- int offsetToLast = getChildCount() - 1;
- mMotionPosition = mFirstPosition + offsetToLast;
+ // Pretend that each frame of a fling scroll is a touch scroll
+ if (delta > 0) {
+ // List is moving towards the top. Use first view as mMotionPosition
+ mMotionPosition = mFirstPosition;
+ final View firstView = getChildAt(0);
+ mMotionViewOriginalTop = firstView.getTop();
- final View lastView = getChildAt(offsetToLast);
- mMotionViewOriginalTop = lastView.getTop();
+ // Don't fling more than 1 screen
+ delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);
+ } else {
+ // List is moving towards the bottom. Use last view as mMotionPosition
+ int offsetToLast = getChildCount() - 1;
+ mMotionPosition = mFirstPosition + offsetToLast;
- // Don't fling more than 1 screen
- delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
- }
+ final View lastView = getChildAt(offsetToLast);
+ mMotionViewOriginalTop = lastView.getTop();
- // Don't stop just because delta is zero (it could have been rounded)
- final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+ // Don't fling more than 1 screen
+ delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
+ }
- if (more && !atEnd) {
- invalidate();
- mLastFlingY = y;
- post(this);
- } else {
- endFling();
+ // Check to see if we have bumped into the scroll limit
+ View motionView = getChildAt(mMotionPosition - mFirstPosition);
+ int oldTop = 0;
+ if (motionView != null) {
+ oldTop = motionView.getTop();
+ }
- if (PROFILE_FLINGING) {
- if (mFlingProfilingStarted) {
- Debug.stopMethodTracing();
- mFlingProfilingStarted = false;
+ // Don't stop just because delta is zero (it could have been rounded)
+ final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+ if (atEnd) {
+ if (motionView != null) {
+ // Tweak the scroll for how far we overshot
+ int overshoot = -(delta - (motionView.getTop() - oldTop));
+ overScrollBy(0, overshoot, 0, mScrollY, 0, 0,
+ 0, mOverflingDistance, false);
}
+ edgeReached(delta);
+ break;
}
+
+ if (more && !atEnd) {
+ invalidate();
+ mLastFlingY = y;
+ post(this);
+ } else {
+ endFling();
+
+ if (PROFILE_FLINGING) {
+ if (mFlingProfilingStarted) {
+ Debug.stopMethodTracing();
+ mFlingProfilingStarted = false;
+ }
+
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
+ }
+ }
+ break;
+ }
+
+ case TOUCH_MODE_OVERFLING: {
+ final OverScroller scroller = mScroller;
+ if (scroller.computeScrollOffset()) {
+ final int scrollY = mScrollY;
+ final int deltaY = scroller.getCurrY() - scrollY;
+ if (overScrollBy(0, deltaY, 0, scrollY, 0, 0,
+ 0, mOverflingDistance, false)) {
+ startSpringback();
+ } else {
+ invalidate();
+ post(this);
+ }
+ } else {
+ endFling();
+ }
+ break;
+ }
}
}
}
@@ -3620,16 +4046,29 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int firstPosition = mFirstPosition;
- if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {
+ // Update our guesses for where the first and last views are
+ if (firstPosition == 0) {
+ mFirstPositionDistanceGuess = firstTop - mListPadding.top;
+ } else {
+ mFirstPositionDistanceGuess += incrementalDeltaY;
+ }
+ if (firstPosition + childCount == mItemCount) {
+ mLastPositionDistanceGuess = lastBottom + mListPadding.bottom;
+ } else {
+ mLastPositionDistanceGuess += incrementalDeltaY;
+ }
+
+ if (firstPosition == 0 && firstTop >= listPadding.top && incrementalDeltaY >= 0) {
// Don't need to move views down if the top of the first position
// is already visible
- return true;
+ return incrementalDeltaY != 0;
}
- if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {
+ if (firstPosition + childCount == mItemCount && lastBottom <= end &&
+ incrementalDeltaY <= 0) {
// Don't need to move views up if the bottom of the last position
// is already visible
- return true;
+ return incrementalDeltaY != 0;
}
final boolean down = incrementalDeltaY < 0;
@@ -3805,6 +4244,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
abstract int findMotionRow(int y);
/**
+ * Find the row closest to y. This row will be used as the motion row when scrolling.
+ *
+ * @param y Where the user touched
+ * @return The position of the first (or only) item in the row closest to y
+ */
+ int findClosestMotionRow(int y) {
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ return INVALID_POSITION;
+ }
+
+ final int motionRow = findMotionRow(y);
+ return motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1;
+ }
+
+ /**
* Causes all the views to be rebuilt and redrawn.
*/
public void invalidateViews() {
@@ -4577,6 +5032,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return result;
}
+ private void finishGlows() {
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.finish();
+ mEdgeGlowBottom.finish();
+ }
+ }
+
/**
* Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
* through the specified intent.
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
new file mode 100644
index 0000000..9b3a6e6
--- /dev/null
+++ b/core/java/android/widget/EdgeGlow.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+/**
+ * This class performs the glow effect used at the edges of scrollable widgets.
+ * @hide
+ */
+public class EdgeGlow {
+ private static final String TAG = "EdgeGlow";
+
+ // Time it will take the effect to fully recede in ms
+ private static final int RECEDE_TIME = 1000;
+
+ // Time it will take before a pulled glow begins receding
+ private static final int PULL_TIME = 167;
+
+ // Time it will take for a pulled glow to decay to partial strength before release
+ private static final int PULL_DECAY_TIME = 1000;
+
+ private static final float MAX_ALPHA = 0.8f;
+ private static final float HELD_EDGE_ALPHA = 0.7f;
+ private static final float HELD_EDGE_SCALE_Y = 0.5f;
+ private static final float HELD_GLOW_ALPHA = 0.5f;
+ private static final float HELD_GLOW_SCALE_Y = 0.5f;
+
+ private static final float MAX_GLOW_HEIGHT = 3.f;
+
+ private static final float PULL_GLOW_BEGIN = 1.f;
+ private static final float PULL_EDGE_BEGIN = 0.6f;
+
+ // Minimum velocity that will be absorbed
+ private static final int MIN_VELOCITY = 100;
+
+ private static final float EPSILON = 0.001f;
+
+ private final Drawable mEdge;
+ private final Drawable mGlow;
+ private int mWidth;
+ private int mHeight;
+
+ private float mEdgeAlpha;
+ private float mEdgeScaleY;
+ private float mGlowAlpha;
+ private float mGlowScaleY;
+
+ private float mEdgeAlphaStart;
+ private float mEdgeAlphaFinish;
+ private float mEdgeScaleYStart;
+ private float mEdgeScaleYFinish;
+ private float mGlowAlphaStart;
+ private float mGlowAlphaFinish;
+ private float mGlowScaleYStart;
+ private float mGlowScaleYFinish;
+
+ private long mStartTime;
+ private float mDuration;
+
+ private final Interpolator mInterpolator;
+
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PULL = 1;
+ private static final int STATE_ABSORB = 2;
+ private static final int STATE_RECEDE = 3;
+ private static final int STATE_PULL_DECAY = 4;
+
+ // How much dragging should effect the height of the edge image.
+ // Number determined by user testing.
+ private static final int PULL_DISTANCE_EDGE_FACTOR = 5;
+
+ // How much dragging should effect the height of the glow image.
+ // Number determined by user testing.
+ private static final int PULL_DISTANCE_GLOW_FACTOR = 5;
+ private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
+
+ private static final int VELOCITY_EDGE_FACTOR = 8;
+ private static final int VELOCITY_GLOW_FACTOR = 16;
+
+ private int mState = STATE_IDLE;
+
+ private float mPullDistance;
+
+ public EdgeGlow(Drawable edge, Drawable glow) {
+ mEdge = edge;
+ mGlow = glow;
+
+ mInterpolator = new DecelerateInterpolator();
+ }
+
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public boolean isFinished() {
+ return mState == STATE_IDLE;
+ }
+
+ public void finish() {
+ mState = STATE_IDLE;
+ }
+
+ /**
+ * Call when the object is pulled by the user.
+ *
+ * @param deltaDistance Change in distance since the last call
+ */
+ public void onPull(float deltaDistance) {
+ final long now = AnimationUtils.currentAnimationTimeMillis();
+ if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
+ return;
+ }
+ if (mState != STATE_PULL) {
+ mGlowScaleY = PULL_GLOW_BEGIN;
+ }
+ mState = STATE_PULL;
+
+ mStartTime = now;
+ mDuration = PULL_TIME;
+
+ mPullDistance += deltaDistance;
+ float distance = Math.abs(mPullDistance);
+
+ mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
+ mEdgeScaleY = mEdgeScaleYStart = Math.max(
+ HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
+
+ mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
+ mGlowAlpha +
+ (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+
+ float glowChange = Math.abs(deltaDistance);
+ if (deltaDistance > 0 && mPullDistance < 0) {
+ glowChange = -glowChange;
+ }
+ if (mPullDistance == 0) {
+ mGlowScaleY = 0;
+ }
+
+ // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
+ mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
+ 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
+
+ mEdgeAlphaFinish = mEdgeAlpha;
+ mEdgeScaleYFinish = mEdgeScaleY;
+ mGlowAlphaFinish = mGlowAlpha;
+ mGlowScaleYFinish = mGlowScaleY;
+ }
+
+ /**
+ * Call when the object is released after being pulled.
+ */
+ public void onRelease() {
+ mPullDistance = 0;
+
+ if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
+ return;
+ }
+
+ mState = STATE_RECEDE;
+ mEdgeAlphaStart = mEdgeAlpha;
+ mEdgeScaleYStart = mEdgeScaleY;
+ mGlowAlphaStart = mGlowAlpha;
+ mGlowScaleYStart = mGlowScaleY;
+
+ mEdgeAlphaFinish = 0.f;
+ mEdgeScaleYFinish = 0.f;
+ mGlowAlphaFinish = 0.f;
+ mGlowScaleYFinish = 0.f;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = RECEDE_TIME;
+ }
+
+ /**
+ * Call when the effect absorbs an impact at the given velocity.
+ *
+ * @param velocity Velocity at impact in pixels per second.
+ */
+ public void onAbsorb(int velocity) {
+ mState = STATE_ABSORB;
+ velocity = Math.max(MIN_VELOCITY, Math.abs(velocity));
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = 0.1f + (velocity * 0.03f);
+
+ // The edge should always be at least partially visible, regardless
+ // of velocity.
+ mEdgeAlphaStart = 0.f;
+ mEdgeScaleY = mEdgeScaleYStart = 0.f;
+ // The glow depends more on the velocity, and therefore starts out
+ // nearly invisible.
+ mGlowAlphaStart = 0.5f;
+ mGlowScaleYStart = 0.f;
+
+ // Factor the velocity by 8. Testing on device shows this works best to
+ // reflect the strength of the user's scrolling.
+ mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
+ // Edge should never get larger than the size of its asset.
+ mEdgeScaleYFinish = Math.max(
+ HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
+
+ // Growth for the size of the glow should be quadratic to properly
+ // respond
+ // to a user's scrolling speed. The faster the scrolling speed, the more
+ // intense the effect should be for both the size and the saturation.
+ mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+ // Alpha should change for the glow as well as size.
+ mGlowAlphaFinish = Math.max(
+ mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+ }
+
+
+ /**
+ * Draw into the provided canvas. Assumes that the canvas has been rotated
+ * accordingly and the size has been set. The effect will be drawn the full
+ * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
+ * 1.f of height.
+ *
+ * @param canvas Canvas to draw into
+ * @return true if drawing should continue beyond this frame to continue the
+ * animation
+ */
+ public boolean draw(Canvas canvas) {
+ update();
+
+ final int edgeHeight = mEdge.getIntrinsicHeight();
+ final int edgeWidth = mEdge.getIntrinsicWidth();
+ final int glowHeight = mGlow.getIntrinsicHeight();
+ final int glowWidth = mGlow.getIntrinsicWidth();
+
+ mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
+
+ // Center the glow inside the width of the container.
+ int glowLeft = (mWidth - glowWidth)/2;
+ mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, (int) Math.min(
+ glowHeight * mGlowScaleY * glowHeight/ glowWidth * 0.6f,
+ glowHeight * MAX_GLOW_HEIGHT));
+ mGlow.draw(canvas);
+
+ mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
+
+ int edgeLeft = (mWidth - edgeWidth)/2;
+ mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, (int) (edgeHeight * mEdgeScaleY));
+ mEdge.draw(canvas);
+
+ return mState != STATE_IDLE;
+ }
+
+ private void update() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final float t = Math.min((time - mStartTime) / mDuration, 1.f);
+
+ final float interp = mInterpolator.getInterpolation(t);
+
+ mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
+ mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
+ mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
+ mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+
+ if (t >= 1.f - EPSILON) {
+ switch (mState) {
+ case STATE_ABSORB:
+ mState = STATE_RECEDE;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = RECEDE_TIME;
+
+ mEdgeAlphaStart = mEdgeAlpha;
+ mEdgeScaleYStart = mEdgeScaleY;
+ mGlowAlphaStart = mGlowAlpha;
+ mGlowScaleYStart = mGlowScaleY;
+
+ // After absorb, the glow and edge should fade to nothing.
+ mEdgeAlphaFinish = 0.f;
+ mEdgeScaleYFinish = 0.f;
+ mGlowAlphaFinish = 0.f;
+ mGlowScaleYFinish = 0.f;
+ break;
+ case STATE_PULL:
+ mState = STATE_PULL_DECAY;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = PULL_DECAY_TIME;
+
+ mEdgeAlphaStart = mEdgeAlpha;
+ mEdgeScaleYStart = mEdgeScaleY;
+ mGlowAlphaStart = mGlowAlpha;
+ mGlowScaleYStart = mGlowScaleY;
+
+ // After pull, the glow and edge should fade to nothing.
+ mEdgeAlphaFinish = 0.f;
+ mEdgeScaleYFinish = 0.f;
+ mGlowAlphaFinish = 0.f;
+ mGlowScaleYFinish = 0.f;
+ break;
+ case STATE_PULL_DECAY:
+ // When receding, we want edge to decrease more slowly
+ // than the glow.
+ float factor = mGlowScaleYFinish != 0 ? 1
+ / (mGlowScaleYFinish * mGlowScaleYFinish)
+ : Float.MAX_VALUE;
+ mEdgeScaleY = mEdgeScaleYStart +
+ (mEdgeScaleYFinish - mEdgeScaleYStart) *
+ interp * factor;
+ break;
+ case STATE_RECEDE:
+ mState = STATE_IDLE;
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 114ae81..4146460 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1954,7 +1954,12 @@ public class GridView extends AbsListView {
// TODO: Account for vertical spacing too
final int numColumns = mNumColumns;
final int rowCount = (mItemCount + numColumns - 1) / numColumns;
- return Math.max(rowCount * 100, 0);
+ int result = Math.max(rowCount * 100, 0);
+ if (mScrollY != 0) {
+ // Compensate for overscroll
+ result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100));
+ }
+ return result;
}
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index e30d4c8..9fc91da 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,19 +16,24 @@
package android.widget;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
+import com.android.internal.R;
+
import android.util.AttributeSet;
-import android.view.FocusFinder;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.view.View;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.KeyEvent;
+import android.view.FocusFinder;
+import android.view.MotionEvent;
import android.view.ViewParent;
import android.view.animation.AnimationUtils;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import java.util.List;
@@ -65,7 +70,9 @@ public class HorizontalScrollView extends FrameLayout {
private long mLastScroll;
private final Rect mTempRect = new Rect();
- private Scroller mScroller;
+ private OverScroller mScroller;
+ private EdgeGlow mEdgeGlowLeft;
+ private EdgeGlow mEdgeGlowRight;
/**
* Flag to indicate that we are moving focus ourselves. This is so the
@@ -119,6 +126,9 @@ public class HorizontalScrollView extends FrameLayout {
private int mMinimumVelocity;
private int mMaximumVelocity;
+ private int mOverscrollDistance;
+ private int mOverflingDistance;
+
/**
* ID of the active pointer. This is used to retain consistency during
* drags/flings if multiple pointers are used.
@@ -191,7 +201,7 @@ public class HorizontalScrollView extends FrameLayout {
private void initScrollView() {
- mScroller = new Scroller(getContext());
+ mScroller = new OverScroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
@@ -199,6 +209,8 @@ public class HorizontalScrollView extends FrameLayout {
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mOverscrollDistance = configuration.getScaledOverscrollDistance();
+ mOverflingDistance = configuration.getScaledOverflingDistance();
}
@Override
@@ -463,6 +475,9 @@ public class HorizontalScrollView extends FrameLayout {
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+ invalidate();
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -495,9 +510,7 @@ public class HorizontalScrollView extends FrameLayout {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
- if (!(mIsBeingDragged = inChild((int) x, (int) ev.getY()))) {
- return false;
- }
+ mIsBeingDragged = true;
/*
* If being flinged and user touches, stop the fling. isFinished
@@ -520,7 +533,36 @@ public class HorizontalScrollView extends FrameLayout {
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
- scrollBy(deltaX, 0);
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
+ final int range = getScrollRange();
+ if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
+ mOverscrollDistance, 0, true)) {
+ // Break our velocity if we hit a scroll barrier.
+ mVelocityTracker.clear();
+ }
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ final int pulledToX = oldX + deltaX;
+ if (pulledToX < 0) {
+ mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+ if (!mEdgeGlowRight.isFinished()) {
+ mEdgeGlowRight.onRelease();
+ }
+ } else if (pulledToX > range) {
+ mEdgeGlowRight.onPull((float) deltaX / getWidth());
+ if (!mEdgeGlowLeft.isFinished()) {
+ mEdgeGlowLeft.onRelease();
+ }
+ }
+ if (mEdgeGlowLeft != null
+ && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
+ invalidate();
+ }
+ }
}
break;
case MotionEvent.ACTION_UP:
@@ -529,8 +571,15 @@ public class HorizontalScrollView extends FrameLayout {
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
- if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
- fling(-initialVelocity);
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ fling(-initialVelocity);
+ } else {
+ final int right = getScrollRange();
+ if (mScroller.springBack(mScrollX, mScrollY, 0, right, 0, 0)) {
+ invalidate();
+ }
+ }
}
mActivePointerId = INVALID_POINTER;
@@ -540,16 +589,27 @@ public class HorizontalScrollView extends FrameLayout {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+ if (mEdgeGlowLeft != null) {
+ mEdgeGlowLeft.onRelease();
+ mEdgeGlowRight.onRelease();
+ }
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+ invalidate();
+ }
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+ if (mEdgeGlowLeft != null) {
+ mEdgeGlowLeft.onRelease();
+ mEdgeGlowRight.onRelease();
+ }
}
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -576,12 +636,28 @@ public class HorizontalScrollView extends FrameLayout {
}
}
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY,
+ boolean clampedX, boolean clampedY) {
+ // Treat animating scrolls differently; see #computeScroll() for why.
+ if (!mScroller.isFinished()) {
+ mScrollX = scrollX;
+ mScrollY = scrollY;
+ if (clampedX) {
+ mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
+ }
+ } else {
+ super.scrollTo(scrollX, scrollY);
+ }
+ awakenScrollBars();
+ }
+
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0,
- child.getWidth() - getWidth() - mPaddingLeft - mPaddingRight);
+ child.getWidth() - (getWidth() - mPaddingLeft - mPaddingRight));
}
return scrollRange;
}
@@ -958,7 +1034,16 @@ public class HorizontalScrollView extends FrameLayout {
return contentWidth;
}
- return getChildAt(0).getRight();
+ int scrollRange = getChildAt(0).getRight();
+ final int scrollX = mScrollX;
+ final int overscrollRight = Math.max(0, scrollRange - contentWidth);
+ if (scrollX < 0) {
+ scrollRange -= scrollX;
+ } else if (scrollX > overscrollRight) {
+ scrollRange += scrollX - overscrollRight;
+ }
+
+ return scrollRange;
}
@Override
@@ -1019,14 +1104,20 @@ public class HorizontalScrollView extends FrameLayout {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
- if (getChildCount() > 0) {
- View child = getChildAt(0);
- x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
- y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
- if (x != oldX || y != oldY) {
- mScrollX = x;
- mScrollY = y;
- onScrollChanged(x, y, oldX, oldY);
+ if (oldX != x || oldY != y) {
+ overScrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
+ mOverflingDistance, 0, false);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int range = getScrollRange();
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ if (x < 0 && oldX >= 0) {
+ mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
+ } else if (x > range && oldX <= range) {
+ mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
+ }
}
}
awakenScrollBars();
@@ -1263,7 +1354,7 @@ public class HorizontalScrollView extends FrameLayout {
int right = getChildAt(0).getWidth();
mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0,
- Math.max(0, right - width), 0, 0);
+ Math.max(0, right - width), 0, 0, width/2, 0);
final boolean movingRight = velocityX > 0;
@@ -1301,6 +1392,56 @@ public class HorizontalScrollView extends FrameLayout {
}
}
+ @Override
+ public void setOverScrollMode(int mode) {
+ if (mode != OVER_SCROLL_NEVER) {
+ if (mEdgeGlowLeft == null) {
+ final Resources res = getContext().getResources();
+ final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+ final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+ mEdgeGlowLeft = new EdgeGlow(edge, glow);
+ mEdgeGlowRight = new EdgeGlow(edge, glow);
+ }
+ } else {
+ mEdgeGlowLeft = null;
+ mEdgeGlowRight = null;
+ }
+ super.setOverScrollMode(mode);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mEdgeGlowLeft != null) {
+ final int scrollX = mScrollX;
+ if (!mEdgeGlowLeft.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int height = getHeight();
+
+ canvas.rotate(270);
+ canvas.translate(-height * 1.5f, Math.min(0, scrollX));
+ mEdgeGlowLeft.setSize(getHeight() * 2, getWidth());
+ if (mEdgeGlowLeft.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowRight.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight();
+
+ canvas.rotate(90);
+ canvas.translate(-height / 2, -(Math.max(getScrollRange(), scrollX) + width));
+ mEdgeGlowRight.setSize(height * 2, width);
+ if (mEdgeGlowRight.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ }
+ }
+
private int clamp(int n, int my, int child) {
if (my >= child || n < 0) {
return 0;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 502cc38..fd4f950 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -104,6 +104,9 @@ public class ListView extends AbsListView {
Drawable mDivider;
int mDividerHeight;
+ Drawable mOverScrollHeader;
+ Drawable mOverScrollFooter;
+
private boolean mIsCacheColorOpaque;
private boolean mDividerIsOpaque;
@@ -152,6 +155,18 @@ public class ListView extends AbsListView {
setDivider(d);
}
+ final Drawable osHeader = a.getDrawable(
+ com.android.internal.R.styleable.ListView_overScrollHeader);
+ if (osHeader != null) {
+ setOverscrollHeader(osHeader);
+ }
+
+ final Drawable osFooter = a.getDrawable(
+ com.android.internal.R.styleable.ListView_overScrollFooter);
+ if (osFooter != null) {
+ setOverscrollFooter(osFooter);
+ }
+
// Use the height specified, zero being the default
final int dividerHeight = a.getDimensionPixelSize(
com.android.internal.R.styleable.ListView_dividerHeight, 0);
@@ -2962,14 +2977,52 @@ public class ListView extends AbsListView {
}
super.setCacheColorHint(color);
}
-
+
+ void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
+ final int height = drawable.getMinimumHeight();
+
+ canvas.save();
+ canvas.clipRect(bounds);
+
+ final int span = bounds.bottom - bounds.top;
+ if (span < height) {
+ bounds.top = bounds.bottom - height;
+ }
+
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
+ void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
+ final int height = drawable.getMinimumHeight();
+
+ canvas.save();
+ canvas.clipRect(bounds);
+
+ final int span = bounds.bottom - bounds.top;
+ if (span < height) {
+ bounds.bottom = bounds.top + height;
+ }
+
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the dividers
final int dividerHeight = mDividerHeight;
+ final Drawable overscrollHeader = mOverScrollHeader;
+ final Drawable overscrollFooter = mOverScrollFooter;
+ final boolean drawOverscrollHeader = overscrollHeader != null;
+ final boolean drawOverscrollFooter = overscrollFooter != null;
final boolean drawDividers = dividerHeight > 0 && mDivider != null;
- if (drawDividers) {
+ if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
// Only modify the top and bottom in the loop, we set the left and right here
final Rect bounds = mTempRect;
bounds.left = mPaddingLeft;
@@ -2998,34 +3051,67 @@ public class ListView extends AbsListView {
final int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
if (!mStackFromBottom) {
- int bottom;
+ int bottom = 0;
+ // Draw top divider or header for overscroll
+ final int scrollY = mScrollY;
+ if (count > 0 && scrollY < 0) {
+ if (drawOverscrollHeader) {
+ bounds.bottom = 0;
+ bounds.top = scrollY;
+ drawOverscrollHeader(canvas, overscrollHeader, bounds);
+ } else if (drawDividers) {
+ bounds.bottom = 0;
+ bounds.top = -dividerHeight;
+ drawDivider(canvas, bounds, -1);
+ }
+ }
+
for (int i = 0; i < count; i++) {
if ((headerDividers || first + i >= headerCount) &&
(footerDividers || first + i < footerLimit)) {
View child = getChildAt(i);
bottom = child.getBottom();
// Don't draw dividers next to items that are not enabled
- if ((areAllItemsSelectable ||
- (adapter.isEnabled(first + i) && (i == count - 1 ||
- adapter.isEnabled(first + i + 1))))) {
- bounds.top = bottom;
- bounds.bottom = bottom + dividerHeight;
- drawDivider(canvas, bounds, i);
- } else if (fillForMissingDividers) {
- bounds.top = bottom;
- bounds.bottom = bottom + dividerHeight;
- canvas.drawRect(bounds, paint);
+
+ if (drawDividers &&
+ (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
+ if ((areAllItemsSelectable ||
+ (adapter.isEnabled(first + i) && (i == count - 1 ||
+ adapter.isEnabled(first + i + 1))))) {
+ bounds.top = bottom;
+ bounds.bottom = bottom + dividerHeight;
+ drawDivider(canvas, bounds, i);
+ } else if (fillForMissingDividers) {
+ bounds.top = bottom;
+ bounds.bottom = bottom + dividerHeight;
+ canvas.drawRect(bounds, paint);
+ }
}
}
}
+
+ final int overFooterBottom = mBottom + mScrollY;
+ if (drawOverscrollFooter && first + count == itemCount &&
+ overFooterBottom > bottom) {
+ bounds.top = bottom;
+ bounds.bottom = overFooterBottom;
+ drawOverscrollFooter(canvas, overscrollFooter, bounds);
+ }
} else {
int top;
int listTop = mListPadding.top;
final int scrollY = mScrollY;
- for (int i = 0; i < count; i++) {
+ if (count > 0 && drawOverscrollHeader) {
+ bounds.top = scrollY;
+ bounds.bottom = getChildAt(0).getTop();
+ drawOverscrollHeader(canvas, overscrollHeader, bounds);
+ }
+
+ final int start = drawOverscrollHeader ? 1 : 0;
+ for (int i = start; i < count; i++) {
if ((headerDividers || first + i >= headerCount) &&
(footerDividers || first + i < footerLimit)) {
View child = getChildAt(i);
@@ -3052,9 +3138,16 @@ public class ListView extends AbsListView {
}
if (count > 0 && scrollY > 0) {
- bounds.top = listBottom;
- bounds.bottom = listBottom + dividerHeight;
- drawDivider(canvas, bounds, -1);
+ if (drawOverscrollFooter) {
+ final int absListBottom = mBottom;
+ bounds.top = absListBottom;
+ bounds.bottom = absListBottom + scrollY;
+ drawOverscrollFooter(canvas, overscrollFooter, bounds);
+ } else if (drawDividers) {
+ bounds.top = listBottom;
+ bounds.bottom = listBottom + dividerHeight;
+ drawDivider(canvas, bounds, -1);
+ }
}
}
}
@@ -3150,6 +3243,45 @@ public class ListView extends AbsListView {
invalidate();
}
+ /**
+ * Sets the drawable that will be drawn above all other list content.
+ * This area can become visible when the user overscrolls the list.
+ *
+ * @param header The drawable to use
+ */
+ public void setOverscrollHeader(Drawable header) {
+ mOverScrollHeader = header;
+ if (mScrollY < 0) {
+ invalidate();
+ }
+ }
+
+ /**
+ * @return The drawable that will be drawn above all other list content
+ */
+ public Drawable getOverscrollHeader() {
+ return mOverScrollHeader;
+ }
+
+ /**
+ * Sets the drawable that will be drawn below all other list content.
+ * This area can become visible when the user overscrolls the list,
+ * or when the list's content does not fully fill the container area.
+ *
+ * @param footer The drawable to use
+ */
+ public void setOverscrollFooter(Drawable footer) {
+ mOverScrollFooter = footer;
+ invalidate();
+ }
+
+ /**
+ * @return The drawable that will be drawn below all other list content
+ */
+ public Drawable getOverscrollFooter() {
+ return mOverScrollFooter;
+ }
+
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
new file mode 100644
index 0000000..cd81e31
--- /dev/null
+++ b/core/java/android/widget/OverScroller.java
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.graphics.Interpolator;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+
+/**
+ * This class encapsulates scrolling with the ability to overshoot the bounds
+ * of a scrolling operation. This class is a drop-in replacement for
+ * {@link android.widget.Scroller} in most cases.
+ */
+public class OverScroller {
+ int mMode;
+
+ private final MagneticOverScroller mScrollerX;
+ private final MagneticOverScroller mScrollerY;
+
+ private float mDeceleration;
+ private final float mPpi;
+ private final boolean mFlywheel;
+
+ private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
+ private static float ALPHA = 800; // pixels / seconds
+ private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
+ private static float END_TENSION = 1.0f - START_TENSION;
+ private static final int NB_SAMPLES = 100;
+ private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
+ private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
+
+ private static final int DEFAULT_DURATION = 250;
+ private static final int SCROLL_MODE = 0;
+ private static final int FLING_MODE = 1;
+
+ static {
+ float x_min = 0.0f;
+ float y_min = 0.0f;
+ for (int i = 0; i < NB_SAMPLES; i++) {
+ final float alpha = (float) i / NB_SAMPLES;
+ {
+ float x_max = 1.0f;
+ float x, tx, coef;
+ while (true) {
+ x = x_min + (x_max - x_min) / 2.0f;
+ coef = 3.0f * x * (1.0f - x);
+ tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
+ if (Math.abs(tx - alpha) < 1E-5) break;
+ if (tx > alpha) x_max = x;
+ else x_min = x;
+ }
+ SPLINE_POSITION[i] = coef + x * x * x;
+ }
+
+ {
+ float y_max = 1.0f;
+ float y, dy, coef;
+ while (true) {
+ y = y_min + (y_max - y_min) / 2.0f;
+ coef = 3.0f * y * (1.0f - y);
+ dy = coef + y * y * y;
+ if (Math.abs(dy - alpha) < 1E-5) break;
+ if (dy > alpha) y_max = y;
+ else y_min = y;
+ }
+ SPLINE_TIME[i] = coef * ((1.0f - y) * START_TENSION + y * END_TENSION) + y * y * y;
+ }
+ }
+ SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
+ }
+
+ public OverScroller(Context context) {
+ this(context, null, 0.f, 0.f, true);
+ }
+
+ /**
+ * Creates an OverScroller.
+ * @param context The context of this application.
+ * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
+ * be used.
+ * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
+ * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
+ * means no bounce.
+ * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
+ */
+ public OverScroller(Context context, Interpolator interpolator,
+ float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
+ mFlywheel = flywheel;
+ mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
+ mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
+ mScrollerX = new MagneticOverScroller();
+ mScrollerY = new MagneticOverScroller();
+
+ mScrollerX.setBounceCoefficient(bounceCoefficientX);
+ mScrollerY.setBounceCoefficient(bounceCoefficientY);
+ }
+
+
+ /**
+ * The amount of friction applied to flings. The default value
+ * is {@link ViewConfiguration#getScrollFriction}.
+ *
+ * @param friction A scalar dimension-less value representing the coefficient of
+ * friction.
+ */
+ public final void setFriction(float friction) {
+ mDeceleration = computeDeceleration(friction);
+ }
+
+ private float computeDeceleration(float friction) {
+ return 9.81f // g (m/s^2)
+ * 39.37f // inch/meter
+ * mPpi // pixels per inch
+ * friction;
+ }
+
+ /**
+ *
+ * Returns whether the scroller has finished scrolling.
+ *
+ * @return True if the scroller has finished scrolling, false otherwise.
+ */
+ public final boolean isFinished() {
+ return mScrollerX.mFinished && mScrollerY.mFinished;
+ }
+
+ /**
+ * Force the finished field to a particular value. Contrary to
+ * {@link #abortAnimation()}, forcing the animation to finished
+ * does NOT cause the scroller to move to the final x and y
+ * position.
+ *
+ * @param finished The new finished value.
+ */
+ public final void forceFinished(boolean finished) {
+ mScrollerX.mFinished = mScrollerY.mFinished = finished;
+ }
+
+ /**
+ * Returns the current X offset in the scroll.
+ *
+ * @return The new X offset as an absolute distance from the origin.
+ */
+ public final int getCurrX() {
+ return mScrollerX.mCurrentPosition;
+ }
+
+ /**
+ * Returns the current Y offset in the scroll.
+ *
+ * @return The new Y offset as an absolute distance from the origin.
+ */
+ public final int getCurrY() {
+ return mScrollerY.mCurrentPosition;
+ }
+
+ /**
+ * @hide
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
+ */
+ public float getCurrVelocity() {
+ float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+ squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+ return (float) Math.sqrt(squaredNorm);
+ }
+
+ /**
+ * Returns the start X offset in the scroll.
+ *
+ * @return The start X offset as an absolute distance from the origin.
+ */
+ public final int getStartX() {
+ return mScrollerX.mStart;
+ }
+
+ /**
+ * Returns the start Y offset in the scroll.
+ *
+ * @return The start Y offset as an absolute distance from the origin.
+ */
+ public final int getStartY() {
+ return mScrollerY.mStart;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final X offset as an absolute distance from the origin.
+ */
+ public final int getFinalX() {
+ return mScrollerX.mFinal;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final Y offset as an absolute distance from the origin.
+ */
+ public final int getFinalY() {
+ return mScrollerY.mFinal;
+ }
+
+ /**
+ * Returns how long the scroll event will take, in milliseconds.
+ *
+ * @return The duration of the scroll in milliseconds.
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScrollers don't necessarily have a fixed duration.
+ * This function will lie to the best of its ability.
+ */
+ @Deprecated
+ public final int getDuration() {
+ return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
+ }
+
+ /**
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+ *
+ * @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScrollers don't necessarily have a fixed duration.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ @Deprecated
+ public void extendDuration(int extend) {
+ mScrollerX.extendDuration(extend);
+ mScrollerY.extendDuration(extend);
+ }
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScroller's final position may change during an animation.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ @Deprecated
+ public void setFinalX(int newX) {
+ mScrollerX.setFinalPosition(newX);
+ }
+
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScroller's final position may change during an animation.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ @Deprecated
+ public void setFinalY(int newY) {
+ mScrollerY.setFinalPosition(newY);
+ }
+
+ /**
+ * Call this when you want to know the new location. If it returns true, the
+ * animation is not yet finished.
+ */
+ public boolean computeScrollOffset() {
+ if (isFinished()) {
+ return false;
+ }
+
+ switch (mMode) {
+ case SCROLL_MODE:
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ // Any scroller can be used for time, since they were started
+ // together in scroll mode. We use X here.
+ final long elapsedTime = time - mScrollerX.mStartTime;
+
+ final int duration = mScrollerX.mDuration;
+ if (elapsedTime < duration) {
+ float q = (float) (elapsedTime) / duration;
+
+ q = Scroller.viscousFluid(q);
+
+ mScrollerX.updateScroll(q);
+ mScrollerY.updateScroll(q);
+ } else {
+ abortAnimation();
+ }
+ break;
+
+ case FLING_MODE:
+ if (!mScrollerX.mFinished) {
+ if (!mScrollerX.update()) {
+ if (!mScrollerX.continueWhenFinished()) {
+ mScrollerX.finish();
+ }
+ }
+ }
+
+ if (!mScrollerY.mFinished) {
+ if (!mScrollerY.update()) {
+ if (!mScrollerY.continueWhenFinished()) {
+ mScrollerY.finish();
+ }
+ }
+ }
+
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * Start scrolling by providing a starting point and the distance to travel.
+ * The scroll will use the default value of 250 milliseconds for the
+ * duration.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy) {
+ startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
+ }
+
+ /**
+ * Start scrolling by providing a starting point and the distance to travel.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ * @param duration Duration of the scroll in milliseconds.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+ mMode = SCROLL_MODE;
+ mScrollerX.startScroll(startX, dx, duration);
+ mScrollerY.startScroll(startY, dy, duration);
+ }
+
+ /**
+ * Call this when you want to 'spring back' into a valid coordinate range.
+ *
+ * @param startX Starting X coordinate
+ * @param startY Starting Y coordinate
+ * @param minX Minimum valid X value
+ * @param maxX Maximum valid X value
+ * @param minY Minimum valid Y value
+ * @param maxY Minimum valid Y value
+ * @return true if a springback was initiated, false if startX and startY were
+ * already within the valid range.
+ */
+ public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+ mMode = FLING_MODE;
+
+ // Make sure both methods are called.
+ final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
+ final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
+ return spingbackX || spingbackY;
+ }
+
+ public void fling(int startX, int startY, int velocityX, int velocityY,
+ int minX, int maxX, int minY, int maxY) {
+ fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+ }
+
+ /**
+ * Start scrolling based on a fling gesture. The distance traveled will
+ * depend on the initial velocity of the fling.
+ *
+ * @param startX Starting point of the scroll (X)
+ * @param startY Starting point of the scroll (Y)
+ * @param velocityX Initial velocity of the fling (X) measured in pixels per
+ * second.
+ * @param velocityY Initial velocity of the fling (Y) measured in pixels per
+ * second
+ * @param minX Minimum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use minX as
+ * a springback boundary.
+ * @param maxX Maximum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use maxX as
+ * a springback boundary.
+ * @param minY Minimum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use minY as
+ * a springback boundary.
+ * @param maxY Maximum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use maxY as
+ * a springback boundary.
+ * @param overX Overfling range. If > 0, horizontal overfling in either
+ * direction will be possible.
+ * @param overY Overfling range. If > 0, vertical overfling in either
+ * direction will be possible.
+ */
+ public void fling(int startX, int startY, int velocityX, int velocityY,
+ int minX, int maxX, int minY, int maxY, int overX, int overY) {
+ // Continue a scroll or fling in progress
+ if (mFlywheel && !isFinished()) {
+ float oldVelocityX = mScrollerX.mCurrVelocity;
+ float oldVelocityY = mScrollerY.mCurrVelocity;
+ if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
+ Math.signum(velocityY) == Math.signum(oldVelocityY)) {
+ velocityX += oldVelocityX;
+ velocityY += oldVelocityY;
+ }
+ }
+
+ mMode = FLING_MODE;
+ mScrollerX.fling(startX, velocityX, minX, maxX, overX);
+ mScrollerY.fling(startY, velocityY, minY, maxY, overY);
+ }
+
+ /**
+ * Notify the scroller that we've reached a horizontal boundary.
+ * Normally the information to handle this will already be known
+ * when the animation is started, such as in a call to one of the
+ * fling functions. However there are cases where this cannot be known
+ * in advance. This function will transition the current motion and
+ * animate from startX to finalX as appropriate.
+ *
+ * @param startX Starting/current X position
+ * @param finalX Desired final X position
+ * @param overX Magnitude of overscroll allowed. This should be the maximum
+ * desired distance from finalX. Absolute value - must be positive.
+ */
+ public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
+ mScrollerX.notifyEdgeReached(startX, finalX, overX);
+ }
+
+ /**
+ * Notify the scroller that we've reached a vertical boundary.
+ * Normally the information to handle this will already be known
+ * when the animation is started, such as in a call to one of the
+ * fling functions. However there are cases where this cannot be known
+ * in advance. This function will animate a parabolic motion from
+ * startY to finalY.
+ *
+ * @param startY Starting/current Y position
+ * @param finalY Desired final Y position
+ * @param overY Magnitude of overscroll allowed. This should be the maximum
+ * desired distance from finalY. Absolute value - must be positive.
+ */
+ public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
+ mScrollerY.notifyEdgeReached(startY, finalY, overY);
+ }
+
+ /**
+ * Returns whether the current Scroller is currently returning to a valid position.
+ * Valid bounds were provided by the
+ * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
+ *
+ * One should check this value before calling
+ * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
+ * to restore a valid position will then be stopped. The caller has to take into account
+ * the fact that the started scroll will start from an overscrolled position.
+ *
+ * @return true when the current position is overscrolled and in the process of
+ * interpolating back to a valid value.
+ */
+ public boolean isOverScrolled() {
+ return ((!mScrollerX.mFinished &&
+ mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+ (!mScrollerY.mFinished &&
+ mScrollerY.mState != MagneticOverScroller.TO_EDGE));
+ }
+
+ /**
+ * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+ * aborting the animating causes the scroller to move to the final x and y
+ * positions.
+ *
+ * @see #forceFinished(boolean)
+ */
+ public void abortAnimation() {
+ mScrollerX.finish();
+ mScrollerY.finish();
+ }
+
+ /**
+ * Returns the time elapsed since the beginning of the scrolling.
+ *
+ * @return The elapsed time in milliseconds.
+ *
+ * @hide
+ */
+ public int timePassed() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+ return (int) (time - startTime);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isScrollingInDirection(float xvel, float yvel) {
+ final int dx = mScrollerX.mFinal - mScrollerX.mStart;
+ final int dy = mScrollerY.mFinal - mScrollerY.mStart;
+ return !isFinished() && Math.signum(xvel) == Math.signum(dx) &&
+ Math.signum(yvel) == Math.signum(dy);
+ }
+
+ class MagneticOverScroller {
+ // Initial position
+ int mStart;
+
+ // Current position
+ int mCurrentPosition;
+
+ // Final position
+ int mFinal;
+
+ // Initial velocity
+ int mVelocity;
+
+ // Current velocity
+ float mCurrVelocity;
+
+ // Constant current deceleration
+ float mDeceleration;
+
+ // Animation starting time, in system milliseconds
+ long mStartTime;
+
+ // Animation duration, in milliseconds
+ int mDuration;
+
+ // Duration to complete spline component of animation
+ int mSplineDuration;
+
+ // Distance to travel along spline animation
+ int mSplineDistance;
+
+ // Whether the animation is currently in progress
+ boolean mFinished;
+
+ private static final int TO_EDGE = 0;
+ private static final int TO_BOUNDARY = 1;
+ private static final int TO_BOUNCE = 2;
+
+ private int mState = TO_EDGE;
+
+ // The allowed overshot distance before boundary is reached.
+ private int mOver;
+
+ // If the velocity is smaller than this value, no bounce is triggered
+ // when the edge limits are reached (would result in a zero pixels
+ // displacement anyway).
+ private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f; //Float.MAX_VALUE;//140.0f;
+
+ // Proportion of the velocity that is preserved when the edge is reached.
+ private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.36f;
+
+ private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
+
+ MagneticOverScroller() {
+ mFinished = true;
+ }
+
+ void updateScroll(float q) {
+ mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
+ }
+
+ /*
+ * Get a signed deceleration that will reduce the velocity.
+ */
+ float getDeceleration(int velocity) {
+ return velocity > 0 ? -OverScroller.this.mDeceleration : OverScroller.this.mDeceleration;
+ }
+
+ /*
+ * Modifies mDuration to the duration it takes to get from start to newFinal using the
+ * spline interpolation. The previous duration was needed to get to oldFinal.
+ */
+ void adjustDuration(int start, int oldFinal, int newFinal) {
+ final int oldDistance = oldFinal - start;
+ final int newDistance = newFinal - start;
+ final float x = (float) Math.abs((float) newDistance / oldDistance);
+ final int index = (int) (NB_SAMPLES * x);
+ if (index < NB_SAMPLES) {
+ final float x_inf = (float) index / NB_SAMPLES;
+ final float x_sup = (float) (index + 1) / NB_SAMPLES;
+ final float t_inf = SPLINE_TIME[index];
+ final float t_sup = SPLINE_TIME[index + 1];
+ final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf);
+
+ mDuration *= timeCoef;
+ }
+ }
+
+ void startScroll(int start, int distance, int duration) {
+ mFinished = false;
+
+ mStart = start;
+ mFinal = start + distance;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = duration;
+
+ // Unused
+ mDeceleration = 0.0f;
+ mVelocity = 0;
+ }
+
+ void finish() {
+ mCurrentPosition = mFinal;
+ // Not reset since WebView relies on this value for fast fling.
+ // TODO: restore when WebView uses the fast fling implemented in this class.
+ // mCurrVelocity = 0.0f;
+ mFinished = true;
+ }
+
+ void setFinalPosition(int position) {
+ mFinal = position;
+ mFinished = false;
+ }
+
+ void extendDuration(int extend) {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final int elapsedTime = (int) (time - mStartTime);
+ mDuration = elapsedTime + extend;
+ mFinished = false;
+ }
+
+ void setBounceCoefficient(float coefficient) {
+ mBounceCoefficient = coefficient;
+ }
+
+ boolean springback(int start, int min, int max) {
+ mFinished = true;
+
+ mStart = mFinal = start;
+ mVelocity = 0;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = 0;
+
+ if (start < min) {
+ startSpringback(start, min, 0);
+ } else if (start > max) {
+ startSpringback(start, max, 0);
+ }
+
+ return !mFinished;
+ }
+
+ private void startSpringback(int start, int end, int velocity) {
+ mFinished = false;
+ mState = TO_BOUNCE;
+ mStart = mFinal = end;
+ final float velocitySign = Math.signum(start - end);
+ mDeceleration = getDeceleration((int) velocitySign);
+ fitOnBounceCurve(start, end, velocity);
+ mDuration = - (int) (2000.0f * mVelocity / mDeceleration);
+ }
+
+ void fling(int start, int velocity, int min, int max, int over) {
+ mOver = over;
+ mFinished = false;
+ mCurrVelocity = mVelocity = velocity;
+ mDuration = mSplineDuration = 0;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStart = start;
+
+ if (start > max || start < min) {
+ startAfterEdge(start, min, max, velocity);
+ return;
+ }
+
+ mState = TO_EDGE;
+ double totalDistance = 0.0;
+
+ if (velocity != 0) {
+ final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA);
+ // Duration are expressed in milliseconds
+ mDuration = mSplineDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
+ totalDistance = (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
+ }
+
+ mSplineDistance = (int) (totalDistance * Math.signum(velocity));
+ mFinal = start + mSplineDistance;
+
+ // Clamp to a valid final position
+ if (mFinal < min) {
+ adjustDuration(mStart, mFinal, min);
+ mFinal = min;
+ }
+
+ if (mFinal > max) {
+ adjustDuration(mStart, mFinal, max);
+ mFinal = max;
+ }
+ }
+
+ private void fitOnBounceCurve(int start, int end, int velocity) {
+ // Simulate a bounce that started from edge
+ final float durationToApex = - velocity / mDeceleration;
+ final float distanceToApex = velocity * velocity / 2.0f / Math.abs(mDeceleration);
+ final float distanceToEdge = Math.abs(end - start);
+ final float totalDuration = (float) Math.sqrt(
+ 2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration));
+ mStartTime -= (int) (1000.0f * (totalDuration - durationToApex));
+ mStart = end;
+ mVelocity = (int) (- mDeceleration * totalDuration);
+ }
+
+ private void startBounceAfterEdge(int start, int end, int velocity) {
+ mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity);
+ fitOnBounceCurve(start, end, velocity);
+ onEdgeReached();
+ }
+
+ private void startAfterEdge(int start, int min, int max, int velocity) {
+ if (start > min && start < max) {
+ mFinished = true;
+ return;
+ }
+ final boolean positive = start > max;
+ final int edge = positive ? max : min;
+ final int overDistance = start - edge;
+ boolean keepIncreasing = overDistance * velocity >= 0;
+ if (keepIncreasing) {
+ // Will result in a bounce or a to_boundary depending on velocity.
+ startBounceAfterEdge(start, edge, velocity);
+ } else {
+ final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA);
+ final double totalDistance =
+ (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
+ if (totalDistance > Math.abs(overDistance)) {
+ fling(start, velocity, positive ? min : start, positive ? start : max, mOver);
+ } else {
+ startSpringback(start, edge, velocity);
+ }
+ }
+ }
+
+ void notifyEdgeReached(int start, int end, int over) {
+ mOver = over;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ // We were in fling/scroll mode before: current velocity is such that distance to edge
+ // is increasing. Ensures that startAfterEdge will not start a new fling.
+ startAfterEdge(start, end, end, (int) mCurrVelocity);
+ }
+
+ private void onEdgeReached() {
+ // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
+ final float distance = - mVelocity * mVelocity / (2.0f * mDeceleration);
+
+ if (Math.abs(distance) < mOver) {
+ // Spring force will bring us back to final position
+ mState = TO_BOUNCE;
+ mFinal = mStart;
+ mDuration = - (int) (2000.0f * mVelocity / mDeceleration);
+ } else {
+ // Velocity is too high, we will hit the boundary limit
+ mState = TO_BOUNDARY;
+ int over = mVelocity > 0 ? mOver : -mOver;
+ mFinal = mStart + over;
+ mDuration = (int) (1000.0 * Math.PI * over / 2.0 / mVelocity);
+ }
+ }
+
+ boolean continueWhenFinished() {
+ switch (mState) {
+ case TO_EDGE:
+ // Duration from start to null velocity
+ if (mDuration < mSplineDuration) {
+ // If the animation was clamped, we reached the edge
+ mStart = mFinal;
+ // Speed when edge was reached
+ mVelocity = (int) mCurrVelocity;
+ mDeceleration = getDeceleration(mVelocity);
+ mStartTime += mDuration;
+ onEdgeReached();
+ } else {
+ // Normal stop, no need to continue
+ return false;
+ }
+ break;
+ case TO_BOUNDARY:
+ mStartTime += mDuration;
+ startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), 0);
+ break;
+ case TO_BOUNCE:
+ mVelocity = (int) (mVelocity * mBounceCoefficient);
+ if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
+ return false;
+ }
+ mStartTime += mDuration;
+ mDuration = - (int) (mVelocity / mDeceleration);
+ break;
+ }
+
+ update();
+ return true;
+ }
+
+ /*
+ * Update the current position and velocity for current time. Returns
+ * true if update has been done and false if animation duration has been
+ * reached.
+ */
+ boolean update() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long currentTime = time - mStartTime;
+
+ if (currentTime > mDuration) {
+ return false;
+ }
+
+ double distance = 0.0;
+ switch (mState) {
+ case TO_EDGE: {
+ final float t = (float) currentTime / mSplineDuration;
+ final int index = (int) (NB_SAMPLES * t);
+ float distanceCoef = 1.f;
+ float velocityCoef = 0.f;
+ if (index < NB_SAMPLES) {
+ final float t_inf = (float) index / NB_SAMPLES;
+ final float t_sup = (float) (index + 1) / NB_SAMPLES;
+ final float d_inf = SPLINE_POSITION[index];
+ final float d_sup = SPLINE_POSITION[index + 1];
+ velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
+ distanceCoef = d_inf + (t - t_inf) * velocityCoef;
+ }
+
+ distance = distanceCoef * mSplineDistance;
+ mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000;
+ break;
+ }
+
+ case TO_BOUNCE: {
+ final float t = currentTime / 1000.0f;
+ mCurrVelocity = mVelocity + mDeceleration * t;
+ distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+ break;
+ }
+
+ case TO_BOUNDARY: {
+ final float t = currentTime / 1000.0f;
+ final float d = t * Math.abs(mVelocity) / mOver;
+ mCurrVelocity = mVelocity * (float) Math.cos(d);
+ distance = (mVelocity > 0 ? mOver : -mOver) * Math.sin(d);
+ break;
+ }
+ }
+
+ mCurrentPosition = mStart + (int) Math.round(distance);
+ return true;
+ }
+ }
+}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index eb527eb..900c9ec 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -19,8 +19,12 @@ package android.widget;
import com.android.internal.R;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.StrictMode;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -61,7 +65,9 @@ public class ScrollView extends FrameLayout {
private long mLastScroll;
private final Rect mTempRect = new Rect();
- private Scroller mScroller;
+ private OverScroller mScroller;
+ private EdgeGlow mEdgeGlowTop;
+ private EdgeGlow mEdgeGlowBottom;
/**
* Flag to indicate that we are moving focus ourselves. This is so the
@@ -115,12 +121,24 @@ public class ScrollView extends FrameLayout {
private int mMinimumVelocity;
private int mMaximumVelocity;
+ private int mOverscrollDistance;
+ private int mOverflingDistance;
+
/**
* ID of the active pointer. This is used to retain consistency during
* drags/flings if multiple pointers are used.
*/
private int mActivePointerId = INVALID_POINTER;
-
+
+ /**
+ * The StrictMode "critical time span" objects to catch animation
+ * stutters. Non-null when a time-sensitive animation is
+ * in-flight. Must call finish() on them when done animating.
+ * These are no-ops on user builds.
+ */
+ private StrictMode.Span mScrollStrictSpan = null; // aka "drag"
+ private StrictMode.Span mFlingStrictSpan = null;
+
/**
* Sentinel value for no current active pointer.
* Used by {@link #mActivePointerId}.
@@ -187,7 +205,7 @@ public class ScrollView extends FrameLayout {
private void initScrollView() {
- mScroller = new Scroller(getContext());
+ mScroller = new OverScroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
@@ -195,6 +213,8 @@ public class ScrollView extends FrameLayout {
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mOverscrollDistance = configuration.getScaledOverscrollDistance();
+ mOverflingDistance = configuration.getScaledOverflingDistance();
}
@Override
@@ -427,6 +447,9 @@ public class ScrollView extends FrameLayout {
if (yDiff > mTouchSlop) {
mIsBeingDragged = true;
mLastMotionY = y;
+ if (mScrollStrictSpan == null) {
+ mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
+ }
}
break;
}
@@ -451,6 +474,9 @@ public class ScrollView extends FrameLayout {
* being flinged.
*/
mIsBeingDragged = !mScroller.isFinished();
+ if (mIsBeingDragged && mScrollStrictSpan == null) {
+ mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
+ }
break;
}
@@ -459,6 +485,9 @@ public class ScrollView extends FrameLayout {
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -491,9 +520,7 @@ public class ScrollView extends FrameLayout {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float y = ev.getY();
- if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) {
- return false;
- }
+ mIsBeingDragged = true;
/*
* If being flinged and user touches, stop the fling. isFinished
@@ -501,6 +528,10 @@ public class ScrollView extends FrameLayout {
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
}
// Remember where the motion event started
@@ -516,7 +547,36 @@ public class ScrollView extends FrameLayout {
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
- scrollBy(0, deltaY);
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
+ final int range = getScrollRange();
+ if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
+ 0, mOverscrollDistance, true)) {
+ // Break our velocity if we hit a scroll barrier.
+ mVelocityTracker.clear();
+ }
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ final int pulledToY = oldY + deltaY;
+ if (pulledToY < 0) {
+ mEdgeGlowTop.onPull((float) deltaY / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (pulledToY > range) {
+ mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ if (mEdgeGlowTop != null
+ && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
+ invalidate();
+ }
+ }
}
break;
case MotionEvent.ACTION_UP:
@@ -525,27 +585,28 @@ public class ScrollView extends FrameLayout {
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
- fling(-initialVelocity);
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ fling(-initialVelocity);
+ } else {
+ final int bottom = getScrollRange();
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, bottom)) {
+ invalidate();
+ }
+ }
}
mActivePointerId = INVALID_POINTER;
- mIsBeingDragged = false;
-
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
+ endDrag();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
- mActivePointerId = INVALID_POINTER;
- mIsBeingDragged = false;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
}
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
}
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -572,6 +633,32 @@ public class ScrollView extends FrameLayout {
}
}
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY,
+ boolean clampedX, boolean clampedY) {
+ // Treat animating scrolls differently; see #computeScroll() for why.
+ if (!mScroller.isFinished()) {
+ mScrollX = scrollX;
+ mScrollY = scrollY;
+ if (clampedY) {
+ mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
+ }
+ } else {
+ super.scrollTo(scrollX, scrollY);
+ }
+ awakenScrollBars();
+ }
+
+ private int getScrollRange() {
+ int scrollRange = 0;
+ if (getChildCount() > 0) {
+ View child = getChildAt(0);
+ scrollRange = Math.max(0,
+ child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+ }
+ return scrollRange;
+ }
+
/**
* <p>
* Finds the next focusable component that fits in this View's bounds
@@ -920,6 +1007,10 @@ public class ScrollView extends FrameLayout {
} else {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
}
scrollBy(dx, dy);
}
@@ -948,7 +1039,16 @@ public class ScrollView extends FrameLayout {
return contentHeight;
}
- return getChildAt(0).getBottom();
+ int scrollRange = getChildAt(0).getBottom();
+ final int scrollY = mScrollY;
+ final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
+ if (scrollY < 0) {
+ scrollRange -= scrollY;
+ } else if (scrollY > overscrollBottom) {
+ scrollRange += scrollY - overscrollBottom;
+ }
+
+ return scrollRange;
}
@Override
@@ -1009,20 +1109,31 @@ public class ScrollView extends FrameLayout {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
- if (getChildCount() > 0) {
- View child = getChildAt(0);
- x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
- y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
- if (x != oldX || y != oldY) {
- mScrollX = x;
- mScrollY = y;
- onScrollChanged(x, y, oldX, oldY);
+ if (oldX != x || oldY != y) {
+ overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
+ 0, mOverflingDistance, false);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int range = getScrollRange();
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ if (y < 0 && oldY >= 0) {
+ mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+ } else if (y > range && oldY <= range) {
+ mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+ }
}
}
awakenScrollBars();
// Keep on drawing until the animation has finished.
postInvalidate();
+ } else {
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
}
}
@@ -1197,6 +1308,20 @@ public class ScrollView extends FrameLayout {
}
@Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mScrollStrictSpan != null) {
+ mScrollStrictSpan.finish();
+ mScrollStrictSpan = null;
+ }
+ if (mFlingStrictSpan != null) {
+ mFlingStrictSpan.finish();
+ mFlingStrictSpan = null;
+ }
+ }
+
+ @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mIsLayoutDirty = false;
@@ -1254,7 +1379,7 @@ public class ScrollView extends FrameLayout {
int bottom = getChildAt(0).getHeight();
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
- Math.max(0, bottom - height));
+ Math.max(0, bottom - height), 0, height/2);
final boolean movingDown = velocityY > 0;
@@ -1269,11 +1394,34 @@ public class ScrollView extends FrameLayout {
mScrollViewMovedFocus = true;
mScrollViewMovedFocus = false;
}
-
+
+ if (mFlingStrictSpan == null) {
+ mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
+ }
+
invalidate();
}
}
+ private void endDrag() {
+ mIsBeingDragged = false;
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
+
+ if (mScrollStrictSpan != null) {
+ mScrollStrictSpan.finish();
+ mScrollStrictSpan = null;
+ }
+ }
+
/**
* {@inheritDoc}
*
@@ -1292,6 +1440,55 @@ public class ScrollView extends FrameLayout {
}
}
+ @Override
+ public void setOverScrollMode(int mode) {
+ if (mode != OVER_SCROLL_NEVER) {
+ if (mEdgeGlowTop == null) {
+ final Resources res = getContext().getResources();
+ final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+ final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+ mEdgeGlowTop = new EdgeGlow(edge, glow);
+ mEdgeGlowBottom = new EdgeGlow(edge, glow);
+ }
+ } else {
+ mEdgeGlowTop = null;
+ mEdgeGlowBottom = null;
+ }
+ super.setOverScrollMode(mode);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mEdgeGlowTop != null) {
+ final int scrollY = mScrollY;
+ if (!mEdgeGlowTop.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+
+ canvas.translate(-width / 2, Math.min(0, scrollY));
+ mEdgeGlowTop.setSize(width * 2, getHeight());
+ if (mEdgeGlowTop.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowBottom.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight();
+
+ canvas.translate(-width / 2, Math.max(getScrollRange(), scrollY) + height);
+ canvas.rotate(180, width, 0);
+ mEdgeGlowBottom.setSize(width * 2, height);
+ if (mEdgeGlowBottom.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ }
+ }
+
private int clamp(int n, int my, int child) {
if (my >= child || n < 0) {
/* my >= child is this case:
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index b1f50ba..f00640e 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -52,14 +52,10 @@ public class Scroller {
private float mDurationReciprocal;
private float mDeltaX;
private float mDeltaY;
- private float mViscousFluidScale;
- private float mViscousFluidNormalize;
private boolean mFinished;
private Interpolator mInterpolator;
private boolean mFlywheel;
- private float mCoeffX = 0.0f;
- private float mCoeffY = 1.0f;
private float mVelocity;
private static final int DEFAULT_DURATION = 250;
@@ -94,8 +90,17 @@ public class Scroller {
SPLINE[i] = d;
}
SPLINE[NB_SAMPLES] = 1.0f;
+
+ // This controls the viscous fluid effect (how much of it)
+ sViscousFluidScale = 8.0f;
+ // must be set to 1.0 (used in viscousFluid())
+ sViscousFluidNormalize = 1.0f;
+ sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
+ private static float sViscousFluidScale;
+ private static float sViscousFluidNormalize;
+
/**
* Create a Scroller with the default duration and interpolator.
*/
@@ -103,6 +108,11 @@ public class Scroller {
this(context, null);
}
+ /**
+ * Create a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
+ * be in effect for apps targeting Honeycomb or newer.
+ */
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
@@ -110,7 +120,8 @@ public class Scroller {
/**
* Create a Scroller with the specified interpolator. If the interpolator is
- * null, the default (viscous) interpolator will be used.
+ * null, the default (viscous) interpolator will be used. Specify whether or
+ * not to support progressive "flywheel" behavior in flinging.
*/
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
@@ -332,12 +343,7 @@ public class Scroller {
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
- mDurationReciprocal = 1.0f / mDuration;
- // This controls the viscous fluid effect (how much of it)
- mViscousFluidScale = 8.0f;
- // must be set to 1.0 (used in viscousFluid())
- mViscousFluidNormalize = 1.0f;
- mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+ mDurationReciprocal = 1.0f / (float) mDuration;
}
/**
@@ -393,8 +399,8 @@ public class Scroller {
mStartX = startX;
mStartY = startY;
- mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity;
- mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
+ float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
+ float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
int totalDistance =
(int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
@@ -404,22 +410,20 @@ public class Scroller {
mMinY = minY;
mMaxY = maxY;
- mFinalX = startX + Math.round(totalDistance * mCoeffX);
+ mFinalX = startX + Math.round(totalDistance * coeffX);
// Pin to mMinX <= mFinalX <= mMaxX
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);
- mFinalY = startY + Math.round(totalDistance * mCoeffY);
+ mFinalY = startY + Math.round(totalDistance * coeffY);
// Pin to mMinY <= mFinalY <= mMaxY
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
-
-
- private float viscousFluid(float x)
+ static float viscousFluid(float x)
{
- x *= mViscousFluidScale;
+ x *= sViscousFluidScale;
if (x < 1.0f) {
x -= (1.0f - (float)Math.exp(-x));
} else {
@@ -427,7 +431,7 @@ public class Scroller {
x = 1.0f - (float)Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
- x *= mViscousFluidNormalize;
+ x *= sViscousFluidNormalize;
return x;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 80a6a2f..9f9fb18 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7565,9 +7565,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void selectCurrentWord() {
- // In case selection mode is started after an orientation change or after a select all,
- // use the current selection instead of creating one
- if (hasSelection()) {
+ if (hasPasswordTransformationMethod()) {
+ // selectCurrentWord is not available on a password field and would return an
+ // arbitrary 10-charater selection around pressed position. Select all instead.
+ // Note that cut/copy menu entries are not available for passwords.
+ // This is however useful to delete or paste to replace the entire content.
+ Selection.setSelection((Spannable) mText, 0, mText.length());
return;
}
@@ -7835,13 +7838,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
}
- if (mSelectionActionMode != null && touchPositionIsInSelection()) {
- final int start = getSelectionStart();
- final int end = getSelectionEnd();
- CharSequence selectedText = mTransformed.subSequence(start, end);
- ClipData data = ClipData.newPlainText(null, null, selectedText);
- startDrag(data, getTextThumbnailBuilder(selectedText), false);
- stopSelectionActionMode();
+ if (mSelectionActionMode != null) {
+ if (touchPositionIsInSelection()) {
+ // Start a drag
+ final int start = getSelectionStart();
+ final int end = getSelectionEnd();
+ CharSequence selectedText = mTransformed.subSequence(start, end);
+ ClipData data = ClipData.newPlainText(null, null, selectedText);
+ startDrag(data, getTextThumbnailBuilder(selectedText), false);
+ stopSelectionActionMode();
+ } else {
+ selectCurrentWord();
+ getSelectionController().show();
+ }
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mEatTouchRelease = true;
return true;
@@ -7886,9 +7895,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* mode is not available.
*/
private ActionMode.Callback getActionModeCallback() {
- // Long press in the current selection.
- // Should initiate a drag. Return false, to rely on context menu for now.
- if (canSelectText() && !touchPositionIsInSelection()) {
+ if (canSelectText()) {
return new SelectionActionModeCallback();
}
return null;
@@ -7983,15 +7990,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean atLeastOne = false;
if (canSelectText()) {
- if (hasPasswordTransformationMethod()) {
- // selectCurrentWord is not available on a password field and would return an
- // arbitrary 10-charater selection around pressed position. Select all instead.
- // Note that cut/copy menu entries are not available for passwords.
- // This is however useful to delete or paste to replace the entire content.
- Selection.setSelection((Spannable) mText, 0, mText.length());
- } else {
- selectCurrentWord();
- }
+ selectCurrentWord();
menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
setAlphabeticShortcut('a').
@@ -9015,7 +9014,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mUserSetTextScaleX;
private final Paint mHighlightPaint;
private int mHighlightColor = 0xCC475925;
- private Layout mLayout;
+ /**
+ * This is temporarily visible to fix bug 3085564 in webView. Do not rely on
+ * this field being protected. Will be restored as private when lineHeight
+ * feature request 3215097 is implemented
+ * @hide
+ */
+ protected Layout mLayout;
private long mShowCursor;
private Blink mBlink;
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ada7f36..1984167 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -120,7 +120,7 @@ public class HeavyWeightSwitcherActivity extends Activity {
private OnClickListener mSwitchOldListener = new OnClickListener() {
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(mCurTask);
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
} catch (RemoteException e) {
}
finish();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 63d05ec..a2e9486 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -32,6 +32,9 @@ interface IInputMethodManager {
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi);
+ // TODO: We should change the return type from List to List<Parcelable>
+ // Currently there is a bug that aidl doesn't accept List<Parcelable>
+ List getShortcutInputMethodsAndSubtypes();
void addClient(in IInputMethodClient client,
in IInputContext inputContext, int uid, int pid);
void removeClient(in IInputMethodClient client);
@@ -52,6 +55,7 @@ interface IInputMethodManager {
void showInputMethodSubtypePickerFromClient(in IInputMethodClient client);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
void setInputMethod(in IBinder token, String id);
+ void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 3c971da..82d990c 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -18,8 +18,14 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal"
android:shareInterpolator="false">
- <!-- Do nothing. -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="0.98" android:toXScale="1.0"
+ android:fromYScale="0.98" android:toYScale="1.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:fromAlpha=".75" android:toAlpha="1.0"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur"/>
</set>
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index 2001e20..13768b5 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -18,13 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top"
android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.9"
+ <scale android:fromXScale="1.0" android:toXScale="1.04"
+ android:fromYScale="1.0" android:toYScale="1.04"
android:pivotX="50%p" android:pivotY="50%p"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@anim/decelerate_cubic_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 6c62e61..4841910 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -18,13 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top"
android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="0.9" android:toYScale="1.0"
+ <scale android:fromXScale="1.04" android:toXScale="1.0"
+ android:fromYScale="1.04" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:interpolator="@anim/decelerate_cubic_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 3c971da..81e902f 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -18,8 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal"
android:shareInterpolator="false">
- <!-- Do nothing. -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="0.98"
+ android:fromYScale="1.0" android:toYScale="0.98"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.75"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set>
diff --git a/core/res/res/anim/dialog_enter.xml b/core/res/res/anim/dialog_enter.xml
index d4983c6..82bfdf6 100644
--- a/core/res/res/anim/dialog_enter.xml
+++ b/core/res/res/anim/dialog_enter.xml
@@ -18,11 +18,13 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
+ android:shareInterpolator="false" >
<scale android:fromXScale="0.9" android:toXScale="1.0"
android:fromYScale="0.9" android:toYScale="1.0"
android:pivotX="50%" android:pivotY="50%"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="@android:integer/config_activityShortDur" />
</set>
diff --git a/core/res/res/anim/dialog_exit.xml b/core/res/res/anim/dialog_exit.xml
index 2aa629a..829c619 100644
--- a/core/res/res/anim/dialog_exit.xml
+++ b/core/res/res/anim/dialog_exit.xml
@@ -18,11 +18,13 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/accelerate_interpolator">
+ android:shareInterpolator="false" >
<scale android:fromXScale="1.0" android:toXScale="0.9"
- android:fromYScale="1.0" android:toYScale="0.9"
- android:pivotX="50%" android:pivotY="50%"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="@android:integer/config_activityDefaultDur" />
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="@android:integer/config_activityShortDur"/>
</set>
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
index 1c170e3..c1b564b 100644
--- a/core/res/res/anim/fragment_close_enter.xml
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -16,12 +16,29 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal">
+
+ <objectAnimator
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="0.98"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+ <objectAnimator
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="0.98"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_activityDefaultDur"/>
<objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
- android:valueFrom="0"
- android:valueTo="1"
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
index a084d63..2ce358f 100644
--- a/core/res/res/anim/fragment_close_exit.xml
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -16,19 +16,28 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top">
<objectAnimator
- android:interpolator="@anim/decelerate_cubic_interpolator"
- android:valueFrom="1"
- android:valueTo="0"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="1.04"
android:valueType="floatType"
- android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_activityDefaultDur"/>
<objectAnimator
android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom="1"
- android:valueTo=".8"
+ android:valueFrom="1.0"
+ android:valueTo="1.04"
android:valueType="floatType"
- android:propertyName="scaleY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+ <objectAnimator
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_enter.xml b/core/res/res/anim/fragment_next_enter.xml
index d3ff7a6..63d3a45 100644
--- a/core/res/res/anim/fragment_next_enter.xml
+++ b/core/res/res/anim/fragment_next_enter.xml
@@ -16,19 +16,13 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top">
<objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <objectAnimator
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom="50"
- android:valueTo="0"
- android:valueType="floatType"
- android:propertyName="translationY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur"/>
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_exit.xml b/core/res/res/anim/fragment_next_exit.xml
index e08804f..91e5786 100644
--- a/core/res/res/anim/fragment_next_exit.xml
+++ b/core/res/res/anim/fragment_next_exit.xml
@@ -16,19 +16,13 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal">
<objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <objectAnimator
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom="0"
- android:valueTo="-50"
- android:valueType="floatType"
- android:propertyName="translationY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityShortDur"/>
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
index 9e625a9..e3bde46 100644
--- a/core/res/res/anim/fragment_open_enter.xml
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -18,17 +18,25 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
- android:interpolator="@anim/decelerate_cubic_interpolator"
- android:valueFrom="0"
- android:valueTo="1"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="1.04"
+ android:valueTo="1.0"
android:valueType="floatType"
- android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_activityDefaultDur"/>
<objectAnimator
android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom=".8"
- android:valueTo="1"
+ android:valueFrom="1.04"
+ android:valueTo="1.0"
android:valueType="floatType"
- android:propertyName="scaleY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+ <objectAnimator
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
index d3278b2..b4c2194 100644
--- a/core/res/res/anim/fragment_open_exit.xml
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -18,10 +18,25 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="0.98"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+ <objectAnimator
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="0.98"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_activityDefaultDur"/>
+ <objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
- android:valueFrom="1"
- android:valueTo="0"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur"/>
+
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_enter.xml b/core/res/res/anim/fragment_prev_enter.xml
index 7ae41e6..63d3a45 100644
--- a/core/res/res/anim/fragment_prev_enter.xml
+++ b/core/res/res/anim/fragment_prev_enter.xml
@@ -16,19 +16,13 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top">
<objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <objectAnimator
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom="-50"
- android:valueTo="0"
- android:valueType="floatType"
- android:propertyName="translationY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityDefaultDur"/>
</set> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_exit.xml b/core/res/res/anim/fragment_prev_exit.xml
index a41f1aa..91e5786 100644
--- a/core/res/res/anim/fragment_prev_exit.xml
+++ b/core/res/res/anim/fragment_prev_exit.xml
@@ -16,19 +16,13 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal">
<objectAnimator
android:interpolator="@anim/decelerate_cubic_interpolator"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <objectAnimator
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:valueFrom="0"
- android:valueTo="50"
- android:valueType="floatType"
- android:propertyName="translationY"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="@android:integer/config_activityShortDur"/>
</set> \ No newline at end of file
diff --git a/core/res/res/anim/grow_fade_in.xml b/core/res/res/anim/grow_fade_in.xml
index 5dc41cb..9180438 100644
--- a/core/res/res/anim/grow_fade_in.xml
+++ b/core/res/res/anim/grow_fade_in.xml
@@ -18,12 +18,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="0.3" android:toYScale="1.0"
- android:pivotX="0%" android:pivotY="0%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="0.9" android:toXScale="1.0"
+ android:fromYScale="0.9" android:toYScale="1.0"
+ android:pivotX="50%" android:pivotY="0%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:duration="@android:integer/config_activityShortDur" />
</set>
diff --git a/core/res/res/anim/grow_fade_in_center.xml b/core/res/res/anim/grow_fade_in_center.xml
index c07a82e..09370e6 100644
--- a/core/res/res/anim/grow_fade_in_center.xml
+++ b/core/res/res/anim/grow_fade_in_center.xml
@@ -18,12 +18,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="0.6" android:toXScale="1.0"
- android:fromYScale="0.6" android:toYScale="1.0"
- android:pivotX="50%" android:pivotY="50%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="0.9" android:toXScale="1.0"
+ android:fromYScale="0.9" android:toYScale="1.0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:duration="@android:integer/config_activityShortDur" />
</set>
diff --git a/core/res/res/anim/grow_fade_in_from_bottom.xml b/core/res/res/anim/grow_fade_in_from_bottom.xml
index 848c677..d1488e9 100644
--- a/core/res/res/anim/grow_fade_in_from_bottom.xml
+++ b/core/res/res/anim/grow_fade_in_from_bottom.xml
@@ -18,12 +18,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="0.5" android:toXScale="1.0"
- android:fromYScale="0.5" android:toYScale="1.0"
- android:pivotX="0%" android:pivotY="100%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="0.9" android:toXScale="1.0"
+ android:fromYScale="0.9" android:toYScale="1.0"
+ android:pivotX="50%" android:pivotY="100%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:duration="@android:integer/config_activityShortDur" />
</set>
diff --git a/core/res/res/anim/input_method_enter.xml b/core/res/res/anim/input_method_enter.xml
index 3858651..e02b310 100644
--- a/core/res/res/anim/input_method_enter.xml
+++ b/core/res/res/anim/input_method_enter.xml
@@ -19,9 +19,11 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <translate android:fromYDelta="20%" android:toYDelta="0"
+ android:shareInterpolator="false">
+ <translate android:fromYDelta="10%" android:toYDelta="0"
+ android:interpolator="@anim/decelerate_quint_interpolator"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+ android:interpolator="@anim/decelerate_cubic_interpolator"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 25369ab..e155f97 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -18,9 +18,11 @@
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/accelerate_interpolator">
- <translate android:fromYDelta="0" android:toYDelta="20%"
+ android:shareInterpolator="false">
+ <translate android:fromYDelta="0" android:toYDelta="10%"
+ android:interpolator="@anim/accelerate_quint_interpolator"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@anim/accelerate_cubic_interpolator"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index b243223..0bfe806 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -17,7 +17,7 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator">
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_cubic_interpolator">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime" />
+ android:duration="@android:integer/config_activityDefaultDur" />
</set>
diff --git a/core/res/res/anim/lock_screen_enter.xml b/core/res/res/anim/lock_screen_enter.xml
index dd47ff8..0a169fd 100644
--- a/core/res/res/anim/lock_screen_enter.xml
+++ b/core/res/res/anim/lock_screen_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
+ android:interpolator="@anim/accelerate_cubic_interpolator">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:duration="@android:integer/config_activityDefaultDur" />
</set>
diff --git a/core/res/res/anim/lock_screen_exit.xml b/core/res/res/anim/lock_screen_exit.xml
index 077fc6b..f06a6e5 100644
--- a/core/res/res/anim/lock_screen_exit.xml
+++ b/core/res/res/anim/lock_screen_exit.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/accelerate_interpolator">
+ android:interpolator="@anim/decelerate_cubic_interpolator">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_longAnimTime" />
+ android:duration="@android:integer/config_activityDefaultDur" />
</set>
diff --git a/core/res/res/anim/shrink_fade_out.xml b/core/res/res/anim/shrink_fade_out.xml
index 4000c23..7229a17 100644
--- a/core/res/res/anim/shrink_fade_out.xml
+++ b/core/res/res/anim/shrink_fade_out.xml
@@ -17,12 +17,14 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.3"
- android:pivotX="0%" android:pivotY="0%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%" android:pivotY="0%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+ android:duration="@android:integer/config_activityShortDur" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/shrink_fade_out_center.xml b/core/res/res/anim/shrink_fade_out_center.xml
index a41731e..23fa0b0 100644
--- a/core/res/res/anim/shrink_fade_out_center.xml
+++ b/core/res/res/anim/shrink_fade_out_center.xml
@@ -17,12 +17,14 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
- android:pivotX="50%" android:pivotY="50%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%" android:pivotY="50%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+ android:duration="@android:integer/config_activityShortDur" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/shrink_fade_out_from_bottom.xml b/core/res/res/anim/shrink_fade_out_from_bottom.xml
index 345a2e0..0639425 100644
--- a/core/res/res/anim/shrink_fade_out_from_bottom.xml
+++ b/core/res/res/anim/shrink_fade_out_from_bottom.xml
@@ -17,12 +17,14 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.3"
- android:pivotX="0%" android:pivotY="100%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+ <scale android:interpolator="@anim/decelerate_quint_interpolator"
+ android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%" android:pivotY="100%"
+ android:duration="@android:integer/config_activityDefaultDur" />
+ <alpha android:interpolator="@anim/decelerate_cubic_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+ android:duration="@android:integer/config_activityShortDur" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 9bec8a3..5057eed 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -19,16 +19,16 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="0.9" android:toYScale="1.0"
+ <scale android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="0.95" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
- android:fillEnabled="true" android:fillBefore="true"
+ android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true"
+ android:startOffset="300"
+ android:duration="300" />
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
android:interpolator="@anim/decelerate_cubic_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:fillEnabled="true" android:fillBefore="true"
+ android:startOffset="300"
+ android:duration="300"/>
</set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index ca60214..169f846 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -18,19 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:detachWallpaper="true" android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.9"
+ android:fillEnabled="true" android:fillAfter="true"
+ android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%p" android:pivotY="50%p"
- android:fillEnabled="true" android:fillAfter="true"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:interpolator="@anim/linear_interpolator"
+ android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillAfter="true"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <!-- This is just to keep the animation running for the entire duration. -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:fillEnabled="true" android:fillAfter="true"
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="150"/>
</set>
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 4f72ba2..8f8515a 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -20,15 +20,15 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="0.9" android:toYScale="1.0"
+ android:fromYScale="0.95" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:startOffset="300"
+ android:duration="300" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@anim/decelerate_cubic_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:startOffset="300"
+ android:duration="300"/>
</set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 6174151..7d2b1b1 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -20,17 +20,13 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillAfter="true"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:interpolator="@anim/linear_interpolator"
+ android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:fillEnabled="true" android:fillAfter="true"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <!-- This is just to keep the animation running for the entire duration. -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="150"/>
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index 760acb3..1ce0738 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -17,7 +17,6 @@
*/
-->
-<!-- New holo animation, zooming contents on top of wallpaper down. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="1.0"
@@ -25,64 +24,11 @@
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:startOffset="200"
+ android:duration="300" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:startOffset="200"
+ android:duration="300"/>
</set>
-
-<!-- This version zooms the new non-wallpaper up out of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:zAdjustment="top">
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@anim/decelerate_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:interpolator="@anim/accelerate_decelerate_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale="2.0" android:toXScale="1.0"
- android:fromYScale="2.0" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <scale android:fromXScale="2.0" android:toXScale="1.0"
- android:fromYScale="2.0" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
- also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <translate android:fromXDelta="33%" android:toXDelta="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml
index b804916..39af5f9 100644
--- a/core/res/res/anim/wallpaper_close_exit.xml
+++ b/core/res/res/anim/wallpaper_close_exit.xml
@@ -17,77 +17,16 @@
*/
-->
-<!-- New holo animation, zooming contents on top of wallpaper down. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="0.8"
- android:fromYScale="1.0" android:toYScale="0.8"
+ <scale android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillAfter="true"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:fillEnabled="true" android:fillAfter="true"
android:interpolator="@anim/decelerate_cubic_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
- <!-- This is just to keep the animation running for the entire duration. -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:duration="150"/>
</set>
-
-<!-- This version zooms the new non-wallpaper up out of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:detachWallpaper="true">
- <scale android:fromXScale="1.0" android:toXScale="2.0"
- android:fromYScale="1.0" android:toYScale="2.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:detachWallpaper="true">
- <scale android:fromXScale="1.0" android:toXScale=".5"
- android:fromYScale="1.0" android:toYScale=".5"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper. The wallpaper here just stays fixed behind. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale="1.0" android:toXScale=".5"
- android:fromYScale="1.0" android:toYScale=".5"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
- also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale="1.0" android:toXScale="2.0"
- android:fromYScale="1.0" android:toYScale="2.0"
- android:pivotX="100%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <translate android:fromXDelta="0%" android:toXDelta="-100%"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
index 73bf9a3..1ce0738 100644
--- a/core/res/res/anim/wallpaper_intra_close_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -18,8 +18,17 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale=".9" android:toYScale="1.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:fillEnabled="true" android:fillBefore="true"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:startOffset="200"
+ android:duration="300" />
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:startOffset="200"
+ android:duration="300"/>
</set>
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
index d43660e..39af5f9 100644
--- a/core/res/res/anim/wallpaper_intra_close_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -18,7 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
+ android:detachWallpaper="true" android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:fillEnabled="true" android:fillAfter="true"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:duration="300" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:fillEnabled="true" android:fillAfter="true"
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="150"/>
</set>
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
index c85ca4c..0a8b7d2 100644
--- a/core/res/res/anim/wallpaper_intra_open_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -18,7 +18,15 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:detachWallpaper="true" android:shareInterpolator="false">
+ <scale android:fromXScale="0.95" android:toXScale="1.0"
+ android:fromYScale="0.95" android:toYScale="1.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/decelerate_quint_interpolator"
+ android:startOffset="200"
+ android:duration="300" />
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:startOffset="200"
+ android:duration="300"/>
</set>
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
index eaeac22..d55ee6e 100644
--- a/core/res/res/anim/wallpaper_intra_open_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -18,8 +18,13 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
- android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="1.0" android:toYScale="0.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/linear_interpolator"
+ android:duration="300" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="160"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index 72ac671..0a8b7d2 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -17,74 +17,16 @@
*/
-->
-<!-- New holo animation, zooming contents on top of wallpaper back up. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="0.8" android:toXScale="1.0"
- android:fromYScale="0.8" android:toYScale="1.0"
+ <scale android:fromXScale="0.95" android:toXScale="1.0"
+ android:fromYScale="0.95" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:interpolator="@anim/decelerate_quint_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:startOffset="200"
+ android:duration="300" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:interpolator="@anim/decelerate_cubic_interpolator"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:startOffset="200"
+ android:duration="300"/>
</set>
-
-
-<!-- This version zooms the exiting non-wallpaper down in to the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:detachWallpaper="true">
- <scale android:fromXScale="2.0" android:toXScale="1.0"
- android:fromYScale="2.0" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:detachWallpaper="true">
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper up off the wallpaper the
- wallpaper. The wallpaper here just stays fixed behind. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
- also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale="2.0" android:toXScale="1.0"
- android:fromYScale="2.0" android:toYScale="1.0"
- android:pivotX="100%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <translate android:fromXDelta="-100%" android:toXDelta="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index 7df3563..d55ee6e 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -17,68 +17,14 @@
*/
-->
-<!-- New holo animation, zooming contents on top of wallpaper back up. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale=".9"
+ android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:interpolator="@anim/linear_interpolator"
+ android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@anim/decelerate_quint_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
-
-<!-- This version zooms the exiting non-wallpaper down in to the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:zAdjustment="top">
- <scale android:fromXScale="1.0" android:toXScale=".5"
- android:fromYScale="1.0" android:toYScale=".5"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@anim/decelerate_interpolator"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@anim/accelerate_decelerate_interpolator"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <scale android:fromXScale="1.0" android:toXScale="2.0"
- android:fromYScale="1.0" android:toYScale="2.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
- wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <scale android:fromXScale="1.0" android:toXScale="2.0"
- android:fromYScale="1.0" android:toYScale="2.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
- also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <translate android:fromXDelta="0%" android:toXDelta="33%"
- android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
+ android:interpolator="@anim/decelerate_cubic_interpolator"
+ android:duration="160"/>
+</set>
diff --git a/core/res/res/drawable-mdpi/stat_notify_error.png b/core/res/res/drawable-mdpi/stat_notify_error.png
index 6ced2b7..1275738 100644
--- a/core/res/res/drawable-mdpi/stat_notify_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard.png b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
index 23093ac..fc0784d 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
index 9abb1c9..93fad38 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
index 9880694..cda4546 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.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 e9c74b4..517c515 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_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
index 12abeda..f0fad76 100644
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_0.png b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
index 750e652..e089120 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_15.png b/core/res/res/drawable-mdpi/stat_sys_battery_15.png
index 0eb58e1..be04321 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_15.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_15.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
index 957dab3..f8011c9 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png
index e6d7da0..499ced9 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png
index 957dab3..c921d6a 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png
index 5aba0bb..f882002 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png
index dc5fac6..e7d1069 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png
index 1233ed8..5e0af3d 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png
index 06d397b..fb99059 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png
index 1056faf..072f907 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
index 44eb313..3984c46 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
index 69b95cd..01b65e0 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
index 1e18eb5..2b0633f 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
index d7f2312..2295a6d 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
index 83f8d0f..99db712 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
index 9c1bd47..738dc87 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
index 3a81164..00ff542 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
index b7a5978..f6fcc7a 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index a203e15..5d05381 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index 4af7630..a8699bc 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
index 1dd76b1..f5ad44a 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
index 36c18bf..b5884f6 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
index 748331f..ae6b8a9 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
Binary files differ
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 3982ed9..e3ba634 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -81,7 +81,8 @@
android:paddingTop="2dip"
android:paddingBottom="12dip"
android:paddingLeft="14dip"
- android:paddingRight="10dip">
+ android:paddingRight="10dip"
+ android:overScrollMode="ifContentScrolls">
<TextView android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_dialog_edittext.xml b/core/res/res/layout/preference_dialog_edittext.xml
index 691ee8c..40b9e69 100644
--- a/core/res/res/layout/preference_dialog_edittext.xml
+++ b/core/res/res/layout/preference_dialog_edittext.xml
@@ -19,7 +19,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
- android:layout_marginBottom="48dp">
+ android:layout_marginBottom="48dp"
+ android:overScrollMode="ifContentScrolls">
<LinearLayout
android:id="@+android:id/edittext_container"
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 6c0e773..0f84418 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -56,7 +56,7 @@
</LinearLayout>
- <FrameLayout android:id="@+id/prefs"
+ <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="20"
@@ -66,8 +66,6 @@
android:layout_marginBottom="16dp"
android:paddingLeft="32dip"
android:paddingRight="32dip"
- android:paddingTop="48dip"
- android:paddingBottom="48dip"
android:background="?attr/preferencePanelBackground"
android:visibility="gone" />
</LinearLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index c499b8a..dbe0df0 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -18,6 +18,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/default_preference_layout"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -27,6 +28,9 @@
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
+ android:paddingTop="48dip"
+ android:paddingBottom="48dip"
+ android:clipToPadding="false"
android:drawSelectorOnTop="false"
android:cacheColorHint="@android:color/transparent"
android:scrollbarAlwaysDrawVerticalTrack="true" />
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index 94dcb6a..80d22f6 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -31,4 +31,5 @@
android:layout_marginTop="5px"
android:cacheColorHint="@null"
android:divider="?android:attr/listDividerAlertDialog"
- android:scrollbars="vertical" />
+ android:scrollbars="vertical"
+ android:overScrollMode="ifContentScrolls" />
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index 9504d04..37f59c8 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -30,8 +30,8 @@
<!-- Enable lockscreen rotation -->
<bool name="config_enableLockScreenRotation">true</bool>
- <!-- Enables 3d task switcher on xlarge device -->
- <bool name="config_enableRecentApps3D">true</bool>
+ <!-- see comment in values/config.xml -->
+ <integer name="config_longPressOnHomeBehavior">1</integer>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 651bfea..3f81a89 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -610,6 +610,9 @@
<!-- Specifies a drawable to use for the 'home as up' indicator. -->
<attr name="homeAsUpIndicator" format="reference" />
+
+ <!-- Preference frame layout styles. -->
+ <attr name="preferenceFrameLayoutStyle" format="reference" />
</declare-styleable>
<!-- **************************************************************** -->
@@ -1632,6 +1635,20 @@
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
+
+ <!-- Defines over-scrolling behavior. This property is used only if the
+ View is scrollable. Over-scrolling is the ability for the user to
+ receive feedback when attempting to scroll beyond meaningful content. -->
+ <attr name="overScrollMode">
+ <!-- Always show over-scroll effects, even if the content fits entirely
+ within the available space. -->
+ <enum name="always" value="0" />
+ <!-- Only show over-scroll effects if the content is large
+ enough to meaningfully scroll. -->
+ <enum name="ifContentScrolls" value="1" />
+ <!-- Never show over-scroll effects. -->
+ <enum name="never" value="2" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -2116,6 +2133,16 @@
<!-- When set to false, the ListView will not draw the divider before each footer view.
The default value is true. -->
<attr name="footerDividersEnabled" format="boolean" />
+ <!-- Drawable to draw above list content. -->
+ <attr name="overScrollHeader" format="reference|color" />
+ <!-- Drawable to draw below list content. -->
+ <attr name="overScrollFooter" format="reference|color" />
+ </declare-styleable>
+ <declare-styleable name="PreferenceFrameLayout">
+ <!-- Padding to use at the top of the prefs content. -->
+ <attr name="topPadding" format="dimension" />
+ <!-- Padding to use at the bottom of the prefs content. -->
+ <attr name="bottomPadding" format="dimension" />
</declare-styleable>
<declare-styleable name="MenuView">
<!-- Default appearance of menu item text. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ac15be6..15b5db4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -66,6 +66,16 @@
<!-- The duration (in milliseconds) of a long animation. -->
<integer name="config_longAnimTime">500</integer>
+ <!-- The duration (in milliseconds) of the activity open/close and fragment open/close animations. -->
+ <integer name="config_activityShortDur">150</integer>
+ <integer name="config_activityDefaultDur">220</integer>
+
+ <!-- Duration for the dim animation behind a dialog. This may be either
+ a percentage, which is relative to the duration of the enter/open
+ animation of the window being shown that is dimming behind, or it may
+ be an integer for a constant duration. -->
+ <fraction name="config_dimBehindFadeDuration">100%</fraction>
+
<!-- The duration (in milliseconds) that the radio will scan for a signal
when there's no network connection. If the scan doesn't timeout, use zero -->
<integer name="config_radioScanningTimeout">0</integer>
@@ -336,8 +346,12 @@
<!-- Diable lockscreen rotation by default -->
<bool name="config_enableLockScreenRotation">false</bool>
- <!-- Enable 3D RecentApplications view -->
- <bool name="config_enableRecentApps3D">false</bool>
+ <!-- Control the behavior when the user long presses the power button.
+ 0 - Nothing
+ 1 - Recent apps dialog
+ 2 - Recent apps activity in SystemUI
+ -->
+ <integer name="config_longPressOnHomeBehavior">0</integer>
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
The N entries of this array define N + 1 zones as follows:
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f4d1df8..f2ab5cd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1253,6 +1253,9 @@
<public type="attr" name="logo" id="0x010102be" />
<public type="attr" name="xlargeScreens" id="0x010102bf" />
<public type="attr" name="immersive" id="0x010102c0" />
+ <public type="attr" name="overScrollMode" id="0x010102c1" />
+ <public type="attr" name="overScrollHeader" id="0x010102c2" />
+ <public type="attr" name="overScrollFooter" id="0x010102c3" />
<public type="attr" name="filterTouchesWhenObscured" id="0x010102c4" />
<public type="attr" name="textSelectHandleLeft" id="0x010102c5" />
<public type="attr" name="textSelectHandleRight" id="0x010102c6" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c8a5de8..0c3361d 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1824,8 +1824,10 @@
<!-- Toast for double-tap -->
<string name="double_tap_toast">Tip: double-tap to zoom in and out.</string>
- <!-- Text to show in the auto complete drop down list on a text view when the browser can auto fill the entire form -->
+ <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form, and the user has configured an AutoFill profile [CHAR-LIMIT=8] -->
<string name="autofill_this_form">AutoFill</string>
+ <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=16] -->
+ <string name="setup_autofill">Setup AutoFill</string>
<!-- String used to separate FirstName and LastName when writing out a local name
e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 7a429b0..6985de6 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -48,10 +48,15 @@
<item name="bottomMedium">@android:drawable/popup_bottom_medium</item>
<item name="centerMedium">@android:drawable/popup_center_medium</item>
</style>
-
+
+ <style name="Widget.PreferenceFrameLayout">
+ <item name="android:topPadding">0dip</item>
+ <item name="android:bottomPadding">0dip</item>
+ </style>
+
<!-- Base style for animations. This style specifies no animations. -->
<style name="Animation" />
-
+
<!-- Standard animations for a full-screen window or activity. -->
<style name="Animation.Activity">
<item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
@@ -1808,7 +1813,7 @@
<item name="android:background">@null</item>
<item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
<item name="android:progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item>
- <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar.Small</item>
+ <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar</item>
</style>
<!-- Animation Styles -->
@@ -1874,4 +1879,8 @@
<item name="android:textAppearance">@style/TextAppearance.Holo.Light.DialogWindowTitle</item>
</style>
+ <style name="Widget.Holo.PreferenceFrameLayout">
+ <item name="android:topPadding">48dip</item>
+ <item name="android:bottomPadding">48dip</item>
+ </style>
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9215bf2..1ef99d0 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -262,13 +262,16 @@
<!-- SearchView attributes -->
<item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
+
+ <!-- PreferenceFrameLayout attributes -->
+ <item name="preferenceFrameLayoutStyle">@android:style/Widget.PreferenceFrameLayout</item>
</style>
-
+
<!-- Variant of the default (dark) theme with no title bar -->
<style name="Theme.NoTitleBar">
<item name="android:windowNoTitle">true</item>
</style>
-
+
<!-- Variant of the default (dark) theme that has no title bar and
fills the entire screen -->
<style name="Theme.NoTitleBar.Fullscreen">
@@ -871,6 +874,9 @@
<!-- SearchView attributes -->
<item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
+
+ <!-- PreferenceFrameLayout attributes -->
+ <item name="preferenceFrameLayoutStyle">@android:style/Widget.Holo.PreferenceFrameLayout</item>
</style>
<!-- New Honeycomb holographic theme. Light version. The widgets in the
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index d298d40..b116bea 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -53,6 +53,15 @@
android.label="Test runner for unit tests"
/>
+ <!-- run stress test suite:
+ "adb shell am instrument -e stressnum <200> -w
+ com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner"
+ -->
+ <instrumentation android:name=".ConnectivityManagerStressTestRunner"
+ android:targetPackage="com.android.connectivitymanagertest"
+ android:label="Test runner for Connectivity Manager Stress Tests"
+ />
+
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
new file mode 100644
index 0000000..47f208a
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.stress.WifiApStress;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all stress tests
+ *
+ * To run the stress tests:
+ *
+ * adb shell am instrument -e stressnum <stress times> \
+ * -w com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner
+ */
+
+public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(WifiApStress.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return ConnectivityManagerTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ String stressValue = (String) icicle.get("stressnum");
+ if (stressValue != null) {
+ int iteration = Integer.parseInt(stressValue);
+ if (iteration > 0) {
+ numStress = iteration;
+ }
+ }
+ }
+
+ public int numStress = 100;
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 7c46e7a..37b9f52 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -55,6 +55,9 @@ public class ConnectivityManagerTestActivity extends Activity {
public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
public static final int SHORT_TIMEOUT = 5 * 1000;
public static final long LONG_TIMEOUT = 50 * 1000;
+ public static final int SUCCESS = 0; // for Wifi tethering state change
+ public static final int FAILURE = 1;
+ public static final int INIT = -1;
private static final String ACCESS_POINT_FILE = "accesspoints.xml";
public ConnectivityReceiver mConnectivityReceiver = null;
public WifiReceiver mWifiReceiver = null;
@@ -87,6 +90,10 @@ public class ConnectivityManagerTestActivity extends Activity {
public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
+ // For wifi tethering tests
+ private String[] mWifiRegexs;
+ public int mWifiTetherResult = INIT; // -1 is initialization state
+
/**
* A wrapper of a broadcast receiver which provides network connectivity information
* for all kinds of network: wifi, mobile, etc.
@@ -150,6 +157,16 @@ public class ConnectivityManagerTestActivity extends Activity {
mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
notifyWifiState();
+ } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+ notifyWifiAPState();
+ } else if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
+ ArrayList<String> available = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ ArrayList<String> active = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ ArrayList<String> errored = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ERRORED_TETHER);
+ updateTetherState(available.toArray(), active.toArray(), errored.toArray());
}
else {
return;
@@ -184,6 +201,8 @@ public class ConnectivityManagerTestActivity extends Activity {
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
registerReceiver(mWifiReceiver, mIntentFilter);
// Get an instance of ConnectivityManager
@@ -196,6 +215,7 @@ public class ConnectivityManagerTestActivity extends Activity {
Log.v(LOG_TAG, "Clear Wifi before we start the test.");
removeConfiguredNetworksAndDisableWifi();
}
+ mWifiRegexs = mCM.getTetherableWifiRegexs();
}
public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
@@ -263,13 +283,57 @@ public class ConnectivityManagerTestActivity extends Activity {
}
}
- public void notifyWifiState() {
+ private void notifyWifiState() {
synchronized (wifiObject) {
Log.v(LOG_TAG, "notify wifi state changed");
wifiObject.notify();
}
}
+ private void notifyWifiAPState() {
+ synchronized (this) {
+ Log.v(LOG_TAG, "notify wifi AP state changed");
+ this.notify();
+ }
+ }
+
+ // Update wifi tethering state
+ private void updateTetherState(Object[] available, Object[] tethered, Object[] errored) {
+ boolean wifiTethered = false;
+ boolean wifiErrored = false;
+
+ synchronized (this) {
+ for (Object obj: tethered) {
+ String str = (String)obj;
+ for (String tethRex: mWifiRegexs) {
+ Log.v(LOG_TAG, "str: " + str +"tethRex: " + tethRex);
+ if (str.matches(tethRex)) {
+ wifiTethered = true;
+ }
+ }
+ }
+
+ for (Object obj: errored) {
+ String str = (String)obj;
+ for (String tethRex: mWifiRegexs) {
+ Log.v(LOG_TAG, "error: str: " + str +"tethRex: " + tethRex);
+ if (str.matches(tethRex)) {
+ wifiErrored = true;
+ }
+ }
+ }
+
+ if (wifiTethered) {
+ mWifiTetherResult = SUCCESS; // wifi tethering is successful
+ } else if (wifiErrored) {
+ mWifiTetherResult = FAILURE; // wifi tethering failed
+ }
+ Log.v(LOG_TAG, "mWifiTetherResult: " + mWifiTetherResult);
+ this.notify();
+ }
+ }
+
+
// Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
// DISCONNECTING, DISCONNECTED, UNKNOWN
public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
@@ -332,6 +396,62 @@ public class ConnectivityManagerTestActivity extends Activity {
}
}
+ // Wait for Wifi AP state: WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING,
+ // WIFI_AP_STATE_ENABLED, WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
+ public boolean waitForWifiAPState(int expectedState, long timeout) {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (mWifiManager.getWifiApState() != expectedState) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for wifi AP state to be: " + expectedState);
+ synchronized (wifiObject) {
+ try {
+ wifiObject.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mWifiManager.getWifiApState() != expectedState) {
+ Log.v(LOG_TAG, "Wifi state is: " + mWifiManager.getWifiApState());
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Wait for the wifi tethering result:
+ * @param timeout is the maximum waiting time
+ * @return SUCCESS if tethering result is successful
+ * FAILURE if tethering result returns error.
+ */
+ public int waitForTetherStateChange(long timeout) {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ return mWifiTetherResult;
+ }
+ Log.v(LOG_TAG, "Wait for wifi tethering result.");
+ synchronized (this) {
+ try {
+ this.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mWifiTetherResult == INIT ) {
+ continue;
+ } else {
+ return mWifiTetherResult;
+ }
+ }
+ }
+ }
+
// Return true if device is currently connected to mobile network
public boolean isConnectedToMobile() {
return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
new file mode 100644
index 0000000..cc53ddc
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest.stress;
+
+
+import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+/**
+ * Stress the wifi driver as access point.
+ */
+public class WifiApStress
+ extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+ private final static String TAG = "WifiApStress";
+ private ConnectivityManagerTestActivity mAct;
+ private static String NETWORK_ID = "AndroidAPTest";
+ private static String PASSWD = "androidwifi";
+ private int max_num;
+
+ public WifiApStress() {
+ super(ConnectivityManagerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAct = getActivity();
+ max_num = ((ConnectivityManagerStressTestRunner)getInstrumentation()).numStress;
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @LargeTest
+ public void testWifiHotSpot() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = NETWORK_ID;
+ config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+ config.preSharedKey = PASSWD;
+
+ // If Wifi is enabled, disable it
+ if (mAct.mWifiManager.isWifiEnabled()) {
+ mAct.disableWifi();
+ }
+ for (int i = 0; i < max_num; i++) {
+ Log.v(TAG, "iteration: " + i);
+ // enable Wifi tethering
+ assertTrue(mAct.mWifiManager.setWifiApEnabled(config, true));
+ // Wait for wifi ap state to be ENABLED
+ assertTrue(mAct.waitForWifiAPState(mAct.mWifiManager.WIFI_AP_STATE_ENABLED,
+ mAct.LONG_TIMEOUT));
+ // Wait for wifi tethering result
+ assertEquals(mAct.SUCCESS, mAct.waitForTetherStateChange(2*mAct.SHORT_TIMEOUT));
+ // Allow the wifi tethering to be enabled for 10 seconds
+ try {
+ Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
+ } catch (Exception e) {
+ fail("thread in sleep is interrupted");
+ }
+ assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false));
+ }
+ }
+
+}
diff --git a/core/tests/coretests/src/android/content/SyncOperationTest.java b/core/tests/coretests/src/android/content/SyncOperationTest.java
new file mode 100644
index 0000000..57435e5
--- /dev/null
+++ b/core/tests/coretests/src/android/content/SyncOperationTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.accounts.Account;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * You can run those tests with:
+ *
+ * adb shell am instrument
+ * -e debug false
+ * -w
+ * -e class android.content.SyncOperationTest com.android.frameworks.coretests/android.test.InstrumentationTestRunner
+ */
+
+public class SyncOperationTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testToKey() {
+ Account account1 = new Account("account1", "type1");
+ Account account2 = new Account("account2", "type2");
+
+ Bundle b1 = new Bundle();
+ Bundle b2 = new Bundle();
+ b2.putBoolean("b2", true);
+
+ SyncOperation op1 = new SyncOperation(account1,
+ 1,
+ "authority1",
+ b1,
+ 100,
+ 1000,
+ 10000);
+
+ // Same as op1 but different time infos
+ SyncOperation op2 = new SyncOperation(account1,
+ 1,
+ "authority1",
+ b1,
+ 200,
+ 2000,
+ 20000);
+
+ // Same as op1 but different authority
+ SyncOperation op3 = new SyncOperation(account1,
+ 1,
+ "authority2",
+ b1,
+ 100,
+ 1000,
+ 10000);
+
+ // Same as op1 but different account
+ SyncOperation op4 = new SyncOperation(account2,
+ 1,
+ "authority1",
+ b1,
+ 100,
+ 1000,
+ 10000);
+
+ // Same as op1 but different bundle
+ SyncOperation op5 = new SyncOperation(account1,
+ 1,
+ "authority1",
+ b2,
+ 100,
+ 1000,
+ 10000);
+
+ assertEquals(op1.key, op2.key);
+ assertNotSame(op1.key, op3.key);
+ assertNotSame(op1.key, op4.key);
+ assertNotSame(op1.key, op5.key);
+ }
+}
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
index 65a6078..8d9f8e5 100644
--- a/core/tests/coretests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -429,4 +429,22 @@ public class TimeUtilsTest extends TestCase {
c.getTimeInMillis(),
country);
}
+
+ public void testFormatDuration() {
+ assertFormatDuration("0", 0);
+ assertFormatDuration("-1ms", -1);
+ assertFormatDuration("+1ms", 1);
+ assertFormatDuration("+10ms", 10);
+ assertFormatDuration("+100ms", 100);
+ assertFormatDuration("+101ms", 101);
+ assertFormatDuration("+330ms", 330);
+ assertFormatDuration("+1s330ms", 1330);
+ assertFormatDuration("+10s24ms", 10024);
+ }
+
+ private void assertFormatDuration(String expected, long duration) {
+ StringBuilder sb = new StringBuilder();
+ TimeUtils.formatDuration(duration, sb);
+ assertEquals("formatDuration(" + duration + ")", expected, sb.toString());
+ }
}
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
new file mode 100644
index 0000000..8cba52d
--- /dev/null
+++ b/data/keyboards/Android.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This makefile performs build time validation of framework keymap files.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(LOCAL_PATH)/common.mk
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 682584c..24b485d 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -22,6 +22,8 @@
type FULL
+### Basic QWERTY keys ###
+
key A {
label: 'A'
base: 'a'
@@ -369,6 +371,8 @@ key APOSTROPHE {
ctrl, alt, meta: none
}
+### Numeric keypad ###
+
key NUMPAD_0 {
label, number: '0'
base: fallback INSERT
@@ -499,3 +503,25 @@ key NUMPAD_ENTER {
base: '\n' fallback ENTER
ctrl, alt, meta: none fallback ENTER
}
+
+### Special keys on phones ###
+
+key AT {
+ label, number: '@'
+ base: '@'
+}
+
+key STAR {
+ label, number: '*'
+ base: '*'
+}
+
+key POUND {
+ label, number: '#'
+ base: '#'
+}
+
+key PLUS {
+ label, number: '+'
+ base: '+'
+}
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index 9ada86a..8d3c7ac 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -19,6 +19,8 @@
type FULL
+### Basic QWERTY keys ###
+
key A {
label: 'A'
base: 'a'
@@ -366,6 +368,8 @@ key APOSTROPHE {
ctrl, alt, meta: none
}
+### Numeric keypad ###
+
key NUMPAD_0 {
label, number: '0'
base: fallback INSERT
@@ -496,3 +500,25 @@ key NUMPAD_ENTER {
base: '\n' fallback ENTER
ctrl, alt, meta: none fallback ENTER
}
+
+### Special keys on phones ###
+
+key AT {
+ label, number: '@'
+ base: '@'
+}
+
+key STAR {
+ label, number: '*'
+ base: '*'
+}
+
+key POUND {
+ label, number: '#'
+ base: '#'
+}
+
+key PLUS {
+ label, number: '+'
+ base: '+'
+}
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
new file mode 100644
index 0000000..3f05edb
--- /dev/null
+++ b/data/keyboards/common.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the list of framework provided keylayouts and key character maps to include.
+# Used by Android.mk and keyboards.mk.
+
+keylayouts := \
+ AVRCP.kl \
+ Generic.kl \
+ Motorola_Bluetooth_Wireless_Keyboard.kl \
+ qwerty.kl \
+ qwerty2.kl
+
+keycharmaps := \
+ Generic.kcm \
+ Virtual.kcm \
+ Motorola_Bluetooth_Wireless_Keyboard.kcm \
+ qwerty.kcm \
+ qwerty2.kcm
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 3a0a553..b32e436 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -14,15 +14,7 @@
# Warning: this is actually a product definition, to be inherited from
-keylayouts := \
- AVRCP.kl \
- Generic.kl \
- Motorola_Bluetooth_Wireless_Keyboard.kl
-
-keycharmaps := \
- Generic.kcm \
- Virtual.kcm \
- Motorola_Bluetooth_Wireless_Keyboard.kcm
+include $(LOCAL_PATH)/common.mk
PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
frameworks/base/data/keyboards/$(file):system/usr/keylayout/$(file))
@@ -30,4 +22,4 @@ PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\
frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file))
-PRODUCT_PACKAGES := $(keycharmaps)
+PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps)
diff --git a/data/keyboards/qwerty.kcm b/data/keyboards/qwerty.kcm
new file mode 100644
index 0000000..f31333e
--- /dev/null
+++ b/data/keyboards/qwerty.kcm
@@ -0,0 +1,508 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Emulator keyboard character map #1.
+#
+# This file is no longer used as the platform's default keyboard character map.
+# Refer to Generic.kcm and Virtual.kcm instead.
+#
+
+type ALPHA
+
+key A {
+ label: 'A'
+ number: '2'
+ base: 'a'
+ shift, capslock: 'A'
+ alt: '#'
+ shift+alt, capslock+alt: none
+}
+
+key B {
+ label: 'B'
+ number: '2'
+ base: 'b'
+ shift, capslock: 'B'
+ alt: '<'
+ shift+alt, capslock+alt: none
+}
+
+key C {
+ label: 'C'
+ number: '2'
+ base: 'c'
+ shift, capslock: 'C'
+ alt: '9'
+ shift+alt, capslock+alt: '\u00e7'
+}
+
+key D {
+ label: 'D'
+ number: '3'
+ base: 'd'
+ shift, capslock: 'D'
+ alt: '5'
+ shift+alt, capslock+alt: none
+}
+
+key E {
+ label: 'E'
+ number: '3'
+ base: 'e'
+ shift, capslock: 'E'
+ alt: '2'
+ shift+alt, capslock+alt: '\u0301'
+}
+
+key F {
+ label: 'F'
+ number: '3'
+ base: 'f'
+ shift, capslock: 'F'
+ alt: '6'
+ shift+alt, capslock+alt: '\u00a5'
+}
+
+key G {
+ label: 'G'
+ number: '4'
+ base: 'g'
+ shift, capslock: 'G'
+ alt: '-'
+ shift+alt, capslock+alt: '_'
+}
+
+key H {
+ label: 'H'
+ number: '4'
+ base: 'h'
+ shift, capslock: 'H'
+ alt: '['
+ shift+alt, capslock+alt: '{'
+}
+
+key I {
+ label: 'I'
+ number: '4'
+ base: 'i'
+ shift, capslock: 'I'
+ alt: '$'
+ shift+alt, capslock+alt: '\u0302'
+}
+
+key J {
+ label: 'J'
+ number: '5'
+ base: 'j'
+ shift, capslock: 'J'
+ alt: ']'
+ shift+alt, capslock+alt: '}'
+}
+
+key K {
+ label: 'K'
+ number: '5'
+ base: 'k'
+ shift, capslock: 'K'
+ alt: '"'
+ shift+alt, capslock+alt: '~'
+}
+
+key L {
+ label: 'L'
+ number: '5'
+ base: 'l'
+ shift, capslock: 'L'
+ alt: '\''
+ shift+alt, capslock+alt: '`'
+}
+
+key M {
+ label: 'M'
+ number: '6'
+ base: 'm'
+ shift, capslock: 'M'
+ alt: '!'
+ shift+alt, capslock+alt: none
+}
+
+key N {
+ label: 'N'
+ number: '6'
+ base: 'n'
+ shift, capslock: 'N'
+ alt: '>'
+ shift+alt, capslock+alt: '\u0303'
+}
+
+key O {
+ label: 'O'
+ number: '6'
+ base: 'o'
+ shift, capslock: 'O'
+ alt: '('
+ shift+alt, capslock+alt: none
+}
+
+key P {
+ label: 'P'
+ number: '7'
+ base: 'p'
+ shift, capslock: 'P'
+ alt: ')'
+ shift+alt, capslock+alt: none
+}
+
+key Q {
+ label: 'Q'
+ number: '7'
+ base: 'q'
+ shift, capslock: 'Q'
+ alt: '*'
+ shift+alt, capslock+alt: '\u0300'
+}
+
+key R {
+ label: 'R'
+ number: '7'
+ base: 'r'
+ shift, capslock: 'R'
+ alt: '3'
+ shift+alt, capslock+alt: '\u20ac'
+}
+
+key S {
+ label: 'S'
+ number: '7'
+ base: 's'
+ shift, capslock: 'S'
+ alt: '4'
+ shift+alt, capslock+alt: '\u00df'
+}
+
+key T {
+ label: 'T'
+ number: '8'
+ base: 't'
+ shift, capslock: 'T'
+ alt: '+'
+ shift+alt, capslock+alt: '\u00a3'
+}
+
+key U {
+ label: 'U'
+ number: '8'
+ base: 'u'
+ shift, capslock: 'U'
+ alt: '&'
+ shift+alt, capslock+alt: '\u0308'
+}
+
+key V {
+ label: 'V'
+ number: '8'
+ base: 'v'
+ shift, capslock: 'V'
+ alt: '='
+ shift+alt, capslock+alt: '^'
+}
+
+key W {
+ label: 'W'
+ number: '9'
+ base: 'w'
+ shift, capslock: 'W'
+ alt: '1'
+ shift+alt, capslock+alt: none
+}
+
+key X {
+ label: 'X'
+ number: '9'
+ base: 'x'
+ shift, capslock: 'X'
+ alt: '8'
+ shift+alt, capslock+alt: '\uef00'
+}
+
+key Y {
+ label: 'Y'
+ number: '9'
+ base: 'y'
+ shift, capslock: 'Y'
+ alt: '%'
+ shift+alt, capslock+alt: '\u00a1'
+}
+
+key Z {
+ label: 'Z'
+ number: '9'
+ base: 'z'
+ shift, capslock: 'Z'
+ alt: '7'
+ shift+alt, capslock+alt: none
+}
+
+key COMMA {
+ label: ','
+ number: ','
+ base: ','
+ shift, capslock: ';'
+ alt: ';'
+ shift+alt, capslock+alt: '|'
+}
+
+key PERIOD {
+ label: '.'
+ number: '.'
+ base: '.'
+ shift: ':'
+ alt: ':'
+ shift+alt: '\u2026'
+}
+
+key AT {
+ label: '@'
+ number: '0'
+ base: '@'
+ shift: '0'
+ alt: '0'
+ shift+alt: '\u2022'
+}
+
+key SLASH {
+ label: '/'
+ number: '/'
+ base: '/'
+ shift: '?'
+ alt: '?'
+ shift+alt: '\\'
+}
+
+key SPACE {
+ label: ' '
+ number: ' '
+ base: ' '
+ shift: ' '
+ alt: '\uef01'
+ shift+alt: '\uef01'
+}
+
+key ENTER {
+ label: '\n'
+ number: '\n'
+ base: '\n'
+ shift: '\n'
+ alt: '\n'
+ shift+alt: '\n'
+}
+
+key TAB {
+ label: '\t'
+ number: '\t'
+ base: '\t'
+ shift: '\t'
+ alt: '\t'
+ shift+alt: '\t'
+}
+
+key 0 {
+ label: '0'
+ number: '0'
+ base: '0'
+ shift: ')'
+ alt: ')'
+ shift+alt: ')'
+}
+
+key 1 {
+ label: '1'
+ number: '1'
+ base: '1'
+ shift: '!'
+ alt: '!'
+ shift+alt: '!'
+}
+
+key 2 {
+ label: '2'
+ number: '2'
+ base: '2'
+ shift: '@'
+ alt: '@'
+ shift+alt: '@'
+}
+
+key 3 {
+ label: '3'
+ number: '3'
+ base: '3'
+ shift: '#'
+ alt: '#'
+ shift+alt: '#'
+}
+
+key 4 {
+ label: '4'
+ number: '4'
+ base: '4'
+ shift: '$'
+ alt: '$'
+ shift+alt: '$'
+}
+
+key 5 {
+ label: '5'
+ number: '5'
+ base: '5'
+ shift: '%'
+ alt: '%'
+ shift+alt: '%'
+}
+
+key 6 {
+ label: '6'
+ number: '6'
+ base: '6'
+ shift: '^'
+ alt: '^'
+ shift+alt: '^'
+}
+
+key 7 {
+ label: '7'
+ number: '7'
+ base: '7'
+ shift: '&'
+ alt: '&'
+ shift+alt: '&'
+}
+
+key 8 {
+ label: '8'
+ number: '8'
+ base: '8'
+ shift: '*'
+ alt: '*'
+ shift+alt: '*'
+}
+
+key 9 {
+ label: '9'
+ number: '9'
+ base: '9'
+ shift: '('
+ alt: '('
+ shift+alt: '('
+}
+
+key GRAVE {
+ label: '`'
+ number: '`'
+ base: '`'
+ shift: '~'
+ alt: '`'
+ shift+alt: '~'
+}
+
+key MINUS {
+ label: '-'
+ number: '-'
+ base: '-'
+ shift: '_'
+ alt: '-'
+ shift+alt: '_'
+}
+
+key EQUALS {
+ label: '='
+ number: '='
+ base: '='
+ shift: '+'
+ alt: '='
+ shift+alt: '+'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ number: '['
+ base: '['
+ shift: '{'
+ alt: '['
+ shift+alt: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ number: ']'
+ base: ']'
+ shift: '}'
+ alt: ']'
+ shift+alt: '}'
+}
+
+key BACKSLASH {
+ label: '\\'
+ number: '\\'
+ base: '\\'
+ shift: '|'
+ alt: '\\'
+ shift+alt: '|'
+}
+
+key SEMICOLON {
+ label: ';'
+ number: ';'
+ base: ';'
+ shift: ':'
+ alt: ';'
+ shift+alt: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ number: '\''
+ base: '\''
+ shift: '"'
+ alt: '\''
+ shift+alt: '"'
+}
+
+key STAR {
+ label: '*'
+ number: '*'
+ base: '*'
+ shift: '*'
+ alt: '*'
+ shift+alt: '*'
+}
+
+key POUND {
+ label: '#'
+ number: '#'
+ base: '#'
+ shift: '#'
+ alt: '#'
+ shift+alt: '#'
+}
+
+key PLUS {
+ label: '+'
+ number: '+'
+ base: '+'
+ shift: '+'
+ alt: '+'
+ shift+alt: '+'
+}
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
new file mode 100644
index 0000000..f1caacd
--- /dev/null
+++ b/data/keyboards/qwerty.kl
@@ -0,0 +1,112 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Emulator keyboard layout #1.
+#
+# This file is no longer used as the platform's default keyboard layout.
+# Refer to Generic.kl instead.
+#
+
+key 399 GRAVE
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 158 BACK WAKE_DROPPED
+key 230 SOFT_RIGHT WAKE
+key 60 SOFT_RIGHT WAKE
+key 107 ENDCALL WAKE_DROPPED
+key 62 ENDCALL WAKE_DROPPED
+key 229 MENU WAKE_DROPPED
+key 139 MENU WAKE_DROPPED
+key 59 MENU WAKE_DROPPED
+key 127 SEARCH WAKE_DROPPED
+key 217 SEARCH WAKE_DROPPED
+key 228 POUND
+key 227 STAR
+key 231 CALL WAKE_DROPPED
+key 61 CALL WAKE_DROPPED
+key 232 DPAD_CENTER WAKE_DROPPED
+key 108 DPAD_DOWN WAKE_DROPPED
+key 103 DPAD_UP WAKE_DROPPED
+key 102 HOME WAKE
+key 105 DPAD_LEFT WAKE_DROPPED
+key 106 DPAD_RIGHT WAKE_DROPPED
+key 115 VOLUME_UP WAKE
+key 114 VOLUME_DOWN WAKE
+key 116 POWER WAKE
+key 212 CAMERA
+
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+key 26 LEFT_BRACKET
+key 27 RIGHT_BRACKET
+key 43 BACKSLASH
+
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 39 SEMICOLON
+key 40 APOSTROPHE
+key 14 DEL
+
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 53 SLASH
+key 28 ENTER
+
+key 56 ALT_LEFT
+key 100 ALT_RIGHT
+key 42 SHIFT_LEFT
+key 54 SHIFT_RIGHT
+key 15 TAB
+key 57 SPACE
+key 150 EXPLORER
+key 155 ENVELOPE
+
+key 12 MINUS
+key 13 EQUALS
+key 215 AT
+
+# On an AT keyboard: ESC, F10
+key 1 BACK WAKE_DROPPED
+key 68 MENU WAKE_DROPPED
diff --git a/data/keyboards/qwerty2.kcm b/data/keyboards/qwerty2.kcm
new file mode 100644
index 0000000..d96914f
--- /dev/null
+++ b/data/keyboards/qwerty2.kcm
@@ -0,0 +1,505 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Emulator keyboard character map #2.
+#
+
+type ALPHA
+
+key A {
+ label: 'A'
+ number: '2'
+ base: 'a'
+ shift, capslock: 'A'
+ alt: 'a'
+ shift+alt, capslock+alt: 'A'
+}
+
+key B {
+ label: 'B'
+ number: '2'
+ base: 'b'
+ shift, capslock: 'B'
+ alt: 'b'
+ shift+alt, capslock+alt: 'B'
+}
+
+key C {
+ label: 'C'
+ number: '2'
+ base: 'c'
+ shift, capslock: 'C'
+ alt: '\u00e7'
+ shift+alt, capslock+alt: '\u00e7'
+}
+
+key D {
+ label: 'D'
+ number: '3'
+ base: 'd'
+ shift, capslock: 'D'
+ alt: '\''
+ shift+alt, capslock+alt: '\''
+}
+
+key E {
+ label: 'E'
+ number: '3'
+ base: 'e'
+ shift, capslock: 'E'
+ alt: '"'
+ shift+alt, capslock+alt: '\u0301'
+}
+
+key F {
+ label: 'F'
+ number: '3'
+ base: 'f'
+ shift, capslock: 'F'
+ alt: '['
+ shift+alt, capslock+alt: '['
+}
+
+key G {
+ label: 'G'
+ number: '4'
+ base: 'g'
+ shift, capslock: 'G'
+ alt: ']'
+ shift+alt, capslock+alt: ']'
+}
+
+key H {
+ label: 'H'
+ number: '4'
+ base: 'h'
+ shift, capslock: 'H'
+ alt: '<'
+ shift+alt, capslock+alt: '<'
+}
+
+key I {
+ label: 'I'
+ number: '4'
+ base: 'i'
+ shift, capslock: 'I'
+ alt: '-'
+ shift+alt, capslock+alt: '\u0302'
+}
+
+key J {
+ label: 'J'
+ number: '5'
+ base: 'j'
+ shift, capslock: 'J'
+ alt: '>'
+ shift+alt, capslock+alt: '>'
+}
+
+key K {
+ label: 'K'
+ number: '5'
+ base: 'k'
+ shift, capslock: 'K'
+ alt: ';'
+ shift+alt, capslock+alt: '~'
+}
+
+key L {
+ label: 'L'
+ number: '5'
+ base: 'l'
+ shift, capslock: 'L'
+ alt: ':'
+ shift+alt, capslock+alt: '`'
+}
+
+key M {
+ label: 'M'
+ number: '6'
+ base: 'm'
+ shift, capslock: 'M'
+ alt: '%'
+ shift+alt, capslock+alt: none
+}
+
+key N {
+ label: 'N'
+ number: '6'
+ base: 'n'
+ shift, capslock: 'N'
+ alt: none
+ shift+alt, capslock+alt: '\u0303'
+}
+
+key O {
+ label: 'O'
+ number: '6'
+ base: 'o'
+ shift, capslock: 'O'
+ alt: '+'
+ shift+alt, capslock+alt: '+'
+}
+
+key P {
+ label: 'P'
+ number: '7'
+ base: 'p'
+ shift, capslock: 'P'
+ alt: '='
+ shift+alt, capslock+alt: '\u00a5'
+}
+
+key Q {
+ label: 'Q'
+ number: '7'
+ base: 'q'
+ shift, capslock: 'Q'
+ alt: '|'
+ shift+alt, capslock+alt: '\u0300'
+}
+
+key R {
+ label: 'R'
+ number: '7'
+ base: 'r'
+ shift, capslock: 'R'
+ alt: '`'
+ shift+alt, capslock+alt: '\u20ac'
+}
+
+key S {
+ label: 'S'
+ number: '7'
+ base: 's'
+ shift, capslock: 'S'
+ alt: '\\'
+ shift+alt, capslock+alt: '\u00df'
+}
+
+key T {
+ label: 'T'
+ number: '8'
+ base: 't'
+ shift, capslock: 'T'
+ alt: '{'
+ shift+alt, capslock+alt: '\u00a3'
+}
+
+key U {
+ label: 'U'
+ number: '8'
+ base: 'u'
+ shift, capslock: 'U'
+ alt: '_'
+ shift+alt, capslock+alt: '\u0308'
+}
+
+key V {
+ label: 'V'
+ number: '8'
+ base: 'v'
+ shift, capslock: 'V'
+ alt: 'v'
+ shift+alt, capslock+alt: 'V'
+}
+
+key W {
+ label: 'W'
+ number: '9'
+ base: 'w'
+ shift, capslock: 'W'
+ alt: '~'
+ shift+alt, capslock+alt: '~'
+}
+
+key X {
+ label: 'X'
+ number: '9'
+ base: 'x'
+ shift, capslock: 'X'
+ alt: 'x'
+ shift+alt, capslock+alt: '\uef00'
+}
+
+key Y {
+ label: 'Y'
+ number: '9'
+ base: 'y'
+ shift, capslock: 'Y'
+ alt: '}'
+ shift+alt, capslock+alt: '\u00a1'
+}
+
+key Z {
+ label: 'Z'
+ number: '9'
+ base: 'z'
+ shift, capslock: 'Z'
+ alt: 'z'
+ shift+alt, capslock+alt: 'Z'
+}
+
+key COMMA {
+ label: ','
+ number: ','
+ base: ','
+ shift: '<'
+ alt: ','
+ shift+alt: ','
+}
+
+key PERIOD {
+ label: '.'
+ number: '.'
+ base: '.'
+ shift: '>'
+ alt: '.'
+ shift+alt: '\u2026'
+}
+
+key AT {
+ label: '@'
+ number: '@'
+ base: '@'
+ shift: '@'
+ alt: '@'
+ shift+alt: '\u2022'
+}
+
+key SLASH {
+ label: '/'
+ number: '/'
+ base: '/'
+ shift: '?'
+ alt: '?'
+ shift+alt: '?'
+}
+
+key SPACE {
+ label: ' '
+ number: ' '
+ base: ' '
+ shift: ' '
+ alt: '\uef01'
+ shift+alt: '\uef01'
+}
+
+key ENTER {
+ label: '\n'
+ number: '\n'
+ base: '\n'
+ shift: '\n'
+ alt: '\n'
+ shift+alt: '\n'
+}
+
+key TAB {
+ label: '\t'
+ number: '\t'
+ base: '\t'
+ shift: '\t'
+ alt: '\t'
+ shift+alt: '\t'
+}
+
+key 0 {
+ label: '0'
+ number: '0'
+ base: '0'
+ shift: ')'
+ alt: ')'
+ shift+alt: ')'
+}
+
+key 1 {
+ label: '1'
+ number: '1'
+ base: '1'
+ shift: '!'
+ alt: '!'
+ shift+alt: '!'
+}
+
+key 2 {
+ label: '2'
+ number: '2'
+ base: '2'
+ shift: '@'
+ alt: '@'
+ shift+alt: '@'
+}
+
+key 3 {
+ label: '3'
+ number: '3'
+ base: '3'
+ shift: '#'
+ alt: '#'
+ shift+alt: '#'
+}
+
+key 4 {
+ label: '4'
+ number: '4'
+ base: '4'
+ shift: '$'
+ alt: '$'
+ shift+alt: '$'
+}
+
+key 5 {
+ label: '5'
+ number: '5'
+ base: '5'
+ shift: '%'
+ alt: '%'
+ shift+alt: '%'
+}
+
+key 6 {
+ label: '6'
+ number: '6'
+ base: '6'
+ shift: '^'
+ alt: '^'
+ shift+alt: '^'
+}
+
+key 7 {
+ label: '7'
+ number: '7'
+ base: '7'
+ shift: '&'
+ alt: '&'
+ shift+alt: '&'
+}
+
+key 8 {
+ label: '8'
+ number: '8'
+ base: '8'
+ shift: '*'
+ alt: '*'
+ shift+alt: '*'
+}
+
+key 9 {
+ label: '9'
+ number: '9'
+ base: '9'
+ shift: '('
+ alt: '('
+ shift+alt: '('
+}
+
+key GRAVE {
+ label: '`'
+ number: '`'
+ base: '`'
+ shift: '~'
+ alt: '`'
+ shift+alt: '~'
+}
+
+key MINUS {
+ label: '-'
+ number: '-'
+ base: '-'
+ shift: '_'
+ alt: '-'
+ shift+alt: '_'
+}
+
+key EQUALS {
+ label: '='
+ number: '='
+ base: '='
+ shift: '+'
+ alt: '='
+ shift+alt: '+'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ number: '['
+ base: '['
+ shift: '{'
+ alt: '['
+ shift+alt: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ number: ']'
+ base: ']'
+ shift: '}'
+ alt: ']'
+ shift+alt: '}'
+}
+
+key BACKSLASH {
+ label: '\\'
+ number: '\\'
+ base: '\\'
+ shift: '|'
+ alt: '\\'
+ shift+alt: '|'
+}
+
+key SEMICOLON {
+ label: ';'
+ number: ';'
+ base: ';'
+ shift: ':'
+ alt: ';'
+ shift+alt: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ number: '\''
+ base: '\''
+ shift: '"'
+ alt: '\''
+ shift+alt: '"'
+}
+
+key STAR {
+ label: '*'
+ number: '*'
+ base: '*'
+ shift: '*'
+ alt: '*'
+ shift+alt: '*'
+}
+
+key POUND {
+ label: '#'
+ number: '#'
+ base: '#'
+ shift: '#'
+ alt: '#'
+ shift+alt: '#'
+}
+
+key PLUS {
+ label: '+'
+ number: '+'
+ base: '+'
+ shift: '+'
+ alt: '+'
+ shift+alt: '+'
+}
diff --git a/data/keyboards/qwerty2.kl b/data/keyboards/qwerty2.kl
new file mode 100644
index 0000000..863a258
--- /dev/null
+++ b/data/keyboards/qwerty2.kl
@@ -0,0 +1,109 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Emulator keyboard layout #2.
+#
+
+key 399 GRAVE
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 158 BACK WAKE_DROPPED
+key 230 SOFT_RIGHT WAKE
+key 60 SOFT_RIGHT WAKE
+key 107 ENDCALL WAKE_DROPPED
+key 62 ENDCALL WAKE_DROPPED
+key 229 MENU WAKE_DROPPED
+key 139 MENU WAKE_DROPPED
+key 59 MENU WAKE_DROPPED
+key 127 SEARCH WAKE_DROPPED
+key 217 SEARCH WAKE_DROPPED
+key 228 POUND
+key 227 STAR
+key 231 CALL WAKE_DROPPED
+key 61 CALL WAKE_DROPPED
+key 232 DPAD_CENTER WAKE_DROPPED
+key 108 DPAD_DOWN WAKE_DROPPED
+key 103 DPAD_UP WAKE_DROPPED
+key 102 HOME WAKE
+key 105 DPAD_LEFT WAKE_DROPPED
+key 106 DPAD_RIGHT WAKE_DROPPED
+key 115 VOLUME_UP WAKE
+key 114 VOLUME_DOWN WAKE
+key 116 POWER WAKE
+key 212 CAMERA
+
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+key 26 LEFT_BRACKET
+key 27 RIGHT_BRACKET
+key 43 BACKSLASH
+
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 39 SEMICOLON
+key 40 APOSTROPHE
+key 14 DEL
+
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 53 SLASH
+key 28 ENTER
+
+key 56 ALT_LEFT
+key 100 ALT_RIGHT
+key 42 SHIFT_LEFT
+key 54 SHIFT_RIGHT
+key 15 TAB
+key 57 SPACE
+key 150 EXPLORER
+key 155 ENVELOPE
+
+key 12 MINUS
+key 13 EQUALS
+key 215 AT
+
+# On an AT keyboard: ESC, F10
+key 1 BACK WAKE_DROPPED
+key 68 MENU WAKE_DROPPED
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
new file mode 100755
index 0000000..550f990
--- /dev/null
+++ b/data/sounds/AudioPackage5.mk
@@ -0,0 +1,72 @@
+#
+# Audio Package 5 - Crespo/Soju
+#
+# Include this file in a product makefile to include these audio files
+#
+#
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+ $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
+ $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
+ $(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+ $(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
+ $(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+ $(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
+ $(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+ $(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+ $(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
+ $(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
+ $(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
+ $(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
+ $(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+ $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
+ $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
+ $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
+ $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
+ $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+ $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
+ $(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
+ $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
+ $(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
+ $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
+ $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
+ $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
+ $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
+ $(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
+ $(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
+ $(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
+ $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
+ $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
+ $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
+ $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
+ $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
+ $(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
+ $(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
+ $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
+ $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
+ $(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
+ $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
+ $(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
+ $(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
+ $(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+ $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
+ $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
+ $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
+ $(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
+ $(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
+ $(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
+ $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
+ $(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
+ $(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
+ $(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
+ $(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+ $(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
+ $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+ $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
+ $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/notifications/Aldebaran.ogg b/data/sounds/notifications/Aldebaran.ogg
new file mode 100755
index 0000000..fe30137
--- /dev/null
+++ b/data/sounds/notifications/Aldebaran.ogg
Binary files differ
diff --git a/data/sounds/notifications/Altair.ogg b/data/sounds/notifications/Altair.ogg
new file mode 100644
index 0000000..660c800
--- /dev/null
+++ b/data/sounds/notifications/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/Antares.ogg b/data/sounds/notifications/Antares.ogg
new file mode 100755
index 0000000..f4f94d7
--- /dev/null
+++ b/data/sounds/notifications/Antares.ogg
Binary files differ
diff --git a/data/sounds/notifications/Betelgeuse.ogg b/data/sounds/notifications/Betelgeuse.ogg
new file mode 100755
index 0000000..7145a16
--- /dev/null
+++ b/data/sounds/notifications/Betelgeuse.ogg
Binary files differ
diff --git a/data/sounds/notifications/Canopus.ogg b/data/sounds/notifications/Canopus.ogg
new file mode 100755
index 0000000..0d55925
--- /dev/null
+++ b/data/sounds/notifications/Canopus.ogg
Binary files differ
diff --git a/data/sounds/notifications/Capella.ogg b/data/sounds/notifications/Capella.ogg
new file mode 100644
index 0000000..ae4f3c5
--- /dev/null
+++ b/data/sounds/notifications/Capella.ogg
Binary files differ
diff --git a/data/sounds/notifications/Castor.ogg b/data/sounds/notifications/Castor.ogg
new file mode 100644
index 0000000..92de8e7
--- /dev/null
+++ b/data/sounds/notifications/Castor.ogg
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.ogg b/data/sounds/notifications/CetiAlpha.ogg
new file mode 100755
index 0000000..cd09526
--- /dev/null
+++ b/data/sounds/notifications/CetiAlpha.ogg
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.wav b/data/sounds/notifications/CetiAlpha.wav
new file mode 100755
index 0000000..d209645
--- /dev/null
+++ b/data/sounds/notifications/CetiAlpha.wav
Binary files differ
diff --git a/data/sounds/notifications/Deneb.ogg b/data/sounds/notifications/Deneb.ogg
new file mode 100644
index 0000000..3b17e28
--- /dev/null
+++ b/data/sounds/notifications/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/Electra.ogg b/data/sounds/notifications/Electra.ogg
new file mode 100644
index 0000000..9335d8d
--- /dev/null
+++ b/data/sounds/notifications/Electra.ogg
Binary files differ
diff --git a/data/sounds/notifications/Fomalhaut.ogg b/data/sounds/notifications/Fomalhaut.ogg
new file mode 100644
index 0000000..9448c18
--- /dev/null
+++ b/data/sounds/notifications/Fomalhaut.ogg
Binary files differ
diff --git a/data/sounds/notifications/Merope.ogg b/data/sounds/notifications/Merope.ogg
new file mode 100755
index 0000000..fb18c73
--- /dev/null
+++ b/data/sounds/notifications/Merope.ogg
Binary files differ
diff --git a/data/sounds/notifications/Polaris.ogg b/data/sounds/notifications/Polaris.ogg
new file mode 100644
index 0000000..d5b991f
--- /dev/null
+++ b/data/sounds/notifications/Polaris.ogg
Binary files differ
diff --git a/data/sounds/notifications/Pollux.ogg b/data/sounds/notifications/Pollux.ogg
new file mode 100644
index 0000000..d37c75c
--- /dev/null
+++ b/data/sounds/notifications/Pollux.ogg
Binary files differ
diff --git a/data/sounds/notifications/Procyon.ogg b/data/sounds/notifications/Procyon.ogg
new file mode 100644
index 0000000..93d1557
--- /dev/null
+++ b/data/sounds/notifications/Procyon.ogg
Binary files differ
diff --git a/data/sounds/notifications/Sirrah.ogg b/data/sounds/notifications/Sirrah.ogg
new file mode 100755
index 0000000..ee79e49
--- /dev/null
+++ b/data/sounds/notifications/Sirrah.ogg
Binary files differ
diff --git a/data/sounds/notifications/arcturus.ogg b/data/sounds/notifications/arcturus.ogg
new file mode 100644
index 0000000..9d73103
--- /dev/null
+++ b/data/sounds/notifications/arcturus.ogg
Binary files differ
diff --git a/data/sounds/notifications/regulus.ogg b/data/sounds/notifications/regulus.ogg
new file mode 100644
index 0000000..4f28d9e
--- /dev/null
+++ b/data/sounds/notifications/regulus.ogg
Binary files differ
diff --git a/data/sounds/notifications/sirius.ogg b/data/sounds/notifications/sirius.ogg
new file mode 100644
index 0000000..78c9991
--- /dev/null
+++ b/data/sounds/notifications/sirius.ogg
Binary files differ
diff --git a/data/sounds/notifications/vega.ogg b/data/sounds/notifications/vega.ogg
new file mode 100644
index 0000000..e596e60
--- /dev/null
+++ b/data/sounds/notifications/vega.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ANDROMEDA.ogg b/data/sounds/ringtones/ANDROMEDA.ogg
new file mode 100644
index 0000000..8f6bd3e
--- /dev/null
+++ b/data/sounds/ringtones/ANDROMEDA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Aquila.ogg b/data/sounds/ringtones/Aquila.ogg
new file mode 100644
index 0000000..b391be9
--- /dev/null
+++ b/data/sounds/ringtones/Aquila.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ArgoNavis.ogg b/data/sounds/ringtones/ArgoNavis.ogg
new file mode 100644
index 0000000..b4202ac
--- /dev/null
+++ b/data/sounds/ringtones/ArgoNavis.ogg
Binary files differ
diff --git a/data/sounds/ringtones/BOOTES.ogg b/data/sounds/ringtones/BOOTES.ogg
new file mode 100644
index 0000000..0716a4f
--- /dev/null
+++ b/data/sounds/ringtones/BOOTES.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CANISMAJOR.ogg b/data/sounds/ringtones/CANISMAJOR.ogg
new file mode 100644
index 0000000..177d3de
--- /dev/null
+++ b/data/sounds/ringtones/CANISMAJOR.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CASSIOPEIA.ogg b/data/sounds/ringtones/CASSIOPEIA.ogg
new file mode 100644
index 0000000..c4a7948
--- /dev/null
+++ b/data/sounds/ringtones/CASSIOPEIA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Carina.ogg b/data/sounds/ringtones/Carina.ogg
new file mode 100644
index 0000000..aeb9b36
--- /dev/null
+++ b/data/sounds/ringtones/Carina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Carina.wav b/data/sounds/ringtones/Carina.wav
new file mode 100755
index 0000000..ecaeb58
--- /dev/null
+++ b/data/sounds/ringtones/Carina.wav
Binary files differ
diff --git a/data/sounds/ringtones/Centaurus.ogg b/data/sounds/ringtones/Centaurus.ogg
new file mode 100644
index 0000000..404bdbc
--- /dev/null
+++ b/data/sounds/ringtones/Centaurus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Cygnus.ogg b/data/sounds/ringtones/Cygnus.ogg
new file mode 100644
index 0000000..b2e1e65
--- /dev/null
+++ b/data/sounds/ringtones/Cygnus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Draco.ogg b/data/sounds/ringtones/Draco.ogg
new file mode 100644
index 0000000..88d5a04
--- /dev/null
+++ b/data/sounds/ringtones/Draco.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Eridani.ogg b/data/sounds/ringtones/Eridani.ogg
new file mode 100644
index 0000000..b665a29
--- /dev/null
+++ b/data/sounds/ringtones/Eridani.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Lyra.ogg b/data/sounds/ringtones/Lyra.ogg
new file mode 100644
index 0000000..696f278
--- /dev/null
+++ b/data/sounds/ringtones/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Machina.ogg b/data/sounds/ringtones/Machina.ogg
new file mode 100644
index 0000000..ac16f7e
--- /dev/null
+++ b/data/sounds/ringtones/Machina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Orion.ogg b/data/sounds/ringtones/Orion.ogg
new file mode 100644
index 0000000..807f107
--- /dev/null
+++ b/data/sounds/ringtones/Orion.ogg
Binary files differ
diff --git a/data/sounds/ringtones/PERSEUS.ogg b/data/sounds/ringtones/PERSEUS.ogg
new file mode 100644
index 0000000..ad06021
--- /dev/null
+++ b/data/sounds/ringtones/PERSEUS.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Pegasus.ogg b/data/sounds/ringtones/Pegasus.ogg
new file mode 100644
index 0000000..66c4970
--- /dev/null
+++ b/data/sounds/ringtones/Pegasus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Pyxis.ogg b/data/sounds/ringtones/Pyxis.ogg
new file mode 100644
index 0000000..2d3adce
--- /dev/null
+++ b/data/sounds/ringtones/Pyxis.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Rigel.ogg b/data/sounds/ringtones/Rigel.ogg
new file mode 100644
index 0000000..af2c176
--- /dev/null
+++ b/data/sounds/ringtones/Rigel.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Scarabaeus.ogg b/data/sounds/ringtones/Scarabaeus.ogg
new file mode 100644
index 0000000..e70fc69
--- /dev/null
+++ b/data/sounds/ringtones/Scarabaeus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Sceptrum.ogg b/data/sounds/ringtones/Sceptrum.ogg
new file mode 100644
index 0000000..fc50aef
--- /dev/null
+++ b/data/sounds/ringtones/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Solarium.ogg b/data/sounds/ringtones/Solarium.ogg
new file mode 100644
index 0000000..d27f141
--- /dev/null
+++ b/data/sounds/ringtones/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Testudo.ogg b/data/sounds/ringtones/Testudo.ogg
new file mode 100644
index 0000000..0ca8d6b
--- /dev/null
+++ b/data/sounds/ringtones/Testudo.ogg
Binary files differ
diff --git a/data/sounds/ringtones/URSAMINOR.ogg b/data/sounds/ringtones/URSAMINOR.ogg
new file mode 100644
index 0000000..c0010e82
--- /dev/null
+++ b/data/sounds/ringtones/URSAMINOR.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Vespa.ogg b/data/sounds/ringtones/Vespa.ogg
new file mode 100644
index 0000000..4423bbb
--- /dev/null
+++ b/data/sounds/ringtones/Vespa.ogg
Binary files differ
diff --git a/data/sounds/ringtones/hydra.ogg b/data/sounds/ringtones/hydra.ogg
new file mode 100644
index 0000000..edde14f
--- /dev/null
+++ b/data/sounds/ringtones/hydra.ogg
Binary files differ
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 3cc1806..2548128 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -71,6 +71,9 @@
<li><a href="<?cs var:toroot ?>guide/topics/ui/menus.html">
<span class="en">Creating Menus</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/actionbar.html">
+ <span class="en">Using the Action Bar</span>
+ </a> <span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/dialogs.html">
<span class="en">Creating Dialogs</span>
</a></li>
diff --git a/docs/html/guide/topics/fragments/index.jd b/docs/html/guide/topics/fragments/index.jd
index ce10ef7..766146e 100644
--- a/docs/html/guide/topics/fragments/index.jd
+++ b/docs/html/guide/topics/fragments/index.jd
@@ -399,7 +399,7 @@ in the back stack:</p>
Fragment newFragment = new MyFragment();
FragmentTransaction ft = openFragmentTransaction();
// Replace and add to back stack
-ft.replace(newFragment, R.id.myfragment);
+ft.replace(R.id.myfragment, newFragment);
ft.addToBackStack(null);
// Apply changes
ft.commit();
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 84c2ed2..fffc1cd 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -19,6 +19,7 @@ page.title=Application Fundamentals
<li><a href="#lmodes">Launch modes</a></li>
<li><a href="#clearstack">Clearing the stack</a></li>
<li><a href="#starttask">Starting tasks</a></li>
+ <li><a href="#commonpatterns">Common patterns</a></li>
</ol></li>
<li><a href="#procthread">Processes and Threads</a>
<ol>
@@ -892,6 +893,60 @@ See <a href="#clearstack">Clearing the stack</a>, earlier.
</p>
+<h3 id="commonpatterns">Common patterns</h3>
+
+<p>
+In most cases an application won't use any flags or special features.
+This gives the standard interaction the user expects: launching the application
+brings any existing task to the foreground, or starts the main activity in
+a new task if there isn't one.
+</p>
+
+<p>
+If an application posts notifications, it needs to decide how the user's
+selection of a notification should impact any currently running task. The
+current suggested behavior is that any current tasks be completely removed,
+replaced with a new task containing a stack of activities representing where
+the user is jumping in to the app. This can be accomplished with a combination
+of the {@link android.app.PendingIntent#getActivities PendingIntent.getActivities()}
+method and {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_CLEAR_TASK}.
+</p>
+
+<p>
+For example, here is sample code to build an array of Intents to launch an
+application into an activity three levels deep. The first Intent is always
+the main Intent of the application as started by the launcher. The exact
+details of the Intent data will of course depend on your application.
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/StatusBarNotifications.java
+ intent_array}
+
+<p>
+In some cases an application may not want to directly launch its application
+from a notification, but instead go to a intermediate summary activity. To
+accomplish this, the summary activity should be given a task affinity that
+is different from the main application (one will typically give it no affinity,
+that is "") so that it does not get launched into any existing application task.
+</p>
+
+{@sample development/samples/ApiDemos/AndroidManifest.xml no_task_affinity}
+
+<p>
+The PendingIntent to launch this then does not need to supply anything special:
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessage.java
+ pending_intent}
+
+<p>
+If an application implements an app widget, it should generally use the same
+approach as the first one for notifications: when the user taps on the app
+widget it should throw away any current task of the application and start a
+new task with potentially multiple activities representing the state the
+user is jumping in to.
+</p>
+
<h2 id="procthread">Processes and Threads</h2>
<p>
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
new file mode 100644
index 0000000..2b942e7
--- /dev/null
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -0,0 +1,332 @@
+page.title=Using the Action Bar
+parent.title=User Interface
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Quickview</h2>
+ <ul>
+ <li>A replacement for the title bar for displaying global actions for the activity</li>
+ <li>Provides toolbar actions and modes of navigating around the application</li>
+ <li>Switches to contextual menu options when one or more items are selected</li>
+ <li>Requires API Level HONEYCOMB</li>
+ </ul>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#Adding">Adding the Action Bar</a></li>
+ <li><a href="#ActionItems">Adding Action Items</a>
+ <ol>
+ <li><a href="#Home">Using the application icon as an action item</a></li>
+ </ol>
+ </li>
+ <li><a href="#Tabs">Adding Tabs</a></li>
+ <li><a href="#Dropdown">Adding Drop-down Navigation</a></li>
+ <li><a href="#Search">Adding Search</a></li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.app.ActionBar}</li>
+ <li>{@link android.view.Menu}</li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a></li>
+ </ol>
+</div>
+</div>
+
+<p>The action bar is a widget for activities that replaces the traditional title bar at
+the top of an activity. By default, the action bar includes the application logo on the left side,
+followed by the activity title. The action bar offers several useful features for
+applications&mdash;especially those targeted to tablet devices. The action bar features include
+the ability to:</p>
+
+<ul>
+ <li>Display menu items from the <a
+href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> as "action
+items"&mdash;providing instant access to key user actions.</li>
+ <li>Provide tabs for navigating between <a
+href="{@docRoot}guide/topics/fragments/index.html">fragments</a>.</li>
+ <li>Provide drop-down navigation items.</li>
+ <li>Embed a {@link android.widget.SearchView} for instant searching.</li>
+ <li>Use the application logo as a "return home" or "up" navigation action.</li>
+</ul>
+
+<img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> A screenshot of the action bar in the NotePad
+sample application, containing action items to save and delete the note.</p>
+
+
+<h2 id="Adding">Adding the Action Bar</h2>
+
+<p>To add the Action Bar to your activity, apply the holographic theme&mdash;{@code
+Theme.Holo}&mdash;or the action bar theme&mdash;{@code Theme.WithActionBar}&mdash;in your manifest
+file. For example:</p>
+
+<pre>
+&lt;activity android:theme="@android:style/Theme.Holo" &gt;
+</pre>
+
+<p>The activity now appears with the action bar in place of the traditional title bar.</p>
+
+
+<h2 id="ActionItems">Adding Action Items</h2>
+
+<p>For each action item you want to add to the action bar, you must add a menu item to the
+activity's <a href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> and declare
+that the item be shown as an action, using the {@code android:showAsAction} attribute in the menu
+XML or with {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link
+android.view.MenuItem}.</p>
+
+<div class="figure" style="width:359px">
+ <img src="{@docRoot}images/ui/actionbar-item-withtext.png" height="57" alt="" />
+ <p class="img-caption"><strong>Figure 2.</strong> A screenshot from an action bar with two
+action items.</p>
+</div>
+
+<p>You can specify a menu item to appear as an action item in the action bar&mdash;if there is room
+for it&mdash;from the <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu
+resource</a> by declaring {@code
+android:showAsAction="ifRoom"} for the {@code &lt;item&gt;} element. This way, the item will display
+in the action bar for quick access only if there is room available for it&mdash;if there's not
+enough room, the item is placed the options menu (revealed by the drop-down icon on the right side
+of the action bar). From your application code, you can specify the item to appear as an action item
+by calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link
+android.view.MenuItem} and passing {@link android.view.MenuItem#SHOW_AS_ACTION_IF_ROOM}.</p>
+
+<p>If your item supplies both a title and an icon, then the action item shows only
+the icon by defult. If you want to include the text with the action item, add the <em>with text</em>
+flag&mdash;in XML, add {@code withText} to the {@code android:showAsAction} attribute or, in
+your application code, use the {@link android.view.MenuItem#SHOW_AS_ACTION_WITH_TEXT} flag when
+calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()}. Figure 2 shows a screenshot
+of an action bar with two action items that include text.</p>
+
+<p>Here's an example of how you can declare a menu item as an action item in a <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> file:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
+ &lt;item android:id="@+id/menu_add"
+ android:icon="@drawable/ic_menu_save"
+ android:title="@string/menu_save"
+ <b>android:showAsAction="ifRoom|withText"</b> /&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>In this case, both the {@code ifRoom} and {@code withText} flags are set, so that when this
+item appears as an action item, it includes the title text along with the icon.</p>
+
+<p>A menu item placed in the action bar triggers the same callback methods as other items in the
+options menu. When the user selects an item in the action bar, your activity receives a call to
+{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, passing the
+item ID. (If you added the item from a fragment, then the respective {@link
+android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method is called
+for that fragment.)</p>
+
+<p class="note"><strong>Note:</strong> Even menu items that are contained in the options menu
+(and not shown as action items) will show an icon, so when using the action bar, you should
+provide an icon for every item in the options menu.</p>
+
+<p>You can also declare an item to <em>always</em> appear as an action item, but you should avoid
+doing so. Most of the time, there will be enough room for several action items and they will appear
+in the order you declare them. If you set items to always appear as action
+items (instead of <em>if room</em>), then they are added without discrimination and there is a risk
+that they will collide with other elements in the action bar, such as tabs or custom views.</p>
+
+<p>For more information about menus, see the <a
+href="{@docRoot}guide/topics/ui/menus.html#options-menu">Creating Menus</a> developer guide.</p>
+
+
+<h3 id="Home">Using the application icon as an action item</h3>
+
+<p>By default, the application icon appears in the action bar on the left side, but does nothing
+when tapped. To use the application icon as an action item when tapped, you simply need to add a
+condition to your {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method
+that performs an action when the {@link android.view.MenuItem} ID is {@code android.R.id.home}.
+This ID is delivered every time the user taps the application icon.</p>
+
+<p>For example, here's an implementation of {@link android.app.Activity#onOptionsItemSelected
+onOptionsItemSelected()} that returns to the application's "home" activity:</p>
+
+<pre>
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // app icon in action bar clicked; go home
+ Intent intent = new Intent(this, HomeActivity.class);
+ startActivity(intent);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<div class="figure" style="width:144px">
+ <img src="{@docRoot}images/ui/actionbar-logo.png" height="140" alt="" />
+ <p class="img-caption"><strong>Figure 3.</strong> The standard icon for the Email application
+(top) and the "up navigation" icon (bottom).</p>
+</div>
+
+<p>You can also use the application icon to provide "up" navigation. The way you handle the event
+when a user taps the icon is the same, but if the user experience for the event is to <em>navigate
+up to the parent activity</em>, then you should indicate this behavior by setting the
+action bar to "show home as up." You can do so by calling {@link
+android.app.ActionBar#setDisplayOptions setDisplayOptions()} on your activity's {@link
+android.app.ActionBar}, and passing the {@link
+android.app.ActionBar#DISPLAY_HOME_AS_UP} display option.</p>
+
+<p>To get the {@link android.app.ActionBar}, call {@link android.app.Activity#getActionBar} from
+your {@link android.app.Activity} during {@link android.app.Activity#onCreate onCreate()} (but be
+sure you do so <em>after</em> you've called {@link android.app.Activity#setContentView
+setContentView()}).</p>
+
+<p>For example, here's how you can change the action bar display mode to show the application
+icon as an "up" action:</p>
+
+<pre>
+&#64;Override
+protected void onStart() {
+ super.onStart();
+ ActionBar actionBar = this.getActionBar();
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+}
+</pre>
+
+<p class="caution"><strong>Caution:</strong> If your activity does not have an action bar (if you
+did not set the theme of your activity or application to the holographic or action bar theme), then
+{@link android.app.Activity#getActionBar} returns null.</p>
+
+
+<h2 id="Tabs">Adding Tabs</h2>
+
+<p>The action bar can display tabs that allow the user navigate between different fragments in the
+activity. Each tab can include a title and/or an icon.</p>
+
+<p>To begin, your layout must include a {@link android.view.View} in which each {@link
+android.app.Fragment} associated with a tab is displayed. Be sure the view has an ID that you
+can use to reference it from your code.</p>
+
+<p>To add tabs to the action bar:</p>
+<ol>
+ <li>Create an implementation of {@link android.app.ActionBar.TabListener} to handle the
+interaction events on the action bar tabs. You must implement all methods: {@link
+android.app.ActionBar.TabListener#onTabSelected onTabSelected()}, {@link
+android.app.ActionBar.TabListener#onTabUnselected onTabUnselected()}, and {@link
+android.app.ActionBar.TabListener#onTabReselected onTabReselected()}.
+ <p>Each callback method passes the {@link android.app.ActionBar.Tab} that received the
+event and a {@link android.app.FragmentTransaction} for you to perform the fragment
+transactions (add or remove fragments).</p>
+ <p>For example:</p>
+<pre>
+private class MyTabListener implements ActionBar.TabListener {
+ private TabContentFragment mFragment;
+
+ // Called to create an instance of the listener when adding a new tab
+ public TabListener(TabContentFragment fragment) {
+ mFragment = fragment;
+ }
+
+ &#64;Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ ft.add(R.id.fragment_content, mFragment, null);
+ }
+
+ &#64;Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ ft.remove(mFragment);
+ }
+
+ &#64;Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ // do nothing
+ }
+
+}
+</pre>
+ <p>This implementation of {@link android.app.ActionBar.TabListener} adds a constructor
+that saves the {@link android.app.Fragment} associated with a tab so that each callback
+can add or remove that fragment.</p>
+ </li>
+ <li>Get the {@link android.app.ActionBar} for your activity by calling {@link
+android.app.Activity#getActionBar} from your {@link android.app.Activity}, during {@link
+android.app.Activity#onCreate onCreate()} (but be sure you do so <em>after</em> you've called
+{@link android.app.Activity#setContentView setContentView()}).</li>
+ <li>Call {@link android.app.ActionBar#setNavigationMode(int)
+setNavigationMode(NAVIGATION_MODE_TABS)} to enable tab mode for the {@link
+android.app.ActionBar}.</li>
+ <li>Create each tab for the action bar:
+ <ol>
+ <li>Create a new {@link android.app.ActionBar.Tab} by calling {@link
+android.app.ActionBar#newTab()} on the {@link android.app.ActionBar}.</li>
+ <li>Add title text and/or an icon for the tab by calling {@link
+android.app.ActionBar.Tab#setText setText()} and/or {@link android.app.ActionBar.Tab#setIcon
+setIcon()}.
+ <p class="note"><strong>Tip:</strong> These methods return the same {@link
+android.app.ActionBar.Tab} instance, so you can chain the calls together.</p></li>
+ <li>Declare the {@link android.app.ActionBar.TabListener} to use for the tab by passing an
+instance of your implementation to {@link android.app.ActionBar.Tab#setTabListener
+setTabListener()}.
+ </ol>
+ </li>
+ <li>Add each {@link android.app.ActionBar.Tab} to the action bar by calling {@link
+android.app.ActionBar#addTab addTab()} on the {@link android.app.ActionBar} and passing the
+{@link android.app.ActionBar.Tab}.</li>
+</ol>
+<p>For example, the following code combines steps 2 - 5 to create two tabs and add them to
+the action bar:</p>
+<pre>
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // setup action bar for tabs
+ final ActionBar actionBar = getActionBar();
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ // remove the activity title to make space for tabs
+ actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
+
+ // instantiate fragment for the tab
+ Fragment artistsFragment = new ArtistsFragment();
+ // add a new tab and set its title text and tab listener
+ bar.addTab(bar.newTab().setText(R.string.tab_artists)
+ .setTabListener(new TabListener(artistsFragment)));
+
+ Fragment albumsFragment = new AlbumsFragment();
+ bar.addTab(bar.newTab().setText(R.string.tab_albums)
+ .setTabListener(new TabListener(albumsFragment)));
+}
+</pre>
+
+<p>All the behaviors that occur when a tab is selected must be defined by your {@link
+android.app.ActionBar.TabListener} callback methods. When a tab is selected, it receives a call to
+{@link android.app.ActionBar.TabListener#onTabSelected onTabSelected()} and that's where you should
+add the appropriate fragment to the designated view in your layout, using {@link
+android.app.FragmentTransaction#add add()} with the provided {@link
+android.app.FragmentTransaction}. Likewise, when a tab is deselected (because another tab becomes
+selected), you should remove that fragment from the layout, using {@link
+android.app.FragmentTransaction#remove remove()}.</p>
+
+<p class="note"><strong>Note:</strong> You <strong>do not</strong> need
+to call {@link android.app.FragmentTransaction#commit} for these transactions. You also
+<strong>cannot</strong> add these fragment transactions to the back stack.</p>
+
+<p>If your activity is stopped, you should retain the currently selected tab with the saved state so
+that when the user returns to your application, you can open the tab. When it's time to save the
+state, you can query the currently selected tab with {@link
+android.app.ActionBar#getSelectedNavigationItem()}. This returns the index position of the selected
+tab.</p>
+
+<p class="caution"><strong>Caution:</strong> It's important that you save
+the state of each fragment as necessary, so when the user switches fragments with the tabs,
+then returns to a previous fragment, it appears the way they left. For information about saving
+the state of your fragment, see the <a
+href="{@docRoot}guide/topics/fragments/index.html">Fragments</a> developer guide.</p>
+
+
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index fe641a2..4d71d28 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -191,10 +191,12 @@ layout parameters for the View that are appropriate for the ViewGroup in which i
<p>Every ViewGroup class implements a nested class that extends {@link
android.view.ViewGroup.LayoutParams}. This subclass
contains property types that define the size and position for each child view, as
-appropriate for the view group. As you can see in the figure below, the parent
+appropriate for the view group. As you can see in figure 1, the parent
view group defines layout parameters for each child view (including the child view group).</p>
<img src="{@docRoot}images/layoutparams.png" alt="" height="300" align="center"/>
+<p class="img-caption"><strong>Figure 1.</strong> Visualization of a view hierarchy with layout
+parameters associated with each view.</p>
<p>Note that every LayoutParams subclass has its own syntax for setting
values. Each child element must define LayoutParams that are appropriate for its parent,
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 879eb8b..d50e1cb 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -22,20 +22,32 @@ parent.link=index.html
</li>
<li><a href="#CustomDialog">Creating a Custom Dialog</a></li>
</ol>
-
+
<h2>Key classes</h2>
<ol>
<li>{@link android.app.Dialog}</li>
+ <li>{@link android.app.AlertDialog}</li>
+ <li>{@link android.app.DialogFragment}</li>
+ </ol>
+
+ <h2>Related tutorials</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Hello
+DatePicker</a></li>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Hello
+TimePicker</a></li>
</ol>
</div>
</div>
<p>A dialog is usually a small window that appears in front of the current Activity.
-The underlying Activity loses focus and the dialog accepts all user interaction.
-Dialogs are normally used
-for notifications and short activities that directly relate to the application in progress.</p>
+The underlying Activity loses focus and the dialog accepts all user interaction. Dialogs are
+normally used for notifications that should interupt the user and to perform short tasks that
+directly relate to the application in progress (such as a progress bar or a login prompt).</p>
-<p>The Android API supports the following types of {@link android.app.Dialog} objects:</p>
+<p>The {@link android.app.Dialog} class is the base class for creating dialogs. However, you
+typically should not instantiate a {@link android.app.Dialog} directly. Instead, you should use one
+of the following subclasses:</p>
<dl>
<dt>{@link android.app.AlertDialog}</dt>
<dd>A dialog that can manage zero, one, two, or three buttons, and/or a list of
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index b4e467c..ce25b9f 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -36,6 +36,7 @@ parent.link=index.html
<h2>See also</h2>
<ol>
+ <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a></li>
<li><a href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a></li>
</ol>
</div>
@@ -48,20 +49,9 @@ for you to provide application menus in your application.</p>
<p>Android provides three types of application menus:</p>
<dl>
<dt><strong>Options Menu</strong></dt>
- <dd>The primary menu for an Activity, which appears when the user presses
- the device MENU key. Within the Options Menu are two groups:
- <dl style="margin-top:1em">
- <dt><em>Icon Menu</em></dt>
- <dd>The menu items visible at the bottom of the screen
- at the press of the MENU key. It supports a maximum of six menu items.
- These are the only menu items that support icons and the only menu items that <em>do not</em> support
- checkboxes or radio buttons.</dd>
- <dt><em>Expanded Menu</em></dt>
- <dd>The vertical list of menu items exposed by the "More" menu item in the Icon Menu.
- When the Icon Menu is full, the expanded menu is comprised of the sixth
- menu item and the rest.</dd>
- </dl>
- </dd>
+ <dd>The primary collection of menu items for an Activity that is associated with the device MENU
+key. To provide instant access to select menu items, you can place some items in the <a
+href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a>, if available.</dd>
<dt><strong>Context Menu</strong></dt>
<dd>A floating list of menu items that appears when the user performs a long-press on a View.
</dd>
@@ -74,7 +64,7 @@ Menu or a context menu. A submenu item cannot support a nested submenu. </dd>
<h2 id="xml">Defining Menus</h2>
-<p>Instead of instantiating {@link android.view.Menu} objects in your application code, you should
+<p>Instead of instantiating a {@link android.view.Menu} in your application code, you should
define a menu and all its items in an XML <a
href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, then inflate the menu
resource (load it as a programmable object) in your application code. Defining your menus in XML is
@@ -104,9 +94,9 @@ href="#groups">Menu groups</a>.</dd>
&lt;item android:id="@+id/new_game"
android:icon="@drawable/ic_new_game"
android:title="@string/new_game" /&gt;
- &lt;item android:id="@+id/quit"
- android:icon="@drawable/ic_quit"
- android:title="@string/quit" /&gt;
+ &lt;item android:id="@+id/help"
+ android:icon="@drawable/ic_help"
+ android:title="@string/help" /&gt;
&lt;/menu&gt;
</pre>
@@ -161,36 +151,64 @@ creating an option menu is discussed more in the next section.)</p>
</div>
-<p>The Options Menu is where you should include basic application functions
-and necessary navigation items (for example, a button
-to open application settings). The user
-can open the Options Menu with the device MENU key.
-Figure 1 shows a screenshot of an Options Menu.</p>
+<p>The Options Menu is where you should include basic application functions and necessary navigation
+items (for example, a button to open the application settings). Items in the Options Menu are
+accessible in two distinct ways: in the Action Bar and in the menu revealed by the MENU
+key.</p>
+
+<p>The Action Bar is an optional widget that appears at the top of the activity in place of the
+title bar. It can display several menu items that you choose from the Options Menu, but items in
+the Action Bar display only an icon (no title text). Users can reveal the other menu items in the
+Options Menu with the MENU key.</p>
-<p>When opened, the first visible portion of the Options Menu is called the Icon Menu. It
-holds the first six menu items.
-If you add more than six items to the Options Menu, Android places the sixth item and those after it
-into the Expanded Menu, which the user can open with the "More" menu item.</p>
+<p>If you include the Action Bar in your activity, the menu items that are not placed in the Action
+Bar can appear in two different styles:</p>
+<dl>
+ <dt>Action Bar Menu</dt>
+ <dd>If the device has an extra-large screen ({@code xlarge}), then all items in the Options Menu
+that are not placed in the Action Bar are placed into a drop-down list at the right side of the
+Action Bar, with icons and title text. The user can reveal the drop-down list by pressing the
+drop-down icon in the Action Bar or the MENU key.</dd>
+ <dt>Standard Options Menu</dt>
+ <dd>If the device <em>does not</em> have an extra-large screen, then all items in the Options
+Menu that are not placed in the Action Bar are placed into the Standard Options Menu at the bottom
+of the activity. The user can reveal the standard Options Menu by pressing the MENU key.
+ <p>The first visible portion of the Standard Options Menu is called the Icon Menu.
+It holds the first six menu items (excluding any added to the Action Bar), with icons and title
+text. If there are more than six items, Android adds a "More" item as the sixth menu item and places
+the remaining items into the Expanded Menu, which the user can open by selecting "More". The
+Expanded Menu displays menu items only by their title text (no icon)</p>
+ </dd>
+</dl>
<p>When the user opens the Options Menu for the first time, Android calls your Activity's
{@link android.app.Activity#onCreateOptionsMenu(Menu)
onCreateOptionsMenu()} method. Override this method in your Activity
and populate the {@link android.view.Menu} that is passed into the method. Populate the
{@link android.view.Menu} by inflating a menu resource as described in <a
-href="#Inflating">Inflating a Menu Resource</a>. (You can
-also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
-add()} to add menu items.)</p>
+href="#Inflating">Inflating a Menu Resource</a>. For example:</p>
-<p>When the user selects a menu item from the Options Menu, the system calls your Activity's
+<pre>
+&#64;Override
+public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.game_menu, menu);
+ return true;
+}
+</pre>
+
+<p>(You can also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
+add()} to add items to the {@link android.view.Menu}.)</p>
+
+<p>When the user selects a menu item from the Options Menu (including items selected from the
+Action Bar), the system calls your Activity's
{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}
method. This method passes the
{@link android.view.MenuItem} that the user selected. You can identify the menu item by calling
{@link android.view.MenuItem#getItemId()}, which returns the unique ID for the menu
item (defined by the {@code android:id} attribute in the menu resource or with an integer passed
to the {@link android.view.Menu#add(int,int,int,int) add()} method). You can match this ID
-against known menu items and perform the appropriate action.</p>
-
-<p>For example:</p>
+against known menu items and perform the appropriate action. For example:</p>
<pre>
&#64;Override
@@ -200,8 +218,8 @@ public boolean onOptionsItemSelected(MenuItem item) {
case R.id.new_game:
newGame();
return true;
- case R.id.quit:
- quit();
+ case R.id.help:
+ showHelp();
return true;
default:
return super.onOptionsItemSelected(item);
@@ -224,8 +242,8 @@ an Activity that implements nothing except the {@link android.app.Activity#onCre
onCreateOptionsMenu()} and {@link android.app.Activity#onOptionsItemSelected(MenuItem)
onOptionsItemSelected()} methods. Then extend this class for each Activity that should share the
same Options Menu. This way, you have to manage only one set of code for handling menu
-actions and each decendent class inherits the menu behaviors.<br/><br/>
-If you want to add menu items to one of your decendent activities,
+actions and each descendant class inherits the menu behaviors.<br/><br/>
+If you want to add menu items to one of your descendant activities,
override {@link android.app.Activity#onCreateOptionsMenu(Menu)
onCreateOptionsMenu()} in that Activity. Call {@code super.onCreateOptionsMenu(menu)} so the
original menu items are created, then add new menu items with {@link
@@ -542,7 +560,7 @@ hardware keyboard. Shortcuts cannot be added to items in a Context Menu.</p>
<h3 id="intents">Intents for menu items</h3>
<p>Sometimes you'll want a menu item to launch an Activity using an Intent (whether it's an
-Actvitity in your application or another application). When you know the Intent you want to use and
+Activity in your application or another application). When you know the Intent you want to use and
have a specific menu item that should initiate the Intent, you can execute the Intent with {@link
android.app.Activity#startActivity(Intent) startActivity()} during the appropriate on-item-selected
callback method (such as the {@link android.app.Activity#onOptionsItemSelected(MenuItem)
diff --git a/docs/html/images/ui/actionbar-item-withtext.png b/docs/html/images/ui/actionbar-item-withtext.png
new file mode 100644
index 0000000..98b5f84
--- /dev/null
+++ b/docs/html/images/ui/actionbar-item-withtext.png
Binary files differ
diff --git a/docs/html/images/ui/actionbar-logo.png b/docs/html/images/ui/actionbar-logo.png
new file mode 100644
index 0000000..481ed2c
--- /dev/null
+++ b/docs/html/images/ui/actionbar-logo.png
Binary files differ
diff --git a/docs/html/images/ui/actionbar.png b/docs/html/images/ui/actionbar.png
new file mode 100644
index 0000000..4fc871c
--- /dev/null
+++ b/docs/html/images/ui/actionbar.png
Binary files differ
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
index 808b2c2..c79a91a 100644
--- a/drm/common/Android.mk
+++ b/drm/common/Android.mk
@@ -18,6 +18,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
DrmConstraints.cpp \
+ DrmMetadata.cpp \
DrmConvertedStatus.cpp \
DrmEngineBase.cpp \
DrmInfo.cpp \
diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp
index 10c64ee..9b16c36 100644
--- a/drm/common/DrmEngineBase.cpp
+++ b/drm/common/DrmEngineBase.cpp
@@ -31,6 +31,10 @@ DrmConstraints* DrmEngineBase::getConstraints(
return onGetConstraints(uniqueId, path, action);
}
+DrmMetadata* DrmEngineBase::getMetadata(int uniqueId, const String8* path) {
+ return onGetMetadata(uniqueId, path);
+}
+
status_t DrmEngineBase::initialize(int uniqueId) {
return onInitialize(uniqueId);
}
@@ -80,7 +84,7 @@ status_t DrmEngineBase::consumeRights(
}
status_t DrmEngineBase::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
}
@@ -116,7 +120,7 @@ DrmSupportInfo* DrmEngineBase::getSupportInfo(int uniqueId) {
}
status_t DrmEngineBase::openDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) {
return onOpenDecryptSession(uniqueId, decryptHandle, fd, offset, length);
}
@@ -146,7 +150,7 @@ status_t DrmEngineBase::finalizeDecryptUnit(
}
ssize_t DrmEngineBase::pread(
- int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+ int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
return onPread(uniqueId, decryptHandle, buffer, numBytes, offset);
}
diff --git a/drm/common/DrmMetadata.cpp b/drm/common/DrmMetadata.cpp
new file mode 100644
index 0000000..6cc5ec1
--- /dev/null
+++ b/drm/common/DrmMetadata.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <drm/DrmMetadata.h>
+
+using namespace android;
+
+int DrmMetadata::getCount(void) const {
+ return mMetadataMap.size();
+}
+
+status_t DrmMetadata::put(const String8* key,
+ const char* value) {
+ if((value != NULL) && (key != NULL)) {
+ int length = strlen(value);
+ char* charValue = new char[length + 1];
+
+ memcpy(charValue, value, length);
+ charValue[length] = '\0';
+ mMetadataMap.add(*key, charValue);
+ }
+ return NO_ERROR;
+}
+
+String8 DrmMetadata::get(const String8& key) const {
+ if (NULL != getValue(&key)) {
+ return String8(getValue(&key));
+ }
+ else {
+ return String8("");
+ }
+}
+
+const char* DrmMetadata::getValue(const String8* key) const {
+ if(key != NULL) {
+ if (NAME_NOT_FOUND != mMetadataMap.indexOfKey(*key)) {
+ return mMetadataMap.valueFor(*key);
+ }
+ else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+const char* DrmMetadata::getAsByteArray(const String8* key) const {
+ return getValue(key);
+}
+
+bool DrmMetadata::KeyIterator::hasNext() {
+ return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+const String8& DrmMetadata::KeyIterator::next() {
+ const String8& key = mDrmMetadata->mMetadataMap.keyAt(mIndex);
+ mIndex++;
+ return key;
+}
+
+DrmMetadata::KeyIterator DrmMetadata::keyIterator() {
+ return KeyIterator(this);
+}
+
+DrmMetadata::KeyIterator::KeyIterator(const DrmMetadata::KeyIterator& keyIterator) :
+ mDrmMetadata(keyIterator.mDrmMetadata),
+ mIndex(keyIterator.mIndex) {
+ LOGV("DrmMetadata::KeyIterator::KeyIterator");
+}
+
+DrmMetadata::KeyIterator& DrmMetadata::KeyIterator::operator=(const DrmMetadata::KeyIterator& keyIterator) {
+ LOGV("DrmMetadata::KeyIterator::operator=");
+ mDrmMetadata = keyIterator.mDrmMetadata;
+ mIndex = keyIterator.mIndex;
+ return *this;
+}
+
+
+DrmMetadata::Iterator DrmMetadata::iterator() {
+ return Iterator(this);
+}
+
+DrmMetadata::Iterator::Iterator(const DrmMetadata::Iterator& iterator) :
+ mDrmMetadata(iterator.mDrmMetadata),
+ mIndex(iterator.mIndex) {
+ LOGV("DrmMetadata::Iterator::Iterator");
+}
+
+DrmMetadata::Iterator& DrmMetadata::Iterator::operator=(const DrmMetadata::Iterator& iterator) {
+ LOGV("DrmMetadata::Iterator::operator=");
+ mDrmMetadata = iterator.mDrmMetadata;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmMetadata::Iterator::hasNext() {
+ return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+String8 DrmMetadata::Iterator::next() {
+ String8 value = String8(mDrmMetadata->mMetadataMap.editValueAt(mIndex));
+ mIndex++;
+ return value;
+}
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index c3d12f0..75edac6 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -24,6 +24,7 @@
#include <drm/DrmInfo.h>
#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
#include <drm/DrmRights.h>
#include <drm/DrmInfoStatus.h>
#include <drm/DrmConvertedStatus.h>
@@ -123,6 +124,35 @@ DrmConstraints* BpDrmManagerService::getConstraints(
return drmConstraints;
}
+DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) {
+ LOGV("Get Metadata");
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ DrmMetadata* drmMetadata = NULL;
+ data.writeString8(*path);
+ remote()->transact(GET_METADATA_FROM_CONTENT, data, &reply);
+
+ if (0 != reply.dataAvail()) {
+ //Filling Drm Metadata
+ drmMetadata = new DrmMetadata();
+
+ const int size = reply.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(reply.readString8());
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ drmMetadata->put(&key, data);
+ }
+ }
+ return drmMetadata;
+}
+
bool BpDrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
LOGV("Can Handle");
Parcel data, reply;
@@ -333,7 +363,7 @@ status_t BpDrmManagerService::consumeRights(
}
status_t BpDrmManagerService::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
LOGV("setPlaybackStatus");
Parcel data, reply;
@@ -429,7 +459,7 @@ DrmConvertedStatus* BpDrmManagerService::convertData(
if (0 != reply.dataAvail()) {
//Filling DRM Converted Status
const int statusCode = reply.readInt32();
- const int offset = reply.readInt32();
+ const off64_t offset = reply.readInt64();
DrmBuffer* convertedData = NULL;
if (0 != reply.dataAvail()) {
@@ -461,7 +491,7 @@ DrmConvertedStatus* BpDrmManagerService::closeConvertSession(int uniqueId, int c
if (0 != reply.dataAvail()) {
//Filling DRM Converted Status
const int statusCode = reply.readInt32();
- const int offset = reply.readInt32();
+ const off64_t offset = reply.readInt64();
DrmBuffer* convertedData = NULL;
if (0 != reply.dataAvail()) {
@@ -515,15 +545,15 @@ status_t BpDrmManagerService::getAllSupportInfo(
}
DecryptHandle* BpDrmManagerService::openDecryptSession(
- int uniqueId, int fd, int offset, int length) {
+ int uniqueId, int fd, off64_t offset, off64_t length) {
LOGV("Entering BpDrmManagerService::openDecryptSession");
Parcel data, reply;
data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
data.writeInt32(uniqueId);
data.writeFileDescriptor(fd);
- data.writeInt32(offset);
- data.writeInt32(length);
+ data.writeInt64(offset);
+ data.writeInt64(length);
remote()->transact(OPEN_DECRYPT_SESSION, data, &reply);
@@ -697,7 +727,7 @@ status_t BpDrmManagerService::finalizeDecryptUnit(
ssize_t BpDrmManagerService::pread(
int uniqueId, DecryptHandle* decryptHandle, void* buffer,
- ssize_t numBytes, off_t offset) {
+ ssize_t numBytes, off64_t offset) {
LOGV("read");
Parcel data, reply;
int result;
@@ -717,7 +747,7 @@ ssize_t BpDrmManagerService::pread(
}
data.writeInt32(numBytes);
- data.writeInt32(offset);
+ data.writeInt64(offset);
remote()->transact(PREAD, data, &reply);
result = reply.readInt32();
@@ -825,6 +855,38 @@ status_t BnDrmManagerService::onTransact(
return DRM_NO_ERROR;
}
+ case GET_METADATA_FROM_CONTENT:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_METADATA_FROM_CONTENT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const String8 path = data.readString8();
+
+ DrmMetadata* drmMetadata = getMetadata(uniqueId, &path);
+ if (NULL != drmMetadata) {
+ //Filling DRM Metadata contents
+ reply->writeInt32(drmMetadata->getCount());
+
+ DrmMetadata::KeyIterator keyIt = drmMetadata->keyIterator();
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ reply->writeString8(key);
+ const char* value = drmMetadata->getAsByteArray(&key);
+ int bufferSize = 0;
+ if (NULL != value) {
+ bufferSize = strlen(value);
+ reply->writeInt32(bufferSize + 1);
+ reply->write(value, bufferSize + 1);
+ } else {
+ reply->writeInt32(0);
+ }
+ }
+ }
+ delete drmMetadata; drmMetadata = NULL;
+ return NO_ERROR;
+ }
+
case CAN_HANDLE:
{
LOGV("BnDrmManagerService::onTransact :CAN_HANDLE");
@@ -1121,7 +1183,7 @@ status_t BnDrmManagerService::onTransact(
if (NULL != drmConvertedStatus) {
//Filling Drm Converted Ststus
reply->writeInt32(drmConvertedStatus->statusCode);
- reply->writeInt32(drmConvertedStatus->offset);
+ reply->writeInt64(drmConvertedStatus->offset);
if (NULL != drmConvertedStatus->convertedData) {
const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
@@ -1150,7 +1212,7 @@ status_t BnDrmManagerService::onTransact(
if (NULL != drmConvertedStatus) {
//Filling Drm Converted Ststus
reply->writeInt32(drmConvertedStatus->statusCode);
- reply->writeInt32(drmConvertedStatus->offset);
+ reply->writeInt64(drmConvertedStatus->offset);
if (NULL != drmConvertedStatus->convertedData) {
const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
@@ -1210,7 +1272,7 @@ status_t BnDrmManagerService::onTransact(
const int fd = data.readFileDescriptor();
DecryptHandle* handle
- = openDecryptSession(uniqueId, fd, data.readInt32(), data.readInt32());
+ = openDecryptSession(uniqueId, fd, data.readInt64(), data.readInt64());
if (NULL != handle) {
reply->writeInt32(handle->decryptId);
@@ -1415,7 +1477,7 @@ status_t BnDrmManagerService::onTransact(
const int numBytes = data.readInt32();
char* buffer = new char[numBytes];
- const off_t offset = data.readInt32();
+ const off64_t offset = data.readInt64();
ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset);
reply->writeInt32(result);
diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp
index 7ec4fa2..c16214e 100644
--- a/drm/common/ReadWriteUtils.cpp
+++ b/drm/common/ReadWriteUtils.cpp
@@ -42,7 +42,7 @@ String8 ReadWriteUtils::readBytes(const String8& filePath) {
struct stat sb;
if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
- int length = sb.st_size;
+ off64_t length = sb.st_size;
char* bytes = new char[length];
if (length == read(fd, (void*) bytes, length)) {
string.append(bytes, length);
@@ -57,7 +57,7 @@ String8 ReadWriteUtils::readBytes(const String8& filePath) {
int ReadWriteUtils::readBytes(const String8& filePath, char** buffer) {
FILE* file = NULL;
file = fopen(filePath.string(), "r");
- int length = 0;
+ off64_t length = 0;
if (NULL != file) {
int fd = fileno(file);
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 35e62f3..9a6f787 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -23,6 +23,7 @@
#include <drm/DrmInfoEvent.h>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
#include <drm/DrmInfoStatus.h>
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
@@ -148,6 +149,15 @@ DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, co
return NULL;
}
+DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.getMetadata(uniqueId, path);
+ }
+ return NULL;
+}
+
status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) {
mPlugInManager.loadPlugIn(absolutePath);
@@ -258,7 +268,7 @@ status_t DrmManager::consumeRights(
}
status_t DrmManager::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
status_t result = DRM_ERROR_UNKNOWN;
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
@@ -370,7 +380,7 @@ status_t DrmManager::getAllSupportInfo(
return DRM_NO_ERROR;
}
-DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, int offset, int length) {
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length) {
Mutex::Autolock _l(mDecryptLock);
status_t result = DRM_ERROR_CANNOT_HANDLE;
Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
@@ -470,7 +480,7 @@ status_t DrmManager::finalizeDecryptUnit(
}
ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) {
+ void* buffer, ssize_t numBytes, off64_t offset) {
ssize_t result = DECRYPT_FILE_ERROR;
if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 8cf510d..0901a44 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -18,18 +18,50 @@
#define LOG_TAG "DrmManagerService(Native)"
#include <utils/Log.h>
+#include <private/android_filesystem_config.h>
+
#include <errno.h>
#include <utils/threads.h>
#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
#include <sys/stat.h>
#include "DrmManagerService.h"
#include "DrmManager.h"
using namespace android;
+static Vector<uid_t> trustedUids;
+
+static bool isProtectedCallAllowed() {
+ // TODO
+ // Following implementation is just for reference.
+ // Each OEM manufacturer should implement/replace with their own solutions.
+ bool result = false;
+
+ IPCThreadState* ipcState = IPCThreadState::self();
+ uid_t uid = ipcState->getCallingUid();
+
+ for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+ if (trustedUids[i] == uid) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
void DrmManagerService::instantiate() {
LOGV("instantiate");
defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService());
+
+ if (0 >= trustedUids.size()) {
+ // TODO
+ // Following implementation is just for reference.
+ // Each OEM manufacturer should implement/replace with their own solutions.
+
+ // Add trusted uids here
+ trustedUids.push(AID_MEDIA);
+ }
}
DrmManagerService::DrmManagerService() :
@@ -79,6 +111,11 @@ DrmConstraints* DrmManagerService::getConstraints(
return mDrmManager->getConstraints(uniqueId, path, action);
}
+DrmMetadata* DrmManagerService::getMetadata(int uniqueId, const String8* path) {
+ LOGV("Entering getMetadata from content");
+ return mDrmManager->getMetadata(uniqueId, path);
+}
+
bool DrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
LOGV("Entering canHandle");
return mDrmManager->canHandle(uniqueId, path, mimeType);
@@ -125,7 +162,7 @@ status_t DrmManagerService::consumeRights(
}
status_t DrmManagerService::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
LOGV("Entering setPlaybackStatus");
return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
}
@@ -170,15 +207,23 @@ status_t DrmManagerService::getAllSupportInfo(
}
DecryptHandle* DrmManagerService::openDecryptSession(
- int uniqueId, int fd, int offset, int length) {
+ int uniqueId, int fd, off64_t offset, off64_t length) {
LOGV("Entering DrmManagerService::openDecryptSession");
- return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+ if (isProtectedCallAllowed()) {
+ return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+ }
+
+ return NULL;
}
DecryptHandle* DrmManagerService::openDecryptSession(
int uniqueId, const char* uri) {
LOGV("Entering DrmManagerService::openDecryptSession with uri");
- return mDrmManager->openDecryptSession(uniqueId, uri);
+ if (isProtectedCallAllowed()) {
+ return mDrmManager->openDecryptSession(uniqueId, uri);
+ }
+
+ return NULL;
}
status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
@@ -206,7 +251,7 @@ status_t DrmManagerService::finalizeDecryptUnit(
}
ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) {
+ void* buffer, ssize_t numBytes, off64_t offset) {
LOGV("Entering pread");
return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
}
diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
index 9294884..20fd8aa 100644
--- a/drm/java/android/drm/DrmErrorEvent.java
+++ b/drm/java/android/drm/DrmErrorEvent.java
@@ -53,11 +53,6 @@ public class DrmErrorEvent extends DrmEvent {
* associated with all DRM schemes.
*/
public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007;
- /**
- * TYPE_DRM_INFO_ACQUISITION_FAILED, when failed to get the required information to
- * communicate with the service.
- */
- public static final int TYPE_DRM_INFO_ACQUISITION_FAILED = 2008;
/**
* constructor to create DrmErrorEvent object with given parameters
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
index 583337f..f7bc5cd 100644
--- a/drm/java/android/drm/DrmEvent.java
+++ b/drm/java/android/drm/DrmEvent.java
@@ -31,14 +31,8 @@ public class DrmEvent {
* Constant field signifies that given information is processed successfully
*/
public static final int TYPE_DRM_INFO_PROCESSED = 1002;
- /**
- * Constant field signifies that the required information to communicate with
- * the service is acquired sucessfully
- */
- public static final int TYPE_DRM_INFO_ACQUIRED = 1003;
public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object";
- public static final String DRM_INFO_OBJECT = "drm_info_object";
private final int mUniqueId;
private final int mType;
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 5044d36..2f54b33 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -102,8 +102,7 @@ public class DrmManagerClient {
}
private static final int ACTION_REMOVE_ALL_RIGHTS = 1001;
- private static final int ACTION_ACQUIRE_DRM_INFO = 1002;
- private static final int ACTION_PROCESS_DRM_INFO = 1003;
+ private static final int ACTION_PROCESS_DRM_INFO = 1002;
private int mUniqueId;
private int mNativeContext;
@@ -126,18 +125,6 @@ public class DrmManagerClient {
HashMap<String, Object> attributes = new HashMap<String, Object>();
switch(msg.what) {
- case ACTION_ACQUIRE_DRM_INFO: {
- final DrmInfoRequest request = (DrmInfoRequest) msg.obj;
- DrmInfo drmInfo = _acquireDrmInfo(mUniqueId, request);
- if (null != drmInfo) {
- attributes.put(DrmEvent.DRM_INFO_OBJECT, drmInfo);
- event = new DrmEvent(mUniqueId, DrmEvent.TYPE_DRM_INFO_ACQUIRED, null);
- } else {
- error = new DrmErrorEvent(mUniqueId,
- DrmErrorEvent.TYPE_DRM_INFO_ACQUISITION_FAILED, null);
- }
- break;
- }
case ACTION_PROCESS_DRM_INFO: {
final DrmInfo drmInfo = (DrmInfo) msg.obj;
DrmInfoStatus status = _processDrmInfo(mUniqueId, drmInfo);
@@ -243,19 +230,14 @@ public class DrmManagerClient {
*/
public DrmManagerClient(Context context) {
mContext = context;
- Looper looper;
- if (null != (looper = Looper.myLooper())) {
- mInfoHandler = new InfoHandler(looper);
- } else if (null != (looper = Looper.getMainLooper())) {
- mInfoHandler = new InfoHandler(looper);
- } else {
- mInfoHandler = null;
- }
+ HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler");
+ infoThread.start();
+ mInfoHandler = new InfoHandler(infoThread.getLooper());
- HandlerThread thread = new HandlerThread("DrmManagerClient.EventHandler");
- thread.start();
- mEventHandler = new EventHandler(thread.getLooper());
+ HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler");
+ eventThread.start();
+ mEventHandler = new EventHandler(eventThread.getLooper());
// save the unique id
mUniqueId = hashCode();
@@ -335,10 +317,24 @@ public class DrmManagerClient {
return _getConstraints(mUniqueId, path, action);
}
+ /**
+ * Get metadata information from DRM content
+ *
+ * @param path Content path from where DRM metadata would be retrieved.
+ * @return ContentValues instance in which metadata key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getMetadata(String path) {
+ if (null == path || path.equals("")) {
+ throw new IllegalArgumentException("Given path is invalid/null");
+ }
+ return _getMetadata(mUniqueId, path);
+ }
+
/**
* Get constraints information evaluated from DRM content
*
- * @param uri The Content URI of the data
+ * @param uri Content URI from where DRM constraints would be retrieved.
* @param action Actions defined in {@link DrmStore.Action}
* @return ContentValues instance in which constraints key-value pairs are embedded
* or null in case of failure
@@ -350,6 +346,20 @@ public class DrmManagerClient {
return getConstraints(convertUriToPath(uri), action);
}
+ /**
+ * Get metadata information from DRM content
+ *
+ * @param uri Content URI from where DRM metadata would be retrieved.
+ * @return ContentValues instance in which metadata key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getMetadata(Uri uri) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Uri should be non null");
+ }
+ return getMetadata(convertUriToPath(uri));
+ }
+
/**
* Save DRM rights to specified rights path
* and make association with content path.
@@ -408,7 +418,7 @@ public class DrmManagerClient {
/**
* Check whether the given mimetype or uri can be handled.
*
- * @param uri The content URI of the data
+ * @param uri Content URI of the data to be handled.
* @param mimeType Mimetype of the object to be handled
* @return
* true - if the given mimeType or path can be handled
@@ -445,20 +455,31 @@ public class DrmManagerClient {
* Retrieves necessary information for register, unregister or rights acquisition.
*
* @param drmInfoRequest Request information to retrieve drmInfo
- * @return
- * ERROR_NONE for success
- * ERROR_UNKNOWN for failure
+ * @return DrmInfo Instance as a result of processing given input
*/
- public int acquireDrmInfo(DrmInfoRequest drmInfoRequest) {
+ public DrmInfo acquireDrmInfo(DrmInfoRequest drmInfoRequest) {
if (null == drmInfoRequest || !drmInfoRequest.isValid()) {
throw new IllegalArgumentException("Given drmInfoRequest is invalid/null");
}
- int result = ERROR_UNKNOWN;
- if (null != mEventHandler) {
- Message msg = mEventHandler.obtainMessage(ACTION_ACQUIRE_DRM_INFO, drmInfoRequest);
- result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result;
- }
- return result;
+ return _acquireDrmInfo(mUniqueId, drmInfoRequest);
+ }
+
+ /**
+ * Executes given DrmInfoRequest and returns the rights information asynchronously.
+ * This is a utility API which consists of {@link #acquireDrmInfo(DrmInfoRequest)}
+ * and {@link #processDrmInfo(DrmInfo)}.
+ * It can be used if selected DRM agent can work with this combined sequences.
+ * In case of some DRM schemes, such as OMA DRM, application needs to invoke
+ * {@link #acquireDrmInfo(DrmInfoRequest)} and {@link #processDrmInfo(DrmInfo)}, separately.
+ *
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int acquireRights(DrmInfoRequest drmInfoRequest) {
+ DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest);
+ return processDrmInfo(drmInfo);
}
/**
@@ -750,6 +771,8 @@ public class DrmManagerClient {
private native ContentValues _getConstraints(int uniqueId, String path, int usage);
+ private native ContentValues _getMetadata(int uniqueId, String path);
+
private native boolean _canHandle(int uniqueId, String path, String mimeType);
private native DrmInfoStatus _processDrmInfo(int uniqueId, DrmInfo drmInfo);
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index e5e4547..e131839 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -29,6 +29,7 @@
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
#include <drm/DrmConvertedStatus.h>
#include <drm/drm_framework_common.h>
@@ -298,6 +299,43 @@ static jobject android_drm_DrmManagerClient_getConstraintsFromContent(
return constraints;
}
+static jobject android_drm_DrmManagerClient_getMetadataFromContent(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath) {
+ LOGV("GetMetadata - Enter");
+ const String8 pathString = Utility::getStringValue(env, jpath);
+ DrmMetadata* pMetadata =
+ getDrmManagerClientImpl(env, thiz)->getMetadata(uniqueId, &pathString);
+
+ jobject metadata = NULL;
+
+ jclass localRef = NULL;
+ localRef = env->FindClass("android/content/ContentValues");
+ if (NULL != localRef && NULL != pMetadata) {
+ // Get the constructor id
+ jmethodID constructorId = NULL;
+ constructorId = env->GetMethodID(localRef, "<init>", "()V");
+ if (NULL != constructorId) {
+ // create the java DrmMetadata object
+ metadata = env->NewObject(localRef, constructorId);
+ if (NULL != metadata) {
+ DrmMetadata::KeyIterator keyIt = pMetadata->keyIterator();
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+ // insert the entry<constraintKey, constraintValue>
+ // to newly created java object
+ String8 value = pMetadata->get(key);
+ env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put",
+ "(Ljava/lang/String;Ljava/lang/String;)V"),
+ env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ }
+ }
+ }
+ }
+ delete pMetadata; pMetadata = NULL;
+ LOGV("GetMetadata - Exit");
+ return metadata;
+}
+
static jobjectArray android_drm_DrmManagerClient_getAllSupportInfo(
JNIEnv* env, jobject thiz, jint uniqueId) {
LOGV("GetAllSupportInfo - Enter");
@@ -682,6 +720,9 @@ static JNINativeMethod nativeMethods[] = {
{"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;",
(void*)android_drm_DrmManagerClient_getConstraintsFromContent},
+ {"_getMetadata", "(ILjava/lang/String;)Landroid/content/ContentValues;",
+ (void*)android_drm_DrmManagerClient_getMetadataFromContent},
+
{"_getAllSupportInfo", "(I)[Landroid/drm/DrmSupportInfo;",
(void*)android_drm_DrmManagerClient_getAllSupportInfo},
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index f0439eb..578e135 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -43,6 +43,10 @@ DrmConstraints* DrmManagerClient::getConstraints(const String8* path, const int
return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action);
}
+DrmMetadata* DrmManagerClient::getMetadata(const String8* path) {
+ return mDrmManagerClientImpl->getMetadata(mUniqueId, path);
+}
+
bool DrmManagerClient::canHandle(const String8& path, const String8& mimeType) {
return mDrmManagerClientImpl->canHandle(mUniqueId, path, mimeType);
}
@@ -78,7 +82,7 @@ status_t DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int actio
}
status_t DrmManagerClient::setPlaybackStatus(
- DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
return mDrmManagerClientImpl
->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position);
}
@@ -112,7 +116,7 @@ status_t DrmManagerClient::getAllSupportInfo(int* length, DrmSupportInfo** drmSu
return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray);
}
-DecryptHandle* DrmManagerClient::openDecryptSession(int fd, int offset, int length) {
+DecryptHandle* DrmManagerClient::openDecryptSession(int fd, off64_t offset, off64_t length) {
return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length);
}
@@ -145,7 +149,7 @@ status_t DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int
}
ssize_t DrmManagerClient::pread(
- DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+ DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
Mutex::Autolock _l(mDecryptLock);
return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset);
}
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index b3ae9a7..f39131d 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -101,6 +101,14 @@ DrmConstraints* DrmManagerClientImpl::getConstraints(
return drmConstraints;
}
+DrmMetadata* DrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) {
+ DrmMetadata *drmMetadata = NULL;
+ if ((NULL != path) && (EMPTY_STRING != *path)) {
+ drmMetadata = getDrmManagerService()->getMetadata(uniqueId, path);
+ }
+ return drmMetadata;
+}
+
bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
bool retCode = false;
if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
@@ -170,7 +178,7 @@ status_t DrmManagerClientImpl::consumeRights(
}
status_t DrmManagerClientImpl::setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
status_t status = DRM_ERROR_UNKNOWN;
if (NULL != decryptHandle) {
status = getDrmManagerService()->setPlaybackStatus(
@@ -231,7 +239,7 @@ status_t DrmManagerClientImpl::getAllSupportInfo(
}
DecryptHandle* DrmManagerClientImpl::openDecryptSession(
- int uniqueId, int fd, int offset, int length) {
+ int uniqueId, int fd, off64_t offset, off64_t length) {
return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length);
}
@@ -283,7 +291,7 @@ status_t DrmManagerClientImpl::finalizeDecryptUnit(
}
ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) {
+ void* buffer, ssize_t numBytes, off64_t offset) {
ssize_t retCode = INVALID_VALUE;
if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) {
retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
index d782f5b..e05366d 100644
--- a/drm/libdrmframework/include/DrmManager.h
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -32,6 +32,7 @@ class DrmUnregistrationInfo;
class DrmRightsAcquisitionInfo;
class DrmContentIds;
class DrmConstraints;
+class DrmMetadata;
class DrmRights;
class DrmInfo;
class DrmInfoStatus;
@@ -74,6 +75,8 @@ public:
DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -92,7 +95,7 @@ public:
status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
bool validateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -109,7 +112,7 @@ public:
status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
- DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
@@ -124,7 +127,7 @@ public:
status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
void onInfo(const DrmInfoEvent& event);
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
index 1c6be46..0a7fcd1 100644
--- a/drm/libdrmframework/include/DrmManagerClientImpl.h
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -86,6 +86,18 @@ public:
DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
/**
+ * Get metadata information associated with input content.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata are embedded in it
+ * @note
+ * In case of error, return NULL
+ */
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ /**
* Check whether the given mimetype or path can be handled
*
* @param[in] uniqueId Unique identifier for a session
@@ -191,7 +203,7 @@ public:
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -291,7 +303,7 @@ public:
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
/**
* Open the decrypt session to decrypt the given protected content
@@ -369,7 +381,7 @@ public:
* @return Number of bytes read. Returns -1 for Failure.
*/
ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
/**
* Notify the event to the registered listener
diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h
index 4a3aeae..d0a0db7 100644
--- a/drm/libdrmframework/include/DrmManagerService.h
+++ b/drm/libdrmframework/include/DrmManagerService.h
@@ -61,6 +61,8 @@ public:
DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -79,7 +81,7 @@ public:
status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
bool validateAction(int uniqueId, const String8& path,
int action, const ActionDescription& description);
@@ -96,7 +98,7 @@ public:
status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
- DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
@@ -111,7 +113,7 @@ public:
status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
private:
DrmManager* mDrmManager;
diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h
index 1275488..2424ea5 100644
--- a/drm/libdrmframework/include/IDrmManagerService.h
+++ b/drm/libdrmframework/include/IDrmManagerService.h
@@ -27,6 +27,7 @@ namespace android {
class DrmContentIds;
class DrmConstraints;
+class DrmMetadata;
class DrmRights;
class DrmInfo;
class DrmInfoStatus;
@@ -51,6 +52,7 @@ public:
SET_DRM_SERVICE_LISTENER,
INSTALL_DRM_ENGINE,
GET_CONSTRAINTS_FROM_CONTENT,
+ GET_METADATA_FROM_CONTENT,
CAN_HANDLE,
PROCESS_DRM_INFO,
ACQUIRE_DRM_INFO,
@@ -96,6 +98,8 @@ public:
virtual DrmConstraints* getConstraints(
int uniqueId, const String8* path, const int action) = 0;
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType) = 0;
virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
@@ -116,7 +120,7 @@ public:
int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
virtual status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
virtual bool validateAction(
int uniqueId, const String8& path,
@@ -136,7 +140,7 @@ public:
virtual status_t getAllSupportInfo(
int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0;
- virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length) = 0;
+ virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length) = 0;
virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri) = 0;
@@ -152,7 +156,7 @@ public:
int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes,off_t offset) = 0;
+ void* buffer, ssize_t numBytes,off64_t offset) = 0;
};
/**
@@ -179,6 +183,8 @@ public:
virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -198,7 +204,7 @@ public:
int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
virtual status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
virtual bool validateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -217,7 +223,7 @@ public:
virtual status_t getAllSupportInfo(
int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
- virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+ virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
@@ -233,7 +239,7 @@ public:
int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
};
/**
diff --git a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
index 5851af5..b61e3d3 100644
--- a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
+++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
@@ -36,6 +36,8 @@ public:
public:
DrmConstraints* getConstraints(int uniqueId, const String8* path, int action);
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
status_t initialize(int uniqueId);
status_t setOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
@@ -60,7 +62,7 @@ public:
status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
status_t setPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
bool validateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -78,7 +80,7 @@ public:
DrmSupportInfo* getSupportInfo(int uniqueId);
status_t openDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length);
status_t openDecryptSession(
int uniqueId, DecryptHandle* decryptHandle, const char* uri);
@@ -94,7 +96,7 @@ public:
status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
protected:
/////////////////////////////////////////////////////
@@ -117,6 +119,18 @@ protected:
int uniqueId, const String8* path, int action) = 0;
/**
+ * Get metadata information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ virtual DrmMetadata* onGetMetadata(int uniqueId, const String8* path) = 0;
+
+ /**
* Initialize plug-in
*
* @param[in] uniqueId Unique identifier for a session
@@ -254,7 +268,7 @@ protected:
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
virtual status_t onSetPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -355,7 +369,7 @@ protected:
* DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
*/
virtual status_t onOpenDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) = 0;
/**
* Open the decrypt session to decrypt the given protected content
@@ -436,7 +450,7 @@ protected:
* @return Number of bytes read. Returns -1 for Failure.
*/
virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) = 0;
+ void* buffer, ssize_t numBytes, off64_t offset) = 0;
};
};
diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
index cc03ef2..d05c24f 100644
--- a/drm/libdrmframework/plugins/common/include/IDrmEngine.h
+++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
@@ -23,6 +23,7 @@ namespace android {
class DrmContentIds;
class DrmConstraints;
+class DrmMetadata;
class DrmRights;
class DrmInfo;
class DrmInfoStatus;
@@ -105,6 +106,18 @@ public:
int uniqueId, const String8* path, int action) = 0;
/**
+ * Get metadata information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+ /**
* Get whether the given content can be handled by this plugin or not
*
* @param[in] uniqueId Unique identifier for a session
@@ -211,7 +224,7 @@ public:
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
- int playbackStatus, int position) = 0;
+ int playbackStatus, int64_t position) = 0;
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -312,7 +325,7 @@ public:
* DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
*/
virtual status_t openDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) = 0;
/**
* Open the decrypt session to decrypt the given protected content
@@ -393,7 +406,7 @@ public:
* @return Number of bytes read. Returns -1 for Failure.
*/
virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) = 0;
+ void* buffer, ssize_t numBytes, off64_t offset) = 0;
};
};
diff --git a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
index ddb7fd3..f941f70 100644
--- a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
+++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
@@ -30,6 +30,8 @@ public:
protected:
DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+ DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
status_t onInitialize(int uniqueId);
status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
@@ -54,7 +56,7 @@ protected:
status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
status_t onSetPlaybackStatus(
- int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
bool onValidateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -72,7 +74,7 @@ protected:
DrmSupportInfo* onGetSupportInfo(int uniqueId);
status_t onOpenDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length);
status_t onOpenDecryptSession(
int uniqueId, DecryptHandle* decryptHandle, const char* uri);
@@ -88,7 +90,7 @@ protected:
status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset);
+ void* buffer, ssize_t numBytes, off64_t offset);
private:
DecryptHandle* openDecryptSessionImpl();
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
index 41f8e91..976978f 100644
--- a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -20,6 +20,7 @@
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
#include <drm/DrmInfo.h>
#include <drm/DrmInfoEvent.h>
#include <drm/DrmInfoStatus.h>
@@ -51,6 +52,10 @@ DrmPassthruPlugIn::~DrmPassthruPlugIn() {
}
+DrmMetadata* DrmPassthruPlugIn::onGetMetadata(int uniqueId, const String8* path) {
+ return NULL;
+}
+
DrmConstraints* DrmPassthruPlugIn::onGetConstraints(
int uniqueId, const String8* path, int action) {
LOGD("DrmPassthruPlugIn::onGetConstraints From Path: %d", uniqueId);
@@ -182,7 +187,7 @@ status_t DrmPassthruPlugIn::onConsumeRights(int uniqueId, DecryptHandle* decrypt
}
status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
- int playbackStatus, int position) {
+ int playbackStatus, int64_t position) {
LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId);
return DRM_NO_ERROR;
}
@@ -229,7 +234,7 @@ DrmConvertedStatus* DrmPassthruPlugIn::onCloseConvertSession(int uniqueId, int c
}
status_t DrmPassthruPlugIn::onOpenDecryptSession(
- int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+ int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) {
LOGD("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId);
#ifdef ENABLE_PASSTHRU_DECRYPTION
@@ -287,7 +292,7 @@ status_t DrmPassthruPlugIn::onFinalizeDecryptUnit(
}
ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* decryptHandle,
- void* buffer, ssize_t numBytes, off_t offset) {
+ void* buffer, ssize_t numBytes, off64_t offset) {
LOGD("DrmPassthruPlugIn::onPread() : %d", uniqueId);
return 0;
}
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 8d5c913..241ab17 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -30,7 +30,7 @@ public class ComposeShader extends Shader {
/** Create a new compose shader, given shaders A, B, and a combining mode.
When the mode is applied, it will be given the result from shader A as its
- "dst", and the result of from shader B as its "src".
+ "dst", and the result from shader B as its "src".
@param shaderA The colors from this shader are seen as the "dst" by the mode
@param shaderB The colors from this shader are seen as the "src" by the mode
@param mode The mode that combines the colors from the two shaders. If mode
@@ -53,7 +53,7 @@ public class ComposeShader extends Shader {
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
When the mode is applied, it will be given the result from shader A as its
- "dst", and the result of from shader B as its "src".
+ "dst", and the result from shader B as its "src".
@param shaderA The colors from this shader are seen as the "dst" by the mode
@param shaderB The colors from this shader are seen as the "src" by the mode
@param mode The PorterDuff mode that combines the colors from the two shaders.
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 0660441..f16e045 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -685,6 +685,8 @@ public class RenderScript {
public static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
public static final int RS_MESSAGE_TO_CLIENT_USER = 4;
+ public static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
+
MessageThread(RenderScript rs) {
super("RSMessageThread");
mRS = rs;
@@ -722,6 +724,10 @@ public class RenderScript {
if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
String e = mRS.nContextGetErrorMessage(mRS.mContext);
+ if (subID >= RS_ERROR_FATAL_UNKNOWN) {
+ throw new RSRuntimeException("Fatal error " + subID + ", details: " + e);
+ }
+
if(mRS.mErrorCallback != null) {
mRS.mErrorCallback.mErrorMessage = e;
mRS.mErrorCallback.mErrorNum = subID;
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index 5963c42..e6ba3c4 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -25,6 +25,7 @@ namespace android {
class DrmInfo;
class DrmRights;
+class DrmMetadata;
class DrmInfoEvent;
class DrmInfoStatus;
class DrmInfoRequest;
@@ -65,7 +66,7 @@ public:
* @return
* Handle for the decryption session
*/
- DecryptHandle* openDecryptSession(int fd, int offset, int length);
+ DecryptHandle* openDecryptSession(int fd, off64_t offset, off64_t length);
/**
* Open the decrypt session to decrypt the given protected content
@@ -109,7 +110,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int position);
+ status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
/**
* Initialize decryption for the given unit of the protected content
@@ -163,7 +164,7 @@ public:
*
* @return Number of bytes read. Returns -1 for Failure.
*/
- ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset);
+ ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset);
/**
* Validates whether an action on the DRM content is allowed or not.
@@ -204,6 +205,17 @@ public:
DrmConstraints* getConstraints(const String8* path, const int action);
/**
+ * Get metadata information associated with input content
+ *
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ DrmMetadata* getMetadata(const String8* path);
+
+ /**
* Check whether the given mimetype or path can be handled
*
* @param[in] path Path of the content needs to be handled
diff --git a/include/drm/DrmMetadata.h b/include/drm/DrmMetadata.h
new file mode 100644
index 0000000..2c7538a
--- /dev/null
+++ b/include/drm/DrmMetadata.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DRM_METADATA_H__
+#define __DRM_METADATA_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getMetadata(const String8*)
+ * an instance of DrmMetadata would be returned.
+ */
+class DrmMetadata {
+public:
+ /**
+ * Iterator for key
+ */
+ class KeyIterator {
+ friend class DrmMetadata;
+ private:
+ KeyIterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+ public:
+ KeyIterator(const KeyIterator& keyIterator);
+ KeyIterator& operator=(const KeyIterator& keyIterator);
+ virtual ~KeyIterator() {}
+
+ public:
+ bool hasNext();
+ const String8& next();
+
+ private:
+ DrmMetadata* mDrmMetadata;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator for constraints
+ */
+ class Iterator {
+ friend class DrmMetadata;
+ private:
+ Iterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8 next();
+
+ private:
+ DrmMetadata* mDrmMetadata;
+ unsigned int mIndex;
+ };
+
+public:
+ DrmMetadata() {}
+ virtual ~DrmMetadata() {
+ DrmMetadata::KeyIterator keyIt = this->keyIterator();
+
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+ const char* value = this->getAsByteArray(&key);
+ if (NULL != value) {
+ delete[] value;
+ value = NULL;
+ }
+ }
+ mMetadataMap.clear();
+ }
+
+public:
+ int getCount(void) const;
+ status_t put(const String8* key, const char* value);
+ String8 get(const String8& key) const;
+ const char* getAsByteArray(const String8* key) const;
+ KeyIterator keyIterator();
+ Iterator iterator();
+
+private:
+ const char* getValue(const String8* key) const;
+
+private:
+ typedef KeyedVector<String8, const char*> DrmMetadataMap;
+ DrmMetadataMap mMetadataMap;
+};
+
+};
+
+#endif /* __DRM_METADATA_H__ */
+
diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h
index bc3f464..2b61f58 100644
--- a/include/media/stagefright/ColorConverter.h
+++ b/include/media/stagefright/ColorConverter.h
@@ -33,35 +33,47 @@ struct ColorConverter {
bool isValid() const;
void convert(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip);
+ const void *srcBits,
+ size_t srcWidth, size_t srcHeight,
+ size_t srcCropLeft, size_t srcCropTop,
+ size_t srcCropRight, size_t srcCropBottom,
+ void *dstBits,
+ size_t dstWidth, size_t dstHeight,
+ size_t dstCropLeft, size_t dstCropTop,
+ size_t dstCropRight, size_t dstCropBottom);
private:
+ struct BitmapParams {
+ BitmapParams(
+ void *bits,
+ size_t width, size_t height,
+ size_t cropLeft, size_t cropTop,
+ size_t cropRight, size_t cropBottom);
+
+ size_t cropWidth() const;
+ size_t cropHeight() const;
+
+ void *mBits;
+ size_t mWidth, mHeight;
+ size_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+ };
+
OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat;
uint8_t *mClip;
uint8_t *initClip();
void convertCbYCrY(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip);
+ const BitmapParams &src, const BitmapParams &dst);
void convertYUV420Planar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip);
+ const BitmapParams &src, const BitmapParams &dst);
void convertQCOMYUV420SemiPlanar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip);
+ const BitmapParams &src, const BitmapParams &dst);
void convertYUV420SemiPlanar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip);
+ const BitmapParams &src, const BitmapParams &dst);
ColorConverter(const ColorConverter &);
ColorConverter &operator=(const ColorConverter &);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 295b127..5f33739 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -32,6 +32,10 @@ enum {
kKeyMIMEType = 'mime', // cstring
kKeyWidth = 'widt', // int32_t
kKeyHeight = 'heig', // int32_t
+
+ // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
+ kKeyCropRect = 'crop',
+
kKeyRotation = 'rotA', // int32_t (angle in degrees)
kKeyIFramesInterval = 'ifiv', // int32_t
kKeyStride = 'strd', // int32_t
@@ -125,6 +129,7 @@ public:
TYPE_INT64 = 'in64',
TYPE_FLOAT = 'floa',
TYPE_POINTER = 'ptr ',
+ TYPE_RECT = 'rect',
};
void clear();
@@ -136,12 +141,22 @@ public:
bool setFloat(uint32_t key, float value);
bool setPointer(uint32_t key, void *value);
+ bool setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom);
+
bool findCString(uint32_t key, const char **value);
bool findInt32(uint32_t key, int32_t *value);
bool findInt64(uint32_t key, int64_t *value);
bool findFloat(uint32_t key, float *value);
bool findPointer(uint32_t key, void **value);
+ bool findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom);
+
bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
bool findData(uint32_t key, uint32_t *type,
@@ -187,6 +202,10 @@ private:
}
};
+ struct Rect {
+ int32_t mLeft, mTop, mRight, mBottom;
+ };
+
KeyedVector<uint32_t, typed_data> mItems;
// MetaData &operator=(const MetaData &);
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 5ebd2c0..00de39b 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -111,11 +111,6 @@ void ResourceCache::recycle(SkBitmap* resource) {
resource->setPixels(NULL, NULL);
return;
}
- recycle((void*) resource);
-}
-
-void ResourceCache::recycle(void* resource) {
- Mutex::Autolock _l(mLock);
ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
if (ref == NULL) {
// Should not get here - shouldn't get a call to recycle if we're not yet tracking it
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index b0abe2c..1bb4390 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -61,7 +61,6 @@ public:
void decrementRefcount(SkBitmap* resource);
void decrementRefcount(SkiaShader* resource);
void decrementRefcount(SkiaColorFilter* resource);
- void recycle(void* resource);
void recycle(SkBitmap* resource);
void destructor(SkBitmap* resource);
void destructor(SkiaShader* resource);
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 0ed129f..f00f748 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -236,11 +236,16 @@ enum RsPrimitive {
};
enum RsError {
- RS_ERROR_NONE,
- RS_ERROR_BAD_SHADER,
- RS_ERROR_BAD_SCRIPT,
- RS_ERROR_BAD_VALUE,
- RS_ERROR_OUT_OF_MEMORY
+ RS_ERROR_NONE = 0,
+ RS_ERROR_BAD_SHADER = 1,
+ RS_ERROR_BAD_SCRIPT = 2,
+ RS_ERROR_BAD_VALUE = 3,
+ RS_ERROR_OUT_OF_MEMORY = 4,
+ RS_ERROR_DRIVER = 5,
+
+ RS_ERROR_FATAL_UNKNOWN = 0x1000,
+ RS_ERROR_FATAL_DRIVER = 0x1001,
+ RS_ERROR_FATAL_PROGRAM_LINK = 0x1002
};
enum RsAnimationInterpolation {
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 1b584c8..7b35305 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -43,11 +43,6 @@ ContextDump {
param int32_t bits
}
-ContextGetError {
- param RsError *err
- ret const char *
- }
-
ContextSetPriority {
param int32_t priority
}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 840a10e..35db332 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -287,10 +287,27 @@ uint32_t Context::runScript(Script *s) {
return ret;
}
-void Context::checkError(const char *msg) const {
+void Context::checkError(const char *msg, bool isFatal) const {
+
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
- LOGE("%p, GL Error, 0x%x, from %s", this, err, msg);
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "GL Error = 0x%08x, from: %s", err, msg);
+
+ if (isFatal) {
+ setError(RS_ERROR_FATAL_DRIVER, buf);
+ } else {
+ switch (err) {
+ case GL_OUT_OF_MEMORY:
+ setError(RS_ERROR_OUT_OF_MEMORY, buf);
+ break;
+ default:
+ setError(RS_ERROR_DRIVER, buf);
+ break;
+ }
+ }
+
+ LOGE("%p, %s", this, buf);
}
}
@@ -597,7 +614,6 @@ Context::Context() {
mPaused = false;
mObjHead = NULL;
mError = RS_ERROR_NONE;
- mErrorMsg = NULL;
}
Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) {
@@ -861,7 +877,8 @@ RsMessageToClientType Context::getMessageToClient(void *data, size_t *receiveLen
return RS_MESSAGE_TO_CLIENT_RESIZE;
}
-bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace) {
+bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID,
+ uint32_t subID, size_t len, bool waitForSpace) const {
//LOGE("sendMessageToClient %i %i %i %i", cmdID, subID, len, waitForSpace);
if (cmdID == 0) {
LOGE("Attempting to send invalid command 0 to client.");
@@ -894,18 +911,8 @@ void Context::deinitToClient() {
mIO.mToClient.shutdown();
}
-const char * Context::getError(RsError *err) {
- *err = mError;
- mError = RS_ERROR_NONE;
- if (*err != RS_ERROR_NONE) {
- return mErrorMsg;
- }
- return NULL;
-}
-
-void Context::setError(RsError e, const char *msg) {
+void Context::setError(RsError e, const char *msg) const {
mError = e;
- mErrorMsg = msg;
sendMessageToClient(msg, RS_MESSAGE_TO_CLIENT_ERROR, e, strlen(msg) + 1, true);
}
@@ -1012,14 +1019,6 @@ void rsi_ContextDump(Context *rsc, int32_t bits) {
ObjectBase::dumpAll(rsc);
}
-const char* rsi_ContextGetError(Context *rsc, RsError *e) {
- const char *msg = rsc->getError(e);
- if (*e != RS_ERROR_NONE) {
- LOGE("RS Error %i %s", *e, msg);
- }
- return msg;
-}
-
}
}
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index c377c73..cafbdff 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -144,7 +144,7 @@ public:
RsMessageToClientType peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait);
RsMessageToClientType getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait);
- bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace);
+ bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace) const;
uint32_t runScript(Script *s);
void initToClient();
@@ -169,7 +169,7 @@ public:
uint32_t getWidth() const {return mWidth;}
uint32_t getHeight() const {return mHeight;}
- ThreadIO mIO;
+ mutable ThreadIO mIO;
// Timers
enum Timers {
@@ -197,9 +197,8 @@ public:
} props;
void dumpDebug() const;
- void checkError(const char *) const;
- const char * getError(RsError *);
- void setError(RsError e, const char *msg = NULL);
+ void checkError(const char *, bool isFatal = false) const;
+ void setError(RsError e, const char *msg = NULL) const;
mutable const ObjectBase * mObjHead;
@@ -259,8 +258,7 @@ protected:
bool mRunning;
bool mExit;
bool mPaused;
- RsError mError;
- const char *mErrorMsg;
+ mutable RsError mError;
pthread_t mThreadId;
pid_t mNativeThreadId;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 40321fe..6ae8bb8 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -348,7 +348,6 @@ RsElement rsi_ElementCreate(Context *rsc,
RsDataKind dk,
bool norm,
uint32_t vecSize) {
- //LOGE("rsi_ElementCreate %i %i %i %i", dt, dk, norm, vecSize);
const Element *e = Element::create(rsc, dt, dk, norm, vecSize);
e->incUserRef();
return (RsElement)e;
@@ -360,7 +359,6 @@ RsElement rsi_ElementCreate2(Context *rsc,
const char ** names,
const size_t * nameLengths,
const uint32_t * arraySizes) {
- //LOGE("rsi_ElementCreate2 %i", count);
const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes);
e->incUserRef();
return (RsElement)e;
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index ffa7d26..0713fb3 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -136,9 +136,9 @@ void ProgramFragment::createShader() {
char buf[256];
for (uint32_t ct=0; ct < mTextureCount; ct++) {
if (mTextureTargets[ct] == RS_TEXTURE_2D) {
- sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
+ snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
} else {
- sprintf(buf, "uniform samplerCube UNI_Tex%i;\n", ct);
+ snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
}
mShader.append(buf);
}
@@ -159,7 +159,7 @@ void ProgramFragment::init(Context *rsc) {
mTextureUniformIndexStart = uniformIndex;
char buf[256];
for (uint32_t ct=0; ct < mTextureCount; ct++) {
- sprintf(buf, "UNI_Tex%i", ct);
+ snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
mUniformNames[uniformIndex].setTo(buf);
mUniformArraySizes[uniformIndex] = 1;
uniformIndex++;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 3fd2981..a28b9bd 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -49,7 +49,7 @@ void ProgramVertex::loadShader(Context *rsc) {
Program::loadShader(rsc, GL_VERTEX_SHADER);
}
-void ProgramVertex::createShader() {
+void ProgramVertex::createShader(Context *rsc) {
if (mUserShader.length() > 1) {
appendUserConstants();
@@ -81,13 +81,12 @@ void ProgramVertex::createShader() {
}
mShader.append(mUserShader);
} else {
- LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
- rsAssert(0);
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "ProgramFragment::createShader cannot create program, shader code not defined");
}
}
void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc) {
- //LOGE("sgl2 vtx1 %x", glGetError());
if ((state->mLast.get() == this) && !mDirty) {
return;
}
@@ -96,8 +95,8 @@ void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCach
if (!isUserProgram()) {
if (mConstants[0].get() == NULL) {
- LOGE("Unable to set fixed function emulation matrices because allocation is missing");
- rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Unable to set fixed function emulation matrices because allocation is missing");
return;
}
float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -120,12 +119,13 @@ void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCach
void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const {
if (isUserProgram()) {
- LOGE("Attempting to set fixed function emulation matrix projection on user program");
- rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Attempting to set fixed function emulation matrix projection on user program");
return;
}
if (mConstants[0].get() == NULL) {
- LOGE("Unable to set fixed function emulation matrix projection because allocation is missing");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Unable to set fixed function emulation matrix projection because allocation is missing");
return;
}
float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -135,13 +135,13 @@ void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const
void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const {
if (isUserProgram()) {
- LOGE("Attempting to set fixed function emulation matrix modelview on user program");
- rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Attempting to set fixed function emulation matrix modelview on user program");
return;
}
if (mConstants[0].get() == NULL) {
- LOGE("Unable to set fixed function emulation matrix modelview because allocation is missing");
- rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Unable to set fixed function emulation matrix modelview because allocation is missing");
return;
}
float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -151,13 +151,13 @@ void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const
void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const {
if (isUserProgram()) {
- LOGE("Attempting to set fixed function emulation matrix texture on user program");
- rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Attempting to set fixed function emulation matrix texture on user program");
return;
}
if (mConstants[0].get() == NULL) {
- LOGE("Unable to set fixed function emulation matrix texture because allocation is missing");
- rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Unable to set fixed function emulation matrix texture because allocation is missing");
return;
}
float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -167,13 +167,13 @@ void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const {
void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const {
if (isUserProgram()) {
- LOGE("Attempting to get fixed function emulation matrix projection on user program");
- rsc->setError(RS_ERROR_BAD_SHADER, "Cannot get emulation matrix on user shader");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Attempting to get fixed function emulation matrix projection on user program");
return;
}
if (mConstants[0].get() == NULL) {
- LOGE("Unable to get fixed function emulation matrix projection because allocation is missing");
- rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+ rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+ "Unable to get fixed function emulation matrix projection because allocation is missing");
return;
}
float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -202,7 +202,7 @@ void ProgramVertex::init(Context *rsc) {
initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
}
}
- createShader();
+ createShader(rsc);
}
void ProgramVertex::serialize(OStream *stream) const {
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 824edbb..2a5c863 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -40,7 +40,7 @@ public:
void transformToScreen(Context *, float *v4out, const float *v3in) const;
- virtual void createShader();
+ virtual void createShader(Context *);
virtual void loadShader(Context *);
virtual void init(Context *);
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index d254018..45384c9 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -150,7 +150,7 @@ bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag
}
}
glDeleteProgram(pgm);
- rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs");
+ rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, "Error linking GL Programs");
return false;
}
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 2a51335..c195b9b 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -136,7 +136,7 @@ void Type::dumpLOGV(const char *prefix) const {
char buf[1024];
ObjectBase::dumpLOGV(prefix);
LOGV("%s Type: x=%i y=%i z=%i mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces);
- sprintf(buf, "%s element: ", prefix);
+ snprintf(buf, sizeof(buf), "%s element: ", prefix);
mElement->dumpLOGV(buf);
}
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 61d8abd..5948e04 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -1,7 +1,46 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
LOCAL_PATH:= $(call my-dir)
+
+# libui is partially built for the host (used by build time keymap validation tool)
+# These files are common to host and target builds.
+commonSources:= \
+ Input.cpp \
+ Keyboard.cpp \
+ KeyLayoutMap.cpp \
+ KeyCharacterMap.cpp \
+
+# For the host
+# =====================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= $(commonSources)
+
+LOCAL_MODULE:= libui
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# For the device
+# =====================================================
+
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ $(commonSources) \
EGLUtils.cpp \
EventHub.cpp \
EventRecurrence.cpp \
@@ -10,10 +49,6 @@ LOCAL_SRC_FILES:= \
GraphicBufferAllocator.cpp \
GraphicBufferMapper.cpp \
GraphicLog.cpp \
- Keyboard.cpp \
- KeyLayoutMap.cpp \
- KeyCharacterMap.cpp \
- Input.cpp \
InputDispatcher.cpp \
InputManager.cpp \
InputReader.cpp \
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index 890cc3f..e689c4b 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -733,6 +733,7 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o
}
combinedMeta |= metaState;
+ start = cur + 1;
if (ch == '\0') {
break;
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index 3aa8950..b082c53 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -96,7 +96,6 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer)
}
void Overlay::destroy() {
- if (mStatus != NO_ERROR) return;
// Must delete the objects in reverse creation order, thus the
// data side must be closed first and then the destroy send to
@@ -104,9 +103,15 @@ void Overlay::destroy() {
if (mOverlayData) {
overlay_data_close(mOverlayData);
mOverlayData = NULL;
+ } else {
+ LOGD("Overlay::destroy mOverlayData is NULL");
}
- mOverlayRef->mOverlayChannel->destroy();
+ if (mOverlayRef != 0) {
+ mOverlayRef->mOverlayChannel->destroy();
+ } else {
+ LOGD("Overlay::destroy mOverlayRef is NULL");
+ }
}
status_t Overlay::getStatus() const {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index def88ae..9058a7b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -984,7 +984,8 @@ public class AudioManager {
* application when it places a phone call, as it will cause signals from the radio layer
* to feed the platform mixer.
*
- * @param mode the requested audio mode (NORMAL, RINGTONE, IN_CALL or IN_COMMUNICATION).
+ * @param mode the requested audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
+ * {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
* Informs the HAL about the current audio state so that
* it can route the audio appropriately.
*/
@@ -1000,7 +1001,8 @@ public class AudioManager {
/**
* Returns the current audio mode.
*
- * @return the current audio mode (NORMAL, RINGTONE, IN_CALL or IN_COMMUNICATION).
+ * @return the current audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
+ * {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
* Returns the current current audio state from the HAL.
*/
public int getMode() {
@@ -1038,7 +1040,6 @@ public class AudioManager {
*/
public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL;
/**
- * @hide
* In communication audio mode. An audio/video chat or VoIP call is established.
*/
public static final int MODE_IN_COMMUNICATION = AudioSystem.MODE_IN_COMMUNICATION;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index e48e9e8..250ec44 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -25,11 +25,11 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Environment;
import android.os.RemoteException;
+import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.MediaColumns;
-import android.provider.Mtp;
import android.util.Log;
import java.io.File;
@@ -1023,7 +1023,7 @@ public class MtpDatabase {
Log.d(TAG, "sessionEnded");
if (mDatabaseModified) {
Log.d(TAG, "sending ACTION_MTP_SESSION_END");
- mContext.sendBroadcast(new Intent(Mtp.ACTION_MTP_SESSION_END));
+ mContext.sendBroadcast(new Intent(MediaStore.ACTION_MTP_SESSION_END));
mDatabaseModified = false;
}
}
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/PtpClient.java
index dcb1983..2e40635 100644
--- a/media/java/android/media/MtpClient.java
+++ b/media/java/android/media/PtpClient.java
@@ -21,9 +21,9 @@ import android.util.Log;
/**
* {@hide}
*/
-public class MtpClient {
+public class PtpClient {
- private static final String TAG = "MtpClient";
+ private static final String TAG = "PtpClient";
private final Listener mListener;
@@ -31,10 +31,10 @@ public class MtpClient {
System.loadLibrary("media_jni");
}
- public MtpClient(Listener listener) {
+ public PtpClient(Listener listener) {
native_setup();
if (listener == null) {
- throw new NullPointerException("MtpClient: listener is null");
+ throw new NullPointerException("PtpClient: listener is null");
}
mListener = listener;
}
@@ -75,10 +75,10 @@ public class MtpClient {
}
public interface Listener {
- // called when a new MTP device has been discovered
+ // called when a new PTP device has been discovered
void deviceAdded(int id);
- // called when an MTP device has been removed
+ // called when an PTP device has been removed
void deviceRemoved(int id);
}
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/PtpCursor.java
index daa3f4d..bb5b1ec 100644
--- a/media/java/android/media/MtpCursor.java
+++ b/media/java/android/media/PtpCursor.java
@@ -18,17 +18,17 @@ package android.media;
import android.database.AbstractWindowedCursor;
import android.database.CursorWindow;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.util.Log;
import java.util.HashMap;
/**
- * Cursor class for MTP content provider
+ * Cursor class for PTP content provider
* @hide
*/
-public final class MtpCursor extends AbstractWindowedCursor {
- static final String TAG = "MtpCursor";
+public final class PtpCursor extends AbstractWindowedCursor {
+ static final String TAG = "PtpCursor";
static final int NO_COUNT = -1;
/* constants for queryType */
@@ -49,10 +49,10 @@ public final class MtpCursor extends AbstractWindowedCursor {
private int mCount = NO_COUNT;
- public MtpCursor(MtpClient client, int queryType, int deviceID, long storageID, long objectID,
+ public PtpCursor(PtpClient client, int queryType, int deviceID, long storageID, long objectID,
String[] projection) {
if (client == null) {
- throw new NullPointerException("client null in MtpCursor constructor");
+ throw new NullPointerException("client null in PtpCursor constructor");
}
mColumns = projection;
@@ -132,19 +132,19 @@ public final class MtpCursor extends AbstractWindowedCursor {
}
/* Device Column IDs */
- /* These must match the values in MtpCursor.cpp */
+ /* These must match the values in PtpCursor.cpp */
private static final int DEVICE_ROW_ID = 1;
private static final int DEVICE_MANUFACTURER = 2;
private static final int DEVICE_MODEL = 3;
/* Storage Column IDs */
- /* These must match the values in MtpCursor.cpp */
+ /* These must match the values in PtpCursor.cpp */
private static final int STORAGE_ROW_ID = 101;
private static final int STORAGE_IDENTIFIER = 102;
private static final int STORAGE_DESCRIPTION = 103;
/* Object Column IDs */
- /* These must match the values in MtpCursor.cpp */
+ /* These must match the values in PtpCursor.cpp */
private static final int OBJECT_ROW_ID = 201;
private static final int OBJECT_STORAGE_ID = 202;
private static final int OBJECT_FORMAT = 203;
@@ -173,45 +173,45 @@ public final class MtpCursor extends AbstractWindowedCursor {
static {
sDeviceProjectionMap = new HashMap<String, Integer>();
- sDeviceProjectionMap.put(Mtp.Device._ID, new Integer(DEVICE_ROW_ID));
- sDeviceProjectionMap.put(Mtp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER));
- sDeviceProjectionMap.put(Mtp.Device.MODEL, new Integer(DEVICE_MODEL));
+ sDeviceProjectionMap.put(Ptp.Device._ID, new Integer(DEVICE_ROW_ID));
+ sDeviceProjectionMap.put(Ptp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER));
+ sDeviceProjectionMap.put(Ptp.Device.MODEL, new Integer(DEVICE_MODEL));
sStorageProjectionMap = new HashMap<String, Integer>();
- sStorageProjectionMap.put(Mtp.Storage._ID, new Integer(STORAGE_ROW_ID));
- sStorageProjectionMap.put(Mtp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER));
- sStorageProjectionMap.put(Mtp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION));
+ sStorageProjectionMap.put(Ptp.Storage._ID, new Integer(STORAGE_ROW_ID));
+ sStorageProjectionMap.put(Ptp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER));
+ sStorageProjectionMap.put(Ptp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION));
sObjectProjectionMap = new HashMap<String, Integer>();
- sObjectProjectionMap.put(Mtp.Object._ID, new Integer(OBJECT_ROW_ID));
- sObjectProjectionMap.put(Mtp.Object.STORAGE_ID, new Integer(OBJECT_STORAGE_ID));
- sObjectProjectionMap.put(Mtp.Object.FORMAT, new Integer(OBJECT_FORMAT));
- sObjectProjectionMap.put(Mtp.Object.PROTECTION_STATUS, new Integer(OBJECT_PROTECTION_STATUS));
- sObjectProjectionMap.put(Mtp.Object.SIZE, new Integer(OBJECT_SIZE));
- sObjectProjectionMap.put(Mtp.Object.THUMB_FORMAT, new Integer(OBJECT_THUMB_FORMAT));
- sObjectProjectionMap.put(Mtp.Object.THUMB_SIZE, new Integer(OBJECT_THUMB_SIZE));
- sObjectProjectionMap.put(Mtp.Object.THUMB_WIDTH, new Integer(OBJECT_THUMB_WIDTH));
- sObjectProjectionMap.put(Mtp.Object.THUMB_HEIGHT, new Integer(OBJECT_THUMB_HEIGHT));
- sObjectProjectionMap.put(Mtp.Object.IMAGE_WIDTH, new Integer(OBJECT_IMAGE_WIDTH));
- sObjectProjectionMap.put(Mtp.Object.IMAGE_HEIGHT, new Integer(OBJECT_IMAGE_HEIGHT));
- sObjectProjectionMap.put(Mtp.Object.IMAGE_DEPTH, new Integer(OBJECT_IMAGE_DEPTH));
- sObjectProjectionMap.put(Mtp.Object.PARENT, new Integer(OBJECT_PARENT));
- sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_TYPE, new Integer(OBJECT_ASSOCIATION_TYPE));
- sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_DESC, new Integer(OBJECT_ASSOCIATION_DESC));
- sObjectProjectionMap.put(Mtp.Object.SEQUENCE_NUMBER, new Integer(OBJECT_SEQUENCE_NUMBER));
- sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
- sObjectProjectionMap.put(Mtp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
- sObjectProjectionMap.put(Mtp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
- sObjectProjectionMap.put(Mtp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
- sObjectProjectionMap.put(Mtp.Object.THUMB, new Integer(OBJECT_THUMB));
-
- sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
+ sObjectProjectionMap.put(Ptp.Object._ID, new Integer(OBJECT_ROW_ID));
+ sObjectProjectionMap.put(Ptp.Object.STORAGE_ID, new Integer(OBJECT_STORAGE_ID));
+ sObjectProjectionMap.put(Ptp.Object.FORMAT, new Integer(OBJECT_FORMAT));
+ sObjectProjectionMap.put(Ptp.Object.PROTECTION_STATUS, new Integer(OBJECT_PROTECTION_STATUS));
+ sObjectProjectionMap.put(Ptp.Object.SIZE, new Integer(OBJECT_SIZE));
+ sObjectProjectionMap.put(Ptp.Object.THUMB_FORMAT, new Integer(OBJECT_THUMB_FORMAT));
+ sObjectProjectionMap.put(Ptp.Object.THUMB_SIZE, new Integer(OBJECT_THUMB_SIZE));
+ sObjectProjectionMap.put(Ptp.Object.THUMB_WIDTH, new Integer(OBJECT_THUMB_WIDTH));
+ sObjectProjectionMap.put(Ptp.Object.THUMB_HEIGHT, new Integer(OBJECT_THUMB_HEIGHT));
+ sObjectProjectionMap.put(Ptp.Object.IMAGE_WIDTH, new Integer(OBJECT_IMAGE_WIDTH));
+ sObjectProjectionMap.put(Ptp.Object.IMAGE_HEIGHT, new Integer(OBJECT_IMAGE_HEIGHT));
+ sObjectProjectionMap.put(Ptp.Object.IMAGE_DEPTH, new Integer(OBJECT_IMAGE_DEPTH));
+ sObjectProjectionMap.put(Ptp.Object.PARENT, new Integer(OBJECT_PARENT));
+ sObjectProjectionMap.put(Ptp.Object.ASSOCIATION_TYPE, new Integer(OBJECT_ASSOCIATION_TYPE));
+ sObjectProjectionMap.put(Ptp.Object.ASSOCIATION_DESC, new Integer(OBJECT_ASSOCIATION_DESC));
+ sObjectProjectionMap.put(Ptp.Object.SEQUENCE_NUMBER, new Integer(OBJECT_SEQUENCE_NUMBER));
+ sObjectProjectionMap.put(Ptp.Object.NAME, new Integer(OBJECT_NAME));
+ sObjectProjectionMap.put(Ptp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
+ sObjectProjectionMap.put(Ptp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
+ sObjectProjectionMap.put(Ptp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
+ sObjectProjectionMap.put(Ptp.Object.THUMB, new Integer(OBJECT_THUMB));
+
+ sObjectProjectionMap.put(Ptp.Object.NAME, new Integer(OBJECT_NAME));
}
// used by the JNI code
private int mNativeContext;
- private native final void native_setup(MtpClient client, int queryType,
+ private native final void native_setup(PtpClient client, int queryType,
int deviceID, long storageID, long objectID, int[] columns);
private native final void native_finalize();
private native void native_wait_for_event();
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index fa8d61b..e6e9bc2 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -17,6 +17,7 @@
package android.media.videoeditor;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import android.graphics.Bitmap;
@@ -178,12 +179,11 @@ public class MediaImageItem extends MediaItem {
// duration change.
invalidateEndTransition();
- final long oldDurationMs = mDurationMs;
mDurationMs = durationMs;
adjustTransitions();
- adjustOverlays();
- adjustEffects();
+ final List<Overlay> adjustedOverlays = adjustOverlays();
+ final List<Effect> adjustedEffects = adjustEffects();
// Invalidate the beginning and end transitions after adjustments.
// This invalidation is necessary for the case in which an effect or
@@ -191,11 +191,7 @@ public class MediaImageItem extends MediaItem {
// before the setDuration reduces the duration of the media item and
// causes an overlap of the beginning and/or end transition with the
// effect.
- // If the duration is growing, the begin transition does not need to
- // be invalidated since the effects, overlays are not adjusted.
- if (mDurationMs < oldDurationMs) {
- invalidateBeginTransition();
- }
+ invalidateBeginTransition(adjustedEffects, adjustedOverlays);
invalidateEndTransition();
}
@@ -293,14 +289,16 @@ public class MediaImageItem extends MediaItem {
/**
* Invalidate the begin transition if any effects and overlays overlap
* with the begin transition.
+ *
+ * @param effects List of effects to check for transition overlap
+ * @param overlays List of overlays to check for transition overlap
*/
- private void invalidateBeginTransition() {
+ private void invalidateBeginTransition(List<Effect> effects, List<Overlay> overlays) {
if (mBeginTransition != null && mBeginTransition.isGenerated()) {
final long transitionDurationMs = mBeginTransition.getDuration();
// The begin transition must be invalidated if it overlaps with
// an effect.
- final List<Effect> effects = getAllEffects();
for (Effect effect : effects) {
// Check if the effect overlaps with the begin transition
if (effect.getStartTime() < transitionDurationMs) {
@@ -312,7 +310,6 @@ public class MediaImageItem extends MediaItem {
if (mBeginTransition.isGenerated()) {
// The end transition must be invalidated if it overlaps with
// an overlay.
- final List<Overlay> overlays = getAllOverlays();
for (Overlay overlay : overlays) {
// Check if the overlay overlaps with the end transition
if (overlay.getStartTime() < transitionDurationMs) {
@@ -362,8 +359,11 @@ public class MediaImageItem extends MediaItem {
/**
* Adjust the start time and/or duration of effects.
+ *
+ * @return The list of effects which were adjusted
*/
- private void adjustEffects() {
+ private List<Effect> adjustEffects() {
+ final List<Effect> adjustedEffects = new ArrayList<Effect>();
final List<Effect> effects = getAllEffects();
for (Effect effect : effects) {
// Adjust the start time if necessary
@@ -385,14 +385,20 @@ public class MediaImageItem extends MediaItem {
if (effectStartTimeMs != effect.getStartTime() ||
effectDurationMs != effect.getDuration()) {
effect.setStartTimeAndDuration(effectStartTimeMs, effectDurationMs);
+ adjustedEffects.add(effect);
}
}
+
+ return adjustedEffects;
}
/**
* Adjust the start time and/or duration of overlays.
+ *
+ * @return The list of overlays which were adjusted
*/
- private void adjustOverlays() {
+ private List<Overlay> adjustOverlays() {
+ final List<Overlay> adjustedOverlays = new ArrayList<Overlay>();
final List<Overlay> overlays = getAllOverlays();
for (Overlay overlay : overlays) {
// Adjust the start time if necessary
@@ -414,8 +420,11 @@ public class MediaImageItem extends MediaItem {
if (overlayStartTimeMs != overlay.getStartTime() ||
overlayDurationMs != overlay.getDuration()) {
overlay.setStartTimeAndDuration(overlayStartTimeMs, overlayDurationMs);
+ adjustedOverlays.add(overlay);
}
}
+
+ return adjustedOverlays;
}
/**
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 25d243b..fbdfa67 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -9,10 +9,10 @@ LOCAL_SRC_FILES:= \
android_media_ResampleInputStream.cpp \
android_media_MediaProfiles.cpp \
android_media_AmrInputStream.cpp \
- android_media_MtpClient.cpp \
- android_media_MtpCursor.cpp \
android_media_MtpDatabase.cpp \
android_media_MtpServer.cpp \
+ android_media_PtpClient.cpp \
+ android_media_PtpCursor.cpp \
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 997d017..28aef0c 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -774,8 +774,8 @@ extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
-extern int register_android_media_MtpClient(JNIEnv *env);
-extern int register_android_media_MtpCursor(JNIEnv *env);
+extern int register_android_media_PtpClient(JNIEnv *env);
+extern int register_android_media_PtpCursor(JNIEnv *env);
extern int register_android_media_MtpDatabase(JNIEnv *env);
extern int register_android_media_MtpServer(JNIEnv *env);
extern int register_android_media_AmrInputStream(JNIEnv *env);
@@ -826,13 +826,13 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
- if (register_android_media_MtpClient(env) < 0) {
- LOGE("ERROR: MtpClient native registration failed");
+ if (register_android_media_PtpClient(env) < 0) {
+ LOGE("ERROR: PtpClient native registration failed");
goto bail;
}
- if (register_android_media_MtpCursor(env) < 0) {
- LOGE("ERROR: MtpCursor native registration failed");
+ if (register_android_media_PtpCursor(env) < 0) {
+ LOGE("ERROR: PtpCursor native registration failed");
goto bail;
}
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index 4525d1f..f04a2ae 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -948,18 +948,21 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
result = new MtpProperty(property, MTP_TYPE_UINT128);
break;
case MTP_PROPERTY_NAME:
- case MTP_PROPERTY_DATE_MODIFIED:
case MTP_PROPERTY_DISPLAY_NAME:
- case MTP_PROPERTY_DATE_ADDED:
case MTP_PROPERTY_ARTIST:
case MTP_PROPERTY_ALBUM_NAME:
case MTP_PROPERTY_ALBUM_ARTIST:
- case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
case MTP_PROPERTY_GENRE:
case MTP_PROPERTY_COMPOSER:
case MTP_PROPERTY_DESCRIPTION:
result = new MtpProperty(property, MTP_TYPE_STR);
break;
+ case MTP_PROPERTY_DATE_MODIFIED:
+ case MTP_PROPERTY_DATE_ADDED:
+ case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+ result = new MtpProperty(property, MTP_TYPE_STR);
+ result->setFormDateTime();
+ break;
case MTP_PROPERTY_OBJECT_FILE_NAME:
// We allow renaming files and folders
result = new MtpProperty(property, MTP_TYPE_STR, true);
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_PtpClient.cpp
index 144dfc8..6af83e4 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_PtpClient.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MtpClientJNI"
+#define LOG_TAG "PtpClientJNI"
#include "utils/Log.h"
#include <stdio.h>
@@ -103,7 +103,7 @@ void MyClient::deviceRemoved(MtpDevice *device) {
// ----------------------------------------------------------------------------
static void
-android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
+android_media_PtpClient_setup(JNIEnv *env, jobject thiz)
{
#ifdef HAVE_ANDROID_OS
LOGD("setup\n");
@@ -114,7 +114,7 @@ android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
}
static void
-android_media_MtpClient_finalize(JNIEnv *env, jobject thiz)
+android_media_PtpClient_finalize(JNIEnv *env, jobject thiz)
{
#ifdef HAVE_ANDROID_OS
LOGD("finalize\n");
@@ -126,7 +126,7 @@ android_media_MtpClient_finalize(JNIEnv *env, jobject thiz)
}
static jboolean
-android_media_MtpClient_start(JNIEnv *env, jobject thiz)
+android_media_PtpClient_start(JNIEnv *env, jobject thiz)
{
#ifdef HAVE_ANDROID_OS
LOGD("start\n");
@@ -138,7 +138,7 @@ android_media_MtpClient_start(JNIEnv *env, jobject thiz)
}
static void
-android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
+android_media_PtpClient_stop(JNIEnv *env, jobject thiz)
{
#ifdef HAVE_ANDROID_OS
LOGD("stop\n");
@@ -148,7 +148,7 @@ android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
}
static jboolean
-android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
+android_media_PtpClient_delete_object(JNIEnv *env, jobject thiz,
jint device_id, jlong object_id)
{
#ifdef HAVE_ANDROID_OS
@@ -162,7 +162,7 @@ android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
}
static jlong
-android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
+android_media_PtpClient_get_parent(JNIEnv *env, jobject thiz,
jint device_id, jlong object_id)
{
#ifdef HAVE_ANDROID_OS
@@ -176,7 +176,7 @@ android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
}
static jlong
-android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
+android_media_PtpClient_get_storage_id(JNIEnv *env, jobject thiz,
jint device_id, jlong object_id)
{
#ifdef HAVE_ANDROID_OS
@@ -190,7 +190,7 @@ android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
}
static jboolean
-android_media_MtpClient_import_file(JNIEnv *env, jobject thiz,
+android_media_PtpClient_import_file(JNIEnv *env, jobject thiz,
jint device_id, jlong object_id, jstring dest_path)
{
#ifdef HAVE_ANDROID_OS
@@ -209,28 +209,28 @@ android_media_MtpClient_import_file(JNIEnv *env, jobject thiz,
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"native_setup", "()V", (void *)android_media_MtpClient_setup},
- {"native_finalize", "()V", (void *)android_media_MtpClient_finalize},
- {"native_start", "()Z", (void *)android_media_MtpClient_start},
- {"native_stop", "()V", (void *)android_media_MtpClient_stop},
- {"native_delete_object", "(IJ)Z", (void *)android_media_MtpClient_delete_object},
- {"native_get_parent", "(IJ)J", (void *)android_media_MtpClient_get_parent},
- {"native_get_storage_id", "(IJ)J", (void *)android_media_MtpClient_get_storage_id},
+ {"native_setup", "()V", (void *)android_media_PtpClient_setup},
+ {"native_finalize", "()V", (void *)android_media_PtpClient_finalize},
+ {"native_start", "()Z", (void *)android_media_PtpClient_start},
+ {"native_stop", "()V", (void *)android_media_PtpClient_stop},
+ {"native_delete_object", "(IJ)Z", (void *)android_media_PtpClient_delete_object},
+ {"native_get_parent", "(IJ)J", (void *)android_media_PtpClient_get_parent},
+ {"native_get_storage_id", "(IJ)J", (void *)android_media_PtpClient_get_storage_id},
{"native_import_file", "(IJLjava/lang/String;)Z",
- (void *)android_media_MtpClient_import_file},
+ (void *)android_media_PtpClient_import_file},
};
-static const char* const kClassPathName = "android/media/MtpClient";
+static const char* const kClassPathName = "android/media/PtpClient";
-int register_android_media_MtpClient(JNIEnv *env)
+int register_android_media_PtpClient(JNIEnv *env)
{
jclass clazz;
- LOGD("register_android_media_MtpClient\n");
+ LOGD("register_android_media_PtpClient\n");
- clazz = env->FindClass("android/media/MtpClient");
+ clazz = env->FindClass("android/media/PtpClient");
if (clazz == NULL) {
- LOGE("Can't find android/media/MtpClient");
+ LOGE("Can't find android/media/PtpClient");
return -1;
}
method_deviceAdded = env->GetMethodID(clazz, "deviceAdded", "(I)V");
@@ -245,10 +245,10 @@ int register_android_media_MtpClient(JNIEnv *env)
}
field_context = env->GetFieldID(clazz, "mNativeContext", "I");
if (field_context == NULL) {
- LOGE("Can't find MtpClient.mNativeContext");
+ LOGE("Can't find PtpClient.mNativeContext");
return -1;
}
return AndroidRuntime::registerNativeMethods(env,
- "android/media/MtpClient", gMethods, NELEM(gMethods));
+ "android/media/PtpClient", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_PtpCursor.cpp
index 7a0ae8a..76c88f6 100644
--- a/media/jni/android_media_MtpCursor.cpp
+++ b/media/jni/android_media_PtpCursor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MtpCursorJNI"
+#define LOG_TAG "PtpCursorJNI"
#include "utils/Log.h"
#include <stdio.h>
@@ -29,7 +29,7 @@
#include "binder/CursorWindow.h"
#include "MtpClient.h"
-#include "MtpCursor.h"
+#include "PtpCursor.h"
using namespace android;
@@ -37,7 +37,7 @@ using namespace android;
static jfieldID field_context;
-// From android_media_MtpClient.cpp
+// From android_media_PtpClient.cpp
MtpClient * get_client_from_object(JNIEnv * env, jobject javaClient);
// ----------------------------------------------------------------------------
@@ -48,11 +48,11 @@ static bool ExceptionCheck(void* env)
}
static void
-android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
+android_media_PtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
jint queryType, jint deviceID, jlong storageID, jlong objectID, jintArray javaColumns)
{
#ifdef HAVE_ANDROID_OS
- LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %lld objectID: %lld\n",
+ LOGD("android_media_PtpCursor_setup queryType: %d deviceID: %d storageID: %lld objectID: %lld\n",
queryType, deviceID, storageID, objectID);
int* columns = NULL;
@@ -63,7 +63,7 @@ android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
}
MtpClient* client = get_client_from_object(env, javaClient);
- MtpCursor* cursor = new MtpCursor(client, queryType,
+ PtpCursor* cursor = new PtpCursor(client, queryType,
deviceID, storageID, objectID, columnCount, columns);
if (columns)
@@ -73,17 +73,17 @@ android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
}
static void
-android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz)
+android_media_PtpCursor_finalize(JNIEnv *env, jobject thiz)
{
#ifdef HAVE_ANDROID_OS
LOGD("finalize\n");
- MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+ PtpCursor *cursor = (PtpCursor *)env->GetIntField(thiz, field_context);
delete cursor;
#endif
}
static jint
-android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
+android_media_PtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
{
#ifdef HAVE_ANDROID_OS
CursorWindow* window = get_window_from_object(env, javaWindow);
@@ -93,7 +93,7 @@ android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindo
"Bad CursorWindow");
return 0;
}
- MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+ PtpCursor *cursor = (PtpCursor *)env->GetIntField(thiz, field_context);
return cursor->fillWindow(window, startPos);
#else
@@ -104,33 +104,33 @@ android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindo
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"native_setup", "(Landroid/media/MtpClient;IIJJ[I)V",
- (void *)android_media_MtpCursor_setup},
- {"native_finalize", "()V", (void *)android_media_MtpCursor_finalize},
+ {"native_setup", "(Landroid/media/PtpClient;IIJJ[I)V",
+ (void *)android_media_PtpCursor_setup},
+ {"native_finalize", "()V", (void *)android_media_PtpCursor_finalize},
{"native_fill_window", "(Landroid/database/CursorWindow;I)I",
- (void *)android_media_MtpCursor_fill_window},
+ (void *)android_media_PtpCursor_fill_window},
};
-static const char* const kClassPathName = "android/media/MtpCursor";
+static const char* const kClassPathName = "android/media/PtpCursor";
-int register_android_media_MtpCursor(JNIEnv *env)
+int register_android_media_PtpCursor(JNIEnv *env)
{
jclass clazz;
- LOGD("register_android_media_MtpCursor\n");
+ LOGD("register_android_media_PtpCursor\n");
- clazz = env->FindClass("android/media/MtpCursor");
+ clazz = env->FindClass("android/media/PtpCursor");
if (clazz == NULL) {
- LOGE("Can't find android/media/MtpCursor");
+ LOGE("Can't find android/media/PtpCursor");
return -1;
}
field_context = env->GetFieldID(clazz, "mNativeContext", "I");
if (field_context == NULL) {
- LOGE("Can't find MtpCursor.mNativeContext");
+ LOGE("Can't find PtpCursor.mNativeContext");
return -1;
}
return AndroidRuntime::registerNativeMethods(env,
- "android/media/MtpCursor", gMethods, NELEM(gMethods));
+ "android/media/PtpCursor", gMethods, NELEM(gMethods));
}
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 3341ff7..a9d537f 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -31,7 +31,6 @@ LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libstagefright \
libstagefright_omx \
- libstagefright_color_conversion \
libstagefright_foundation \
libsurfaceflinger_client
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 4ad1eb4..db23836 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -71,6 +71,7 @@ LOCAL_SHARED_LIBRARIES := \
libcrypto
LOCAL_STATIC_LIBRARIES := \
+ libstagefright_color_conversion \
libstagefright_aacdec \
libstagefright_aacenc \
libstagefright_amrnbdec \
@@ -97,7 +98,6 @@ LOCAL_SHARED_LIBRARIES += \
libstagefright_enc_common \
libstagefright_avc_common \
libstagefright_foundation \
- libstagefright_color_conversion
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
LOCAL_LDLIBS += -lpthread -ldl
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 922aaa8..ec58919 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -81,16 +81,8 @@ private:
struct AwesomeLocalRenderer : public AwesomeRenderer {
AwesomeLocalRenderer(
- OMX_COLOR_FORMATTYPE colorFormat,
- const sp<Surface> &surface,
- size_t displayWidth, size_t displayHeight,
- size_t decodedWidth, size_t decodedHeight,
- int32_t rotationDegrees)
- : mTarget(NULL) {
- init(colorFormat, surface,
- displayWidth, displayHeight,
- decodedWidth, decodedHeight,
- rotationDegrees);
+ const sp<Surface> &surface, const sp<MetaData> &meta)
+ : mTarget(new SoftwareRenderer(surface, meta)) {
}
virtual void render(MediaBuffer *buffer) {
@@ -111,28 +103,10 @@ protected:
private:
SoftwareRenderer *mTarget;
- void init(
- OMX_COLOR_FORMATTYPE colorFormat,
- const sp<Surface> &surface,
- size_t displayWidth, size_t displayHeight,
- size_t decodedWidth, size_t decodedHeight,
- int32_t rotationDegrees);
-
AwesomeLocalRenderer(const AwesomeLocalRenderer &);
AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
};
-void AwesomeLocalRenderer::init(
- OMX_COLOR_FORMATTYPE colorFormat,
- const sp<Surface> &surface,
- size_t displayWidth, size_t displayHeight,
- size_t decodedWidth, size_t decodedHeight,
- int32_t rotationDegrees) {
- mTarget = new SoftwareRenderer(
- colorFormat, surface, displayWidth, displayHeight,
- decodedWidth, decodedHeight, rotationDegrees);
-}
-
struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
AwesomeNativeWindowRenderer(
const sp<ANativeWindow> &nativeWindow,
@@ -188,9 +162,7 @@ AwesomePlayer::AwesomePlayer()
mAudioPlayer(NULL),
mFlags(0),
mExtractorFlags(0),
- mLastVideoBuffer(NULL),
mVideoBuffer(NULL),
- mSuspensionState(NULL),
mDecryptHandle(NULL) {
CHECK_EQ(mClient.connect(), OK);
@@ -433,11 +405,6 @@ void AwesomePlayer::reset_l() {
mVideoRenderer.clear();
- if (mLastVideoBuffer) {
- mLastVideoBuffer->release();
- mLastVideoBuffer = NULL;
- }
-
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
@@ -469,7 +436,6 @@ void AwesomePlayer::reset_l() {
mDurationUs = -1;
mFlags = 0;
mExtractorFlags = 0;
- mVideoWidth = mVideoHeight = -1;
mTimeSourceDeltaUs = 0;
mVideoTimeUs = 0;
@@ -482,9 +448,6 @@ void AwesomePlayer::reset_l() {
mFileSource.clear();
- delete mSuspensionState;
- mSuspensionState = NULL;
-
mBitrate = -1;
}
@@ -637,11 +600,6 @@ void AwesomePlayer::partial_reset_l() {
mVideoRenderer.clear();
- if (mLastVideoBuffer) {
- mLastVideoBuffer->release();
- mLastVideoBuffer = NULL;
- }
-
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
@@ -749,6 +707,13 @@ status_t AwesomePlayer::play_l() {
bool deferredAudioSeek = false;
+ if (mDecryptHandle != NULL) {
+ int64_t position;
+ getPosition(&position);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, position / 1000);
+ }
+
if (mAudioSource != NULL) {
if (mAudioPlayer == NULL) {
if (mAudioSink != NULL) {
@@ -766,6 +731,11 @@ status_t AwesomePlayer::play_l() {
mFlags &= ~(PLAYING | FIRST_FRAME);
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::STOP, 0);
+ }
+
return err;
}
@@ -802,22 +772,31 @@ status_t AwesomePlayer::play_l() {
seekTo_l(0);
}
- if (mDecryptHandle != NULL) {
- int64_t position;
- getPosition(&position);
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
- Playback::START, position / 1000);
- }
-
return OK;
}
void AwesomePlayer::notifyVideoSize_l() {
sp<MetaData> meta = mVideoSource->getFormat();
- int32_t decodedWidth, decodedHeight;
- CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
- CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+ int32_t cropLeft, cropTop, cropRight, cropBottom;
+ if (!meta->findRect(
+ kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ cropLeft = cropTop = 0;
+ cropRight = width - 1;
+ cropBottom = height - 1;
+
+ LOGV("got dimensions only %d x %d", width, height);
+ } else {
+ LOGV("got crop rect %d, %d, %d, %d",
+ cropLeft, cropTop, cropRight, cropBottom);
+ }
+
+ int32_t usableWidth = cropRight - cropLeft + 1;
+ int32_t usableHeight = cropBottom - cropTop + 1;
int32_t rotationDegrees;
if (!mVideoTrack->getFormat()->findInt32(
@@ -827,10 +806,10 @@ void AwesomePlayer::notifyVideoSize_l() {
if (rotationDegrees == 90 || rotationDegrees == 270) {
notifyListener_l(
- MEDIA_SET_VIDEO_SIZE, decodedHeight, decodedWidth);
+ MEDIA_SET_VIDEO_SIZE, usableHeight, usableWidth);
} else {
notifyListener_l(
- MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+ MEDIA_SET_VIDEO_SIZE, usableWidth, usableHeight);
}
}
@@ -872,12 +851,7 @@ void AwesomePlayer::initRenderer_l() {
// allocate their buffers in local address space. This renderer
// then performs a color conversion and copy to get the data
// into the ANativeBuffer.
- mVideoRenderer = new AwesomeLocalRenderer(
- (OMX_COLOR_FORMATTYPE)format,
- mSurface,
- mVideoWidth, mVideoHeight,
- decodedWidth, decodedHeight,
- rotationDegrees);
+ mVideoRenderer = new AwesomeLocalRenderer(mSurface, meta);
}
}
@@ -1041,20 +1015,6 @@ void AwesomePlayer::seekAudioIfNecessary_l() {
}
}
-status_t AwesomePlayer::getVideoDimensions(
- int32_t *width, int32_t *height) const {
- Mutex::Autolock autoLock(mLock);
-
- if (mVideoWidth < 0 || mVideoHeight < 0) {
- return UNKNOWN_ERROR;
- }
-
- *width = mVideoWidth;
- *height = mVideoHeight;
-
- return OK;
-}
-
void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
CHECK(source != NULL);
@@ -1123,9 +1083,6 @@ status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
}
}
- CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
- CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
-
status_t err = mVideoSource->start();
if (err != OK) {
@@ -1180,11 +1137,6 @@ void AwesomePlayer::onVideoEvent() {
mVideoEventPending = false;
if (mSeeking) {
- if (mLastVideoBuffer) {
- mLastVideoBuffer->release();
- mLastVideoBuffer = NULL;
- }
-
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
@@ -1327,11 +1279,7 @@ void AwesomePlayer::onVideoEvent() {
mVideoRenderer->render(mVideoBuffer);
}
- if (mLastVideoBuffer) {
- mLastVideoBuffer->release();
- mLastVideoBuffer = NULL;
- }
- mLastVideoBuffer = mVideoBuffer;
+ mVideoBuffer->release();
mVideoBuffer = NULL;
postVideoEvent_l();
@@ -1745,142 +1693,6 @@ void AwesomePlayer::finishAsyncPrepare_l() {
mPreparedCondition.broadcast();
}
-status_t AwesomePlayer::suspend() {
- LOGV("suspend");
- Mutex::Autolock autoLock(mLock);
-
- if (mSuspensionState != NULL) {
- if (mLastVideoBuffer == NULL) {
- //go into here if video is suspended again
- //after resuming without being played between
- //them
- SuspensionState *state = mSuspensionState;
- mSuspensionState = NULL;
- reset_l();
- mSuspensionState = state;
- return OK;
- }
-
- delete mSuspensionState;
- mSuspensionState = NULL;
- }
-
- if (mFlags & PREPARING) {
- mFlags |= PREPARE_CANCELLED;
- if (mConnectingDataSource != NULL) {
- LOGI("interrupting the connection process");
- mConnectingDataSource->disconnect();
- }
- }
-
- while (mFlags & PREPARING) {
- mPreparedCondition.wait(mLock);
- }
-
- SuspensionState *state = new SuspensionState;
- state->mUri = mUri;
- state->mUriHeaders = mUriHeaders;
- state->mFileSource = mFileSource;
-
- state->mFlags = mFlags & (PLAYING | AUTO_LOOPING | LOOPING | AT_EOS);
- getPosition(&state->mPositionUs);
-
- if (mLastVideoBuffer) {
- size_t size = mLastVideoBuffer->range_length();
-
- if (size) {
- int32_t unreadable;
- if (!mLastVideoBuffer->meta_data()->findInt32(
- kKeyIsUnreadable, &unreadable)
- || unreadable == 0) {
- state->mLastVideoFrameSize = size;
- state->mLastVideoFrame = malloc(size);
- memcpy(state->mLastVideoFrame,
- (const uint8_t *)mLastVideoBuffer->data()
- + mLastVideoBuffer->range_offset(),
- size);
-
- state->mVideoWidth = mVideoWidth;
- state->mVideoHeight = mVideoHeight;
-
- sp<MetaData> meta = mVideoSource->getFormat();
- CHECK(meta->findInt32(kKeyColorFormat, &state->mColorFormat));
- CHECK(meta->findInt32(kKeyWidth, &state->mDecodedWidth));
- CHECK(meta->findInt32(kKeyHeight, &state->mDecodedHeight));
- } else {
- LOGV("Unable to save last video frame, we have no access to "
- "the decoded video data.");
- }
- }
- }
-
- reset_l();
-
- mSuspensionState = state;
-
- return OK;
-}
-
-status_t AwesomePlayer::resume() {
- LOGV("resume");
- Mutex::Autolock autoLock(mLock);
-
- if (mSuspensionState == NULL) {
- return INVALID_OPERATION;
- }
-
- SuspensionState *state = mSuspensionState;
- mSuspensionState = NULL;
-
- status_t err;
- if (state->mFileSource != NULL) {
- err = setDataSource_l(state->mFileSource);
-
- if (err == OK) {
- mFileSource = state->mFileSource;
- }
- } else {
- err = setDataSource_l(state->mUri, &state->mUriHeaders);
- }
-
- if (err != OK) {
- delete state;
- state = NULL;
-
- return err;
- }
-
- seekTo_l(state->mPositionUs);
-
- mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS);
-
- if (state->mLastVideoFrame && mSurface != NULL) {
- mVideoRenderer =
- new AwesomeLocalRenderer(
- (OMX_COLOR_FORMATTYPE)state->mColorFormat,
- mSurface,
- state->mVideoWidth,
- state->mVideoHeight,
- state->mDecodedWidth,
- state->mDecodedHeight,
- 0);
-
- mVideoRendererIsPreview = true;
-
- ((AwesomeLocalRenderer *)mVideoRenderer.get())->render(
- state->mLastVideoFrame, state->mLastVideoFrameSize);
- }
-
- if (state->mFlags & PLAYING) {
- play_l();
- }
-
- mSuspensionState = state;
- state = NULL;
-
- return OK;
-}
-
uint32_t AwesomePlayer::flags() const {
return mExtractorFlags;
}
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 63b476e..884f3b4 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -70,6 +70,19 @@ bool MetaData::setPointer(uint32_t key, void *value) {
return setData(key, TYPE_POINTER, &value, sizeof(value));
}
+bool MetaData::setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom) {
+ Rect r;
+ r.mLeft = left;
+ r.mTop = top;
+ r.mRight = right;
+ r.mBottom = bottom;
+
+ return setData(key, TYPE_RECT, &r, sizeof(r));
+}
+
bool MetaData::findCString(uint32_t key, const char **value) {
uint32_t type;
const void *data;
@@ -143,6 +156,28 @@ bool MetaData::findPointer(uint32_t key, void **value) {
return true;
}
+bool MetaData::findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(Rect));
+
+ const Rect *r = (const Rect *)data;
+ *left = r->mLeft;
+ *top = r->mTop;
+ *right = r->mRight;
+ *bottom = r->mBottom;
+
+ return true;
+}
+
bool MetaData::setData(
uint32_t key, uint32_t type, const void *data, size_t size) {
bool overwrote_existing = true;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6ca0f4f..3108e4e 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -38,11 +38,11 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/HardwareAPI.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
@@ -526,7 +526,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
size_t size;
if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
- CHECK_EQ(esds.InitCheck(), OK);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
const void *codec_specific_data;
size_t codec_specific_data_size;
@@ -541,7 +541,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
const uint8_t *ptr = (const uint8_t *)data;
CHECK(size >= 7);
- CHECK_EQ(ptr[0], 1); // configurationVersion == 1
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
uint8_t profile = ptr[1];
uint8_t level = ptr[3];
@@ -730,7 +730,7 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus))
|| (def.nBufferSize < size)) {
@@ -739,11 +739,11 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) {
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
// Make sure the setting actually stuck.
if (portIndex == kPortIndexInput
@@ -923,7 +923,7 @@ void OMXCodec::setVideoInputFormat(
}
OMX_COLOR_FORMATTYPE colorFormat;
- CHECK_EQ(OK, findTargetColorFormat(meta, &colorFormat));
+ CHECK_EQ((status_t)OK, findTargetColorFormat(meta, &colorFormat));
status_t err;
OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -932,19 +932,19 @@ void OMXCodec::setVideoInputFormat(
//////////////////////// Input port /////////////////////////
CHECK_EQ(setVideoPortFormatType(
kPortIndexInput, OMX_VIDEO_CodingUnused,
- colorFormat), OK);
+ colorFormat), (status_t)OK);
InitOMXParams(&def);
def.nPortIndex = kPortIndexInput;
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
def.nBufferSize = getFrameSize(colorFormat,
stride > 0? stride: -stride, sliceHeight);
- CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
@@ -956,20 +956,20 @@ void OMXCodec::setVideoInputFormat(
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
//////////////////////// Output port /////////////////////////
CHECK_EQ(setVideoPortFormatType(
kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
- OK);
+ (status_t)OK);
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
- CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+ CHECK_EQ(err, (status_t)OK);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
@@ -984,23 +984,23 @@ void OMXCodec::setVideoInputFormat(
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
/////////////////// Codec-specific ////////////////////////
switch (compressionFormat) {
case OMX_VIDEO_CodingMPEG4:
{
- CHECK_EQ(setupMPEG4EncoderParameters(meta), OK);
+ CHECK_EQ(setupMPEG4EncoderParameters(meta), (status_t)OK);
break;
}
case OMX_VIDEO_CodingH263:
- CHECK_EQ(setupH263EncoderParameters(meta), OK);
+ CHECK_EQ(setupH263EncoderParameters(meta), (status_t)OK);
break;
case OMX_VIDEO_CodingAVC:
{
- CHECK_EQ(setupAVCEncoderParameters(meta), OK);
+ CHECK_EQ(setupAVCEncoderParameters(meta), (status_t)OK);
break;
}
@@ -1059,7 +1059,7 @@ status_t OMXCodec::setupBitRate(int32_t bitRate) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoBitrate,
&bitrateType, sizeof(bitrateType));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
bitrateType.nTargetBitrate = bitRate;
@@ -1067,7 +1067,7 @@ status_t OMXCodec::setupBitRate(int32_t bitRate) {
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoBitrate,
&bitrateType, sizeof(bitrateType));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
return OK;
}
@@ -1132,7 +1132,7 @@ status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
h263type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
@@ -1159,10 +1159,10 @@ status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(setupBitRate(bitRate), OK);
- CHECK_EQ(setupErrorCorrectionParameters(), OK);
+ CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
+ CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
return OK;
}
@@ -1179,7 +1179,7 @@ status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
mpeg4type.nSliceHeaderSpacing = 0;
mpeg4type.bSVH = OMX_FALSE;
@@ -1211,10 +1211,10 @@ status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(setupBitRate(bitRate), OK);
- CHECK_EQ(setupErrorCorrectionParameters(), OK);
+ CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
+ CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
return OK;
}
@@ -1232,7 +1232,7 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
h264type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
@@ -1284,9 +1284,9 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) {
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(setupBitRate(bitRate), OK);
+ CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
return OK;
}
@@ -1324,8 +1324,8 @@ status_t OMXCodec::setVideoOutputFormat(
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
- CHECK_EQ(err, OK);
- CHECK_EQ(format.eCompressionFormat, OMX_VIDEO_CodingUnused);
+ CHECK_EQ(err, (status_t)OK);
+ CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
@@ -1353,7 +1353,7 @@ status_t OMXCodec::setVideoOutputFormat(
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
#if 1
// XXX Need a (much) better heuristic to compute input buffer sizes.
@@ -1363,7 +1363,7 @@ status_t OMXCodec::setVideoOutputFormat(
}
#endif
- CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
@@ -1385,8 +1385,8 @@ status_t OMXCodec::setVideoOutputFormat(
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
- CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+ CHECK_EQ(err, (status_t)OK);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
#if 0
def.nBufferSize =
@@ -1510,7 +1510,7 @@ OMXCodec::~OMXCodec() {
CHECK(mState == LOADED || mState == ERROR);
status_t err = mOMX->freeNode(mNode);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
mNode = NULL;
setState(DEAD);
@@ -1527,21 +1527,21 @@ OMXCodec::~OMXCodec() {
status_t OMXCodec::init() {
// mLock is held.
- CHECK_EQ(mState, LOADED);
+ CHECK_EQ((int)mState, (int)LOADED);
status_t err;
if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
setState(LOADED_TO_IDLE);
}
err = allocateBuffers();
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
setState(LOADED_TO_IDLE);
}
@@ -1896,10 +1896,10 @@ void OMXCodec::on_message(const omx_message &msg) {
CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
status_t err = freeBuffer(kPortIndexInput, i);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
} else if (mState != ERROR
&& mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
- CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
+ CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED);
drainInputBuffer(&buffers->editItemAt(i));
}
break;
@@ -1937,7 +1937,7 @@ void OMXCodec::on_message(const omx_message &msg) {
CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
status_t err = freeBuffer(kPortIndexOutput, i);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
#if 0
} else if (mPortStatus[kPortIndexOutput] == ENABLED
@@ -1947,7 +1947,7 @@ void OMXCodec::on_message(const omx_message &msg) {
mBufferFilled.signal();
#endif
} else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
- CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+ CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
if (info->mMediaBuffer == NULL) {
CHECK(mOMXLivesLocally);
@@ -2045,84 +2045,6 @@ void OMXCodec::on_message(const omx_message &msg) {
}
}
-void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
- switch (event) {
- case OMX_EventCmdComplete:
- {
- onCmdComplete((OMX_COMMANDTYPE)data1, data2);
- break;
- }
-
- case OMX_EventError:
- {
- CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
-
- setState(ERROR);
- break;
- }
-
- case OMX_EventPortSettingsChanged:
- {
- CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
- data1, data2);
-
- if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
- onPortSettingsChanged(data1);
- } else if (data1 == kPortIndexOutput
- && data2 == OMX_IndexConfigCommonOutputCrop) {
-
- OMX_CONFIG_RECTTYPE rect;
- rect.nPortIndex = kPortIndexOutput;
- InitOMXParams(&rect);
-
- status_t err =
- mOMX->getConfig(
- mNode, OMX_IndexConfigCommonOutputCrop,
- &rect, sizeof(rect));
-
- if (err == OK) {
- CODEC_LOGV(
- "output crop (%ld, %ld, %ld, %ld)",
- rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
-
- if (mNativeWindow != NULL) {
- android_native_rect_t crop;
- crop.left = rect.nLeft;
- crop.top = rect.nTop;
- crop.right = crop.left + rect.nWidth - 1;
- crop.bottom = crop.top + rect.nHeight - 1;
-
- CHECK_EQ(0, native_window_set_crop(
- mNativeWindow.get(), &crop));
- }
- } else {
- CODEC_LOGE("getConfig(OMX_IndexConfigCommonOutputCrop) "
- "returned error 0x%08x", err);
- }
- }
- break;
- }
-
-#if 0
- case OMX_EventBufferFlag:
- {
- CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
-
- if (data1 == kPortIndexOutput) {
- mNoMoreOutputData = true;
- }
- break;
- }
-#endif
-
- default:
- {
- CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
- break;
- }
- }
-}
-
// Has the format changed in any way that the client would have to be aware of?
static bool formatHasNotablyChanged(
const sp<MetaData> &from, const sp<MetaData> &to) {
@@ -2167,6 +2089,21 @@ static bool formatHasNotablyChanged(
if (height_from != height_to) {
return true;
}
+
+ int32_t left_from, top_from, right_from, bottom_from;
+ CHECK(from->findRect(
+ kKeyCropRect,
+ &left_from, &top_from, &right_from, &bottom_from));
+
+ int32_t left_to, top_to, right_to, bottom_to;
+ CHECK(to->findRect(
+ kKeyCropRect,
+ &left_to, &top_to, &right_to, &bottom_to));
+
+ if (left_to != left_from || top_to != top_from
+ || right_to != right_from || bottom_to != bottom_from) {
+ return true;
+ }
} else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) {
int32_t numChannels_from, numChannels_to;
CHECK(from->findInt32(kKeyChannelCount, &numChannels_from));
@@ -2188,6 +2125,78 @@ static bool formatHasNotablyChanged(
return false;
}
+void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ onCmdComplete((OMX_COMMANDTYPE)data1, data2);
+ break;
+ }
+
+ case OMX_EventError:
+ {
+ CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
+
+ setState(ERROR);
+ break;
+ }
+
+ case OMX_EventPortSettingsChanged:
+ {
+ CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
+ data1, data2);
+
+ if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
+ onPortSettingsChanged(data1);
+ } else if (data1 == kPortIndexOutput
+ && data2 == OMX_IndexConfigCommonOutputCrop) {
+
+ sp<MetaData> oldOutputFormat = mOutputFormat;
+ initOutputFormat(mSource->getFormat());
+
+ if (formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) {
+ mOutputPortSettingsHaveChanged = true;
+
+ if (mNativeWindow != NULL) {
+ int32_t left, top, right, bottom;
+ CHECK(mOutputFormat->findRect(
+ kKeyCropRect,
+ &left, &top, &right, &bottom));
+
+ android_native_rect_t crop;
+ crop.left = left;
+ crop.top = top;
+ crop.right = right;
+ crop.bottom = bottom;
+
+ CHECK_EQ(0, native_window_set_crop(
+ mNativeWindow.get(), &crop));
+ }
+ }
+ }
+ break;
+ }
+
+#if 0
+ case OMX_EventBufferFlag:
+ {
+ CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
+
+ if (data1 == kPortIndexOutput) {
+ mNoMoreOutputData = true;
+ }
+ break;
+ }
+#endif
+
+ default:
+ {
+ CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
+ break;
+ }
+ }
+}
+
void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
switch (cmd) {
case OMX_CommandStateSet:
@@ -2202,13 +2211,13 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
CODEC_LOGV("PORT_DISABLED(%ld)", portIndex);
CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ(mPortStatus[portIndex], DISABLING);
- CHECK_EQ(mPortBuffers[portIndex].size(), 0);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLING);
+ CHECK_EQ(mPortBuffers[portIndex].size(), 0u);
mPortStatus[portIndex] = DISABLED;
if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, kPortIndexOutput);
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
sp<MetaData> oldOutputFormat = mOutputFormat;
initOutputFormat(mSource->getFormat());
@@ -2222,7 +2231,7 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
enablePortAsync(portIndex);
status_t err = allocateBuffersOnPort(portIndex);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
break;
}
@@ -2233,12 +2242,12 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
CODEC_LOGV("PORT_ENABLED(%ld)", portIndex);
CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ(mPortStatus[portIndex], ENABLING);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLING);
mPortStatus[portIndex] = ENABLED;
if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, kPortIndexOutput);
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
setState(EXECUTING);
@@ -2253,14 +2262,14 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
CODEC_LOGV("FLUSH_DONE(%ld)", portIndex);
- CHECK_EQ(mPortStatus[portIndex], SHUTTING_DOWN);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN);
mPortStatus[portIndex] = ENABLED;
CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
mPortBuffers[portIndex].size());
if (mState == RECONFIGURING) {
- CHECK_EQ(portIndex, kPortIndexOutput);
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
disablePortAsync(portIndex);
} else if (mState == EXECUTING_TO_IDLE) {
@@ -2274,7 +2283,7 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
status_t err =
mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
} else {
// We're flushing both ports in preparation for seeking.
@@ -2314,11 +2323,11 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) {
status_t err = mOMX->sendCommand(
mNode, OMX_CommandStateSet, OMX_StateExecuting);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
setState(IDLE_TO_EXECUTING);
} else {
- CHECK_EQ(mState, EXECUTING_TO_IDLE);
+ CHECK_EQ((int)mState, (int)EXECUTING_TO_IDLE);
CHECK_EQ(
countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
@@ -2331,13 +2340,13 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) {
status_t err = mOMX->sendCommand(
mNode, OMX_CommandStateSet, OMX_StateLoaded);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
err = freeBuffersOnPort(kPortIndexInput);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
err = freeBuffersOnPort(kPortIndexOutput);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -2349,7 +2358,7 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) {
case OMX_StateExecuting:
{
- CHECK_EQ(mState, IDLE_TO_EXECUTING);
+ CHECK_EQ((int)mState, (int)IDLE_TO_EXECUTING);
CODEC_LOGV("Now Executing.");
@@ -2365,7 +2374,7 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) {
case OMX_StateLoaded:
{
- CHECK_EQ(mState, IDLE_TO_LOADED);
+ CHECK_EQ((int)mState, (int)IDLE_TO_LOADED);
CODEC_LOGV("Now Loaded.");
@@ -2412,7 +2421,7 @@ status_t OMXCodec::freeBuffersOnPort(
continue;
}
- CHECK_EQ(info->mOwnedByComponent, false);
+ CHECK_EQ((int)info->mOwnedByComponent, (int)false);
CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
@@ -2437,7 +2446,7 @@ status_t OMXCodec::freeBuffer(OMX_U32 portIndex, size_t bufIndex) {
status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
if (err == OK && info->mMediaBuffer != NULL) {
- CHECK_EQ(portIndex, kPortIndexOutput);
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
info->mMediaBuffer->setObserver(NULL);
// Make sure nobody but us owns this buffer at this point.
@@ -2463,8 +2472,8 @@ status_t OMXCodec::freeBuffer(OMX_U32 portIndex, size_t bufIndex) {
void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
- CHECK_EQ(mState, EXECUTING);
- CHECK_EQ(portIndex, kPortIndexOutput);
+ CHECK_EQ((int)mState, (int)EXECUTING);
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
setState(RECONFIGURING);
if (mQuirks & kNeedsFlushBeforeDisable) {
@@ -2484,7 +2493,7 @@ bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
mPortBuffers[portIndex].size());
- CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
mPortStatus[portIndex] = SHUTTING_DOWN;
if ((mQuirks & kRequiresFlushCompleteEmulation)
@@ -2498,7 +2507,7 @@ bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
status_t err =
mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
return true;
}
@@ -2506,13 +2515,13 @@ bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
mPortStatus[portIndex] = DISABLING;
CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
status_t err =
mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
freeBuffersOnPort(portIndex, true);
}
@@ -2520,17 +2529,17 @@ void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
void OMXCodec::enablePortAsync(OMX_U32 portIndex) {
CHECK(mState == EXECUTING || mState == RECONFIGURING);
- CHECK_EQ(mPortStatus[portIndex], DISABLED);
+ CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED);
mPortStatus[portIndex] = ENABLING;
CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
status_t err =
mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
void OMXCodec::fillOutputBuffers() {
- CHECK_EQ(mState, EXECUTING);
+ CHECK_EQ((int)mState, (int)EXECUTING);
// This is a workaround for some decoders not properly reporting
// end-of-output-stream. If we own all input buffers and also own
@@ -2566,7 +2575,7 @@ void OMXCodec::drainInputBuffers() {
}
void OMXCodec::drainInputBuffer(BufferInfo *info) {
- CHECK_EQ(info->mOwnedByComponent, false);
+ CHECK_EQ((int)info->mOwnedByComponent, (int)false);
if (mSignalledEOS) {
return;
@@ -2603,7 +2612,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
mNode, info->mBuffer, 0, size,
OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
0);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
info->mOwnedByComponent = true;
@@ -2771,7 +2780,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
}
void OMXCodec::fillOutputBuffer(BufferInfo *info) {
- CHECK_EQ(info->mOwnedByComponent, false);
+ CHECK_EQ((int)info->mOwnedByComponent, (int)false);
if (mNoMoreOutputData) {
CODEC_LOGV("There is no more output data available, not "
@@ -2779,19 +2788,21 @@ void OMXCodec::fillOutputBuffer(BufferInfo *info) {
return;
}
- sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
- if (graphicBuffer != 0) {
- // When using a native buffer we need to lock the buffer before giving
- // it to OMX.
- CHECK(!info->mOwnedByNativeWindow);
- CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
- int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
- graphicBuffer.get());
- if (err != 0) {
- CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
+ if (info->mMediaBuffer != NULL) {
+ sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
+ if (graphicBuffer != 0) {
+ // When using a native buffer we need to lock the buffer before
+ // giving it to OMX.
+ CHECK(!info->mOwnedByNativeWindow);
+ CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
+ int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
+ graphicBuffer.get());
+ if (err != 0) {
+ CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
- setState(ERROR);
- return;
+ setState(ERROR);
+ return;
+ }
}
}
@@ -2850,10 +2861,10 @@ void OMXCodec::setRawAudioFormat(
def.nPortIndex = portIndex;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), OK);
+ &def, sizeof(def)), (status_t)OK);
// pcm param
OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
@@ -2863,7 +2874,7 @@ void OMXCodec::setRawAudioFormat(
err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
pcmParams.nChannels = numChannels;
pcmParams.eNumData = OMX_NumericalDataSigned;
@@ -2884,7 +2895,7 @@ void OMXCodec::setRawAudioFormat(
err = mOMX->setParameter(
mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(bool isAMRWB, int32_t bps) {
@@ -2941,13 +2952,13 @@ void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) {
status_t err =
mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitRate);
err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
////////////////////////
@@ -2976,33 +2987,33 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit
status_t err = OMX_ErrorNone;
while (OMX_ErrorNone == err) {
CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat,
- &format, sizeof(format)), OK);
+ &format, sizeof(format)), (status_t)OK);
if (format.eEncoding == OMX_AUDIO_CodingAAC) {
break;
}
format.nIndex++;
}
- CHECK_EQ(OK, err);
+ CHECK_EQ((status_t)OK, err);
CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat,
- &format, sizeof(format)), OK);
+ &format, sizeof(format)), (status_t)OK);
// port definition
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), OK);
+ &def, sizeof(def)), (status_t)OK);
def.format.audio.bFlagErrorConcealment = OMX_TRUE;
def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
- &def, sizeof(def)), OK);
+ &def, sizeof(def)), (status_t)OK);
// profile
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
InitOMXParams(&profile);
profile.nPortIndex = kPortIndexOutput;
CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac,
- &profile, sizeof(profile)), OK);
+ &profile, sizeof(profile)), (status_t)OK);
profile.nChannels = numChannels;
profile.eChannelMode = (numChannels == 1?
OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo);
@@ -3015,7 +3026,7 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit
profile.eAACProfile = OMX_AUDIO_AACObjectLC;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioAac,
- &profile, sizeof(profile)), OK);
+ &profile, sizeof(profile)), (status_t)OK);
} else {
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
@@ -3024,7 +3035,7 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
profile.nChannels = numChannels;
profile.nSampleRate = sampleRate;
@@ -3032,7 +3043,7 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit
err = mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
}
@@ -3044,10 +3055,10 @@ void OMXCodec::setImageOutputFormat(
OMX_INDEXTYPE index;
status_t err = mOMX->get_extension_index(
mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
err = mOMX->set_config(mNode, index, &format, sizeof(format));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
#endif
OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -3056,13 +3067,13 @@ void OMXCodec::setImageOutputFormat(
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
- CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+ CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingUnused);
imageDef->eColorFormat = format;
imageDef->nFrameWidth = width;
imageDef->nFrameHeight = height;
@@ -3105,7 +3116,7 @@ void OMXCodec::setImageOutputFormat(
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
void OMXCodec::setJPEGInputFormat(
@@ -3116,12 +3127,12 @@ void OMXCodec::setJPEGInputFormat(
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
- CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingJPEG);
+ CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingJPEG);
imageDef->nFrameWidth = width;
imageDef->nFrameHeight = height;
@@ -3130,7 +3141,7 @@ void OMXCodec::setJPEGInputFormat(
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
void OMXCodec::addCodecSpecificData(const void *data, size_t size) {
@@ -3231,7 +3242,7 @@ status_t OMXCodec::stop() {
status_t err =
mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
while (mState != LOADED && mState != ERROR) {
@@ -3323,7 +3334,7 @@ status_t OMXCodec::read(
mFilledBuffers.clear();
- CHECK_EQ(mState, EXECUTING);
+ CHECK_EQ((int)mState, (int)EXECUTING);
bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput);
bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput);
@@ -3377,7 +3388,7 @@ void OMXCodec::signalBufferReturned(MediaBuffer *buffer) {
BufferInfo *info = &buffers->editItemAt(i);
if (info->mMediaBuffer == buffer) {
- CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+ CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
if (buffer->graphicBuffer() == 0) {
fillOutputBuffer(info);
} else {
@@ -3629,7 +3640,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output");
@@ -3695,7 +3706,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
printf(" nSamplingRate = %ld\n", params.nSamplingRate);
printf(" nChannels = %ld\n", params.nChannels);
@@ -3714,7 +3725,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
printf(" nChannels = %ld\n", amr.nChannels);
printf(" eAMRBandMode = %s\n",
@@ -3764,13 +3775,14 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
switch (def.eDomain) {
case OMX_PortDomainImage:
{
OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
- CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+ CHECK_EQ((int)imageDef->eCompressionFormat,
+ (int)OMX_IMAGE_CodingUnused);
mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat);
@@ -3790,11 +3802,11 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(params.eNumData, OMX_NumericalDataSigned);
- CHECK_EQ(params.nBitPerSample, 16);
- CHECK_EQ(params.ePCMMode, OMX_AUDIO_PCMModeLinear);
+ CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
+ CHECK_EQ(params.nBitPerSample, 16u);
+ CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
int32_t numChannels, sampleRate;
inputFormat->findInt32(kKeyChannelCount, &numChannels);
@@ -3828,9 +3840,9 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
- CHECK_EQ(amr.nChannels, 1);
+ CHECK_EQ(amr.nChannels, 1u);
mOutputFormat->setInt32(kKeyChannelCount, 1);
if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0
@@ -3885,6 +3897,37 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+
+ if (!mIsEncoder) {
+ OMX_CONFIG_RECTTYPE rect;
+ status_t err =
+ mOMX->getConfig(
+ mNode, OMX_IndexConfigCommonOutputCrop,
+ &rect, sizeof(rect));
+
+ if (err == OK) {
+ CHECK_GE(rect.nLeft, 0);
+ CHECK_GE(rect.nTop, 0);
+ CHECK_GE(rect.nWidth, 0u);
+ CHECK_GE(rect.nHeight, 0u);
+ CHECK_LE(rect.nLeft + rect.nWidth - 1, video_def->nFrameWidth);
+ CHECK_LE(rect.nTop + rect.nHeight - 1, video_def->nFrameHeight);
+
+ mOutputFormat->setRect(
+ kKeyCropRect,
+ rect.nLeft,
+ rect.nTop,
+ rect.nLeft + rect.nWidth - 1,
+ rect.nTop + rect.nHeight - 1);
+ } else {
+ mOutputFormat->setRect(
+ kKeyCropRect,
+ 0, 0,
+ video_def->nFrameWidth - 1,
+ video_def->nFrameHeight - 1);
+ }
+ }
+
break;
}
@@ -3988,7 +4031,7 @@ status_t QueryCodecs(
caps->mColorFormats.push(portFormat.eColorFormat);
}
- CHECK_EQ(omx->freeNode(node), OK);
+ CHECK_EQ(omx->freeNode(node), (status_t)OK);
}
}
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index e922c73..a9163fc 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -419,8 +419,10 @@ status_t SampleTable::findSyncSampleNear(
++left;
}
+ if (left > 0) {
+ --left;
+ }
- --left;
uint32_t x;
if (mDataSource->readAt(
mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 9b2dec9..763a914 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -191,17 +191,26 @@ static VideoFrame *extractVideoFrameWithCodecFlags(
CHECK(meta->findInt32(kKeyWidth, &width));
CHECK(meta->findInt32(kKeyHeight, &height));
+ int32_t crop_left, crop_top, crop_right, crop_bottom;
+ if (!meta->findRect(
+ kKeyCropRect,
+ &crop_left, &crop_top, &crop_right, &crop_bottom)) {
+ crop_left = crop_top = 0;
+ crop_right = width - 1;
+ crop_bottom = height - 1;
+ }
+
int32_t rotationAngle;
if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
rotationAngle = 0; // By default, no rotation
}
VideoFrame *frame = new VideoFrame;
- frame->mWidth = width;
- frame->mHeight = height;
- frame->mDisplayWidth = width;
- frame->mDisplayHeight = height;
- frame->mSize = width * height * 2;
+ frame->mWidth = crop_right - crop_left + 1;
+ frame->mHeight = crop_bottom - crop_top + 1;
+ frame->mDisplayWidth = frame->mWidth;
+ frame->mDisplayHeight = frame->mHeight;
+ frame->mSize = frame->mWidth * frame->mHeight * 2;
frame->mData = new uint8_t[frame->mSize];
frame->mRotationAngle = rotationAngle;
@@ -213,10 +222,13 @@ static VideoFrame *extractVideoFrameWithCodecFlags(
CHECK(converter.isValid());
converter.convert(
- width, height,
(const uint8_t *)buffer->data() + buffer->range_offset(),
- 0,
- frame->mData, width * 2);
+ width, height,
+ crop_left, crop_top, crop_right, crop_bottom,
+ frame->mData,
+ frame->mWidth,
+ frame->mHeight,
+ 0, 0, frame->mWidth - 1, frame->mHeight - 1);
buffer->release();
buffer = NULL;
@@ -418,5 +430,4 @@ void StagefrightMetadataRetriever::parseMetaData() {
}
}
-
} // namespace android
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 868c514..5bbba35 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -73,6 +73,7 @@ AVCDecoder::AVCDecoder(const sp<MediaSource> &source)
CHECK(mSource->getFormat()->findInt32(kKeyHeight, &height));
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
+ mFormat->setRect(kKeyCropRect, 0, 0, width - 1, height - 1);
mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
mFormat->setCString(kKeyDecoderComponent, "AVCDecoder");
@@ -418,16 +419,32 @@ status_t AVCDecoder::read(
crop_top = crop_left = 0;
}
- int32_t aligned_width = (crop_right - crop_left + 1 + 15) & ~15;
- int32_t aligned_height = (crop_bottom - crop_top + 1 + 15) & ~15;
+ int32_t prevCropLeft, prevCropTop;
+ int32_t prevCropRight, prevCropBottom;
+ if (!mFormat->findRect(
+ kKeyCropRect,
+ &prevCropLeft, &prevCropTop,
+ &prevCropRight, &prevCropBottom)) {
+ prevCropLeft = prevCropTop = 0;
+ prevCropRight = width - 1;
+ prevCropBottom = height - 1;
+ }
int32_t oldWidth, oldHeight;
CHECK(mFormat->findInt32(kKeyWidth, &oldWidth));
CHECK(mFormat->findInt32(kKeyHeight, &oldHeight));
- if (oldWidth != aligned_width || oldHeight != aligned_height) {
- mFormat->setInt32(kKeyWidth, aligned_width);
- mFormat->setInt32(kKeyHeight, aligned_height);
+ if (oldWidth != width || oldHeight != height
+ || prevCropLeft != crop_left
+ || prevCropTop != crop_top
+ || prevCropRight != crop_right
+ || prevCropBottom != crop_bottom) {
+ mFormat->setRect(
+ kKeyCropRect,
+ crop_left, crop_top, crop_right, crop_bottom);
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
err = INFO_FORMAT_CHANGED;
} else {
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index ef2dba0..62ba40f 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,20 +9,6 @@ LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/hardware/msm7k
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libmedia \
- libutils \
- libui \
- libcutils \
- libsurfaceflinger_client\
- libcamera_client
-
-# ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
-ifeq ($(TARGET_PRODUCT),passion)
- LOCAL_CFLAGS += -DHAS_YCBCR420_SP_ADRENO
-endif
-
LOCAL_MODULE:= libstagefright_color_conversion
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 5b16997..600f040 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -50,31 +50,64 @@ bool ColorConverter::isValid() const {
}
}
-void ColorConverter::convert(
+ColorConverter::BitmapParams::BitmapParams(
+ void *bits,
size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip) {
+ size_t cropLeft, size_t cropTop,
+ size_t cropRight, size_t cropBottom)
+ : mBits(bits),
+ mWidth(width),
+ mHeight(height),
+ mCropLeft(cropLeft),
+ mCropTop(cropTop),
+ mCropRight(cropRight),
+ mCropBottom(cropBottom) {
+}
+
+size_t ColorConverter::BitmapParams::cropWidth() const {
+ return mCropRight - mCropLeft + 1;
+}
+
+size_t ColorConverter::BitmapParams::cropHeight() const {
+ return mCropBottom - mCropTop + 1;
+}
+
+void ColorConverter::convert(
+ const void *srcBits,
+ size_t srcWidth, size_t srcHeight,
+ size_t srcCropLeft, size_t srcCropTop,
+ size_t srcCropRight, size_t srcCropBottom,
+ void *dstBits,
+ size_t dstWidth, size_t dstHeight,
+ size_t dstCropLeft, size_t dstCropTop,
+ size_t dstCropRight, size_t dstCropBottom) {
CHECK_EQ(mDstFormat, OMX_COLOR_Format16bitRGB565);
+ BitmapParams src(
+ const_cast<void *>(srcBits),
+ srcWidth, srcHeight,
+ srcCropLeft, srcCropTop, srcCropRight, srcCropBottom);
+
+ BitmapParams dst(
+ dstBits,
+ dstWidth, dstHeight,
+ dstCropLeft, dstCropTop, dstCropRight, dstCropBottom);
+
switch (mSrcFormat) {
case OMX_COLOR_FormatYUV420Planar:
- convertYUV420Planar(
- width, height, srcBits, srcSkip, dstBits, dstSkip);
+ convertYUV420Planar(src, dst);
break;
case OMX_COLOR_FormatCbYCrY:
- convertCbYCrY(
- width, height, srcBits, srcSkip, dstBits, dstSkip);
+ convertCbYCrY(src, dst);
break;
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
- convertQCOMYUV420SemiPlanar(
- width, height, srcBits, srcSkip, dstBits, dstSkip);
+ convertQCOMYUV420SemiPlanar(src, dst);
break;
case OMX_COLOR_FormatYUV420SemiPlanar:
- convertYUV420SemiPlanar(
- width, height, srcBits, srcSkip, dstBits, dstSkip);
+ convertYUV420SemiPlanar(src, dst);
break;
default:
@@ -86,25 +119,27 @@ void ColorConverter::convert(
}
void ColorConverter::convertCbYCrY(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip) {
- CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
- CHECK(dstSkip >= width * 2);
- CHECK((dstSkip & 3) == 0);
+ const BitmapParams &src, const BitmapParams &dst) {
+ // XXX Untested
uint8_t *kAdjustedClip = initClip();
- uint32_t *dst_ptr = (uint32_t *)dstBits;
+ CHECK((src.mCropLeft & 1) == 0);
+ CHECK_EQ(src.cropWidth(), dst.cropWidth());
+ CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+ uint32_t *dst_ptr = (uint32_t *)dst.mBits
+ + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
- const uint8_t *src = (const uint8_t *)srcBits;
+ const uint8_t *src_ptr = (const uint8_t *)src.mBits
+ + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; x += 2) {
- signed y1 = (signed)src[2 * x + 1] - 16;
- signed y2 = (signed)src[2 * x + 3] - 16;
- signed u = (signed)src[2 * x] - 128;
- signed v = (signed)src[2 * x + 2] - 128;
+ for (size_t y = 0; y < src.cropHeight(); ++y) {
+ for (size_t x = 0; x < src.cropWidth(); x += 2) {
+ signed y1 = (signed)src_ptr[2 * x + 1] - 16;
+ signed y2 = (signed)src_ptr[2 * x + 3] - 16;
+ signed u = (signed)src_ptr[2 * x] - 128;
+ signed v = (signed)src_ptr[2 * x + 2] - 128;
signed u_b = u * 517;
signed u_g = -u * 100;
@@ -134,32 +169,35 @@ void ColorConverter::convertCbYCrY(
dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
}
- src += width * 2;
- dst_ptr += dstSkip / 4;
+ src_ptr += src.mWidth * 2;
+ dst_ptr += dst.mWidth / 2;
}
}
void ColorConverter::convertYUV420Planar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip) {
- CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
- CHECK(dstSkip >= width * 2);
- CHECK((dstSkip & 3) == 0);
-
+ const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- uint32_t *dst_ptr = (uint32_t *)dstBits;
- const uint8_t *src_y = (const uint8_t *)srcBits;
+ CHECK((dst.mWidth & 3) == 0);
+ CHECK((src.mCropLeft & 1) == 0);
+ CHECK_EQ(src.cropWidth(), dst.cropWidth());
+ CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+ uint32_t *dst_ptr = (uint32_t *)dst.mBits
+ + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
const uint8_t *src_u =
- (const uint8_t *)src_y + width * height;
+ (const uint8_t *)src_y + src.mWidth * src.mHeight
+ + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
const uint8_t *src_v =
- (const uint8_t *)src_u + (width / 2) * (height / 2);
+ src_u + (src.mWidth / 2) * (src.mHeight / 2);
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; x += 2) {
+ for (size_t y = 0; y < src.cropHeight(); ++y) {
+ for (size_t x = 0; x < src.cropWidth(); x += 2) {
// B = 1.164 * (Y - 16) + 2.018 * (U - 128)
// G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
// R = 1.164 * (Y - 16) + 1.596 * (V - 128)
@@ -212,35 +250,38 @@ void ColorConverter::convertYUV420Planar(
dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
}
- src_y += width;
+ src_y += src.mWidth;
if (y & 1) {
- src_u += width / 2;
- src_v += width / 2;
+ src_u += src.mWidth / 2;
+ src_v += src.mWidth / 2;
}
- dst_ptr += dstSkip / 4;
+ dst_ptr += dst.mWidth / 2;
}
}
void ColorConverter::convertQCOMYUV420SemiPlanar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip) {
- CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
- CHECK(dstSkip >= width * 2);
- CHECK((dstSkip & 3) == 0);
-
+ const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- uint32_t *dst_ptr = (uint32_t *)dstBits;
- const uint8_t *src_y = (const uint8_t *)srcBits;
+ CHECK((dst.mWidth & 3) == 0);
+ CHECK((src.mCropLeft & 1) == 0);
+ CHECK_EQ(src.cropWidth(), dst.cropWidth());
+ CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+ uint32_t *dst_ptr = (uint32_t *)dst.mBits
+ + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
const uint8_t *src_u =
- (const uint8_t *)src_y + width * height;
+ (const uint8_t *)src_y + src.mWidth * src.mHeight
+ + src.mCropTop * src.mWidth + src.mCropLeft;
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; x += 2) {
+ for (size_t y = 0; y < src.cropHeight(); ++y) {
+ for (size_t x = 0; x < src.cropWidth(); x += 2) {
signed y1 = (signed)src_y[x] - 16;
signed y2 = (signed)src_y[x + 1] - 16;
@@ -275,34 +316,39 @@ void ColorConverter::convertQCOMYUV420SemiPlanar(
dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
}
- src_y += width;
+ src_y += src.mWidth;
if (y & 1) {
- src_u += width;
+ src_u += src.mWidth;
}
- dst_ptr += dstSkip / 4;
+ dst_ptr += dst.mWidth / 2;
}
}
void ColorConverter::convertYUV420SemiPlanar(
- size_t width, size_t height,
- const void *srcBits, size_t srcSkip,
- void *dstBits, size_t dstSkip) {
- CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
- CHECK(dstSkip >= width * 2);
- CHECK((dstSkip & 3) == 0);
+ const BitmapParams &src, const BitmapParams &dst) {
+ // XXX Untested
uint8_t *kAdjustedClip = initClip();
- uint32_t *dst_ptr = (uint32_t *)dstBits;
- const uint8_t *src_y = (const uint8_t *)srcBits;
+ CHECK((dst.mWidth & 3) == 0);
+ CHECK((src.mCropLeft & 1) == 0);
+ CHECK_EQ(src.cropWidth(), dst.cropWidth());
+ CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+ uint32_t *dst_ptr = (uint32_t *)dst.mBits
+ + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
const uint8_t *src_u =
- (const uint8_t *)src_y + width * height;
+ (const uint8_t *)src_y + src.mWidth * src.mHeight
+ + src.mCropTop * src.mWidth + src.mCropLeft;
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; x += 2) {
+ for (size_t y = 0; y < src.cropHeight(); ++y) {
+ for (size_t x = 0; x < src.cropWidth(); x += 2) {
signed y1 = (signed)src_y[x] - 16;
signed y2 = (signed)src_y[x + 1] - 16;
@@ -337,13 +383,13 @@ void ColorConverter::convertYUV420SemiPlanar(
dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
}
- src_y += width;
+ src_y += src.mWidth;
if (y & 1) {
- src_u += width;
+ src_u += src.mWidth;
}
- dst_ptr += dstSkip / 4;
+ dst_ptr += dst.mWidth / 2;
}
}
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index acbea05..70408d7 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -21,55 +21,41 @@
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryHeapPmem.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MetaData.h>
#include <surfaceflinger/Surface.h>
#include <ui/android_native_buffer.h>
#include <ui/GraphicBufferMapper.h>
-// XXX: Temporary hack to allow referencing the _ADRENO pixel format here.
-#include <libgralloc-qsd8k/gralloc_priv.h>
-
namespace android {
SoftwareRenderer::SoftwareRenderer(
- OMX_COLOR_FORMATTYPE colorFormat,
- const sp<Surface> &surface,
- size_t displayWidth, size_t displayHeight,
- size_t decodedWidth, size_t decodedHeight,
- int32_t rotationDegrees)
- : mColorFormat(colorFormat),
- mConverter(NULL),
+ const sp<Surface> &surface, const sp<MetaData> &meta)
+ : mConverter(NULL),
mYUVMode(None),
- mSurface(surface),
- mDisplayWidth(displayWidth),
- mDisplayHeight(displayHeight),
- mDecodedWidth(decodedWidth),
- mDecodedHeight(decodedHeight) {
- LOGI("input format = %d", mColorFormat);
- LOGI("display = %d x %d, decoded = %d x %d",
- mDisplayWidth, mDisplayHeight, mDecodedWidth, mDecodedHeight);
-
- mDecodedWidth = mDisplayWidth;
- mDecodedHeight = mDisplayHeight;
+ mSurface(surface) {
+ int32_t tmp;
+ CHECK(meta->findInt32(kKeyColorFormat, &tmp));
+ mColorFormat = (OMX_COLOR_FORMATTYPE)tmp;
+
+ CHECK(meta->findInt32(kKeyWidth, &mWidth));
+ CHECK(meta->findInt32(kKeyHeight, &mHeight));
+
+ if (!meta->findRect(
+ kKeyCropRect,
+ &mCropLeft, &mCropTop, &mCropRight, &mCropBottom)) {
+ mCropLeft = mCropTop = 0;
+ mCropRight = mWidth - 1;
+ mCropBottom = mHeight - 1;
+ }
+
+ int32_t rotationDegrees;
+ if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
+ rotationDegrees = 0;
+ }
int halFormat;
switch (mColorFormat) {
-#if HAS_YCBCR420_SP_ADRENO
- case OMX_COLOR_FormatYUV420Planar:
- {
- halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
- mYUVMode = YUV420ToYUV420sp;
- break;
- }
-
- case 0x7fa30c00:
- {
- halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
- mYUVMode = YUV420spToYUV420sp;
- break;
- }
-#endif
-
default:
halFormat = HAL_PIXEL_FORMAT_RGB_565;
@@ -80,8 +66,8 @@ SoftwareRenderer::SoftwareRenderer(
}
CHECK(mSurface.get() != NULL);
- CHECK(mDecodedWidth > 0);
- CHECK(mDecodedHeight > 0);
+ CHECK(mWidth > 0);
+ CHECK(mHeight > 0);
CHECK(mConverter == NULL || mConverter->isValid());
CHECK_EQ(0,
@@ -94,7 +80,9 @@ SoftwareRenderer::SoftwareRenderer(
// Width must be multiple of 32???
CHECK_EQ(0, native_window_set_buffers_geometry(
- mSurface.get(), mDecodedWidth, mDecodedHeight,
+ mSurface.get(),
+ mCropRight - mCropLeft + 1,
+ mCropBottom - mCropTop + 1,
halFormat));
uint32_t transform;
@@ -117,10 +105,6 @@ SoftwareRenderer::~SoftwareRenderer() {
mConverter = NULL;
}
-static inline size_t ALIGN(size_t x, size_t alignment) {
- return (x + alignment - 1) & ~(alignment - 1);
-}
-
void SoftwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
android_native_buffer_t *buf;
@@ -134,7 +118,7 @@ void SoftwareRenderer::render(
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
- Rect bounds(mDecodedWidth, mDecodedHeight);
+ Rect bounds(mWidth, mHeight);
void *dst;
CHECK_EQ(0, mapper.lock(
@@ -142,69 +126,16 @@ void SoftwareRenderer::render(
if (mConverter) {
mConverter->convert(
- mDecodedWidth, mDecodedHeight,
- data, 0, dst, buf->stride * 2);
- } else if (mYUVMode == YUV420spToYUV420sp) {
- // Input and output are both YUV420sp, but the alignment requirements
- // are different.
- size_t srcYStride = mDecodedWidth;
- const uint8_t *srcY = (const uint8_t *)data;
- uint8_t *dstY = (uint8_t *)dst;
- for (size_t i = 0; i < mDecodedHeight; ++i) {
- memcpy(dstY, srcY, mDecodedWidth);
- srcY += srcYStride;
- dstY += buf->stride;
- }
-
- size_t srcUVStride = (mDecodedWidth + 1) & ~1;
- size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
-
- const uint8_t *srcUV = (const uint8_t *)data
- + mDecodedHeight * mDecodedWidth;
-
- size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
- uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
-
- for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
- memcpy(dstUV, srcUV, (mDecodedWidth + 1) & ~1);
- srcUV += srcUVStride;
- dstUV += dstUVStride;
- }
- } else if (mYUVMode == YUV420ToYUV420sp) {
- // Input is YUV420 planar, output is YUV420sp, adhere to proper
- // alignment requirements.
- size_t srcYStride = mDecodedWidth;
- const uint8_t *srcY = (const uint8_t *)data;
- uint8_t *dstY = (uint8_t *)dst;
- for (size_t i = 0; i < mDecodedHeight; ++i) {
- memcpy(dstY, srcY, mDecodedWidth);
- srcY += srcYStride;
- dstY += buf->stride;
- }
-
- size_t srcUVStride = (mDecodedWidth + 1) / 2;
- size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
-
- const uint8_t *srcU = (const uint8_t *)data
- + mDecodedHeight * mDecodedWidth;
-
- const uint8_t *srcV =
- srcU + ((mDecodedWidth + 1) / 2) * ((mDecodedHeight + 1) / 2);
-
- size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
- uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
-
- for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
- for (size_t j = 0; j < (mDecodedWidth + 1) / 2; ++j) {
- dstUV[2 * j + 1] = srcU[j];
- dstUV[2 * j] = srcV[j];
- }
- srcU += srcUVStride;
- srcV += srcUVStride;
- dstUV += dstUVStride;
- }
+ data,
+ mWidth, mHeight,
+ mCropLeft, mCropTop, mCropRight, mCropBottom,
+ dst,
+ buf->stride, buf->height,
+ 0, 0,
+ mCropRight - mCropLeft,
+ mCropBottom - mCropTop);
} else {
- memcpy(dst, data, size);
+ TRESPASS();
}
CHECK_EQ(0, mapper.unlock(buf->handle));
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4e63b7a..e33f467 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -88,11 +88,6 @@ struct AwesomePlayer {
status_t seekTo(int64_t timeUs);
- status_t getVideoDimensions(int32_t *width, int32_t *height) const;
-
- status_t suspend();
- status_t resume();
-
// This is a mask of MediaExtractor::Flags.
uint32_t flags() const;
@@ -153,7 +148,6 @@ private:
uint32_t mFlags;
uint32_t mExtractorFlags;
- int32_t mVideoWidth, mVideoHeight;
int64_t mTimeSourceDeltaUs;
int64_t mVideoTimeUs;
@@ -187,7 +181,6 @@ private:
void postCheckAudioStatusEvent_l();
status_t play_l();
- MediaBuffer *mLastVideoBuffer;
MediaBuffer *mVideoBuffer;
sp<NuHTTPDataSource> mConnectingDataSource;
@@ -198,32 +191,6 @@ private:
sp<ARTPSession> mRTPSession;
sp<UDPPusher> mRTPPusher, mRTCPPusher;
- struct SuspensionState {
- String8 mUri;
- KeyedVector<String8, String8> mUriHeaders;
- sp<DataSource> mFileSource;
-
- uint32_t mFlags;
- int64_t mPositionUs;
-
- void *mLastVideoFrame;
- size_t mLastVideoFrameSize;
- int32_t mColorFormat;
- int32_t mVideoWidth, mVideoHeight;
- int32_t mDecodedWidth, mDecodedHeight;
-
- SuspensionState()
- : mLastVideoFrame(NULL) {
- }
-
- ~SuspensionState() {
- if (mLastVideoFrame) {
- free(mLastVideoFrame);
- mLastVideoFrame = NULL;
- }
- }
- } *mSuspensionState;
-
DrmManagerClient *mDrmManagerClient;
DecryptHandle *mDecryptHandle;
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 9cafc68..90d3fe1 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -23,16 +23,13 @@
namespace android {
+struct MetaData;
class Surface;
class SoftwareRenderer {
public:
SoftwareRenderer(
- OMX_COLOR_FORMATTYPE colorFormat,
- const sp<Surface> &surface,
- size_t displayWidth, size_t displayHeight,
- size_t decodedWidth, size_t decodedHeight,
- int32_t rotationDegrees);
+ const sp<Surface> &surface, const sp<MetaData> &meta);
~SoftwareRenderer();
@@ -42,16 +39,14 @@ public:
private:
enum YUVMode {
None,
- YUV420ToYUV420sp,
- YUV420spToYUV420sp,
};
OMX_COLOR_FORMATTYPE mColorFormat;
ColorConverter *mConverter;
YUVMode mYUVMode;
sp<Surface> mSurface;
- size_t mDisplayWidth, mDisplayHeight;
- size_t mDecodedWidth, mDecodedHeight;
+ int32_t mWidth, mHeight;
+ int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
SoftwareRenderer(const SoftwareRenderer &);
SoftwareRenderer &operator=(const SoftwareRenderer &);
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index ead1675..6e069c8 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -31,7 +31,6 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libui \
libcutils \
- libstagefright_color_conversion
ifneq ($(BUILD_WITHOUT_PV),true)
LOCAL_SHARED_LIBRARIES += \
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 7502f6e..b7e1a2a 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -22,7 +22,6 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
MtpClient.cpp \
- MtpCursor.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
@@ -38,6 +37,7 @@ LOCAL_SRC_FILES:= \
MtpStringBuffer.cpp \
MtpStorage.cpp \
MtpUtils.cpp \
+ PtpCursor.cpp \
LOCAL_MODULE:= libmtp
@@ -47,32 +47,3 @@ include $(BUILD_STATIC_LIBRARY)
endif
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- MtpClient.cpp \
- MtpCursor.cpp \
- MtpDataPacket.cpp \
- MtpDebug.cpp \
- MtpDevice.cpp \
- MtpEventPacket.cpp \
- MtpDeviceInfo.cpp \
- MtpObjectInfo.cpp \
- MtpPacket.cpp \
- MtpProperty.cpp \
- MtpRequestPacket.cpp \
- MtpResponsePacket.cpp \
- MtpStorageInfo.cpp \
- MtpStringBuffer.cpp \
- MtpStorage.cpp \
- MtpUtils.cpp \
-
-LOCAL_MODULE:= libmtp
-
-LOCAL_CFLAGS := -DMTP_HOST
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp
index 42bf8ba..bd6196f 100644
--- a/media/mtp/MtpPacket.cpp
+++ b/media/mtp/MtpPacket.cpp
@@ -123,7 +123,7 @@ void MtpPacket::setTransactionID(MtpTransactionID id) {
uint32_t MtpPacket::getParameter(int index) const {
if (index < 1 || index > 5) {
- LOGE("index %d out of range in MtpRequestPacket::getParameter", index);
+ LOGE("index %d out of range in MtpPacket::getParameter", index);
return 0;
}
return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
@@ -131,7 +131,7 @@ uint32_t MtpPacket::getParameter(int index) const {
void MtpPacket::setParameter(int index, uint32_t value) {
if (index < 1 || index > 5) {
- LOGE("index %d out of range in MtpResponsePacket::setParameter", index);
+ LOGE("index %d out of range in MtpPacket::setParameter", index);
return;
}
int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index 86889c3..42945f5 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -312,6 +312,10 @@ void MtpProperty::setFormEnum(const int* values, int count) {
}
}
+void MtpProperty::setFormDateTime() {
+ mFormFlag = kFormDateTime;
+}
+
void MtpProperty::print() {
LOGV("MtpProperty %04X\n", mCode);
LOGV(" type %04X\n", mType);
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
index c12399c..f783a87 100644
--- a/media/mtp/MtpProperty.h
+++ b/media/mtp/MtpProperty.h
@@ -58,6 +58,7 @@ public:
kFormNone = 0,
kFormRange = 1,
kFormEnum = 2,
+ kFormDateTime = 3,
};
uint32_t mGroupCode;
@@ -90,6 +91,7 @@ public:
void setFormRange(int min, int max, int step);
void setFormEnum(const int* values, int count);
+ void setFormDateTime();
void print();
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index ca13636..c3755f3 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -66,7 +66,7 @@ static const MtpOperationCode kSupportedOperationCodes[] = {
// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
// MTP_OPERATION_MOVE_OBJECT,
// MTP_OPERATION_COPY_OBJECT,
-// MTP_OPERATION_GET_PARTIAL_OBJECT,
+ MTP_OPERATION_GET_PARTIAL_OBJECT,
// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
MTP_OPERATION_GET_OBJECT_PROP_DESC,
@@ -289,6 +289,9 @@ bool MtpServer::handleRequest() {
case MTP_OPERATION_GET_OBJECT:
response = doGetObject();
break;
+ case MTP_OPERATION_GET_PARTIAL_OBJECT:
+ response = doGetPartialObject();
+ break;
case MTP_OPERATION_SEND_OBJECT_INFO:
response = doSendObjectInfo();
break;
@@ -536,7 +539,7 @@ MtpResponseCode MtpServer::doGetObjectPropList() {
MtpObjectFormat format = mRequest.getParameter(2);
MtpDeviceProperty property = mRequest.getParameter(3);
int groupCode = mRequest.getParameter(4);
- int depth = mRequest.getParameter(4);
+ int depth = mRequest.getParameter(5);
LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
handle, MtpDebug::getFormatCodeName(format),
MtpDebug::getObjectPropCodeName(property), groupCode, depth);
@@ -583,6 +586,45 @@ MtpResponseCode MtpServer::doGetObject() {
return MTP_RESPONSE_OK;
}
+MtpResponseCode MtpServer::doGetPartialObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ uint32_t offset = mRequest.getParameter(2);
+ uint32_t length = mRequest.getParameter(3);
+ MtpString pathBuf;
+ int64_t fileLength;
+ int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+ if (offset + length > fileLength)
+ length = fileLength - offset;
+
+ const char* filePath = (const char *)pathBuf;
+ mtp_file_range mfr;
+ mfr.fd = open(filePath, O_RDONLY);
+ if (mfr.fd < 0) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ mfr.offset = offset;
+ mfr.length = length;
+ mResponse.setParameter(1, length);
+
+ // send data header
+ mData.setOperationCode(mRequest.getOperationCode());
+ mData.setTransactionID(mRequest.getTransactionID());
+ mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
+
+ // then transfer the file
+ int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
+ close(mfr.fd);
+ if (ret < 0) {
+ if (errno == ECANCELED)
+ return MTP_RESPONSE_TRANSACTION_CANCELLED;
+ else
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ return MTP_RESPONSE_OK;
+}
+
MtpResponseCode MtpServer::doSendObjectInfo() {
MtpString path;
MtpStorageID storageID = mRequest.getParameter(1);
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index e65ddb0..5aee4ea 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -96,6 +96,7 @@ private:
MtpResponseCode doGetObjectPropList();
MtpResponseCode doGetObjectInfo();
MtpResponseCode doGetObject();
+ MtpResponseCode doGetPartialObject();
MtpResponseCode doSendObjectInfo();
MtpResponseCode doSendObject();
MtpResponseCode doDeleteObject();
diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/PtpCursor.cpp
index 35d90dc..7294cef 100644
--- a/media/mtp/MtpCursor.cpp
+++ b/media/mtp/PtpCursor.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "MtpCursor"
+#define LOG_TAG "PtpCursor"
#include "MtpDebug.h"
#include "MtpClient.h"
-#include "MtpCursor.h"
+#include "PtpCursor.h"
#include "MtpDevice.h"
#include "MtpDeviceInfo.h"
#include "MtpObjectInfo.h"
@@ -30,19 +30,19 @@
namespace android {
/* Device Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
#define DEVICE_ROW_ID 1
#define DEVICE_MANUFACTURER 2
#define DEVICE_MODEL 3
/* Storage Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
#define STORAGE_ROW_ID 101
#define STORAGE_IDENTIFIER 102
#define STORAGE_DESCRIPTION 103
/* Object Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
#define OBJECT_ROW_ID 201
#define OBJECT_STORAGE_ID 202
#define OBJECT_FORMAT 203
@@ -65,7 +65,7 @@ namespace android {
#define OBJECT_KEYWORDS 220
#define OBJECT_THUMB 221
-MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
+PtpCursor::PtpCursor(MtpClient* client, int queryType, int deviceID,
MtpStorageID storageID, MtpObjectHandle objectID,
int columnCount, int* columns)
: mClient(client),
@@ -82,12 +82,12 @@ MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
}
}
-MtpCursor::~MtpCursor() {
+PtpCursor::~PtpCursor() {
delete[] mColumns;
}
-int MtpCursor::fillWindow(CursorWindow* window, int startPos) {
- LOGD("MtpCursor::fillWindow mQueryType: %d\n", mQueryType);
+int PtpCursor::fillWindow(CursorWindow* window, int startPos) {
+ LOGD("PtpCursor::fillWindow mQueryType: %d\n", mQueryType);
switch (mQueryType) {
case DEVICE:
@@ -107,12 +107,12 @@ int MtpCursor::fillWindow(CursorWindow* window, int startPos) {
case OBJECT_CHILDREN:
return fillObjects(window, mQbjectID, startPos);
default:
- LOGE("MtpCursor::fillWindow: unknown query type %d\n", mQueryType);
+ LOGE("PtpCursor::fillWindow: unknown query type %d\n", mQueryType);
return 0;
}
}
-int MtpCursor::fillDevices(CursorWindow* window, int startPos) {
+int PtpCursor::fillDevices(CursorWindow* window, int startPos) {
int count = 0;
MtpDeviceList& deviceList = mClient->getDeviceList();
for (int i = 0; i < deviceList.size(); i++) {
@@ -127,7 +127,7 @@ int MtpCursor::fillDevices(CursorWindow* window, int startPos) {
return count;
}
-int MtpCursor::fillDevice(CursorWindow* window, int startPos) {
+int PtpCursor::fillDevice(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillDevice(window, device, startPos))
return 1;
@@ -135,7 +135,7 @@ int MtpCursor::fillDevice(CursorWindow* window, int startPos) {
return 0;
}
-int MtpCursor::fillStorages(CursorWindow* window, int startPos) {
+int PtpCursor::fillStorages(CursorWindow* window, int startPos) {
int count = 0;
MtpDevice* device = mClient->getDevice(mDeviceID);
if (!device)
@@ -157,7 +157,7 @@ int MtpCursor::fillStorages(CursorWindow* window, int startPos) {
return count;
}
-int MtpCursor::fillStorage(CursorWindow* window, int startPos) {
+int PtpCursor::fillStorage(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillStorage(window, device, mStorageID, startPos))
return 1;
@@ -165,7 +165,7 @@ int MtpCursor::fillStorage(CursorWindow* window, int startPos) {
return 0;
}
-int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
+int PtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
int count = 0;
MtpDevice* device = mClient->getDevice(mDeviceID);
if (!device)
@@ -187,7 +187,7 @@ int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
return count;
}
-int MtpCursor::fillObject(CursorWindow* window, int startPos) {
+int PtpCursor::fillObject(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillObject(window, device, mQbjectID, startPos))
return 1;
@@ -195,7 +195,7 @@ int MtpCursor::fillObject(CursorWindow* window, int startPos) {
return 0;
}
-bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
+bool PtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
if (!deviceInfo)
return false;
@@ -225,7 +225,7 @@ bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
return true;
}
-bool MtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
+bool PtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
MtpStorageID storageID, int row) {
LOGD("fillStorage %d\n", storageID);
@@ -273,7 +273,7 @@ fail:
return false;
}
-bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
+bool PtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
MtpObjectHandle objectID, int row) {
MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
@@ -385,7 +385,7 @@ fail:
return false;
}
-bool MtpCursor::prepareRow(CursorWindow* window) {
+bool PtpCursor::prepareRow(CursorWindow* window) {
if (!window->setNumColumns(mColumnCount)) {
LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount);
return false;
@@ -399,7 +399,7 @@ bool MtpCursor::prepareRow(CursorWindow* window) {
}
-bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
+bool PtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
if (!window->putLong(row, column, value)) {
window->freeLastRow();
LOGE("Failed allocating space for a long in column %d", column);
@@ -408,7 +408,7 @@ bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column
return true;
}
-bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
+bool PtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
int size = strlen(text) + 1;
int offset = window->alloc(size);
if (!offset) {
@@ -427,7 +427,7 @@ bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int c
return true;
}
-bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+bool PtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
MtpObjectFormat format, int row, int column) {
MtpDevice* device = mClient->getDevice(mDeviceID);
void* thumbnail;
diff --git a/media/mtp/MtpCursor.h b/media/mtp/PtpCursor.h
index 2e03c29..38a1d47 100644
--- a/media/mtp/MtpCursor.h
+++ b/media/mtp/PtpCursor.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef _MTP_CURSOR_H
-#define _MTP_CURSOR_H
+#ifndef _PTP_CURSOR_H
+#define _PTP_CURSOR_H
#include "MtpTypes.h"
@@ -23,7 +23,7 @@ namespace android {
class CursorWindow;
-class MtpCursor {
+class PtpCursor {
private:
enum {
DEVICE = 1,
@@ -45,10 +45,10 @@ private:
int* mColumns;
public:
- MtpCursor(MtpClient* client, int queryType, int deviceID,
+ PtpCursor(MtpClient* client, int queryType, int deviceID,
MtpStorageID storageID, MtpObjectHandle objectID,
int columnCount, int* columns);
- virtual ~MtpCursor();
+ virtual ~PtpCursor();
int fillWindow(CursorWindow* window, int startPos);
@@ -75,4 +75,4 @@ private:
}; // namespace android
-#endif // _MTP_CURSOR_H
+#endif // _PTP_CURSOR_H
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
index c04873a..0942d1f 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
@@ -24,7 +24,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.util.Log;
import android.view.View;
import android.widget.ListAdapter;
@@ -58,7 +58,7 @@ public class CameraBrowser extends ListActivity {
}
private static final String[] DEVICE_COLUMNS =
- new String[] { Mtp.Device._ID, Mtp.Device.MANUFACTURER, Mtp.Device.MODEL };
+ new String[] { Ptp.Device._ID, Ptp.Device.MANUFACTURER, Ptp.Device.MODEL };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -71,7 +71,7 @@ public class CameraBrowser extends ListActivity {
protected void onResume() {
super.onResume();
- Cursor c = getContentResolver().query(Mtp.Device.CONTENT_URI,
+ Cursor c = getContentResolver().query(Ptp.Device.CONTENT_URI,
DEVICE_COLUMNS, null, null, null);
Log.d(TAG, "query returned " + c);
startManagingCursor(c);
@@ -80,12 +80,12 @@ public class CameraBrowser extends ListActivity {
// Map Cursor columns to views defined in simple_list_item_2.xml
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, c,
- new String[] { Mtp.Device.MANUFACTURER, Mtp.Device.MODEL },
+ new String[] { Ptp.Device.MANUFACTURER, Ptp.Device.MODEL },
new int[] { android.R.id.text1, android.R.id.text2 });
setListAdapter(mAdapter);
// register for changes to the device list
- mResolver.registerContentObserver(Mtp.Device.CONTENT_URI, true, mDeviceObserver);
+ mResolver.registerContentObserver(Ptp.Device.CONTENT_URI, true, mDeviceObserver);
}
@Override
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
index 2060657..40c5978 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -25,7 +25,7 @@ import android.graphics.BitmapFactory;
import android.media.MtpConstants;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
@@ -49,7 +49,7 @@ public class ObjectBrowser extends ListActivity {
private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] OBJECT_COLUMNS =
- new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
+ new String[] { Ptp.Object._ID, Ptp.Object.NAME, Ptp.Object.FORMAT, Ptp.Object.THUMB };
static final int ID_COLUMN = 0;
static final int NAME_COLUMN = 1;
@@ -74,9 +74,9 @@ public class ObjectBrowser extends ListActivity {
Cursor c;
Uri uri;
if (mObjectID == 0) {
- uri = Mtp.Object.getContentUriForStorageChildren(mDeviceID, mStorageID);
+ uri = Ptp.Object.getContentUriForStorageChildren(mDeviceID, mStorageID);
} else {
- uri = Mtp.Object.getContentUriForObjectChildren(mDeviceID, mObjectID);
+ uri = Ptp.Object.getContentUriForObjectChildren(mDeviceID, mObjectID);
}
Log.d(TAG, "query " + uri);
c = getContentResolver().query(uri, OBJECT_COLUMNS, null, null, null);
@@ -99,7 +99,7 @@ public class ObjectBrowser extends ListActivity {
protected void onListItemClick(ListView l, View v, int position, long id) {
long rowID = mAdapter.getItemId(position);
Cursor c = getContentResolver().query(
- Mtp.Object.getContentUri(mDeviceID, rowID),
+ Ptp.Object.getContentUri(mDeviceID, rowID),
OBJECT_COLUMNS, null, null, null);
Log.d(TAG, "query returned " + c + " count: " + c.getCount());
long format = 0;
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
index 4e63128..3a6c6a4 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -24,7 +24,7 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -51,21 +51,21 @@ public class ObjectViewer extends Activity implements View.OnClickListener {
private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] OBJECT_COLUMNS =
- new String[] { Mtp.Object._ID,
- Mtp.Object.NAME,
- Mtp.Object.SIZE,
- Mtp.Object.THUMB_WIDTH,
- Mtp.Object.THUMB_HEIGHT,
- Mtp.Object.THUMB_SIZE,
- Mtp.Object.IMAGE_WIDTH,
- Mtp.Object.IMAGE_HEIGHT,
- Mtp.Object.IMAGE_DEPTH,
- Mtp.Object.SEQUENCE_NUMBER,
- Mtp.Object.DATE_CREATED,
- Mtp.Object.DATE_MODIFIED,
- Mtp.Object.KEYWORDS,
- Mtp.Object.THUMB,
- Mtp.Object.FORMAT,
+ new String[] { Ptp.Object._ID,
+ Ptp.Object.NAME,
+ Ptp.Object.SIZE,
+ Ptp.Object.THUMB_WIDTH,
+ Ptp.Object.THUMB_HEIGHT,
+ Ptp.Object.THUMB_SIZE,
+ Ptp.Object.IMAGE_WIDTH,
+ Ptp.Object.IMAGE_HEIGHT,
+ Ptp.Object.IMAGE_DEPTH,
+ Ptp.Object.SEQUENCE_NUMBER,
+ Ptp.Object.DATE_CREATED,
+ Ptp.Object.DATE_MODIFIED,
+ Ptp.Object.KEYWORDS,
+ Ptp.Object.THUMB,
+ Ptp.Object.FORMAT,
};
@Override
@@ -91,7 +91,7 @@ public class ObjectViewer extends Activity implements View.OnClickListener {
if (mDeviceID != 0 && mObjectID != 0) {
Cursor c = getContentResolver().query(
- Mtp.Object.getContentUri(mDeviceID, mObjectID),
+ Ptp.Object.getContentUri(mDeviceID, mObjectID),
OBJECT_COLUMNS, null, null, null);
c.moveToFirst();
TextView view = (TextView)findViewById(R.id.name);
@@ -147,7 +147,7 @@ public class ObjectViewer extends Activity implements View.OnClickListener {
dest.mkdirs();
dest = new File(dest, mFileName);
- Uri requestUri = Mtp.Object.getContentUriForImport(mDeviceID, mObjectID,
+ Uri requestUri = Ptp.Object.getContentUriForImport(mDeviceID, mObjectID,
dest.getAbsolutePath());
Uri resultUri = getContentResolver().insert(requestUri, new ContentValues());
Log.d(TAG, "save returned " + resultUri);
@@ -162,7 +162,7 @@ public class ObjectViewer extends Activity implements View.OnClickListener {
}
private void deleteObject() {
- Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
+ Uri uri = Ptp.Object.getContentUri(mDeviceID, mObjectID);
Log.d(TAG, "deleting " + uri);
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
index 4da88d6..62187b0 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -21,7 +21,7 @@ import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.util.Log;
import android.view.View;
import android.widget.ListAdapter;
@@ -40,7 +40,7 @@ public class StorageBrowser extends ListActivity {
private DeviceDisconnectedReceiver mDisconnectedReceiver;
private static final String[] STORAGE_COLUMNS =
- new String[] { Mtp.Storage._ID, Mtp.Storage.DESCRIPTION };
+ new String[] { Ptp.Storage._ID, Ptp.Storage.DESCRIPTION };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -54,7 +54,7 @@ public class StorageBrowser extends ListActivity {
super.onResume();
if (mDeviceID != 0) {
- Cursor c = getContentResolver().query(Mtp.Storage.getContentUri(mDeviceID),
+ Cursor c = getContentResolver().query(Ptp.Storage.getContentUri(mDeviceID),
STORAGE_COLUMNS, null, null, null);
Log.d(TAG, "query returned " + c);
startManagingCursor(c);
@@ -62,7 +62,7 @@ public class StorageBrowser extends ListActivity {
// Map Cursor columns to views defined in simple_list_item_1.xml
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, c,
- new String[] { Mtp.Storage.DESCRIPTION },
+ new String[] { Ptp.Storage.DESCRIPTION },
new int[] { android.R.id.text1, android.R.id.text2 });
setListAdapter(mAdapter);
}
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
deleted file mode 100644
index 9ababb7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
deleted file mode 100644
index ffbd2d3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
index e81cd7c..9216030 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
index e81cd7c..9216030 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
index c56ae8a..e529f6f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
index bf720cbb..e529f6f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png
new file mode 100644
index 0000000..02c27ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
index 38e3b24..57558ad 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
index 3559497..57558ad 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
index 6f98e72..e4425b2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
index 8b932a6..e4425b2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png
new file mode 100644
index 0000000..84ac927
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
index 759631c..09de6b0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
index 45c4509..09de6b0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png
new file mode 100644
index 0000000..13cae40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
index da5b02c..690b5f6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png
new file mode 100644
index 0000000..d0a4fd0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png
new file mode 100644
index 0000000..05976bd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png
new file mode 100644
index 0000000..2cc3cd6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
index 2989939..1c59b2a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
new file mode 100644
index 0000000..1c59b2a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
index 6f40cb4..32e9165 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
index 60e3794a..32e9165 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
index ab1b0c6..ea71298 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
index eb25919..ea71298 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
index b574115..869a497 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
index 14cd8fa..869a497 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
index 3b81ceb..1711c82 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
index ef72980..1711c82 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 4fa306e..488dbba 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -80,21 +80,24 @@
android:layout_width="48dip"
android:layout_height="match_parent"
android:orientation="horizontal"
+ android:gravity="center"
>
<ImageView
- android:id="@+id/battery"
+ android:id="@+id/network"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="top"
- android:layout_marginTop="18dp"
+ android:layout_marginTop="19dp"
+ android:layout_marginRight="4dp"
/>
<ImageView
- android:id="@+id/network"
+ android:id="@+id/battery"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="top"
- android:layout_marginTop="14dp"
- android:src="@drawable/ic_sysbar_wifi_mini"
+ android:layout_marginTop="19dp"
+ android:layout_marginLeft="2dp"
+ android:layout_marginRight="2dp"
/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index f9e2d5e..5fa8b3b 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -78,7 +78,6 @@
android:layout_below="@id/date"
android:layout_marginTop="16dp"
android:layout_marginLeft="48dp"
- android:src="@drawable/ic_sysbar_battery_mini"
android:baseline="17dp"
/>
@@ -98,7 +97,6 @@
android:layout_width="wrap_content"
android:layout_toRightOf="@id/battery_text"
android:layout_alignBaseline="@id/battery"
- android:src="@drawable/ic_sysbar_wifi_mini"
android:baseline="21dp"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 3ad199e..18e8273 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -71,6 +71,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="none"
+ android:overScrollMode="ifContentScrolls"
>
<LinearLayout
android:id="@+id/notificationLinearLayout"
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index ff2a4ed..a98ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -193,14 +193,13 @@ public class RecentApplicationsActivity extends Activity {
ActivityDescription item = mActivityDescriptions.get(n);
if (item.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(item.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(item.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else if (item.intent != null) {
// prepare a launch intent and send it
- item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
try {
if (DBG) Log.v(TAG, "Starting intent " + item.intent);
startActivity(item.intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 4ff2429..18003dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -37,6 +37,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Slog;
+import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -57,8 +58,6 @@ public class NetworkController extends BroadcastReceiver {
boolean mHspaDataDistinguishable;
final TelephonyManager mPhone;
boolean mDataConnected;
- int mPhoneSignalIconId;
- int mDataIconId;
IccCard.State mSimState = IccCard.State.READY;
int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
int mDataState = TelephonyManager.DATA_DISCONNECTED;
@@ -66,6 +65,10 @@ public class NetworkController extends BroadcastReceiver {
ServiceState mServiceState;
SignalStrength mSignalStrength;
int[] mDataIconList = TelephonyIcons.DATA_G[0];
+ int mPhoneSignalIconId;
+ int mDataDirectionIconId;
+ int mDataSignalIconId;
+ int mDataTypeIconId;
// wifi
final WifiManager mWifiManager;
@@ -81,11 +84,17 @@ public class NetworkController extends BroadcastReceiver {
// our ui
Context mContext;
- ArrayList<ImageView> mPhoneIconViews = new ArrayList<ImageView>();
- ArrayList<ImageView> mDataIconViews = new ArrayList<ImageView>();
+ ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>();
+ ArrayList<ImageView> mDataDirectionIconViews = new ArrayList<ImageView>();
+ ArrayList<ImageView> mWifiIconViews = new ArrayList<ImageView>();
+ ArrayList<ImageView> mCombinedSignalIconViews = new ArrayList<ImageView>();
+ ArrayList<ImageView> mDataTypeIconViews = new ArrayList<ImageView>();
ArrayList<TextView> mLabelViews = new ArrayList<TextView>();
int mLastPhoneSignalIconId = -1;
- int mLastCombinedDataIconId = -1;
+ int mLastDataDirectionIconId = -1;
+ int mLastWifiIconId = -1;
+ int mLastCombinedSignalIconId = -1;
+ int mLastDataTypeIconId = -1;
String mLastLabel = "";
// yuck -- stop doing this here and put it in the framework
@@ -116,7 +125,6 @@ public class NetworkController extends BroadcastReceiver {
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
context.registerReceiver(this, filter);
@@ -125,12 +133,24 @@ public class NetworkController extends BroadcastReceiver {
mBatteryStats = BatteryStatsService.getService();
}
- public void addPhoneIconView(ImageView v) {
- mPhoneIconViews.add(v);
+ public void addPhoneSignalIconView(ImageView v) {
+ mPhoneSignalIconViews.add(v);
+ }
+
+ public void addDataDirectionIconView(ImageView v) {
+ mDataDirectionIconViews.add(v);
+ }
+
+ public void addWifiIconView(ImageView v) {
+ mWifiIconViews.add(v);
+ }
+
+ public void addCombinedSignalIconView(ImageView v) {
+ mCombinedSignalIconViews.add(v);
}
- public void addCombinedDataIconView(ImageView v) {
- mDataIconViews.add(v);
+ public void addDataTypeIconView(ImageView v) {
+ mDataTypeIconViews.add(v);
}
public void addLabelView(TextView v) {
@@ -141,7 +161,6 @@ public class NetworkController extends BroadcastReceiver {
final String action = intent.getAction();
if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
|| action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
- || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
|| action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
updateWifiState(intent);
refreshViews();
@@ -317,12 +336,15 @@ public class NetworkController extends BroadcastReceiver {
if (Settings.System.getInt(mContext.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode;
+ mDataSignalIconId = R.drawable.stat_sys_signal_flightmode;
} else {
mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+ mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
}
} else {
if (mSignalStrength == null) {
mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+ mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
} else if (isCdma()) {
// If 3G(EV) and 1x network are available than 3G should be
// displayed, displayed RSSI should be from the EV side.
@@ -340,6 +362,7 @@ public class NetworkController extends BroadcastReceiver {
iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
}
mPhoneSignalIconId = iconList[iconLevel];
+ mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
} else {
int asu = mSignalStrength.getGsmSignalStrength();
@@ -362,6 +385,7 @@ public class NetworkController extends BroadcastReceiver {
iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
}
mPhoneSignalIconId = iconList[iconLevel];
+ mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
}
}
}
@@ -370,36 +394,47 @@ public class NetworkController extends BroadcastReceiver {
switch (net) {
case TelephonyManager.NETWORK_TYPE_EDGE:
mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_edge;
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_3g;
break;
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
if (mHspaDataDistinguishable) {
mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_hsdpa;
} else {
mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_3g;
}
break;
case TelephonyManager.NETWORK_TYPE_CDMA:
// display 1xRTT for IS95A/B
mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_1x;
break;
case TelephonyManager.NETWORK_TYPE_1xRTT:
mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_1x;
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_3g;
break;
// TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD
default:
mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+ mDataTypeIconId = R.drawable.stat_sys_signal_gprs;
break;
}
+ if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
+ mDataTypeIconId = R.drawable.stat_sys_signal_roam;
+ }
}
boolean isCdmaEri() {
@@ -436,7 +471,7 @@ public class NetworkController extends BroadcastReceiver {
iconId = mDataIconList[0];
break;
}
- mDataIconId = iconId;
+ mDataDirectionIconId = iconId;
} else {
iconId = 0;
visible = false;
@@ -477,7 +512,7 @@ public class NetworkController extends BroadcastReceiver {
Binder.restoreCallingIdentity(ident);
}
- mDataIconId = iconId;
+ mDataDirectionIconId = iconId;
mDataConnected = visible;
}
@@ -489,8 +524,7 @@ public class NetworkController extends BroadcastReceiver {
mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)
- || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
final NetworkInfo networkInfo = (NetworkInfo)
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
boolean wasConnected = mWifiConnected;
@@ -575,8 +609,10 @@ public class NetworkController extends BroadcastReceiver {
void refreshViews() {
Context context = mContext;
- int combinedDataIconId;
+ int combinedSignalIconId;
+ int dataTypeIconId;
String label;
+ int N;
if (mWifiConnected) {
if (mWifiSsid == null) {
@@ -585,35 +621,84 @@ public class NetworkController extends BroadcastReceiver {
label = context.getString(R.string.system_panel_signal_meter_wifi_ssid_format,
mWifiSsid);
}
- combinedDataIconId = mWifiIconId;
- } else if (mDataConnected) {
- label = context.getString(R.string.system_panel_signal_meter_data_connected);
- combinedDataIconId = mDataIconId;
+ combinedSignalIconId = mWifiIconId;
+ dataTypeIconId = 0;
} else {
- label = context.getString(R.string.system_panel_signal_meter_disconnected);
- combinedDataIconId = 0;
+ if (mDataConnected) {
+ label = context.getString(R.string.system_panel_signal_meter_data_connected);
+ } else {
+ label = context.getString(R.string.system_panel_signal_meter_disconnected);
+ }
+ combinedSignalIconId = mDataSignalIconId;
+ dataTypeIconId = mDataTypeIconId;
}
- int N;
+ if (false) {
+ Slog.d(TAG, "refreshViews combinedSignalIconId=0x"
+ + Integer.toHexString(mPhoneSignalIconId)
+ + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
+ + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
+ + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
+ + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
+ + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId));
+ }
+ // the phone icon on phones
if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
mLastPhoneSignalIconId = mPhoneSignalIconId;
- N = mPhoneIconViews.size();
+ N = mPhoneSignalIconViews.size();
for (int i=0; i<N; i++) {
- ImageView v = mPhoneIconViews.get(i);
+ final ImageView v = mPhoneSignalIconViews.get(i);
v.setImageResource(mPhoneSignalIconId);
}
}
- if (mLastCombinedDataIconId != combinedDataIconId) {
- mLastCombinedDataIconId = combinedDataIconId;
- N = mDataIconViews.size();
+ // the data icon on phones
+ if (mLastDataDirectionIconId != mDataDirectionIconId) {
+ mLastDataDirectionIconId = mDataDirectionIconId;
+ N = mDataDirectionIconViews.size();
for (int i=0; i<N; i++) {
- ImageView v = mDataIconViews.get(i);
- v.setImageResource(combinedDataIconId);
+ final ImageView v = mDataDirectionIconViews.get(i);
+ v.setImageResource(mDataDirectionIconId);
+ }
+ }
+
+ // the wifi icon on phones
+ if (mLastWifiIconId != mWifiIconId) {
+ mLastWifiIconId = mWifiIconId;
+ N = mWifiIconViews.size();
+ for (int i=0; i<N; i++) {
+ final ImageView v = mWifiIconViews.get(i);
+ v.setImageResource(mWifiIconId);
+ }
+ }
+
+ // the combined data signal icon
+ if (mLastCombinedSignalIconId != combinedSignalIconId) {
+ mLastCombinedSignalIconId = combinedSignalIconId;
+ N = mCombinedSignalIconViews.size();
+ for (int i=0; i<N; i++) {
+ final ImageView v = mCombinedSignalIconViews.get(i);
+ v.setImageResource(combinedSignalIconId);
+ }
+ }
+
+ // the data network type overlay
+ if (mLastDataTypeIconId != dataTypeIconId) {
+ mLastDataTypeIconId = dataTypeIconId;
+ N = mDataTypeIconViews.size();
+ for (int i=0; i<N; i++) {
+ final ImageView v = mDataTypeIconViews.get(i);
+ if (dataTypeIconId == 0) {
+ v.setVisibility(View.INVISIBLE);
+ } else {
+ v.setVisibility(View.VISIBLE);
+ v.setImageResource(dataTypeIconId);
+ }
}
}
+ // the label in the notification panel
if (!mLastLabel.equals(label)) {
mLastLabel = label;
N = mLabelViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 050a746..94c68ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -48,6 +48,8 @@ class TelephonyIcons {
R.drawable.stat_sys_r_signal_4_fully }
};
+ static final int[][] DATA_SIGNAL_STRENGTH = TELEPHONY_SIGNAL_STRENGTH;
+
//***** Data connection icons
//GSM/UMTS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index e0b05f9..0c31304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -252,13 +252,13 @@ public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnC
ActivityDescription ad = (ActivityDescription)v.getTag();
if (ad.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(ad.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
getContext().startActivity(intent);
}
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 ab509ef..a934cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -157,7 +157,7 @@ public class TabletStatusBar extends StatusBar {
mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
mBatteryController.addLabelView(
(TextView)mNotificationPanel.findViewById(R.id.battery_text));
- mNetworkController.addCombinedDataIconView(
+ mNetworkController.addCombinedSignalIconView(
(ImageView)mNotificationPanel.findViewById(R.id.network));
mNetworkController.addLabelView(
(TextView)mNotificationPanel.findViewById(R.id.network_text));
@@ -282,7 +282,7 @@ public class TabletStatusBar extends StatusBar {
mBatteryController = new BatteryController(mContext);
mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
mNetworkController = new NetworkController(mContext);
- mNetworkController.addCombinedDataIconView((ImageView)sb.findViewById(R.id.network));
+ mNetworkController.addCombinedSignalIconView((ImageView)sb.findViewById(R.id.network));
// The navigation buttons
mNavigationArea = sb.findViewById(R.id.navigationArea);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 5287289..1c1a46e 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -139,6 +139,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
+ static final int LONG_PRESS_HOME_NOTHING = 0;
+ static final int LONG_PRESS_HOME_RECENT_DIALOG = 1;
+ static final int LONG_PRESS_HOME_RECENT_ACTIVITY = 2;
+
// wallpaper is at the bottom, though the window manager may move it.
static final int WALLPAPER_LAYER = 2;
static final int APPLICATION_LAYER = 2;
@@ -327,8 +331,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Nothing to see here, move along...
int mFancyRotationAnimation;
- // Enable 3D recents based on config settings.
- private Boolean mUse3dRecents;
+ // What we do when the user long presses on home
+ private int mLongPressOnHomeBehavior = -1;
ShortcutManager mShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
@@ -565,8 +569,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* the user lets go of the home key
*/
mHomePressed = false;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
showRecentAppsDialog();
}
};
@@ -576,16 +578,32 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
void showRecentAppsDialog() {
// We can't initialize this in init() since the configuration hasn't been loaded yet.
- if (mUse3dRecents == null) {
- mUse3dRecents = mContext.getResources().getBoolean(R.bool.config_enableRecentApps3D);
+ if (mLongPressOnHomeBehavior < 0) {
+ mLongPressOnHomeBehavior
+ = mContext.getResources().getInteger(R.integer.config_longPressOnPowerBehavior);
+ if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
+ mLongPressOnHomeBehavior > LONG_PRESS_HOME_RECENT_ACTIVITY) {
+ mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ }
+ }
+
+ if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
}
// Use 3d Recents dialog
- if (mUse3dRecents) {
+ if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
+ // Fallback to dialog if we fail to launch the above.
+ if (mRecentAppsDialog == null) {
+ mRecentAppsDialog = new RecentApplicationsDialog(mContext);
+ }
+ mRecentAppsDialog.show();
+ } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_ACTIVITY) {
try {
Intent intent = new Intent();
intent.setClassName("com.android.systemui",
- "com.android.systemui.statusbar.RecentApplicationsActivity");
+ "com.android.systemui.recent.RecentApplicationsActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
mContext.startActivity(intent);
@@ -594,12 +612,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Log.e(TAG, "Failed to launch RecentAppsIntent", e);
}
}
-
- // Fallback to dialog if we fail to launch the above.
- if (mRecentAppsDialog == null) {
- mRecentAppsDialog = new RecentApplicationsDialog(mContext);
- }
- mRecentAppsDialog.show();
}
/** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index d9e8c2b..f53092d 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -138,13 +138,12 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
RecentTag tag = (RecentTag)b.getTag();
if (tag.info.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(tag.info.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else if (tag.intent != null) {
- tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
try {
getContext().startActivity(tag.intent);
} catch (ActivityNotFoundException e) {
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0e45145..0a28da7 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -343,16 +343,17 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) {
DropBoxManager.Entry dbe = null;
+ InputStreamReader isr = null;
try {
dbe = new DropBoxManager.Entry(
entry.tag, entry.timestampMillis, entry.file, entry.flags);
if (doPrint) {
- InputStreamReader r = new InputStreamReader(dbe.getInputStream());
+ isr = new InputStreamReader(dbe.getInputStream());
char[] buf = new char[4096];
boolean newline = false;
for (;;) {
- int n = r.read(buf);
+ int n = isr.read(buf);
if (n <= 0) break;
out.append(buf, 0, n);
newline = (buf[n - 1] == '\n');
@@ -376,6 +377,12 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
Slog.e(TAG, "Can't read: " + entry.file, e);
} finally {
if (dbe != null) dbe.close();
+ if (isr != null) {
+ try {
+ isr.close();
+ } catch (IOException unused) {
+ }
+ }
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 84bc100..723432d 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -55,6 +55,7 @@ import android.os.IBinder;
import android.os.IInterface;
import android.os.Message;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -120,6 +121,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// If IME doesn't support the system locale, the default subtype will be the first defined one.
private static final int DEFAULT_SUBTYPE_ID = 0;
+ private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+ private static final String SUBTYPE_MODE_VOICE = "voice";
+
final Context mContext;
final Handler mHandler;
final InputMethodSettings mSettings;
@@ -235,6 +239,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
private InputMethodSubtype mCurrentSubtype;
+ // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
+ private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+ mShortcutInputMethodsAndSubtypes =
+ new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
/**
* Set to true if our ServiceConnection is currently actively bound to
@@ -983,6 +991,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurMethodId = null;
unbindCurrentMethodLocked(true);
}
+ mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
mCurMethodId = null;
@@ -1291,7 +1300,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
public void setInputMethod(IBinder token, String id) {
- setInputMethodWithSubtype(token, id, NOT_A_SUBTYPE_ID);
+ setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
+ }
+
+ public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+ synchronized (mMethodMap) {
+ if (subtype != null) {
+ setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode(
+ mMethodMap.get(id), subtype.hashCode()));
+ } else {
+ setInputMethod(token, id);
+ }
+ }
}
public boolean switchToLastInputMethod(IBinder token) {
@@ -1300,7 +1320,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (lastIme != null) {
InputMethodInfo imi = mMethodMap.get(lastIme.first);
if (imi != null) {
- setInputMethodWithSubtype(token, lastIme.first, getSubtypeIdFromHashCode(
+ setInputMethodWithSubtypeId(token, lastIme.first, getSubtypeIdFromHashCode(
imi, Integer.valueOf(lastIme.second)));
return true;
}
@@ -1309,7 +1329,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private void setInputMethodWithSubtype(IBinder token, String id, int subtypeId) {
+ private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -1900,35 +1920,43 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
private int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
- ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
- for (int i = 0; i < subtypes.size(); ++i) {
- InputMethodSubtype ims = subtypes.get(i);
- if (subtypeHashCode == ims.hashCode()) {
- return i;
+ if (imi != null) {
+ ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+ for (int i = 0; i < subtypes.size(); ++i) {
+ InputMethodSubtype ims = subtypes.get(i);
+ if (subtypeHashCode == ims.hashCode()) {
+ return i;
+ }
}
}
return NOT_A_SUBTYPE_ID;
}
- // If there are no selected subtypes, tries finding the most applicable one according to the
- // current system locale
- private int findApplicableSubtype(String id) {
- InputMethodInfo imi = mMethodMap.get(id);
- if (imi == null) {
- return NOT_A_SUBTYPE_ID;
- }
- ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+ /**
+ * If there are no selected subtypes, tries finding the most applicable one according to the
+ * given locale.
+ * @param subtypes this function will search the most applicable subtype in subtypes
+ * @param mode subtypes will be filtered by mode
+ * @param locale subtypes will be filtered by locale
+ * @param defaultSubtypeId if this function can't find the most applicable subtype, it will
+ * return defaultSubtypeId
+ * @return the most applicable subtypeId
+ */
+ private int findLastResortApplicableSubtypeLocked(
+ List<InputMethodSubtype> subtypes, String mode, String locale, int defaultSubtypeId) {
if (subtypes == null || subtypes.size() == 0) {
return NOT_A_SUBTYPE_ID;
}
- final String locale = mContext.getResources().getConfiguration().locale.toString();
+ if (TextUtils.isEmpty(locale)) {
+ locale = mContext.getResources().getConfiguration().locale.toString();
+ }
final String language = locale.substring(0, 2);
boolean partialMatchFound = false;
- int applicableSubtypeId = DEFAULT_SUBTYPE_ID;
+ int applicableSubtypeId = defaultSubtypeId;
for (int i = 0; i < subtypes.size(); ++i) {
final String subtypeLocale = subtypes.get(i).getLocale();
- // An applicable subtype should be a keyboard subtype
- if (subtypes.get(i).getMode().equalsIgnoreCase("keyboard")) {
+ // An applicable subtype should match "mode".
+ if (subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
if (locale.equals(subtypeLocale)) {
// Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
applicableSubtypeId = i;
@@ -1950,6 +1978,69 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return applicableSubtypeId;
}
+ // If there are no selected shortcuts, tries finding the most applicable ones.
+ private Pair<InputMethodInfo, InputMethodSubtype>
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
+ List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ InputMethodInfo mostApplicableIMI = null;
+ int mostApplicableSubtypeId = NOT_A_SUBTYPE_ID;
+ boolean foundInSystemIME = false;
+
+ // Search applicable subtype for each InputMethodInfo
+ for (InputMethodInfo imi: imis) {
+ int subtypeId = NOT_A_SUBTYPE_ID;
+ if (mCurrentSubtype != null) {
+ // 1. Search with the current subtype's locale and the enabled subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(
+ mSettings.getEnabledInputMethodSubtypeListLocked(
+ imi), mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ // 2. Search with the current subtype's locale and all subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+ }
+ }
+ // 3. Search with the system locale and the enabled subtypes
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ subtypeId = findLastResortApplicableSubtypeLocked(
+ mSettings.getEnabledInputMethodSubtypeListLocked(
+ imi), mode, null, NOT_A_SUBTYPE_ID);
+ }
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ // 4. Search with the system locale and all subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ mode, null, NOT_A_SUBTYPE_ID);
+ }
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ if (imi.getId().equals(mCurMethodId)) {
+ // The current input method is the most applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ break;
+ } else if ((imi.getServiceInfo().applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // The system input method is 2nd applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ foundInSystemIME = true;
+ } else if (!foundInSystemIME) {
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.w(TAG, "Most applicable shortcut input method subtype was:"
+ + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
+ }
+ if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
+ return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
+ mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
+ } else {
+ return null;
+ }
+ }
+
/**
* @return Return the current subtype of this input method.
*/
@@ -1960,18 +2051,65 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
} catch (SettingNotFoundException e) {
}
- if (!subtypeIsSelected || mCurrentSubtype == null) {
- String lastInputMethodId = Settings.Secure.getString(mContext
- .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
- if (subtypeId == NOT_A_SUBTYPE_ID) {
- subtypeId = findApplicableSubtype(lastInputMethodId);
+ synchronized (mMethodMap) {
+ if (!subtypeIsSelected || mCurrentSubtype == null) {
+ String lastInputMethodId = Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ InputMethodInfo imi = mMethodMap.get(lastInputMethodId);
+ if (imi != null) {
+ // If there are no selected subtypes, the framework will try to find
+ // the most applicable subtype from all subtypes whose mode is
+ // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode
+ // the mode.
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ SUBTYPE_MODE_KEYBOARD, null, DEFAULT_SUBTYPE_ID);
+ }
+ }
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ mCurrentSubtype =
+ mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
+ } else {
+ mCurrentSubtype = null;
+ }
}
- if (subtypeId != NOT_A_SUBTYPE_ID) {
- mCurrentSubtype = mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
+ return mCurrentSubtype;
+ }
+ }
+
+ private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) {
+ mShortcutInputMethodsAndSubtypes.get(imi).add(subtype);
+ } else {
+ ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(subtype);
+ mShortcutInputMethodsAndSubtypes.put(imi, subtypes);
+ }
+ }
+
+ // TODO: We should change the return type from List to List<Parcelable>
+ public List getShortcutInputMethodsAndSubtypes() {
+ synchronized (mMethodMap) {
+ if (mShortcutInputMethodsAndSubtypes.size() == 0) {
+ // If there are no selected shortcut subtypes, the framework will try to find
+ // the most applicable subtype from all subtypes whose mode is
+ // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
+ Pair<InputMethodInfo, InputMethodSubtype> info =
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
+ SUBTYPE_MODE_VOICE);
+ addShortcutInputMethodAndSubtypes(info.first, info.second);
+ }
+ ArrayList ret = new ArrayList<Object>();
+ for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
+ ret.add(imi);
+ for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
+ ret.add(subtype);
+ }
}
+ return ret;
}
- return mCurrentSubtype;
}
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/StrictModeFlash.java
new file mode 100644
index 0000000..0a6c625
--- /dev/null
+++ b/services/java/com/android/server/StrictModeFlash.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server; // TODO: use com.android.server.wm, once things move there
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceSession;
+
+class StrictModeFlash {
+ private static final String TAG = "StrictModeFlash";
+
+ Surface mSurface;
+ int mLastDW;
+ int mLastDH;
+ boolean mDrawNeeded;
+ final int mThickness = 20;
+
+ public StrictModeFlash(Display display, SurfaceSession session) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ display.getMetrics(dm);
+
+ try {
+ mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ } catch (Surface.OutOfResourcesException e) {
+ return;
+ }
+
+ mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
+ mSurface.setPosition(0, 0);
+ mDrawNeeded = true;
+ }
+
+ private void drawIfNeeded() {
+ if (!mDrawNeeded) {
+ return;
+ }
+ mDrawNeeded = false;
+ final int dw = mLastDW;
+ final int dh = mLastDH;
+
+ Rect dirty = new Rect(0, 0, dw, dh);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (Surface.OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+
+ // Top
+ c.clipRect(new Rect(0, 0, dw, mThickness), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Left
+ c.clipRect(new Rect(0, 0, mThickness, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Right
+ c.clipRect(new Rect(dw - mThickness, 0, dw, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Bottom
+ c.clipRect(new Rect(0, dh - mThickness, dw, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+
+ mSurface.unlockCanvasAndPost(c);
+ }
+
+ // Note: caller responsible for being inside
+ // Surface.openTransaction() / closeTransaction()
+ public void setVisibility(boolean on) {
+ if (mSurface == null) {
+ return;
+ }
+ drawIfNeeded();
+ if (on) {
+ mSurface.show();
+ } else {
+ mSurface.hide();
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ if (mLastDW == dw && mLastDH == dh) {
+ return;
+ }
+ mLastDW = dw;
+ mLastDH = dh;
+ mSurface.setSize(dw, dh);
+ mDrawNeeded = true;
+ }
+
+}
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
index cfa83be..4a7df8f 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -24,7 +24,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.UEventObserver;
-import android.provider.Mtp;
+import android.provider.Ptp;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -155,7 +155,7 @@ class UsbObserver extends UEventObserver {
// called from JNI in monitorUsbHostBus()
private void usbCameraAdded(int deviceID) {
Intent intent = new Intent(Usb.ACTION_USB_CAMERA_ATTACHED,
- Mtp.Device.getContentUri(deviceID));
+ Ptp.Device.getContentUri(deviceID));
Log.d(TAG, "usbCameraAdded, sending " + intent);
mContext.sendBroadcast(intent);
}
@@ -163,7 +163,7 @@ class UsbObserver extends UEventObserver {
// called from JNI in monitorUsbHostBus()
private void usbCameraRemoved(int deviceID) {
Intent intent = new Intent(Usb.ACTION_USB_CAMERA_DETACHED,
- Mtp.Device.getContentUri(deviceID));
+ Ptp.Device.getContentUri(deviceID));
Log.d(TAG, "usbCameraRemoved, sending " + intent);
mContext.sendBroadcast(intent);
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0c20064..b3ea836 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -59,6 +59,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -200,10 +201,6 @@ public class WindowManagerService extends IWindowManager.Stub
*/
static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
- /** Adjustment to time to perform a dim, to make it more dramatic.
- */
- static final int DIM_DURATION_MULTIPLIER = 6;
-
// Maximum number of milliseconds to wait for input event injection.
// FIXME is this value reasonable?
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -376,6 +373,7 @@ public class WindowManagerService extends IWindowManager.Stub
Surface mBlurSurface;
boolean mBlurShown;
Watermark mWatermark;
+ StrictModeFlash mStrictModeFlash;
int mTransactionSequence = 0;
@@ -4883,6 +4881,36 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ // TODO: more accounting of which pid(s) turned it on, keep count,
+ // only allow disables from pids which have count on, etc.
+ public void showStrictModeViolation(boolean on) {
+ int pid = Binder.getCallingPid();
+ synchronized(mWindowMap) {
+ // Ignoring requests to enable the red border from clients
+ // which aren't on screen. (e.g. Broadcast Receivers in
+ // the background..)
+ if (on) {
+ boolean isVisible = false;
+ for (WindowState ws : mWindows) {
+ if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
+ isVisible = true;
+ break;
+ }
+ }
+ if (!isVisible) {
+ return;
+ }
+ }
+
+ Surface.openTransaction();
+ if (mStrictModeFlash == null) {
+ mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession);
+ }
+ mStrictModeFlash.setVisibility(on);
+ Surface.closeTransaction();
+ }
+ }
+
public void freezeRotation() {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"setRotation()")) {
@@ -6596,6 +6624,7 @@ public class WindowManagerService extends IWindowManager.Stub
final Rect mContainingFrame = new Rect();
final Rect mDisplayFrame = new Rect();
final Rect mContentFrame = new Rect();
+ final Rect mParentFrame = new Rect();
final Rect mVisibleFrame = new Rect();
boolean mContentChanged;
@@ -6801,8 +6830,12 @@ public class WindowManagerService extends IWindowManager.Stub
h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
}
+ if (!mParentFrame.equals(pf)) {
+ mParentFrame.set(pf);
+ mContentChanged = true;
+ }
+
final Rect content = mContentFrame;
- mContentChanged |= !content.equals(cf);
content.set(cf);
final Rect visible = mVisibleFrame;
@@ -7668,6 +7701,21 @@ public class WindowManagerService extends IWindowManager.Stub
&& !mDrawPending && !mCommitDrawPending;
}
+ /**
+ * Return whether this window is wanting to have a translation
+ * animation applied to it for an in-progress move. (Only makes
+ * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
+ */
+ boolean shouldAnimateMove() {
+ return mContentChanged && !mAnimating && !mLastHidden && !mDisplayFrozen
+ && (mFrame.top != mLastFrame.top
+ || mFrame.left != mLastFrame.left)
+ && (mAttachedWindow == null
+ || (mAttachedWindow.mAnimation == null
+ && !mAttachedWindow.shouldAnimateMove()))
+ && mPolicy.isScreenOn();
+ }
+
boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
return
// only if the application is requesting compatible window
@@ -7894,6 +7942,8 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println();
pw.print(prefix); pw.print("mContainingFrame=");
mContainingFrame.printShortString(pw);
+ pw.print(" mParentFrame=");
+ mParentFrame.printShortString(pw);
pw.print(" mDisplayFrame=");
mDisplayFrame.printShortString(pw);
pw.println();
@@ -9126,11 +9176,11 @@ public class WindowManagerService extends IWindowManager.Stub
|| win.mAttachedHidden
|| win.mExiting || win.mDestroying;
- if (!win.mLayoutAttached) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win
+ if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+ Slog.v(TAG, "First pass " + win
+ ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+ " mLayoutAttached=" + win.mLayoutAttached);
- if (DEBUG_LAYOUT && gone) Slog.v(TAG, " (mViewVisibility="
+ if (gone) Slog.v(TAG, " (mViewVisibility="
+ win.mViewVisibility + " mRelayoutCalled="
+ win.mRelayoutCalled + " hidden="
+ win.mRootToken.hidden + " hiddenRequested="
@@ -9167,16 +9217,16 @@ public class WindowManagerService extends IWindowManager.Stub
for (i = topAttached; i >= 0; i--) {
WindowState win = mWindows.get(i);
- // If this view is GONE, then skip it -- keep the current
- // frame, and let the caller know so they can ignore it
- // if they want. (We do the normal layout for INVISIBLE
- // windows, since that means "perform layout as normal,
- // just don't display").
if (win.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win
+ " mHaveFrame=" + win.mHaveFrame
+ " mViewVisibility=" + win.mViewVisibility
+ " mRelayoutCalled=" + win.mRelayoutCalled);
+ // If this view is GONE, then skip it -- keep the current
+ // frame, and let the caller know so they can ignore it
+ // if they want. (We do the normal layout for INVISIBLE
+ // windows, since that means "perform layout as normal,
+ // just don't display").
if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
|| !win.mHaveFrame) {
if (initial) {
@@ -9198,6 +9248,7 @@ public class WindowManagerService extends IWindowManager.Stub
return mPolicy.finishLayoutLw();
}
+ // "Something has changed! Let's make it correct now."
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
if (mDisplay == null) {
@@ -9249,6 +9300,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (mWatermark != null) {
mWatermark.positionSurface(dw, dh);
}
+ if (mStrictModeFlash != null) {
+ mStrictModeFlash.positionSurface(dw, dh);
+ }
try {
boolean wallpaperForceHidingChanged = false;
@@ -9355,10 +9409,7 @@ public class WindowManagerService extends IWindowManager.Stub
// content frame changing, then we'd like to animate
// it. The checks here are ordered by what is least
// likely to be true first.
- if (w.mContentChanged && !wasAnimating && !w.mLastHidden && !mDisplayFrozen
- && (w.mFrame.top != w.mLastFrame.top
- || w.mFrame.left != w.mLastFrame.left)
- && mPolicy.isScreenOn()) {
+ if (w.shouldAnimateMove()) {
// Frame has moved, containing content frame
// has also moved, and we're not currently animating...
// let's do something.
@@ -9367,6 +9418,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.setAnimation(a);
animDw = w.mLastFrame.left - w.mFrame.left;
animDh = w.mLastFrame.top - w.mFrame.top;
+ w.mContentChanged = false;
}
// Execute animation.
@@ -10230,7 +10282,8 @@ public class WindowManagerService extends IWindowManager.Stub
mDimAnimator = new DimAnimator(mFxSession);
}
mDimAnimator.show(dw, dh);
- mDimAnimator.updateParameters(w, currentTime);
+ mDimAnimator.updateParameters(mContext.getResources(),
+ w, currentTime);
}
}
if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
@@ -11292,7 +11345,7 @@ public class WindowManagerService extends IWindowManager.Stub
* Set's the dim surface's layer and update dim parameters that will be used in
* {@link updateSurface} after all windows are examined.
*/
- void updateParameters(WindowState w, long currentTime) {
+ void updateParameters(Resources res, WindowState w, long currentTime) {
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
@@ -11306,11 +11359,15 @@ public class WindowManagerService extends IWindowManager.Stub
? w.mAnimation.computeDurationHint()
: DEFAULT_DIM_DURATION;
if (target > mDimTargetAlpha) {
- // This is happening behind the activity UI,
- // so we can make it run a little longer to
- // give a stronger impression without disrupting
- // the user.
- duration *= DIM_DURATION_MULTIPLIER;
+ TypedValue tv = new TypedValue();
+ res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
+ tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long)tv.getFraction((float)duration, (float)duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT
+ && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
}
if (duration < 1) {
// Don't divide by zero
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e815524..1a10cff 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -557,7 +557,7 @@ public final class ActivityManagerService extends ActivityManagerNative
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
- * Fingerprints (String.hashCode()) of stack traces that we've
+ * Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
* something (rogue user app) forces this over
* MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
@@ -2026,7 +2026,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
@@ -2082,7 +2082,7 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
}
@@ -2121,13 +2121,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mPendingActivityLaunches.clear();
}
-
+
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -2138,7 +2138,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -2149,12 +2149,12 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
- public int startActivityIntentSender(IApplicationThread caller,
+ public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) {
@@ -2267,7 +2267,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// those are not yet exposed to user code, so there is no need.
int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
- requestCode, -1, r.launchedFromUid, false, false);
+ requestCode, -1, r.launchedFromUid, false, false, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -2289,38 +2289,28 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- final boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
+ return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false, null, null);
+ }
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- }
+ public final int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ }
- synchronized(this) {
- return mMainStack.startActivityLocked(null, intent, resolvedType,
- null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
- onlyIfNeeded, componentSpecified);
+ public final int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+
+ // This is so super not safe, that only the system (or okay root)
+ // can do it.
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ throw new SecurityException(
+ "startActivityInPackage only available to the system");
}
+
+ return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
}
final void addRecentTaskLocked(TaskRecord task) {
@@ -3890,16 +3880,30 @@ public final class ActivityManagerService extends ActivityManagerNative
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
// Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- if (type == INTENT_SENDER_BROADCAST) {
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ if (intents != null) {
+ if (intents.length < 1) {
+ throw new IllegalArgumentException("Intents array length must be >= 1");
+ }
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ throw new IllegalArgumentException("Null intent at index " + i);
+ }
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ if (type == INTENT_SENDER_BROADCAST &&
+ (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+ intents[i] = new Intent(intent);
+ }
+ if (resolvedTypes != null && resolvedTypes.length != intents.length) {
throw new IllegalArgumentException(
- "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ "Intent array length does not match resolvedTypes length");
}
}
@@ -3922,7 +3926,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return getIntentSenderLocked(type, packageName, callingUid,
- token, resultWho, requestCode, intent, resolvedType, flags);
+ token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
throw new SecurityException(e);
@@ -3932,7 +3936,7 @@ public final class ActivityManagerService extends ActivityManagerNative
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
int index = mMainStack.indexOfTokenLocked(token);
@@ -3953,14 +3957,24 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
+ requestCode, intents, resolvedTypes, flags);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec != null) {
if (!cancelCurrent) {
if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
+ if (rec.key.requestIntent != null) {
+ rec.key.requestIntent.replaceExtras(intents != null ? intents[0] : null);
+ }
+ if (intents != null) {
+ intents[intents.length-1] = rec.key.requestIntent;
+ rec.key.allIntents = intents;
+ rec.key.allResolvedTypes = resolvedTypes;
+ } else {
+ rec.key.allIntents = null;
+ rec.key.allResolvedTypes = null;
+ }
}
return rec;
}
@@ -5006,7 +5020,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* TODO: Add mController hook
*/
- public void moveTaskToFront(int task) {
+ public void moveTaskToFront(int task, int flags) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToFront()");
@@ -5021,6 +5035,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
@@ -5028,6 +5047,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
@@ -6647,7 +6671,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ProcessRecord r = findAppProcess(app);
if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
- Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
+ Integer stackFingerprint = info.hashCode();
boolean logIt = true;
synchronized (mAlreadyLoggedViolatedStacks) {
if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 51dc84e..b4ea036 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -99,8 +99,8 @@ public class ActivityStack {
static final int DESTROY_TIMEOUT = 10*1000;
// How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+ // disabled.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
// How long between activity launches that we consider safe to not warn
// the user about an unexpected activity being launched on top.
@@ -1487,7 +1487,8 @@ public class ActivityStack {
ActivityRecord newActivity) {
boolean forceReset = (newActivity.info.flags
&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if (ACTIVITY_INACTIVE_RESET_TIME > 0
+ && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
if ((newActivity.info.flags
&ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
forceReset = true;
@@ -1573,8 +1574,7 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ target.task = new TaskRecord(mService.mCurTask, target.info, null);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1776,11 +1776,11 @@ public class ActivityStack {
* activities on top of it and return the instance.
*
* @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
+ * @return Returns the old activity that should be continued to be used,
* or null if none was found.
*/
private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags, boolean doClear) {
+ ActivityRecord newR, int launchFlags) {
int i = mHistory.size();
// First find the requested task.
@@ -1806,17 +1806,18 @@ public class ActivityStack {
if (r.realActivity.equals(newR.realActivity)) {
// Here it is! Now finish everything in front...
ActivityRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ break;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
}
}
@@ -1843,6 +1844,51 @@ public class ActivityStack {
}
/**
+ * Completely remove all activities associated with an existing task.
+ */
+ private final void performClearTaskLocked(int taskId) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.task.taskId != taskId) {
+ // We hit the bottom. Now finish it all...
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ // Whoops hit the end.
+ return;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ /**
* Find the activity in the history stack within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
*/
@@ -1882,7 +1928,7 @@ public class ActivityStack {
int grantedMode, ActivityInfo aInfo, IBinder resultTo,
String resultWho, int requestCode,
int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
+ boolean componentSpecified, ActivityRecord[] outActivity) {
int err = START_SUCCESS;
@@ -2004,6 +2050,9 @@ public class ActivityStack {
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
+ if (outActivity != null) {
+ outActivity[0] = r;
+ }
if (mMainStack) {
if (mResumedActivity == null
@@ -2038,6 +2087,16 @@ public class ActivityStack {
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}
+ final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity, so before starting
+ // their own activity we will bring home to the front.
+ moveHomeToFrontLocked();
+ }
+ }
+
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
@@ -2111,6 +2170,7 @@ public class ActivityStack {
}
boolean addingToTask = false;
+ TaskRecord reuseTask = null;
if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
@@ -2148,6 +2208,7 @@ public class ActivityStack {
if (callerAtFront) {
// We really do want to push this one into the
// user's face, right now.
+ moveHomeToFrontFromLaunchLocked(launchFlags);
moveTaskToFrontLocked(taskTop.task, r);
}
}
@@ -2166,7 +2227,16 @@ public class ActivityStack {
}
return START_RETURN_INTENT_TO_CALLER;
}
- if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // exising task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = taskTop.task;
+ performClearTaskLocked(taskTop.task.taskId);
+ reuseTask.setIntent(r.intent, r.info);
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// In this situation we want to remove all activities
@@ -2174,7 +2244,7 @@ public class ActivityStack {
// cases this means we are resetting the task to its
// initial state.
ActivityRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags, true);
+ taskTop.task.taskId, r, launchFlags);
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different
@@ -2235,7 +2305,7 @@ public class ActivityStack {
// for now we'll just drop it.
taskTop.task.setIntent(r.intent, r.info);
}
- if (!addingToTask) {
+ if (!addingToTask && reuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
@@ -2298,19 +2368,23 @@ public class ActivityStack {
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
+ if (reuseTask == null) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ } else {
+ r.task = reuseTask;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
newTask = true;
if (mMainStack) {
mService.addRecentTaskLocked(r.task);
}
+ moveHomeToFrontFromLaunchLocked(launchFlags);
} else if (sourceRecord != null) {
if (!addingToTask &&
@@ -2319,7 +2393,7 @@ public class ActivityStack {
// task, but the caller has asked to clear that task if the
// activity is already running.
ActivityRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags, true);
+ sourceRecord.task.taskId, r, launchFlags);
if (top != null) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
@@ -2361,9 +2435,8 @@ public class ActivityStack {
ActivityRecord prev =
N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
r.task = prev != null
- ? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -2386,21 +2459,7 @@ public class ActivityStack {
return START_SUCCESS;
}
- final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, boolean debug) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
@@ -2429,11 +2488,32 @@ public class ActivityStack {
}
}
}
+ return aInfo;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug);
synchronized (mService) {
int callingPid;
- int callingUid;
- if (caller == null) {
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
callingPid = Binder.getCallingPid();
callingUid = Binder.getCallingUid();
} else {
@@ -2472,8 +2552,8 @@ public class ActivityStack {
IIntentSender target = mService.getIntentSenderLocked(
IActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, intent,
- resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ realCallingUid, null, null, 0, new Intent[] { intent },
+ new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT);
Intent newIntent = new Intent();
@@ -2518,7 +2598,7 @@ public class ActivityStack {
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
+ onlyIfNeeded, componentSpecified, null);
if (mConfigWillChange && mMainStack) {
// If the caller also wants to switch to a new configuration,
@@ -2569,6 +2649,75 @@ public class ActivityStack {
}
}
+ final int startActivities(IApplicationThread caller, int callingUid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ if (intents == null) {
+ throw new NullPointerException("intents is null");
+ }
+ if (resolvedTypes == null) {
+ throw new NullPointerException("resolvedTypes is null");
+ }
+ if (intents.length != resolvedTypes.length) {
+ throw new IllegalArgumentException("intents are length different than resolvedTypes");
+ }
+
+ ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ continue;
+ }
+
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false);
+
+ if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_CANT_SAVE_STATE not supported here");
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedTypes[i],
+ null, 0, aInfo, resultTo, null, -1, callingPid, callingUid,
+ false, componentSpecified, outActivity);
+ if (res < 0) {
+ return res;
+ }
+
+ resultTo = outActivity[0];
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return IActivityManager.START_SUCCESS;
+ }
+
void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
long thisTime, long totalTime) {
for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
@@ -3252,6 +3401,24 @@ public class ActivityStack {
removeHistoryRecordsForAppLocked(mFinishingActivities, app);
}
+ /**
+ * Move the current home activity's task (if one exists) to the front
+ * of the stack.
+ */
+ final void moveHomeToFrontLocked() {
+ TaskRecord homeTask = null;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ if (hr.isHomeActivity) {
+ homeTask = hr.task;
+ }
+ }
+ if (homeTask != null) {
+ moveTaskToFrontLocked(homeTask, null);
+ }
+ }
+
+
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 7a85eb8..ee6e420 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -47,20 +47,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
final int requestCode;
final Intent requestIntent;
final String requestResolvedType;
+ Intent[] allIntents;
+ String[] allResolvedTypes;
final int flags;
final int hashCode;
private static final int ODD_PRIME_NUMBER = 37;
Key(int _t, String _p, ActivityRecord _a, String _w,
- int _r, Intent _i, String _it, int _f) {
+ int _r, Intent[] _i, String[] _it, int _f) {
type = _t;
packageName = _p;
activity = _a;
who = _w;
requestCode = _r;
- requestIntent = _i;
- requestResolvedType = _it;
+ requestIntent = _i != null ? _i[_i.length-1] : null;
+ requestResolvedType = _it != null ? _it[_it.length-1] : null;
+ allIntents = _i;
+ allResolvedTypes = _it;
flags = _f;
int hash = 23;
@@ -72,11 +76,11 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (_a != null) {
hash = (ODD_PRIME_NUMBER*hash) + _a.hashCode();
}
- if (_i != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _i.filterHashCode();
+ if (requestIntent != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestIntent.filterHashCode();
}
- if (_it != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _it.hashCode();
+ if (requestResolvedType != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
}
hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
hash = (ODD_PRIME_NUMBER*hash) + _t;
@@ -209,9 +213,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
switch (key.type) {
case IActivityManager.INTENT_SENDER_ACTIVITY:
try {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, false);
+ if (key.allIntents != null && key.allIntents.length > 1) {
+ Intent[] allIntents = new Intent[key.allIntents.length];
+ String[] allResolvedTypes = new String[key.allIntents.length];
+ System.arraycopy(key.allIntents, 0, allIntents, 0,
+ key.allIntents.length);
+ if (key.allResolvedTypes != null) {
+ System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+ key.allResolvedTypes.length);
+ }
+ allIntents[allIntents.length-1] = finalIntent;
+ allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
+ owner.startActivitiesInPackage(uid, allIntents,
+ allResolvedTypes, resultTo);
+ } else {
+ owner.startActivityInPackage(uid,
+ finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, false);
+ }
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startActivity intent", e);
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index bcb8f54..09d9c3b6 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -26,7 +26,6 @@ import java.io.PrintWriter;
class TaskRecord {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
- final boolean clearOnBackground; // As per the original activity.
Intent intent; // The original intent that started the task.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
@@ -38,11 +37,9 @@ class TaskRecord {
String stringName; // caching of toString() result.
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
- boolean _clearOnBackground) {
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
affinity = info.taskAffinity;
- clearOnBackground = _clearOnBackground;
setIntent(_intent, info);
}
@@ -86,9 +83,8 @@ class TaskRecord {
}
void dump(PrintWriter pw, String prefix) {
- if (clearOnBackground || numActivities != 0 || rootWasReset) {
- pw.print(prefix); pw.print("clearOnBackground="); pw.print(clearOnBackground);
- pw.print(" numActivities="); pw.print(numActivities);
+ if (numActivities != 0 || rootWasReset) {
+ pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
pw.print(" rootWasReset="); pw.println(rootWasReset);
}
if (affinity != null) {
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
new file mode 100644
index 0000000..3543275
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.gsm.SmsCbHeader;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Describes an SMS-CB message.
+ *
+ * {@hide}
+ */
+public class SmsCbMessage {
+
+ /**
+ * Cell wide immediate geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+ /**
+ * PLMN wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+ /**
+ * Location / service area wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+ /**
+ * Cell wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+ /**
+ * Create an instance of this class from a received PDU
+ *
+ * @param pdu PDU bytes
+ * @return An instance of this class, or null if invalid pdu
+ */
+ public static SmsCbMessage createFromPdu(byte[] pdu) {
+ try {
+ return new SmsCbMessage(pdu);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+ */
+ private static final String[] LANGUAGE_CODES_GROUP_0 = {
+ "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
+ "pl", null
+ };
+
+ /**
+ * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+ */
+ private static final String[] LANGUAGE_CODES_GROUP_2 = {
+ "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
+ null, null
+ };
+
+ private static final char CARRIAGE_RETURN = 0x0d;
+
+ private SmsCbHeader mHeader;
+
+ private String mLanguage;
+
+ private String mBody;
+
+ private SmsCbMessage(byte[] pdu) throws IllegalArgumentException {
+ mHeader = new SmsCbHeader(pdu);
+ parseBody(pdu);
+ }
+
+ /**
+ * Return the geographical scope of this message, one of
+ * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE},
+ * {@link #GEOGRAPHICAL_SCOPE_PLMN_WIDE},
+ * {@link #GEOGRAPHICAL_SCOPE_LA_WIDE},
+ * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE}
+ *
+ * @return Geographical scope
+ */
+ public int getGeographicalScope() {
+ return mHeader.geographicalScope;
+ }
+
+ /**
+ * Get the ISO-639-1 language code for this message, or null if unspecified
+ *
+ * @return Language code
+ */
+ public String getLanguageCode() {
+ return mLanguage;
+ }
+
+ /**
+ * Get the body of this message, or null if no body available
+ *
+ * @return Body, or null
+ */
+ public String getMessageBody() {
+ return mBody;
+ }
+
+ /**
+ * Get the message identifier of this message (0-65535)
+ *
+ * @return Message identifier
+ */
+ public int getMessageIdentifier() {
+ return mHeader.messageIdentifier;
+ }
+
+ /**
+ * Get the message code of this message (0-1023)
+ *
+ * @return Message code
+ */
+ public int getMessageCode() {
+ return mHeader.messageCode;
+ }
+
+ /**
+ * Get the update number of this message (0-15)
+ *
+ * @return Update number
+ */
+ public int getUpdateNumber() {
+ return mHeader.updateNumber;
+ }
+
+ private void parseBody(byte[] pdu) {
+ int encoding;
+ boolean hasLanguageIndicator = false;
+
+ // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
+ // section 5.
+ switch ((mHeader.dataCodingScheme & 0xf0) >> 4) {
+ case 0x00:
+ encoding = SmsMessage.ENCODING_7BIT;
+ mLanguage = LANGUAGE_CODES_GROUP_0[mHeader.dataCodingScheme & 0x0f];
+ break;
+
+ case 0x01:
+ hasLanguageIndicator = true;
+ if ((mHeader.dataCodingScheme & 0x0f) == 0x01) {
+ encoding = SmsMessage.ENCODING_16BIT;
+ } else {
+ encoding = SmsMessage.ENCODING_7BIT;
+ }
+ break;
+
+ case 0x02:
+ encoding = SmsMessage.ENCODING_7BIT;
+ mLanguage = LANGUAGE_CODES_GROUP_2[mHeader.dataCodingScheme & 0x0f];
+ break;
+
+ case 0x03:
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+
+ case 0x04:
+ case 0x05:
+ switch ((mHeader.dataCodingScheme & 0x0c) >> 2) {
+ case 0x01:
+ encoding = SmsMessage.ENCODING_8BIT;
+ break;
+
+ case 0x02:
+ encoding = SmsMessage.ENCODING_16BIT;
+ break;
+
+ case 0x00:
+ default:
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+ }
+ break;
+
+ case 0x06:
+ case 0x07:
+ // Compression not supported
+ case 0x09:
+ // UDH structure not supported
+ case 0x0e:
+ // Defined by the WAP forum not supported
+ encoding = SmsMessage.ENCODING_UNKNOWN;
+ break;
+
+ case 0x0f:
+ if (((mHeader.dataCodingScheme & 0x04) >> 2) == 0x01) {
+ encoding = SmsMessage.ENCODING_8BIT;
+ } else {
+ encoding = SmsMessage.ENCODING_7BIT;
+ }
+ break;
+
+ default:
+ // Reserved values are to be treated as 7-bit
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+ }
+
+ switch (encoding) {
+ case SmsMessage.ENCODING_7BIT:
+ mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
+ (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+
+ if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
+ mLanguage = mBody.substring(0, 2);
+ mBody = mBody.substring(3);
+ }
+ break;
+
+ case SmsMessage.ENCODING_16BIT:
+ int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+
+ if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
+ mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
+ SmsCbHeader.PDU_HEADER_LENGTH, 2);
+ offset += 2;
+ }
+
+ try {
+ mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+ } catch (UnsupportedEncodingException e) {
+ // Eeeek
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (mBody != null) {
+ // Remove trailing carriage return
+ for (int i = mBody.length() - 1; i >= 0; i--) {
+ if (mBody.charAt(i) != CARRIAGE_RETURN) {
+ mBody = mBody.substring(0, i + 1);
+ break;
+ }
+ }
+ } else {
+ mBody = "";
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 953696b..6a346af 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -338,7 +338,67 @@ public final class SmsManager {
}
return createMessageListFromRawRecords(records);
- }
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
/**
* Create a list of <code>SmsMessage</code>s from a list of RawSmsData
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 65bad96..90de5e1 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -144,4 +144,30 @@ interface ISms {
in List<String> parts, in List<PendingIntent> sentIntents,
in List<PendingIntent> deliveryIntents);
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #disableCellBroadcast(int)
+ */
+ boolean enableCellBroadcast(int messageIdentifier);
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ */
+ boolean disableCellBroadcast(int messageIdentifier);
+
}
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 1910a9c..5049249 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -68,4 +68,12 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub {
parts, sentIntents, deliveryIntents);
}
+ public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier);
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 3a7ce47..ec49a19 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -112,6 +112,9 @@ public abstract class SMSDispatcher extends Handler {
/** Radio is ON */
static final protected int EVENT_RADIO_ON = 12;
+ /** New broadcast SMS */
+ static final protected int EVENT_NEW_BROADCAST_SMS = 13;
+
protected Phone mPhone;
protected Context mContext;
protected ContentResolver mResolver;
@@ -390,6 +393,9 @@ public abstract class SMSDispatcher extends Handler {
mCm.reportSmsMemoryStatus(mStorageAvailable,
obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
}
+
+ case EVENT_NEW_BROADCAST_SMS:
+ handleBroadcastSms((AsyncResult)msg.obj);
break;
}
}
@@ -985,4 +991,17 @@ public abstract class SMSDispatcher extends Handler {
}
}
};
+
+ protected abstract void handleBroadcastSms(AsyncResult ar);
+
+ protected void dispatchBroadcastPdus(byte[][] pdus) {
+ Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
+ intent.putExtra("pdus", pdus);
+
+ if (Config.LOGD)
+ Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
+
+ dispatch(intent, "android.permission.RECEIVE_SMS");
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 37a33bc..53555d8 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -494,6 +494,11 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
mCm.setCdmaBroadcastConfig(configValuesArray, response);
}
+ protected void handleBroadcastSms(AsyncResult ar) {
+ // Not supported
+ Log.e(TAG, "Error! Not implemented for CDMA.");
+ }
+
private int resultToCause(int rc) {
switch (rc) {
case Activity.RESULT_OK:
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index d84b6ab..29f3bc1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -191,6 +191,18 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager {
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index ed7066b..70f8f86 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -22,20 +22,26 @@ import android.app.PendingIntent.CanceledException;
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.ServiceState;
+import android.telephony.SmsCbMessage;
+import android.telephony.gsm.GsmCellLocation;
import android.util.Config;
import android.util.Log;
+import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import static android.telephony.SmsMessage.MessageClass;
@@ -47,6 +53,8 @@ final class GsmSMSDispatcher extends SMSDispatcher {
GsmSMSDispatcher(GSMPhone phone) {
super(phone);
mGsmPhone = phone;
+
+ ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
}
/**
@@ -394,4 +402,162 @@ final class GsmSMSDispatcher extends SMSDispatcher {
return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
}
}
+
+ /**
+ * Holds all info about a message page needed to assemble a complete
+ * concatenated message
+ */
+ private static final class SmsCbConcatInfo {
+ private final SmsCbHeader mHeader;
+
+ private final String mPlmn;
+
+ private final int mLac;
+
+ private final int mCid;
+
+ public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) {
+ mHeader = header;
+ mPlmn = plmn;
+ mLac = lac;
+ mCid = cid;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHeader.messageIdentifier * 31 + mHeader.updateNumber;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SmsCbConcatInfo) {
+ SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
+
+ // Two pages match if all header attributes (except the page
+ // index) are identical, and both pages belong to the same
+ // location (which is also determined by the scope parameter)
+ if (mHeader.geographicalScope == other.mHeader.geographicalScope
+ && mHeader.messageCode == other.mHeader.messageCode
+ && mHeader.updateNumber == other.mHeader.updateNumber
+ && mHeader.messageIdentifier == other.mHeader.messageIdentifier
+ && mHeader.dataCodingScheme == other.mHeader.dataCodingScheme
+ && mHeader.nrOfPages == other.mHeader.nrOfPages) {
+ return matchesLocation(other.mPlmn, other.mLac, other.mCid);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if this concatenation info matches the given location. The
+ * granularity of the match depends on the geographical scope.
+ *
+ * @param plmn PLMN
+ * @param lac Location area code
+ * @param cid Cell ID
+ * @return true if matching, false otherwise
+ */
+ public boolean matchesLocation(String plmn, int lac, int cid) {
+ switch (mHeader.geographicalScope) {
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
+ if (mCid != cid) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
+ if (mLac != lac) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
+ return mPlmn != null && mPlmn.equals(plmn);
+ }
+
+ return false;
+ }
+ }
+
+ // This map holds incomplete concatenated messages waiting for assembly
+ private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
+ new HashMap<SmsCbConcatInfo, byte[][]>();
+
+ protected void handleBroadcastSms(AsyncResult ar) {
+ try {
+ byte[][] pdus = null;
+ byte[] receivedPdu = (byte[])ar.result;
+
+ if (Config.LOGD) {
+ for (int i = 0; i < receivedPdu.length; i += 8) {
+ StringBuilder sb = new StringBuilder("SMS CB pdu data: ");
+ for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
+ int b = receivedPdu[j] & 0xff;
+ if (b < 0x10) {
+ sb.append("0");
+ }
+ sb.append(Integer.toHexString(b)).append(" ");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ }
+
+ SmsCbHeader header = new SmsCbHeader(receivedPdu);
+ String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
+ GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation();
+ int lac = cellLocation.getLac();
+ int cid = cellLocation.getCid();
+
+ if (header.nrOfPages > 1) {
+ // Multi-page message
+ SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
+
+ // Try to find other pages of the same message
+ pdus = mSmsCbPageMap.get(concatInfo);
+
+ if (pdus == null) {
+ // This it the first page of this message, make room for all
+ // pages and keep until complete
+ pdus = new byte[header.nrOfPages][];
+
+ mSmsCbPageMap.put(concatInfo, pdus);
+ }
+
+ // Page parameter is one-based
+ pdus[header.pageIndex - 1] = receivedPdu;
+
+ for (int i = 0; i < pdus.length; i++) {
+ if (pdus[i] == null) {
+ // Still missing pages, exit
+ return;
+ }
+ }
+
+ // Message complete, remove and dispatch
+ mSmsCbPageMap.remove(concatInfo);
+ } else {
+ // Single page message
+ pdus = new byte[1][];
+ pdus[0] = receivedPdu;
+ }
+
+ dispatchBroadcastPdus(pdus);
+
+ // Remove messages that are out of scope to prevent the map from
+ // growing indefinitely, containing incomplete messages that were
+ // never assembled
+ Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator();
+
+ while (iter.hasNext()) {
+ SmsCbConcatInfo info = iter.next();
+
+ if (!info.matchesLocation(plmn, lac, cid)) {
+ iter.remove();
+ }
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in decoding SMS CB pdu", e);
+ }
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index aab359f..5abba6e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -17,7 +17,9 @@
package com.android.internal.telephony.gsm;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
@@ -30,7 +32,10 @@ import com.android.internal.telephony.SmsRawData;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -45,9 +50,15 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
private final Object mLock = new Object();
private boolean mSuccess;
private List<SmsRawData> mSms;
+ private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
+ new HashMap<Integer, HashSet<String>>();
private static final int EVENT_LOAD_DONE = 1;
private static final int EVENT_UPDATE_DONE = 2;
+ private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
+ private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
+ private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+ private static final int SMS_CB_CODE_SCHEME_MAX = 255;
Handler mHandler = new Handler() {
@Override
@@ -75,6 +86,14 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
mLock.notifyAll();
}
break;
+ case EVENT_SET_BROADCAST_ACTIVATION_DONE:
+ case EVENT_SET_BROADCAST_CONFIG_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ mSuccess = (ar.exception == null);
+ mLock.notifyAll();
+ }
+ break;
}
}
};
@@ -193,6 +212,126 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("enableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Enabling cell broadcast SMS");
+
+ String client = context.getPackageManager().getNameForUid(
+ Binder.getCallingUid());
+ HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+ if (clients == null) {
+ // This is a new message identifier
+ clients = new HashSet<String>();
+ mCellBroadcastSubscriptions.put(messageIdentifier, clients);
+
+ if (!updateCellBroadcastConfig()) {
+ mCellBroadcastSubscriptions.remove(messageIdentifier);
+ return false;
+ }
+ }
+
+ clients.add(client);
+
+ if (DBG)
+ log("Added cell broadcast subscription for MID " + messageIdentifier
+ + " from client " + client);
+
+ return true;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("disableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Disabling cell broadcast SMS");
+
+ String client = context.getPackageManager().getNameForUid(
+ Binder.getCallingUid());
+ HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+ if (clients != null && clients.remove(client)) {
+ if (DBG)
+ log("Removed cell broadcast subscription for MID " + messageIdentifier
+ + " from client " + client);
+
+ if (clients.isEmpty()) {
+ mCellBroadcastSubscriptions.remove(messageIdentifier);
+ updateCellBroadcastConfig();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean updateCellBroadcastConfig() {
+ Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+
+ if (messageIdentifiers.size() > 0) {
+ SmsBroadcastConfigInfo[] configs =
+ new SmsBroadcastConfigInfo[messageIdentifiers.size()];
+ int i = 0;
+
+ for (int messageIdentifier : messageIdentifiers) {
+ configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
+ SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+ }
+
+ return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
+ } else {
+ return setCellBroadcastActivation(false);
+ }
+ }
+
+ private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
+ if (DBG)
+ log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastConfig(configs, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast config");
+ }
+ }
+
+ return mSuccess;
+ }
+
+ private boolean setCellBroadcastActivation(boolean activate) {
+ if (DBG)
+ log("Calling setCellBroadcastActivation(" + activate + ")");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastActivation(activate, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast activation");
+ }
+ }
+
+ return mSuccess;
+ }
+
@Override
protected void log(String msg) {
Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
new file mode 100644
index 0000000..5f27cfc
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+public class SmsCbHeader {
+ public static final int PDU_HEADER_LENGTH = 6;
+
+ public final int geographicalScope;
+
+ public final int messageCode;
+
+ public final int updateNumber;
+
+ public final int messageIdentifier;
+
+ public final int dataCodingScheme;
+
+ public final int pageIndex;
+
+ public final int nrOfPages;
+
+ public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
+ if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
+ throw new IllegalArgumentException("Illegal PDU");
+ }
+
+ geographicalScope = (pdu[0] & 0xc0) >> 6;
+ messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+ updateNumber = pdu[1] & 0x0f;
+ messageIdentifier = (pdu[2] << 8) | pdu[3];
+ dataCodingScheme = pdu[4];
+
+ // Check for invalid page parameter
+ int pageIndex = (pdu[5] & 0xf0) >> 4;
+ int nrOfPages = pdu[5] & 0x0f;
+
+ if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+ pageIndex = 1;
+ nrOfPages = 1;
+ }
+
+ this.pageIndex = pageIndex;
+ this.nrOfPages = nrOfPages;
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
new file mode 100644
index 0000000..7136ea0
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.SmsCbMessage;
+import android.test.AndroidTestCase;
+
+/**
+ * Test cases for basic SmsCbMessage operations
+ */
+public class GsmSmsCbTest extends AndroidTestCase {
+
+ private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+ pdu[0] = b;
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded", expectedGs, msg
+ .getGeographicalScope());
+ }
+
+ public void testCreateNullPdu() {
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(null);
+
+ assertNull("createFromPdu(byte[] with null pdu should return null", msg);
+ }
+
+ public void testCreateTooShortPdu() {
+ byte[] pdu = new byte[4];
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertNull("createFromPdu(byte[] with too short pdu should return null", msg);
+ }
+
+ public void testGetGeographicalScope() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ doTestGeographicalScopeValue(pdu, (byte)0x00,
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE);
+ doTestGeographicalScopeValue(pdu, (byte)0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0x80, SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
+ }
+
+ public void testGetMessageBody7Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitFull() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5,
+ (byte)0xB4, (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+ (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40, (byte)0x63,
+ (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3, (byte)0xCB, (byte)0xF2,
+ (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76, (byte)0x9F, (byte)0x59, (byte)0xA0,
+ (byte)0x76, (byte)0x39, (byte)0xEC, (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20,
+ (byte)0x3A, (byte)0xBA, (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73,
+ (byte)0x90, (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+ (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, " +
+ "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitWithLanguage() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody7BitWithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73,
+ (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E, (byte)0x9B, (byte)0x20,
+ (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE, (byte)0xB3, (byte)0xE9, (byte)0xA0,
+ (byte)0x30, (byte)0x1B, (byte)0x8E, (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74,
+ (byte)0x50, (byte)0xBB, (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65,
+ (byte)0xD0, (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+ (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41, (byte)0xF2,
+ (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83, (byte)0xE0, (byte)0x61,
+ (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E, (byte)0x37, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody8Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00,
+ (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43,
+ (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00,
+ (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69, (byte)0x00,
+ (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04,
+ (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+ (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2WithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
+ (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+ (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00,
+ (byte)0x65, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63,
+ (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00,
+ (byte)0x61, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
+ public void testGetMessageIdentifier() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+ }
+
+ public void testGetMessageCode() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+ }
+
+ public void testGetUpdateNumber() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+ }
+}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c31c9cc..3b52252 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -246,6 +246,11 @@ public class MockContext extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/tests/StatusBar/AndroidManifest.xml b/tests/StatusBar/AndroidManifest.xml
index b1734bb..ddb756b 100644
--- a/tests/StatusBar/AndroidManifest.xml
+++ b/tests/StatusBar/AndroidManifest.xml
@@ -21,7 +21,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <activity android:name="NotificationBuilderTest" android:label="_Notify Builder">
+ <activity android:name="NotificationBuilderTest"
+ android:label="_Notify Builder"
+ android:theme="@android:style/Theme.Holo"
+ android:hardwareAccelerated="true"
+ >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png b/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png
new file mode 100644
index 0000000..56378f6
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png
Binary files differ
diff --git a/tests/StatusBar/res/layout/notification_builder_test.xml b/tests/StatusBar/res/layout/notification_builder_test.xml
new file mode 100644
index 0000000..58c4fbb
--- /dev/null
+++ b/tests/StatusBar/res/layout/notification_builder_test.xml
@@ -0,0 +1,819 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:paddingLeft="40dp"
+ android:paddingTop="12dp"
+ android:paddingRight="24dp"
+ android:paddingBottom="12dp"
+ >
+
+ <LinearLayout
+ android:layout_width="220sp"
+ android:layout_height="match_parent"
+ android:layout_marginRight="24dp"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_1"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="1"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_1"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_2"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="2"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_2"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_3"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="3"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_3"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_4"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="4"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_4"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_5"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="5"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_5"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_6"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="6"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_6"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_7"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="7"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_7"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_8"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="8"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_8"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_9"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="9"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_9"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ style="@style/IdButton.Minus"
+ android:id="@+id/clear_10"
+ />
+ <TextView
+ style="@style/IdTitle"
+ android:text="10"
+ />
+ <Button
+ style="@style/IdButton.Plus"
+ android:id="@+id/notify_10"
+ />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/clear_all"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="Clear All"
+ />
+ <Button
+ android:id="@+id/ten"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Ten notifications"
+ />
+
+ </LinearLayout>
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <!-- setWhen -->
+ <RadioGroup
+ android:id="@+id/group_when"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setWhen"
+ />
+ <RadioButton
+ android:id="@+id/when_midnight"
+ style="@style/FieldContents.Disabled"
+ android:text="midnight"
+ />
+ <RadioButton
+ android:id="@+id/when_now"
+ style="@style/FieldContents"
+ android:text="now"
+ />
+ <RadioButton
+ android:id="@+id/when_now_plus_1h"
+ style="@style/FieldContents.Disabled"
+ android:text="now + 1h"
+ />
+ <RadioButton
+ android:id="@+id/when_tomorrow"
+ style="@style/FieldContents.Disabled"
+ android:text="tomorrow"
+ />
+ </RadioGroup>
+
+ <!-- icon -->
+ <RadioGroup
+ android:id="@+id/group_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setSmallIcon"
+ />
+ <RadioButton
+ android:id="@+id/icon_im"
+ style="@style/FieldContents"
+ android:text="IM"
+ />
+ <RadioButton
+ android:id="@+id/icon_alert"
+ style="@style/FieldContents"
+ android:text="alert"
+ />
+ <RadioButton
+ android:id="@+id/icon_surprise"
+ style="@style/FieldContents"
+ android:text="surprise"
+ />
+ <RadioButton
+ android:id="@+id/icon_level0"
+ style="@style/FieldContents.Disabled"
+ android:text="level 0"
+ />
+ <RadioButton
+ android:id="@+id/icon_level50"
+ style="@style/FieldContents.Disabled"
+ android:text="level 50"
+ />
+ <RadioButton
+ android:id="@+id/icon_level100"
+ style="@style/FieldContents.Disabled"
+ android:text="level 100"
+ />
+ <!-- todo setSmallIcon(int icon, int level) -->
+ </RadioGroup>
+
+ <!-- setContentTitle -->
+ <RadioGroup
+ android:id="@+id/group_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setContentTitle"
+ />
+ <RadioButton
+ android:id="@+id/title_short"
+ style="@style/FieldContents"
+ android:text="none"
+ android:tag=""
+ />
+ <RadioButton
+ android:id="@+id/title_short"
+ style="@style/FieldContents"
+ android:text="cwshort"
+ android:tag="Title"
+ />
+ <RadioButton
+ android:id="@+id/title_medium"
+ style="@style/FieldContents"
+ android:text="medium"
+ android:tag="Notification Test"
+ />
+ <RadioButton
+ android:id="@+id/title_long"
+ style="@style/FieldContents"
+ android:text="long"
+ android:tag="This is one heckuva long title for a notification"
+ />
+ </RadioGroup>
+
+ <!-- setContentText -->
+ <RadioGroup
+ android:id="@+id/group_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setContentText"
+ />
+ <RadioButton
+ android:id="@+id/text_none"
+ style="@style/FieldContents"
+ android:text="none"
+ android:tag=""
+ />
+ <RadioButton
+ android:id="@+id/text_short"
+ style="@style/FieldContents"
+ android:tag="short"
+ android:text="text"
+ />
+ <RadioButton
+ android:id="@+id/text_medium"
+ style="@style/FieldContents"
+ android:text="medium"
+ android:tag="Something happened"
+ />
+ <RadioButton
+ android:id="@+id/text_long"
+ style="@style/FieldContents"
+ android:text="long"
+ android:tag="Oh my goodness. SOMETHING HAPPENED!!!!"
+ />
+ <RadioButton
+ android:id="@+id/text_haiku"
+ style="@style/FieldContents"
+ android:text="haiku"
+ android:tag="sholes final approach\nlanding gear punted to flan\nrunway foam glistens"
+ />
+ </RadioGroup>
+
+ <!-- setContentInfo -->
+ <RadioGroup
+ android:id="@+id/group_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setContentInfo"
+ />
+ <RadioButton
+ android:id="@+id/info_none"
+ style="@style/FieldContents"
+ android:text="none"
+ android:tag=""
+ />
+ <RadioButton
+ android:id="@+id/info_number"
+ style="@style/FieldContents"
+ android:text="snoozed"
+ android:tag="snoozed"
+ />
+ <RadioButton
+ android:id="@+id/info_long"
+ style="@style/FieldContents"
+ android:text="longer"
+ android:tag="this content info is way too long"
+ />
+ </RadioGroup>
+
+ <!-- setNumber -->
+ <RadioGroup
+ android:id="@+id/group_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setNumber"
+ />
+ <RadioButton
+ android:id="@+id/number_0"
+ style="@style/FieldContents"
+ android:text="0"
+ android:tag="0"
+ />
+ <RadioButton
+ android:id="@+id/number_1"
+ style="@style/FieldContents"
+ android:text="1"
+ android:tag="1"
+ />
+ <RadioButton
+ android:id="@+id/number_42"
+ style="@style/FieldContents"
+ android:text="42"
+ android:tag="42"
+ />
+ <RadioButton
+ android:id="@+id/number_334"
+ style="@style/FieldContents"
+ android:text="334"
+ android:tag="334"
+ />
+ <RadioButton
+ android:id="@+id/number_999"
+ style="@style/FieldContents"
+ android:text="999"
+ android:tag="999"
+ />
+ <RadioButton
+ android:id="@+id/number_9876"
+ style="@style/FieldContents"
+ android:text="9,876"
+ android:tag="9876"
+ />
+ <RadioButton
+ android:id="@+id/number_12345"
+ style="@style/FieldContents"
+ android:text="12,345"
+ android:tag="12345"
+ />
+ </RadioGroup>
+
+ <!-- setContentIntent -->
+ <RadioGroup
+ android:id="@+id/group_intent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setContentIntent"
+ />
+ <RadioButton
+ android:id="@+id/intent_none"
+ style="@style/FieldContents"
+ android:text="none"
+ />
+ <RadioButton
+ android:id="@+id/intent_alert"
+ style="@style/FieldContents"
+ android:text="alert"
+ />
+ </RadioGroup>
+
+ <!-- setDeleteIntent -->
+ <RadioGroup
+ android:id="@+id/group_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setDeleteIntent"
+ />
+ <RadioButton
+ android:id="@+id/delete_none"
+ style="@style/FieldContents"
+ android:text="none"
+ />
+ <RadioButton
+ android:id="@+id/delete_alert"
+ style="@style/FieldContents"
+ android:text="alert"
+ />
+ </RadioGroup>
+
+
+ <!-- setFullScreenIntent -->
+ <RadioGroup
+ android:id="@+id/group_full_screen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setFullScreenIntent"
+ />
+ <RadioButton
+ android:id="@+id/full_screen_none"
+ style="@style/FieldContents.Disabled"
+ android:text="none"
+ />
+ <RadioButton
+ android:id="@+id/full_screen_activity"
+ style="@style/FieldContents.Disabled"
+ android:text="full screen"
+ />
+ </RadioGroup>
+
+
+ <!-- setTicker -->
+ <RadioGroup
+ android:id="@+id/group_ticker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setTicker"
+ />
+ <RadioButton
+ android:id="@+id/ticker_none"
+ style="@style/FieldContents"
+ android:text="none"
+ android:tag=""
+ />
+ <RadioButton
+ android:id="@+id/ticker_short"
+ style="@style/FieldContents"
+ android:text="short"
+ android:tag="tick"
+ />
+ <RadioButton
+ android:id="@+id/ticker_wrap"
+ style="@style/FieldContents"
+ android:text="wrap"
+ android:tag="tick tick tick tock tock tock something fun has happened but i don't know what it is just yet"
+ />
+ <RadioButton
+ android:id="@+id/ticker_haiku"
+ style="@style/FieldContents"
+ android:text="haiku"
+ android:tag="sholes final approach\nlanding gear punted to flan\nrunway foam glistens"
+ />
+ <RadioButton
+ android:id="@+id/ticker_custom"
+ style="@style/FieldContents.Disabled"
+ android:text="custom view"
+ />
+ </RadioGroup>
+
+
+ <!-- setLargeIcon -->
+ <RadioGroup
+ android:id="@+id/group_large_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setLargeIcon"
+ />
+ <RadioButton
+ android:id="@+id/large_icon_none"
+ style="@style/FieldContents"
+ android:text="none"
+ />
+ <RadioButton
+ android:id="@+id/large_icon_pineapple"
+ style="@style/FieldContents"
+ android:text="pineapple"
+ />
+ </RadioGroup>
+
+
+ <!-- setSound -->
+ <RadioGroup
+ android:id="@+id/group_sound"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setSound"
+ />
+ <RadioButton
+ android:id="@+id/sound_none"
+ style="@style/FieldContents.Disabled"
+ android:text="none"
+ />
+ </RadioGroup>
+
+
+ <!-- setVibrate -->
+ <RadioGroup
+ android:id="@+id/group_vibrate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setVibrate"
+ />
+ <RadioButton
+ android:id="@+id/vibrate_none"
+ style="@style/FieldContents"
+ android:text="none"
+ />
+ <RadioButton
+ android:id="@+id/vibrate_short"
+ style="@style/FieldContents"
+ android:text="short"
+ />
+ <RadioButton
+ android:id="@+id/vibrate_medium"
+ style="@style/FieldContents"
+ android:text="long"
+ />
+ <RadioButton
+ android:id="@+id/vibrate_long"
+ style="@style/FieldContents"
+ android:text="long"
+ />
+ <RadioButton
+ android:id="@+id/vibrate_pattern"
+ style="@style/FieldContents"
+ android:text="longer"
+ />
+ </RadioGroup>
+
+
+ <!-- setLights -->
+ <RadioGroup
+ android:id="@+id/group_lights_color"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setLights (color)"
+ />
+ <RadioButton
+ android:id="@+id/lights_red"
+ style="@style/FieldContents"
+ android:text="red"
+ android:tag="0xff0000"
+ />
+ <RadioButton
+ android:id="@+id/lights_green"
+ style="@style/FieldContents"
+ android:text="green"
+ android:tag="0x00ff00"
+ />
+ <RadioButton
+ android:id="@+id/lights_blue"
+ style="@style/FieldContents"
+ android:text="blue"
+ android:tag="0x0000ff"
+ />
+ <RadioButton
+ android:id="@+id/lights_cyan"
+ style="@style/FieldContents"
+ android:text="cyan"
+ android:tag="0x00ffff"
+ />
+ <RadioButton
+ android:id="@+id/lights_magenta"
+ style="@style/FieldContents"
+ android:text="magenta"
+ android:tag="0xff00ff"
+ />
+ <RadioButton
+ android:id="@+id/lights_yellow"
+ style="@style/FieldContents"
+ android:text="yellow"
+ android:tag="0xffff00"
+ />
+ <RadioButton
+ android:id="@+id/lights_white"
+ style="@style/FieldContents"
+ android:text="white"
+ android:tag="0xffffff"
+ />
+ </RadioGroup>
+
+ <!-- setLights -->
+ <RadioGroup
+ android:id="@+id/group_lights_blink"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="setLights (blink)"
+ />
+ <RadioButton
+ android:id="@+id/lights_off"
+ style="@style/FieldContents"
+ android:text="off"
+ />
+ <RadioButton
+ android:id="@+id/lights_slow"
+ style="@style/FieldContents"
+ android:text="slow"
+ />
+ <RadioButton
+ android:id="@+id/lights_fast"
+ style="@style/FieldContents"
+ android:text="fast"
+ />
+ <RadioButton
+ android:id="@+id/lights_on"
+ style="@style/FieldContents"
+ android:text="on"
+ />
+ </RadioGroup>
+
+ <!-- flags -->
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="12dp"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="flags"
+ />
+ <CheckBox
+ android:id="@+id/flag_ongoing"
+ style="@style/FieldContents"
+ android:text="setOngoing"
+ />
+ <CheckBox
+ android:id="@+id/flag_once"
+ style="@style/FieldContents"
+ android:text="setOnlyAlertOnce"
+ />
+ <CheckBox
+ android:id="@+id/flag_auto_cancel"
+ style="@style/FieldContents"
+ android:text="setAutoCancel"
+ />
+ </LinearLayout>
+
+ <!-- defaults -->
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ style="@style/FieldTitle"
+ android:text="defaults"
+ />
+ <CheckBox
+ android:id="@+id/default_sound"
+ style="@style/FieldContents"
+ android:text="sound"
+ />
+ <CheckBox
+ android:id="@+id/default_vibrate"
+ style="@style/FieldContents"
+ android:text="vibrate"
+ />
+ <CheckBox
+ android:id="@+id/default_lights"
+ style="@style/FieldContents"
+ android:text="lights"
+ />
+ </LinearLayout>
+
+
+
+
+ </LinearLayout>
+ </ScrollView>
+
+
+</LinearLayout>
diff --git a/tests/StatusBar/res/values/styles.xml b/tests/StatusBar/res/values/styles.xml
new file mode 100644
index 0000000..e051efd
--- /dev/null
+++ b/tests/StatusBar/res/values/styles.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+<resources>
+
+ <style name="IdTitle">
+ <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+ <item name="android:layout_width">30sp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="IdButton">
+ <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:layout_marginRight">8dp</item>
+ <item name="android:layout_marginLeft">8dp</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="IdButton.Minus">
+ <item name="android:text">-</item>
+ </style>
+
+ <style name="IdButton.Plus">
+ <item name="android:text">+</item>
+ </style>
+
+ <style name="FieldTitle">
+ <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+ <item name="android:layout_width">208sp</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="FieldContents">
+ <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginRight">20dp</item>
+ </style>
+
+ <style name="FieldContents.Disabled">
+ <item name="android:clickable">false</item>
+ <item name="android:visibility">gone</item>
+ </style>
+
+</resources>
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
index 3c26212..e9a3513 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -16,6 +16,7 @@
package com.android.statusbartest;
+import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -25,106 +26,363 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Vibrator;
import android.os.Handler;
+import android.text.TextUtils;
import android.util.Log;
import android.net.Uri;
import android.os.SystemClock;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.RemoteViews;
import android.os.PowerManager;
-public class NotificationBuilderTest extends TestActivity
+public class NotificationBuilderTest extends Activity
{
private final static String TAG = "NotificationTestList";
NotificationManager mNM;
@Override
- protected String tag() {
- return TAG;
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+ setContentView(R.layout.notification_builder_test);
+ if (icicle == null) {
+ setDefaults();
+ }
+ for (int id: new int[] {
+ R.id.clear_1,
+ R.id.clear_2,
+ R.id.clear_3,
+ R.id.clear_4,
+ R.id.clear_5,
+ R.id.clear_6,
+ R.id.clear_7,
+ R.id.clear_8,
+ R.id.clear_9,
+ R.id.clear_10,
+ R.id.notify_1,
+ R.id.notify_2,
+ R.id.notify_3,
+ R.id.notify_4,
+ R.id.notify_5,
+ R.id.notify_6,
+ R.id.notify_7,
+ R.id.notify_8,
+ R.id.notify_9,
+ R.id.notify_10,
+ R.id.ten,
+ R.id.clear_all,
+ }) {
+ findViewById(id).setOnClickListener(mClickListener);
+ }
}
- @Override
- protected Test[] tests() {
- mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
-
- return mTests;
+ private void setDefaults() {
+ setChecked(R.id.when_now);
+ setChecked(R.id.icon_surprise);
+ setChecked(R.id.title_medium);
+ setChecked(R.id.text_medium);
+ setChecked(R.id.info_none);
+ setChecked(R.id.number_0);
+ setChecked(R.id.intent_alert);
+ setChecked(R.id.delete_none);
+ setChecked(R.id.full_screen_none);
+ setChecked(R.id.ticker_none);
+ setChecked(R.id.large_icon_none);
+ setChecked(R.id.sound_none);
+ setChecked(R.id.vibrate_none);
+ setChecked(R.id.lights_red);
+ setChecked(R.id.lights_off);
}
- private Test[] mTests = new Test[] {
- new Test("Cancel (1)") {
- public void run() {
- mNM.cancel(1);
+ private View.OnClickListener mClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.clear_1:
+ mNM.cancel(1);
+ break;
+ case R.id.clear_2:
+ mNM.cancel(2);
+ break;
+ case R.id.clear_3:
+ mNM.cancel(3);
+ break;
+ case R.id.clear_4:
+ mNM.cancel(4);
+ break;
+ case R.id.clear_5:
+ mNM.cancel(5);
+ break;
+ case R.id.clear_6:
+ mNM.cancel(6);
+ break;
+ case R.id.clear_7:
+ mNM.cancel(7);
+ break;
+ case R.id.clear_8:
+ mNM.cancel(8);
+ break;
+ case R.id.clear_9:
+ mNM.cancel(9);
+ break;
+ case R.id.clear_10:
+ mNM.cancel(10);
+ break;
+ case R.id.notify_1:
+ sendNotification(1);
+ break;
+ case R.id.notify_2:
+ sendNotification(2);
+ break;
+ case R.id.notify_3:
+ sendNotification(3);
+ break;
+ case R.id.notify_4:
+ sendNotification(4);
+ break;
+ case R.id.notify_5:
+ sendNotification(5);
+ break;
+ case R.id.notify_6:
+ sendNotification(6);
+ break;
+ case R.id.notify_7:
+ sendNotification(7);
+ break;
+ case R.id.notify_8:
+ sendNotification(8);
+ break;
+ case R.id.notify_9:
+ sendNotification(9);
+ break;
+ case R.id.notify_10:
+ sendNotification(10);
+ break;
+ case R.id.ten: {
+ for (int id=1; id<=10; id++) {
+ sendNotification(id);
+ }
+ break;
+ }
+ case R.id.clear_all: {
+ for (int id=1; id<=10; id++) {
+ mNM.cancel(id);
+ }
+ break;
+ }
}
- },
+ }
+ };
- new Test("Basic Content (1)") {
- public void run() {
- int id = 1;
- final Notification.Builder b = makeBasicBuilder(this, id);
+ private void sendNotification(int id) {
+ final Notification n = buildNotification(id);
+ mNM.notify(id, n);
+ }
- mNM.notify(id, b.getNotification());
- }
- },
+ private Notification buildNotification(int id) {
+ Notification.Builder b = new Notification.Builder(this);
- new Test("Content w/ Info (1)") {
- public void run() {
- int id = 1;
- final Notification.Builder b = makeBasicBuilder(this, id);
+ // when
+ switch (getRadioChecked(R.id.group_when)) {
+ case R.id.when_midnight:
+ break;
+ case R.id.when_now:
+ b.setWhen(System.currentTimeMillis());
+ break;
+ case R.id.when_now_plus_1h:
+ break;
+ case R.id.when_tomorrow:
+ break;
+ }
- b.setContentInfo("Snoozed");
+ // icon
+ switch (getRadioChecked(R.id.group_icon)) {
+ case R.id.icon_im:
+ b.setSmallIcon(R.drawable.icon1);
+ break;
+ case R.id.icon_alert:
+ b.setSmallIcon(R.drawable.icon2);
+ break;
+ case R.id.icon_surprise:
+ b.setSmallIcon(R.drawable.emo_im_kissing);
+ break;
+ }
- mNM.notify(id, b.getNotification());
- }
- },
+ // title
+ final String title = getRadioTag(R.id.group_title);
+ if (!TextUtils.isEmpty(title)) {
+ b.setContentTitle(title);
+ }
- new Test("w/ Number (1)") {
- public void run() {
- int id = 1;
- final Notification.Builder b = makeBasicBuilder(this, id);
+ // text
+ final String text = getRadioTag(R.id.group_text);
+ if (!TextUtils.isEmpty(text)) {
+ b.setContentText(text);
+ }
- b.setNumber(12345);
+ // info
+ final String info = getRadioTag(R.id.group_info);
+ if (!TextUtils.isEmpty(info)) {
+ b.setContentInfo(info);
+ }
- mNM.notify(id, b.getNotification());
- }
- },
+ // number
+ b.setNumber(getRadioInt(R.id.group_number, 0));
- new Test("w/ Number and Large Icon (1)") {
- public void run() {
- int id = 1;
- final Notification.Builder b = makeBasicBuilder(this, id);
+ // contentIntent
+ switch (getRadioChecked(R.id.group_intent)) {
+ case R.id.intent_none:
+ break;
+ case R.id.intent_alert:
+ b.setContentIntent(makeContentIntent(id));
+ break;
+ }
- b.setNumber(42);
+ // deleteIntent
+ switch (getRadioChecked(R.id.group_delete)) {
+ case R.id.delete_none:
+ break;
+ case R.id.delete_alert:
+ b.setDeleteIntent(makeDeleteIntent(id));
+ break;
+ }
- final BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(
- R.drawable.pineapple);
- b.setLargeIcon(Bitmap.createBitmap(bd.getBitmap()));
+ // fullScreenIntent TODO
- mNM.notify(id, b.getNotification());
- }
- },
- };
+ // ticker
+ switch (getRadioChecked(R.id.group_ticker)) {
+ case R.id.ticker_none:
+ break;
+ case R.id.ticker_short:
+ case R.id.ticker_wrap:
+ case R.id.ticker_haiku:
+ b.setTicker(getRadioTag(R.id.group_ticker));
+ break;
+ case R.id.ticker_custom:
+ // TODO
+ break;
+ }
- private Notification.Builder makeBasicBuilder(Test t, int id) {
- final Notification.Builder b = new Notification.Builder(this);
+ // largeIcon
+ switch (getRadioChecked(R.id.group_large_icon)) {
+ case R.id.large_icon_none:
+ break;
+ case R.id.large_icon_pineapple:
+ b.setLargeIcon(loadBitmap(R.drawable.pineapple));
+ break;
+ }
- b.setWhen(System.currentTimeMillis());
- b.setSmallIcon(R.drawable.ic_statusbar_chat);
- b.setContentTitle("Notification builder Test");
- b.setContentText(t.name + "\nhappy notifying");
- b.setContentIntent(makeContentIntent(id));
- b.setDeleteIntent(makeDeleteIntent(id));
+ // sound TODO
- return b;
+ // vibrate
+ switch (getRadioChecked(R.id.group_vibrate)) {
+ case R.id.vibrate_none:
+ break;
+ case R.id.vibrate_short:
+ b.setVibrate(new long[] { 0, 200 });
+ break;
+ case R.id.vibrate_medium:
+ b.setVibrate(new long[] { 0, 500 });
+ break;
+ case R.id.vibrate_long:
+ b.setVibrate(new long[] { 0, 1000 });
+ break;
+ case R.id.vibrate_pattern:
+ b.setVibrate(new long[] { 0, 250, 250, 250, 250, 250, 250, 250 });
+ break;
+ }
+
+ // lights
+ final int color = getRadioInt(R.id.group_lights_color, 0xff0000);
+ int onMs;
+ int offMs;
+ switch (getRadioChecked(R.id.group_lights_blink)) {
+ case R.id.lights_slow:
+ onMs = 1300;
+ offMs = 1300;
+ break;
+ case R.id.lights_fast:
+ onMs = 300;
+ offMs = 300;
+ break;
+ case R.id.lights_on:
+ onMs = 1;
+ offMs = 0;
+ break;
+ case R.id.lights_off:
+ default:
+ onMs = 0;
+ offMs = 0;
+ break;
+ }
+ if (onMs != 0 && offMs != 0) {
+ b.setLights(color, onMs, offMs);
+ }
+
+ // flags
+ b.setOngoing(getChecked(R.id.flag_ongoing));
+ b.setOnlyAlertOnce(getChecked(R.id.flag_once));
+ b.setAutoCancel(getChecked(R.id.flag_auto_cancel));
+
+ // defaults
+ int defaults = 0;
+ if (getChecked(R.id.default_sound)) {
+ defaults |= Notification.DEFAULT_SOUND;
+ }
+ if (getChecked(R.id.default_vibrate)) {
+ defaults |= Notification.DEFAULT_VIBRATE;
+ }
+ if (getChecked(R.id.default_lights)) {
+ defaults |= Notification.DEFAULT_LIGHTS;
+ }
+ b.setDefaults(defaults);
+
+ return b.getNotification();
}
- private PendingIntent makeContentIntent(int id) {
- Intent intent = new Intent(this, ConfirmationActivity.class);
- intent.setData(Uri.fromParts("content", "//status_bar_test/content/" + id, null));
- intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Content intent");
- intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
- return PendingIntent.getActivity(this, 0, intent, 0);
+ private void setChecked(int id) {
+ final CompoundButton b = (CompoundButton)findViewById(id);
+ b.setChecked(true);
+ }
+
+ private int getRadioChecked(int id) {
+ final RadioGroup g = (RadioGroup)findViewById(id);
+ return g.getCheckedRadioButtonId();
+ }
+
+ private String getRadioTag(int id) {
+ final RadioGroup g = (RadioGroup)findViewById(id);
+ final View v = findViewById(g.getCheckedRadioButtonId());
+ return (String)v.getTag();
+ }
+
+ private int getRadioInt(int id, int def) {
+ String str = getRadioTag(id);
+ if (TextUtils.isEmpty(str)) {
+ return def;
+ } else {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return def;
+ }
+ }
+ }
+
+ private boolean getChecked(int id) {
+ final CompoundButton b = (CompoundButton)findViewById(id);
+ return b.isChecked();
+ }
+
+ private Bitmap loadBitmap(int id) {
+ final BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(id);
+ return Bitmap.createBitmap(bd.getBitmap());
}
private PendingIntent makeDeleteIntent(int id) {
@@ -134,5 +392,13 @@ public class NotificationBuilderTest extends TestActivity
intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
return PendingIntent.getActivity(this, 0, intent, 0);
}
+
+ private PendingIntent makeContentIntent(int id) {
+ Intent intent = new Intent(this, ConfirmationActivity.class);
+ intent.setData(Uri.fromParts("content", "//status_bar_test/content/" + id, null));
+ intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Content intent");
+ intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
+ return PendingIntent.getActivity(this, 0, intent, 0);
+ }
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index fec3671..4cacbc4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -39,7 +39,7 @@ public class ActivityManagerPermissionTests extends TestCase {
@SmallTest
public void testREORDER_TASKS() {
try {
- mAm.moveTaskToFront(-1);
+ mAm.moveTaskToFront(0, 0);
fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 3cb614f..faae89b 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -33,8 +33,8 @@ static const char* kNoCompressExt[] = {
/* fwd decls, so I can write this downward */
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptDir>& dir, const AaptGroupEntry& ge);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+ const AaptGroupEntry& ge, const ResourceFilter* filter);
bool processFile(Bundle* bundle, ZipFile* zip,
const sp<AaptGroup>& group, const sp<AaptFile>& file);
bool okayToCompress(Bundle* bundle, const String8& pathName);
@@ -204,34 +204,45 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip,
const size_t N = assets->getGroupEntries().size();
for (size_t i=0; i<N; i++) {
const AaptGroupEntry& ge = assets->getGroupEntries()[i];
- if (!filter.match(ge.toParams())) {
- continue;
- }
- ssize_t res = processAssets(bundle, zip, assets, ge);
+
+ ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
if (res < 0) {
return res;
}
+
count += res;
}
return count;
}
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptDir>& dir, const AaptGroupEntry& ge)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+ const AaptGroupEntry& ge, const ResourceFilter* filter)
{
ssize_t count = 0;
const size_t ND = dir->getDirs().size();
size_t i;
for (i=0; i<ND; i++) {
- ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge);
+ const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+
+ const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
+
+ if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
+ continue;
+ }
+
+ ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
if (res < 0) {
return res;
}
count += res;
}
+ if (filter != NULL && !filter->match(ge.toParams())) {
+ return count;
+ }
+
const size_t NF = dir->getFiles().size();
for (i=0; i<NF; i++) {
sp<AaptGroup> gp = dir->getFiles().valueAt(i);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 822262e..7b74506 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -150,7 +150,7 @@ bool isValidResourceType(const String8& type)
{
return type == "anim" || type == "drawable" || type == "layout"
|| type == "values" || type == "xml" || type == "raw"
- || type == "color" || type == "menu";
+ || type == "color" || type == "menu" || type == "mipmap";
}
static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
@@ -284,9 +284,9 @@ static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
}
static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
- const sp<ResourceTypeSet>& set)
+ const sp<ResourceTypeSet>& set, const char* type)
{
- ResourceDirIterator it(set, String8("drawable"));
+ ResourceDirIterator it(set, String8(type));
Vector<sp<AaptFile> > newNameFiles;
Vector<String8> newNamePaths;
bool hasErrors = false;
@@ -802,6 +802,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
sp<ResourceTypeSet> menus;
+ sp<ResourceTypeSet> mipmaps;
ASSIGN_IT(drawable);
ASSIGN_IT(layout);
@@ -810,6 +811,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
ASSIGN_IT(raw);
ASSIGN_IT(color);
ASSIGN_IT(menu);
+ ASSIGN_IT(mipmap);
assets->setResources(resources);
// now go through any resource overlays and collect their files
@@ -828,7 +830,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
- !applyFileOverlay(bundle, assets, &menus, "menu")) {
+ !applyFileOverlay(bundle, assets, &menus, "menu") ||
+ !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
return UNKNOWN_ERROR;
}
@@ -836,7 +839,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
if (drawables != NULL) {
if (bundle->getOutputAPKFile() != NULL) {
- err = preProcessImages(bundle, assets, drawables);
+ err = preProcessImages(bundle, assets, drawables, "drawable");
}
if (err == NO_ERROR) {
err = makeFileResources(bundle, assets, &table, drawables, "drawable");
@@ -848,6 +851,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
}
}
+ if (mipmaps != NULL) {
+ if (bundle->getOutputAPKFile() != NULL) {
+ err = preProcessImages(bundle, assets, mipmaps, "mipmap");
+ }
+ if (err == NO_ERROR) {
+ err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ } else {
+ hasErrors = true;
+ }
+ }
+
if (layouts != NULL) {
err = makeFileResources(bundle, assets, &table, layouts, "layout");
if (err != NO_ERROR) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index a77042a..196b06c 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2539,7 +2539,7 @@ ResourceFilter::parse(const char* arg)
}
bool
-ResourceFilter::match(int axis, uint32_t value)
+ResourceFilter::match(int axis, uint32_t value) const
{
if (value == 0) {
// they didn't specify anything so take everything
@@ -2555,7 +2555,7 @@ ResourceFilter::match(int axis, uint32_t value)
}
bool
-ResourceFilter::match(const ResTable_config& config)
+ResourceFilter::match(const ResTable_config& config) const
{
if (config.locale) {
uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
@@ -2608,6 +2608,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
const size_t N = mOrderedPackages.size();
size_t pi;
+ const static String16 mipmap16("mipmap");
+
bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
// Iterate through all data, collecting all values (strings,
@@ -2630,7 +2632,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
typeStrings.add(String16("<empty>"), false);
continue;
}
- typeStrings.add(t->getName(), false);
+ const String16 typeName(t->getName());
+ typeStrings.add(typeName, false);
+
+ const bool filterable = (typeName != mipmap16);
const size_t N = t->getOrderedConfigs().size();
for (size_t ci=0; ci<N; ci++) {
@@ -2641,7 +2646,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
ConfigDescription config = c->getEntries().keyAt(ei);
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2721,6 +2726,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
"Type name %s not found",
String8(typeName).string());
+ const bool filterable = (typeName != mipmap16);
+
const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
// First write the typeSpec chunk, containing information about
@@ -2745,7 +2752,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
(((uint8_t*)data->editData())
+ typeSpecStart + sizeof(ResTable_typeSpec));
memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-
+
for (size_t ei=0; ei<N; ei++) {
sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
if (cl->getPublic()) {
@@ -2753,11 +2760,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
}
const size_t CN = cl->getEntries().size();
for (size_t ci=0; ci<CN; ci++) {
- if (!filter.match(cl->getEntries().keyAt(ci))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
continue;
}
for (size_t cj=ci+1; cj<CN; cj++) {
- if (!filter.match(cl->getEntries().keyAt(cj))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
continue;
}
typeSpecFlags[ei] |= htodl(
@@ -2794,7 +2801,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
config.screenWidth,
config.screenHeight));
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 186c7ca..bbb8140 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -549,9 +549,9 @@ class ResourceFilter
public:
ResourceFilter() : mData(), mContainsPseudo(false) {}
status_t parse(const char* arg);
- bool match(int axis, uint32_t value);
- bool match(const ResTable_config& config);
- inline bool containsPseudo() { return mContainsPseudo; }
+ bool match(int axis, uint32_t value) const;
+ bool match(const ResTable_config& config) const;
+ inline bool containsPseudo() const { return mContainsPseudo; }
private:
KeyedVector<int,SortedVector<uint32_t> > mData;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
index 6fd59c4..212223c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -456,7 +456,11 @@ public class BitmapFactory {
// into is.read(...) This number is not related to the value passed
// to mark(...) above.
try {
- bm = Bitmap_Delegate.createBitmap(is, Density.MEDIUM);
+ Density density = Density.MEDIUM;
+ if (opts != null) {
+ density = Density.getEnum(opts.inDensity);
+ }
+ bm = Bitmap_Delegate.createBitmap(is, true, density);
} catch (IOException e) {
return null;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 0920497..b4c51b2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -75,32 +75,56 @@ public class Bitmap_Delegate {
/**
* Creates and returns a {@link Bitmap} initialized with the given file content.
+ *
+ * @param input the file from which to read the bitmap content
+ * @param isMutable whether the bitmap is mutable
+ * @param density the density associated with the bitmap
+ *
+ * @see Bitmap#isMutable()
+ * @see Bitmap#getDensity()
*/
- public static Bitmap createBitmap(File input, Density density) throws IOException {
+ public static Bitmap createBitmap(File input, boolean isMutable, Density density)
+ throws IOException {
// create a delegate with the content of the file.
Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
- return createBitmap(delegate, density.getValue());
+ return createBitmap(delegate, isMutable, density.getValue());
}
/**
* Creates and returns a {@link Bitmap} initialized with the given stream content.
+ *
+ * @param input the stream from which to read the bitmap content
+ * @param isMutable whether the bitmap is mutable
+ * @param density the density associated with the bitmap
+ *
+ * @see Bitmap#isMutable()
+ * @see Bitmap#getDensity()
*/
- public static Bitmap createBitmap(InputStream input, Density density) throws IOException {
+ public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
+ throws IOException {
// create a delegate with the content of the stream.
Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
- return createBitmap(delegate, density.getValue());
+ return createBitmap(delegate, isMutable, density.getValue());
}
/**
* Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
+ *
+ * @param image the bitmap content
+ * @param isMutable whether the bitmap is mutable
+ * @param density the density associated with the bitmap
+ *
+ * @see Bitmap#isMutable()
+ * @see Bitmap#getDensity()
*/
- public static Bitmap createBitmap(BufferedImage image, Density density) throws IOException {
+ public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density)
+ throws IOException {
// create a delegate with the given image.
Bitmap_Delegate delegate = new Bitmap_Delegate(image);
- return createBitmap(delegate, density.getValue());
+ return createBitmap(delegate, isMutable, density.getValue());
}
/**
@@ -153,7 +177,7 @@ public class Bitmap_Delegate {
// create a delegate with the content of the stream.
Bitmap_Delegate delegate = new Bitmap_Delegate(image);
- return createBitmap(delegate, Bitmap.getDefaultDensity());
+ return createBitmap(delegate, mutable, Bitmap.getDefaultDensity());
}
/*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
@@ -166,8 +190,7 @@ public class Bitmap_Delegate {
}
/*package*/ static void nativeRecycle(int nativeBitmap) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+ sManager.removeDelegate(nativeBitmap);
}
/*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
@@ -336,11 +359,11 @@ public class Bitmap_Delegate {
mImage = image;
}
- private static Bitmap createBitmap(Bitmap_Delegate delegate, int density) {
+ private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) {
// get its native_int
int nativeInt = sManager.addDelegate(delegate);
// and create/return a new Bitmap with it
- return new Bitmap(nativeInt, true /*isMutable*/, null /*ninePatchChunk*/, density);
+ return new Bitmap(nativeInt, isMutable, null /*ninePatchChunk*/, density);
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index cea07af..08f3c7a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -960,7 +960,7 @@ public class Canvas_Delegate {
* Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
*/
- private Graphics2D getCustomGraphics(Paint_Delegate paint) {
+ /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) {
// make new one
Graphics2D g = getGraphics2d();
g = (Graphics2D)g.create();
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
new file mode 100644
index 0000000..3d26e47
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.ninepatch.NinePatchChunk;
+
+import android.graphics.drawable.NinePatchDrawable;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.graphics.NinePatch
+ *
+ * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public class NinePatch_Delegate {
+
+ /**
+ * Cache map for {@link NinePatchChunk}.
+ * When the chunks are created they are serialized into a byte[], and both are put
+ * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
+ * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
+ * provide this for drawing.
+ * Using the cache map allows us to not have to deserialize the byte[] back into a
+ * {@link NinePatchChunk} every time a rendering is done.
+ */
+ private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
+ new HashMap<byte[], SoftReference<NinePatchChunk>>();
+
+ // ---- Public Helper methods ----
+
+ /**
+ * Serializes the given chunk.
+ *
+ * @return the serialized data for the chunk.
+ */
+ public static byte[] serialize(NinePatchChunk chunk) {
+ // serialize the chunk to get a byte[]
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = null;
+ try {
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(chunk);
+ } catch (IOException e) {
+ //FIXME log this.
+ return null;
+ } finally {
+ if (oos != null) {
+ try {
+ oos.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // get the array and add it to the cache
+ byte[] array = baos.toByteArray();
+ sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+ return array;
+ }
+
+ // ---- native methods ----
+
+ /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
+ NinePatchChunk chunkObject = getChunk(chunk);
+ if (chunkObject != null) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
+ // the default JNI implementation only checks that the byte[] has the same
+ // size as the C struct it represent. Since we cannot do the same check (serialization
+ // will return different size depending on content), we do nothing.
+ }
+
+ /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
+ byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+ draw(canvas_instance,
+ (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
+ bitmap_instance, c, paint_instance_or_null,
+ destDensity, srcDensity);
+ }
+
+ /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
+ byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+ draw(canvas_instance,
+ loc.left, loc.top, loc.width(), loc.height(),
+ bitmap_instance, c, paint_instance_or_null,
+ destDensity, srcDensity);
+ }
+
+ private static void draw(int canvas_instance,
+ int left, int top, int right, int bottom,
+ int bitmap_instance, byte[] c, int paint_instance_or_null,
+ int destDensity, int srcDensity) {
+ // get the delegate from the native int.
+ Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
+ if (bitmap_delegate == null) {
+ assert false;
+ return;
+ }
+
+ if (c == null) {
+ // not a 9-patch?
+ BufferedImage image = bitmap_delegate.getImage();
+ Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
+ new Rect(0, 0, image.getWidth(), image.getHeight()),
+ new Rect(left, top, right, bottom),
+ paint_instance_or_null, destDensity, srcDensity);
+ return;
+ }
+
+ NinePatchChunk chunkObject = getChunk(c);
+ assert chunkObject != null;
+ if (chunkObject == null) {
+ return;
+ }
+
+ Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
+ if (canvas_delegate == null) {
+ assert false;
+ return;
+ }
+
+ // this one can be null
+ Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
+
+ Graphics2D graphics;
+ if (paint_delegate != null) {
+ graphics = canvas_delegate.getCustomGraphics(paint_delegate);
+ } else {
+ graphics = canvas_delegate.getGraphics2d();
+ }
+
+ try {
+ chunkObject.draw(bitmap_delegate.getImage(), graphics,
+ left, top, right - left, bottom - top);
+ } finally {
+ if (paint_delegate != null) {
+ graphics.dispose();
+ }
+ }
+
+ }
+
+ /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
+ return 0;
+ }
+
+ // ---- Private Helper methods ----
+
+ /**
+ * Returns a {@link NinePatchChunk} object for the given serialized representation.
+ *
+ * If the chunk is present in the cache then the object from the cache is returned, otherwise
+ * the array is deserialized into a {@link NinePatchChunk} object.
+ *
+ * @param array the serialized representation of the chunk.
+ * @return the NinePatchChunk or null if deserialization failed.
+ */
+ private static NinePatchChunk getChunk(byte[] array) {
+ SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
+ NinePatchChunk chunk = chunkRef.get();
+ if (chunk == null) {
+ ByteArrayInputStream bais = new ByteArrayInputStream(array);
+ ObjectInputStream ois = null;
+ try {
+ ois = new ObjectInputStream(bais);
+ chunk = (NinePatchChunk) ois.readObject();
+
+ // put back the chunk in the cache
+ if (chunk != null) {
+ sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+ }
+ } catch (IOException e) {
+ // FIXME: log this
+ return null;
+ } catch (ClassNotFoundException e) {
+ // FIXME: log this
+ return null;
+ } finally {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ return chunk;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index e691fdf..35ba73d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,7 @@ import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.bridge.android.BridgeAssetManager;
import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.LayoutSceneImpl;
-import com.android.ninepatch.NinePatch;
+import com.android.ninepatch.NinePatchChunk;
import com.android.tools.layoutlib.create.MethodAdapter;
import com.android.tools.layoutlib.create.OverrideMethod;
@@ -73,13 +73,13 @@ public final class Bridge extends LayoutBridge {
private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
new HashMap<Object, Map<String, SoftReference<Bitmap>>>();
- private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache =
- new HashMap<Object, Map<String, SoftReference<NinePatch>>>();
+ private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
+ new HashMap<Object, Map<String, SoftReference<NinePatchChunk>>>();
private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache =
new HashMap<String, SoftReference<Bitmap>>();
- private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache =
- new HashMap<String, SoftReference<NinePatch>>();
+ private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
+ new HashMap<String, SoftReference<NinePatchChunk>>();
private static Map<String, Map<String, Integer>> sEnumValueMap;
@@ -252,23 +252,23 @@ public final class Bridge extends LayoutBridge {
}
/**
- * Sets a 9 patch in a project cache or in the framework cache.
+ * Sets a 9 patch chunk in a project cache or in the framework cache.
* @param value the path of the 9 patch
* @param ninePatch the 9 patch object
* @param projectKey the key of the project, or null to put the bitmap in the framework cache.
*/
- public static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
+ public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
if (projectKey != null) {
- Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+ Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
if (map == null) {
- map = new HashMap<String, SoftReference<NinePatch>>();
+ map = new HashMap<String, SoftReference<NinePatchChunk>>();
sProject9PatchCache.put(projectKey, map);
}
- map.put(value, new SoftReference<NinePatch>(ninePatch));
+ map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
} else {
- sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch));
+ sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
}
}
@@ -436,24 +436,24 @@ public final class Bridge extends LayoutBridge {
}
/**
- * Returns the 9 patch for a specific path, from a specific project cache, or from the
+ * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
* framework cache.
* @param value the path of the 9 patch
* @param projectKey the key of the project, or null to query the framework cache.
* @return the cached 9 patch or null if not found.
*/
- public static NinePatch getCached9Patch(String value, Object projectKey) {
+ public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
if (projectKey != null) {
- Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+ Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
if (map != null) {
- SoftReference<NinePatch> ref = map.get(value);
+ SoftReference<NinePatchChunk> ref = map.get(value);
if (ref != null) {
return ref.get();
}
}
} else {
- SoftReference<NinePatch> ref = sFramework9PatchCache.get(value);
+ SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
if (ref != null) {
return ref.get();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java
deleted file mode 100644
index 4efa631..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2008 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.layoutlib.bridge.android;
-
-import com.android.ninepatch.NinePatch;
-
-import android.graphics.Canvas;
-import android.graphics.Canvas_Delegate;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-public class NinePatchDrawable extends Drawable {
-
- private NinePatch m9Patch;
-
- public NinePatchDrawable(NinePatch ninePatch) {
- m9Patch = ninePatch;
- }
-
- @Override
- public int getMinimumWidth() {
- return m9Patch.getWidth();
- }
-
- @Override
- public int getMinimumHeight() {
- return m9Patch.getHeight();
- }
-
- /**
- * Return the intrinsic width of the underlying drawable object. Returns
- * -1 if it has no intrinsic width, such as with a solid color.
- */
- @Override
- public int getIntrinsicWidth() {
- return m9Patch.getWidth();
- }
-
- /**
- * Return the intrinsic height of the underlying drawable object. Returns
- * -1 if it has no intrinsic height, such as with a solid color.
- */
- @Override
- public int getIntrinsicHeight() {
- return m9Patch.getHeight();
- }
-
- /**
- * Return in padding the insets suggested by this Drawable for placing
- * content inside the drawable's bounds. Positive values move toward the
- * center of the Drawable (set Rect.inset). Returns true if this drawable
- * actually has a padding, else false. When false is returned, the padding
- * is always set to 0.
- */
- @Override
- public boolean getPadding(Rect padding) {
- int[] padd = new int[4];
- m9Patch.getPadding(padd);
- padding.left = padd[0];
- padding.top = padd[1];
- padding.right = padd[2];
- padding.bottom = padd[3];
- return true;
- }
-
- @Override
- public void draw(Canvas canvas) {
- Rect r = getBounds();
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
- m9Patch.draw(canvasDelegate.getGraphics2d(), r.left, r.top, r.width(), r.height());
-
- return;
- }
-
-
- // ----------- Not implemented methods ---------------
-
-
- @Override
- public int getOpacity() {
- // FIXME
- return 0xFF;
- }
-
- @Override
- public void setAlpha(int arg0) {
- // FIXME !
- }
-
- @Override
- public void setColorFilter(ColorFilter arg0) {
- // FIXME
- }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 2e3f9a8..f7d249e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -313,10 +313,12 @@ public class LayoutSceneImpl {
// create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+ true /*isMutable*/,
Density.getEnum(mParams.getDensity()));
// create a Canvas around the Android bitmap
Canvas canvas = new Canvas(bitmap);
+ canvas.setDensity(mParams.getDensity());
// to set the logger, get the native delegate
Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 3e506b8..ceb8a0d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -22,8 +22,8 @@ import com.android.layoutlib.api.IResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.NinePatchDrawable;
import com.android.ninepatch.NinePatch;
+import com.android.ninepatch.NinePatchChunk;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
@@ -31,9 +31,12 @@ import org.xmlpull.v1.XmlPullParserException;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
+import android.graphics.NinePatch_Delegate;
+import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
import android.util.TypedValue;
import java.io.File;
@@ -121,15 +124,38 @@ public final class ResourceHelper {
if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
File file = new File(stringValue);
if (file.isFile()) {
- NinePatch ninePatch = Bridge.getCached9Patch(stringValue,
+ // see if we still have both the chunk and the bitmap in the caches
+ NinePatchChunk chunk = Bridge.getCached9Patch(stringValue,
+ isFramework ? null : context.getProjectKey());
+ Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
isFramework ? null : context.getProjectKey());
- if (ninePatch == null) {
+ // if either chunk or bitmap is null, then we reload the 9-patch file.
+ if (chunk == null || bitmap == null) {
try {
- ninePatch = NinePatch.load(file.toURL(), false /* convert */);
+ NinePatch ninePatch = NinePatch.load(file.toURL(), false /* convert */);
+ if (ninePatch != null) {
+ if (chunk == null) {
+ chunk = ninePatch.getChunk();
- Bridge.setCached9Patch(stringValue, ninePatch,
- isFramework ? null : context.getProjectKey());
+ Bridge.setCached9Patch(stringValue, chunk,
+ isFramework ? null : context.getProjectKey());
+ }
+
+ if (bitmap == null) {
+ Density density = Density.MEDIUM;
+ if (value instanceof IDensityBasedResourceValue) {
+ density = ((IDensityBasedResourceValue)value).getDensity();
+ }
+
+ bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
+ false /*isMutable*/,
+ density);
+
+ Bridge.setCachedBitmap(stringValue, bitmap,
+ isFramework ? null : context.getProjectKey());
+ }
+ }
} catch (MalformedURLException e) {
// URL is wrong, we'll return null below
} catch (IOException e) {
@@ -137,8 +163,13 @@ public final class ResourceHelper {
}
}
- if (ninePatch != null) {
- return new NinePatchDrawable(ninePatch);
+ if (chunk != null && bitmap != null) {
+ int[] padding = chunk.getPadding();
+ Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
+
+ return new NinePatchDrawable(context.getResources(), bitmap,
+ NinePatch_Delegate.serialize(chunk),
+ paddingRect, null);
}
}
@@ -174,27 +205,15 @@ public final class ResourceHelper {
isFramework ? null : context.getProjectKey());
if (bitmap == null) {
- // always create the cache copy in the original density.
- bitmap = Bitmap_Delegate.createBitmap(bmpFile, Density.MEDIUM);
- Bridge.setCachedBitmap(stringValue, bitmap,
- isFramework ? null : context.getProjectKey());
- }
-
- try {
+ Density density = Density.MEDIUM;
if (value instanceof IDensityBasedResourceValue) {
- Density density = ((IDensityBasedResourceValue)value).getDensity();
- if (density != Density.MEDIUM) {
- // create a copy of the bitmap
- bitmap = Bitmap.createBitmap(bitmap);
-
- // apply the density
- bitmap.setDensity(density.getValue());
- }
+ density = ((IDensityBasedResourceValue)value).getDensity();
}
- } catch (NoClassDefFoundError error) {
- // look like we're running in an older version of ADT that doesn't include
- // the new layoutlib_api. Let's just ignore this, the drawing will just be
- // wrong.
+
+ bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
+ density);
+ Bridge.setCachedBitmap(stringValue, bitmap,
+ isFramework ? null : context.getProjectKey());
}
return new BitmapDrawable(context.getResources(), bitmap);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
index 23351ab..a3219e7 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
@@ -48,5 +48,4 @@ public class NinePatchTest extends TestCase {
assertEquals(36, mPatch.getWidth());
assertEquals(25, mPatch.getHeight());
}
-
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index b9c7113..bb2e6b3 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -109,6 +109,7 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.DashPathEffect",
"android.graphics.LinearGradient",
"android.graphics.Matrix",
+ "android.graphics.NinePatch",
"android.graphics.Paint",
"android.graphics.PathEffect",
"android.graphics.PorterDuffXfermode",
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
new file mode 100644
index 0000000..90979e1
--- /dev/null
+++ b/tools/validatekeymaps/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright 2010 The Android Open Source Project
+#
+# Keymap validation tool.
+#
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ Main.cpp
+
+LOCAL_CFLAGS := -Wall -Werror
+
+#LOCAL_C_INCLUDES +=
+
+LOCAL_STATIC_LIBRARIES := \
+ libui \
+ libutils \
+ libcutils
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_MODULE := validatekeymaps
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_BUILD_APPS
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
new file mode 100644
index 0000000..6ec223b
--- /dev/null
+++ b/tools/validatekeymaps/Main.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/KeyCharacterMap.h>
+#include <ui/KeyLayoutMap.h>
+#include <utils/String8.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace android;
+
+static const char* gProgName = "validatekeymaps";
+
+enum FileType {
+ FILETYPE_UNKNOWN,
+ FILETYPE_KEYLAYOUT,
+ FILETYPE_KEYCHARACTERMAP,
+};
+
+
+static void usage() {
+ fprintf(stderr, "Keymap Validation Tool\n\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s [FILENAME.kl] [FILENAME.kcm] [...]\n"
+ " Validates the specified key layout and/or key character map files.\n\n", gProgName);
+}
+
+static FileType getFileType(const char* filename) {
+ const char *extension = strrchr(filename, '.');
+ if (extension) {
+ if (strcmp(extension, ".kl") == 0) {
+ return FILETYPE_KEYLAYOUT;
+ }
+ if (strcmp(extension, ".kcm") == 0) {
+ return FILETYPE_KEYCHARACTERMAP;
+ }
+ }
+ return FILETYPE_UNKNOWN;
+}
+
+static bool validateFile(const char* filename) {
+ fprintf(stdout, "Validating file '%s'...\n", filename);
+
+ FileType fileType = getFileType(filename);
+ switch (fileType) {
+ case FILETYPE_UNKNOWN:
+ fprintf(stderr, "File extension must be .kl or .kcm.\n\n");
+ return false;
+
+ case FILETYPE_KEYLAYOUT: {
+ KeyLayoutMap* map;
+ status_t status = KeyLayoutMap::load(String8(filename), &map);
+ if (status) {
+ fprintf(stderr, "Error %d parsing key layout file.\n\n", status);
+ return false;
+ }
+ break;
+ }
+
+ case FILETYPE_KEYCHARACTERMAP: {
+ KeyCharacterMap* map;
+ status_t status = KeyCharacterMap::load(String8(filename), &map);
+ if (status) {
+ fprintf(stderr, "Error %d parsing key character map file.\n\n", status);
+ return false;
+ }
+ break;
+ }
+ }
+
+ fputs("No errors.\n\n", stdout);
+ return true;
+}
+
+int main(int argc, const char** argv) {
+ if (argc < 2) {
+ usage();
+ return 1;
+ }
+
+ int result = 0;
+ for (int i = 1; i < argc; i++) {
+ if (!validateFile(argv[i])) {
+ result = 1;
+ }
+ }
+
+ if (result) {
+ fputs("Failed!\n", stderr);
+ } else {
+ fputs("Success.\n", stdout);
+ }
+ return result;
+}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 90abd02..7e3df1a 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1529,7 +1529,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
}
void setNetworkAvailable(boolean available) {
- sendMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0);
+ sendMessage(obtainMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0, 0));
}
/********************************************************