summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml1235
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java8
-rw-r--r--core/java/android/animation/AnimatorInflater.java126
-rw-r--r--core/java/android/animation/AnimatorSet.java42
-rw-r--r--core/java/android/app/ActionBar.java48
-rw-r--r--core/java/android/app/Activity.java35
-rw-r--r--core/java/android/app/ActivityThread.java144
-rw-r--r--core/java/android/app/Dialog.java6
-rw-r--r--core/java/android/app/FragmentBreadCrumbs.java8
-rw-r--r--core/java/android/app/SearchDialog.java1018
-rw-r--r--core/java/android/app/SuggestionsAdapter.java653
-rw-r--r--core/java/android/content/ContentProviderNative.java19
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java24
-rw-r--r--core/java/android/hardware/Camera.java45
-rw-r--r--core/java/android/hardware/SensorEvent.java48
-rw-r--r--core/java/android/hardware/SensorManager.java21
-rw-r--r--core/java/android/os/Debug.java33
-rw-r--r--core/java/android/os/StrictMode.java40
-rw-r--r--core/java/android/preference/PreferenceFrameLayout.java113
-rw-r--r--core/java/android/provider/Downloads.java9
-rw-r--r--core/java/android/provider/Ptp.java28
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/text/TextLine.java21
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java10
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java52
-rw-r--r--core/java/android/text/method/TextKeyListener.java7
-rw-r--r--core/java/android/view/DragEvent.java17
-rw-r--r--core/java/android/view/HardwareRenderer.java12
-rw-r--r--core/java/android/view/KeyCharacterMap.java12
-rwxr-xr-xcore/java/android/view/KeyEvent.java74
-rw-r--r--core/java/android/view/MotionEvent.java12
-rw-r--r--core/java/android/view/SurfaceView.java8
-rw-r--r--core/java/android/view/View.java156
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/ViewRoot.java173
-rw-r--r--core/java/android/view/WindowManagerPolicy.java29
-rw-r--r--core/java/android/webkit/BrowserFrame.java8
-rw-r--r--core/java/android/webkit/CookieManager.java15
-rw-r--r--core/java/android/webkit/FindActionModeCallback.java4
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java11
-rw-r--r--core/java/android/webkit/SelectActionModeCallback.java4
-rw-r--r--core/java/android/webkit/WebView.java184
-rw-r--r--core/java/android/webkit/WebViewCore.java8
-rw-r--r--core/java/android/widget/AbsSeekBar.java34
-rw-r--r--core/java/android/widget/AbsSpinner.java4
-rw-r--r--core/java/android/widget/AbsoluteLayout.java4
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java24
-rw-r--r--core/java/android/widget/AnalogClock.java4
-rw-r--r--core/java/android/widget/ButtonGroup.java10
-rw-r--r--core/java/android/widget/FrameLayout.java7
-rw-r--r--core/java/android/widget/Gallery.java2
-rw-r--r--core/java/android/widget/GridView.java11
-rw-r--r--core/java/android/widget/ImageView.java6
-rw-r--r--core/java/android/widget/LinearLayout.java265
-rw-r--r--core/java/android/widget/ListView.java12
-rw-r--r--core/java/android/widget/OverScroller.java283
-rw-r--r--core/java/android/widget/ProgressBar.java4
-rw-r--r--core/java/android/widget/RatingBar.java3
-rw-r--r--core/java/android/widget/SearchView.java288
-rw-r--r--core/java/android/widget/Spinner.java2
-rw-r--r--core/java/android/widget/StackView.java34
-rw-r--r--core/java/android/widget/Switch.java3
-rw-r--r--core/java/android/widget/TextView.java265
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java32
-rw-r--r--core/java/com/android/internal/app/AlertController.java37
-rw-r--r--core/java/com/android/internal/os/SamplingProfilerIntegration.java69
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java80
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java2
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java76
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuView.java2
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java16
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java6
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java19
-rw-r--r--core/jni/android_app_NativeActivity.cpp33
-rw-r--r--core/jni/android_view_KeyEvent.cpp43
-rw-r--r--core/jni/android_view_KeyEvent.h16
-rw-r--r--core/jni/android_view_MotionEvent.cpp28
-rw-r--r--core/jni/android_view_MotionEvent.h14
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.pngbin0 -> 281 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_activated_holo_light.9.pngbin0 -> 279 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_default_holo_dark.9.pngbin1082 -> 281 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_default_holo_light.9.pngbin1088 -> 279 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.pngbin1081 -> 285 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.pngbin1080 -> 285 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.pngbin1069 -> 282 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.pngbin1078 -> 280 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.pngbin0 -> 284 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_focused_holo_light.9.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_longpress_holo.9.pngbin0 -> 196 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.pngbin0 -> 320 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.pngbin0 -> 314 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.pngbin1128 -> 320 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.pngbin1120 -> 313 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.pngbin1113 -> 325 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.pngbin1113 -> 324 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.pngbin1117 -> 320 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.pngbin1128 -> 313 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.pngbin0 -> 325 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.pngbin0 -> 325 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_pressed_holo.9.pngbin0 -> 192 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_control_holo.pngbin2378 -> 2763 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_primary_holo.9.pngbin0 -> 1074 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_secondary_holo.9.pngbin0 -> 1075 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.pngbin287 -> 1116 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_track_holo_light.9.pngbin291 -> 1120 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.pngbin0 -> 281 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_activated_holo_light.9.pngbin0 -> 279 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_default_holo_dark.9.pngbin1068 -> 281 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_default_holo_light.9.pngbin1071 -> 279 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.pngbin1067 -> 282 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.pngbin1069 -> 280 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.pngbin0 -> 284 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_focused_holo_light.9.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_longpress_holo.9.pngbin0 -> 196 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.pngbin0 -> 320 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.pngbin0 -> 314 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.pngbin1103 -> 232 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.pngbin1105 -> 223 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.pngbin1101 -> 320 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.pngbin1106 -> 313 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.pngbin1090 -> 320 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.pngbin1107 -> 313 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.pngbin0 -> 325 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.pngbin0 -> 325 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_pressed_holo.9.pngbin0 -> 192 bytes
-rw-r--r--core/res/res/drawable/edit_text_holo_dark.xml3
-rw-r--r--core/res/res/drawable/edit_text_holo_light.xml3
-rw-r--r--core/res/res/drawable/edit_text_multiline_holo_dark.xml3
-rw-r--r--core/res/res/drawable/edit_text_multiline_holo_light.xml3
-rw-r--r--core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml15
-rw-r--r--core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml15
-rw-r--r--core/res/res/layout-xlarge/alert_dialog_holo.xml23
-rw-r--r--core/res/res/layout-xlarge/select_dialog_holo.xml36
-rw-r--r--core/res/res/layout-xlarge/select_dialog_item_holo.xml36
-rw-r--r--core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml29
-rw-r--r--core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml29
-rw-r--r--core/res/res/layout/preference_list_content.xml2
-rw-r--r--core/res/res/layout/preference_list_fragment.xml6
-rw-r--r--core/res/res/layout/search_bar.xml95
-rw-r--r--core/res/res/layout/search_view.xml2
-rw-r--r--core/res/res/values-ar/strings.xml2
-rw-r--r--core/res/res/values-bg/strings.xml2
-rw-r--r--core/res/res/values-ca/strings.xml2
-rw-r--r--core/res/res/values-cs/strings.xml15
-rw-r--r--core/res/res/values-da/strings.xml15
-rw-r--r--core/res/res/values-de/strings.xml15
-rw-r--r--core/res/res/values-el/strings.xml15
-rw-r--r--core/res/res/values-en-rGB/strings.xml2
-rw-r--r--core/res/res/values-es-rUS/strings.xml12
-rw-r--r--core/res/res/values-es/strings.xml15
-rw-r--r--core/res/res/values-fa/strings.xml2
-rw-r--r--core/res/res/values-fi/strings.xml2
-rw-r--r--core/res/res/values-fr/strings.xml15
-rw-r--r--core/res/res/values-hr/strings.xml2
-rw-r--r--core/res/res/values-hu/strings.xml2
-rw-r--r--core/res/res/values-in-rID/arrays.xml (renamed from core/res/res/values-id-rID/arrays.xml)0
-rw-r--r--core/res/res/values-in-rID/donottranslate-cldr.xml (renamed from core/res/res/values-id-rID/donottranslate-cldr.xml)0
-rw-r--r--core/res/res/values-in/donottranslate-cldr.xml (renamed from core/res/res/values-id/donottranslate-cldr.xml)0
-rw-r--r--core/res/res/values-in/strings.xml (renamed from core/res/res/values-id/strings.xml)2
-rw-r--r--core/res/res/values-it/strings.xml15
-rw-r--r--core/res/res/values-iw-rIL/arrays.xml (renamed from core/res/res/values-he-rIL/arrays.xml)0
-rw-r--r--core/res/res/values-iw/donottranslate-cldr.xml (renamed from core/res/res/values-he/donottranslate-cldr.xml)0
-rw-r--r--core/res/res/values-iw/strings.xml (renamed from core/res/res/values-he/strings.xml)2
-rw-r--r--core/res/res/values-ja/strings.xml15
-rw-r--r--core/res/res/values-ko/strings.xml15
-rw-r--r--core/res/res/values-lt/strings.xml2
-rw-r--r--core/res/res/values-lv/strings.xml2
-rw-r--r--core/res/res/values-nb/strings.xml15
-rw-r--r--core/res/res/values-nl/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml15
-rw-r--r--core/res/res/values-pt-rPT/strings.xml15
-rw-r--r--core/res/res/values-pt/strings.xml15
-rw-r--r--core/res/res/values-rm/strings.xml2
-rw-r--r--core/res/res/values-ro/strings.xml2
-rw-r--r--core/res/res/values-ru/strings.xml15
-rw-r--r--core/res/res/values-sk/strings.xml2
-rw-r--r--core/res/res/values-sl/strings.xml2
-rw-r--r--core/res/res/values-sr/strings.xml2
-rw-r--r--core/res/res/values-sv/strings.xml15
-rw-r--r--core/res/res/values-th/strings.xml2
-rw-r--r--core/res/res/values-tl/strings.xml2
-rw-r--r--core/res/res/values-tr/strings.xml15
-rw-r--r--core/res/res/values-uk/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml2
-rw-r--r--core/res/res/values-xlarge/dimens.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml15
-rw-r--r--core/res/res/values-zh-rTW/strings.xml15
-rwxr-xr-xcore/res/res/values/attrs.xml57
-rw-r--r--core/res/res/values/public.xml5
-rwxr-xr-xcore/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/styles.xml26
-rw-r--r--core/res/res/values/themes.xml74
-rwxr-xr-xcore/tests/ConnectivityManagerTest/assets/accesspoints.xml12
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java148
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java197
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java97
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java16
-rw-r--r--data/keyboards/Vendor_046d_Product_c532.kl (renamed from data/keyboards/Logitech_USB_Receiver.kl)4
-rw-r--r--data/keyboards/Vendor_05ac_Product_0239.kl (renamed from data/keyboards/Apple_Wireless_Keyboard.kl)2
-rw-r--r--data/keyboards/Vendor_22b8_Product_093d.kl (renamed from data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl)0
-rw-r--r--data/keyboards/common.mk18
-rw-r--r--data/keyboards/keyboards.mk6
-rw-r--r--data/keyboards/qwerty.idc23
-rw-r--r--data/keyboards/qwerty.kcm4
-rw-r--r--data/keyboards/qwerty2.idc23
-rw-r--r--data/keyboards/qwerty2.kl109
-rw-r--r--docs/html/guide/topics/data/backup.jd70
-rw-r--r--docs/html/guide/topics/ui/actionbar.jd22
-rw-r--r--docs/html/resources/dashboard/platform-versions.jd36
-rw-r--r--docs/html/sdk/ndk/index.jd326
-rw-r--r--docs/html/sdk/ndk/overview.jd277
-rw-r--r--graphics/java/android/graphics/Paint.java1
-rw-r--r--include/camera/CameraParameters.h23
-rw-r--r--include/ui/EventHub.h122
-rw-r--r--include/ui/Input.h48
-rw-r--r--include/ui/InputDispatcher.h7
-rw-r--r--include/ui/InputReader.h87
-rw-r--r--include/ui/Keyboard.h49
-rw-r--r--include/ui/VirtualKeyMap.h79
-rw-r--r--include/utils/PropertyMap.h100
-rw-r--r--include/utils/String8.h11
-rw-r--r--include/utils/Tokenizer.h4
-rw-r--r--libs/camera/CameraParameters.cpp9
-rw-r--r--libs/hwui/Patch.cpp13
-rw-r--r--libs/hwui/SkiaColorFilter.cpp5
-rw-r--r--libs/ui/Android.mk1
-rw-r--r--libs/ui/EventHub.cpp598
-rw-r--r--libs/ui/Input.cpp114
-rw-r--r--libs/ui/InputDispatcher.cpp59
-rw-r--r--libs/ui/InputReader.cpp249
-rw-r--r--libs/ui/Keyboard.cpp224
-rw-r--r--libs/ui/VirtualKeyMap.cpp171
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp4
-rw-r--r--libs/ui/tests/InputReader_test.cpp251
-rw-r--r--libs/utils/Android.mk1
-rw-r--r--libs/utils/FileMap.cpp8
-rw-r--r--libs/utils/PropertyMap.cpp212
-rw-r--r--libs/utils/String8.cpp18
-rw-r--r--libs/utils/Tokenizer.cpp45
-rw-r--r--media/java/android/media/MediaFile.java1
-rw-r--r--media/java/android/media/MediaScanner.java7
-rw-r--r--media/java/android/media/MtpDatabase.java373
-rw-r--r--media/java/android/media/MtpPropertyGroup.java442
-rw-r--r--media/java/android/media/MtpPropertyList.java28
-rwxr-xr-xmedia/java/android/media/audiofx/Visualizer.java46
-rwxr-xr-xmedia/java/android/media/videoeditor/VideoEditor.java30
-rwxr-xr-xmedia/java/android/media/videoeditor/VideoEditorFactory.java53
-rw-r--r--media/java/android/media/videoeditor/VideoEditorImpl.java (renamed from media/java/android/media/videoeditor/VideoEditorTestImpl.java)14
-rw-r--r--media/jni/android_media_MtpDatabase.cpp31
-rw-r--r--media/libmedia/Visualizer.cpp8
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp20
-rw-r--r--media/libstagefright/AwesomePlayer.cpp3
-rw-r--r--media/libstagefright/MPEG4Writer.cpp5
-rw-r--r--media/libstagefright/codecs/avc/enc/AVCEncoder.cpp2
-rw-r--r--media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp2
-rw-r--r--media/libstagefright/httplive/LiveSource.cpp8
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp22
-rw-r--r--media/libstagefright/include/M3UParser.h2
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp12
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.cpp47
-rw-r--r--media/mtp/MtpDatabase.h3
-rw-r--r--media/mtp/MtpProperty.cpp2
-rw-r--r--media/mtp/MtpServer.cpp5
-rwxr-xr-xpackages/DefaultContainerService/AndroidManifest.xml1
-rw-r--r--packages/DefaultContainerService/res/values-in/strings.xml (renamed from packages/DefaultContainerService/res/values-id/strings.xml)0
-rw-r--r--packages/DefaultContainerService/res/values-iw/strings.xml (renamed from packages/DefaultContainerService/res/values-he/strings.xml)0
-rw-r--r--packages/SettingsProvider/res/values-in/strings.xml (renamed from packages/SettingsProvider/res/values-id/strings.xml)0
-rw-r--r--packages/SettingsProvider/res/values-iw/strings.xml (renamed from packages/SettingsProvider/res/values-he/strings.xml)0
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.pngbin1108 -> 1072 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.pngbin738 -> 897 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.pngbin1630 -> 2692 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.pngbin2223 -> 2920 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.pngbin958 -> 1076 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.pngbin1787 -> 2673 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.pngbin456 -> 370 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.pngbin1306 -> 1524 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.pngbin416 -> 407 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.pngbin1114 -> 1912 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.pngbin0 -> 1471 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.pngbin0 -> 4766 bytes
-rw-r--r--packages/SystemUI/res/drawable-nodpi/panel_notification.pngbin226 -> 152 bytes
-rw-r--r--packages/SystemUI/res/drawable/status_bar_toggle_button.xml23
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar.xml10
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml31
-rw-r--r--packages/SystemUI/res/layout/status_bar_toggle_slider.xml54
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml26
-rw-r--r--packages/SystemUI/res/values-da/strings.xml26
-rw-r--r--packages/SystemUI/res/values-de/strings.xml26
-rw-r--r--packages/SystemUI/res/values-el/strings.xml26
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml26
-rw-r--r--packages/SystemUI/res/values-es/strings.xml26
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml26
-rw-r--r--packages/SystemUI/res/values-in/strings.xml (renamed from packages/SystemUI/res/values-id/strings.xml)0
-rw-r--r--packages/SystemUI/res/values-it/strings.xml26
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml (renamed from packages/SystemUI/res/values-he/strings.xml)0
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml26
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml26
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml26
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml26
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml26
-rw-r--r--packages/SystemUI/res/values-xlarge/strings.xml36
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml26
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml26
-rw-r--r--packages/SystemUI/res/values/attrs.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java25
-rw-r--r--packages/VpnServices/res/values-in/strings.xml (renamed from packages/VpnServices/res/values-id/strings.xml)0
-rw-r--r--packages/VpnServices/res/values-iw/strings.xml (renamed from packages/VpnServices/res/values-he/strings.xml)0
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java7
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java86
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java46
-rw-r--r--policy/src/com/android/internal/policy/impl/ShortcutManager.java14
-rw-r--r--services/audioflinger/AudioPolicyManagerBase.cpp104
-rw-r--r--services/java/com/android/server/ConnectivityService.java18
-rw-r--r--services/java/com/android/server/InputManager.java115
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java5
-rw-r--r--services/java/com/android/server/SamplingProfilerService.java11
-rw-r--r--services/java/com/android/server/ScreenRotationAnimation.java4
-rw-r--r--services/java/com/android/server/WifiService.java2
-rw-r--r--services/java/com/android/server/WindowManagerService.java52
-rw-r--r--services/jni/com_android_server_InputManager.cpp252
-rw-r--r--services/sensorservice/GravitySensor.cpp10
-rw-r--r--services/sensorservice/GravitySensor.h4
-rw-r--r--services/sensorservice/LinearAccelerationSensor.cpp4
-rw-r--r--services/sensorservice/LinearAccelerationSensor.h1
-rw-r--r--services/sensorservice/RotationVectorSensor.cpp29
-rw-r--r--services/sensorservice/RotationVectorSensor.h2
-rw-r--r--services/sensorservice/SecondOrderLowPassFilter.cpp19
-rw-r--r--services/sensorservice/SecondOrderLowPassFilter.h12
-rw-r--r--services/sensorservice/SensorDevice.cpp22
-rw-r--r--services/sensorservice/SensorDevice.h3
-rw-r--r--services/sensorservice/SensorInterface.cpp13
-rw-r--r--services/sensorservice/SensorInterface.h3
-rw-r--r--services/surfaceflinger/Layer.cpp2
-rw-r--r--telephony/java/com/android/internal/telephony/CallManager.java30
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfo.java175
-rw-r--r--telephony/java/com/android/internal/telephony/CommandsInterface.java20
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnection.java13
-rw-r--r--telephony/java/com/android/internal/telephony/RIL.java34
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java4
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java55
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java9
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhone.java9
-rw-r--r--telephony/java/com/android/internal/telephony/test/SimulatedCommands.java10
-rw-r--r--tests/DumpRenderTree/AndroidManifest.xml4
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java26
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java65
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java86
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java18
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java258
-rw-r--r--tools/validatekeymaps/Main.cpp25
-rw-r--r--voip/java/android/net/rtp/AudioGroup.java110
-rw-r--r--voip/java/android/net/rtp/AudioStream.java28
-rw-r--r--voip/java/android/net/rtp/RtpStream.java7
-rw-r--r--voip/java/android/net/sip/SipAudioCall.java49
-rw-r--r--voip/java/android/net/sip/SipManager.java18
-rw-r--r--voip/java/com/android/server/sip/SipService.java24
-rw-r--r--voip/java/com/android/server/sip/SipSessionGroup.java39
-rw-r--r--voip/jni/rtp/AmrCodec.cpp2
-rw-r--r--voip/jni/rtp/AudioGroup.cpp18
-rw-r--r--voip/jni/rtp/EchoSuppressor.cpp21
373 files changed, 9315 insertions, 5898 deletions
diff --git a/api/current.xml b/api/current.xml
index a8193d7..8af429d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2539,6 +2539,17 @@
visibility="public"
>
</field>
+<field name="borderlessButtonStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843580"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="bottom"
type="int"
transient="false"
@@ -3562,6 +3573,28 @@
visibility="public"
>
</field>
+<field name="dividerHorizontal"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843581"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="dividerPadding"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843579"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="dividerVertical"
type="int"
transient="false"
@@ -8391,6 +8424,17 @@
visibility="public"
>
</field>
+<field name="showDividers"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843578"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="showSilent"
type="int"
transient="false"
@@ -15520,6 +15564,17 @@
visibility="public"
>
</field>
+<field name="Widget_Holo_Button_Borderless"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16974047"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget_Holo_Button_Inset"
type="int"
transient="false"
@@ -19042,6 +19097,19 @@
<parameter name="items" type="android.animation.Animator...">
</parameter>
</method>
+<method name="playSequentially"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="java.util.List&lt;android.animation.Animator&gt;">
+</parameter>
+</method>
<method name="playTogether"
return="void"
abstract="false"
@@ -19055,6 +19123,19 @@
<parameter name="items" type="android.animation.Animator...">
</parameter>
</method>
+<method name="playTogether"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="java.util.Collection&lt;android.animation.Animator&gt;">
+</parameter>
+</method>
<method name="setDuration"
return="android.animation.AnimatorSet"
abstract="false"
@@ -20723,6 +20804,19 @@
visibility="public"
>
</constructor>
+<method name="addOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
<method name="addTab"
return="void"
abstract="true"
@@ -20972,6 +21066,19 @@
visibility="public"
>
</method>
+<method name="removeOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
<method name="removeTab"
return="void"
abstract="true"
@@ -21105,7 +21212,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
</method>
<method name="setDropdownNavigationMode"
@@ -21120,7 +21227,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
<parameter name="defaultSelectedPosition" type="int">
</parameter>
@@ -21137,7 +21244,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
</method>
<method name="setNavigationMode"
@@ -21449,7 +21556,28 @@
>
</field>
</class>
-<interface name="ActionBar.NavigationCallback"
+<interface name="ActionBar.OnMenuVisibilityListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onMenuVisibilityChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
+</interface>
+<interface name="ActionBar.OnNavigationListener"
abstract="true"
static="true"
final="false"
@@ -23155,6 +23283,17 @@
<parameter name="exitAnim" type="int">
</parameter>
</method>
+<method name="recreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="registerForContextMenu"
return="void"
abstract="false"
@@ -89679,6 +89818,17 @@
visibility="public"
>
</method>
+<method name="getPreferredPreviewSizeForVideo"
+ return="android.hardware.Camera.Size"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPreviewFormat"
return="int"
abstract="false"
@@ -89868,6 +90018,17 @@
visibility="public"
>
</method>
+<method name="getSupportedVideoSizes"
+ return="java.util.List&lt;android.hardware.Camera.Size&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSupportedWhiteBalance"
return="java.util.List&lt;java.lang.String&gt;"
abstract="false"
@@ -160973,6 +161134,468 @@
>
</field>
</interface>
+<class name="Ptp"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Ptp"
+ type="android.provider.Ptp"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="AUTHORITY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ptp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Device"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Device"
+ type="android.provider.Ptp.Device"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+</method>
+<field name="CONTENT_URI"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MANUFACTURER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;manufacturer&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODEL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;model&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Object"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Object"
+ type="android.provider.Ptp.Object"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+</method>
+<method name="getContentUriForImport"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+<parameter name="destPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="getContentUriForObjectChildren"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+</method>
+<method name="getContentUriForStorageChildren"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="storageID" type="long">
+</parameter>
+</method>
+<field name="ASSOCIATION_DESC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;association_desc&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ASSOCIATION_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;association_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATE_CREATED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;date_created&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATE_MODIFIED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;date_modified&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FORMAT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;format&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_DEPTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_depth&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_HEIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_height&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_WIDTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_width&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYWORDS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;keywords&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;parent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PROTECTION_STATUS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;protection_status&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SEQUENCE_NUMBER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sequence_number&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STORAGE_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;storage_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_FORMAT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_format&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_HEIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_height&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_SIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_WIDTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_width&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Storage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Storage"
+ type="android.provider.Ptp.Storage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+</method>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="storageID" type="long">
+</parameter>
+</method>
+<field name="DESCRIPTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;description&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IDENTIFIER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;identifier&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="SearchRecentSuggestions"
extends="java.lang.Object"
abstract="false"
@@ -185610,7 +186233,7 @@
>
<parameter name="cap" type="android.text.method.TextKeyListener.Capitalize">
</parameter>
-<parameter name="autotext" type="boolean">
+<parameter name="autoText" type="boolean">
</parameter>
</constructor>
<method name="getInputType"
@@ -185634,11 +186257,22 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="autotext" type="boolean">
+<parameter name="autoText" type="boolean">
</parameter>
<parameter name="cap" type="android.text.method.TextKeyListener.Capitalize">
</parameter>
</method>
+<method name="getInstanceForFullKeyboard"
+ return="android.text.method.QwertyKeyListener"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="markAsReplaced"
return="void"
abstract="false"
@@ -194772,6 +195406,17 @@
visibility="public"
>
</method>
+<method name="getLocalState"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getResult"
return="boolean"
abstract="false"
@@ -196664,7 +197309,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -204845,6 +205490,21 @@
visibility="public"
>
</method>
+<method name="combineMeasuredStates"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="curState" type="int">
+</parameter>
+<parameter name="newState" type="int">
+</parameter>
+</method>
<method name="computeHorizontalScrollExtent"
return="int"
abstract="false"
@@ -205772,6 +206432,28 @@
visibility="public"
>
</method>
+<method name="getMeasuredHeightAndState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMeasuredState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getMeasuredWidth"
return="int"
abstract="false"
@@ -205783,6 +206465,17 @@
visibility="public"
>
</method>
+<method name="getMeasuredWidthAndState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNextFocusDownId"
return="int"
abstract="false"
@@ -207688,6 +208381,23 @@
<parameter name="measureSpec" type="int">
</parameter>
</method>
+<method name="resolveSizeAndState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="size" type="int">
+</parameter>
+<parameter name="measureSpec" type="int">
+</parameter>
+<parameter name="childMeasuredState" type="int">
+</parameter>
+</method>
<method name="restoreHierarchyState"
return="void"
abstract="false"
@@ -208773,6 +209483,8 @@
</parameter>
<parameter name="myWindowOnly" type="boolean">
</parameter>
+<parameter name="myLocalState" type="java.lang.Object">
+</parameter>
</method>
<method name="unscheduleDrawable"
return="void"
@@ -209145,6 +209857,50 @@
visibility="public"
>
</field>
+<field name="MEASURED_HEIGHT_STATE_SHIFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEASURED_SIZE_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16777215"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEASURED_STATE_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-16777216"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEASURED_STATE_TOO_SMALL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16777216"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="NO_ID"
type="int"
transient="false"
@@ -236799,6 +237555,17 @@
visibility="public"
>
</method>
+<method name="getShowDividers"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getWeightSum"
return="float"
abstract="false"
@@ -236879,6 +237646,19 @@
<parameter name="i" type="int">
</parameter>
</method>
+<method name="setDividerDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="divider" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
<method name="setGravity"
return="void"
abstract="false"
@@ -236931,6 +237711,19 @@
<parameter name="orientation" type="int">
</parameter>
</method>
+<method name="setShowDividers"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="showDividers" type="int">
+</parameter>
+</method>
<method name="setVerticalGravity"
return="void"
abstract="false"
@@ -236968,6 +237761,50 @@
visibility="public"
>
</field>
+<field name="SHOW_DIVIDER_BEGINNING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_DIVIDER_END"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_DIVIDER_MIDDLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_DIVIDER_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="VERTICAL"
type="int"
transient="false"
@@ -238734,7 +239571,35 @@
>
<parameter name="context" type="android.content.Context">
</parameter>
-<parameter name="interpolator" type="android.graphics.Interpolator">
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</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.view.animation.Interpolator">
+</parameter>
+<parameter name="bounceCoefficientX" type="float">
+</parameter>
+<parameter name="bounceCoefficientY" type="float">
+</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.view.animation.Interpolator">
</parameter>
<parameter name="bounceCoefficientX" type="float">
</parameter>
@@ -245069,19 +245934,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="drawable" type="android.graphics.drawable.Drawable">
-</parameter>
-</method>
-<method name="setDividerDrawable"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
<parameter name="resId" type="int">
</parameter>
</method>
@@ -245879,6 +246731,17 @@
visibility="public"
>
</method>
+<method name="getCustomSelectionActionModeCallback"
+ return="android.view.ActionMode.Callback"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDefaultEditable"
return="boolean"
abstract="false"
@@ -246677,6 +247540,19 @@
<parameter name="visible" type="boolean">
</parameter>
</method>
+<method name="setCustomSelectionActionModeCallback"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="actionModeCallback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
<method name="setEditableFactory"
return="void"
abstract="false"
@@ -249356,6 +250232,25 @@
</package>
<package name="dalvik.bytecode"
>
+<class name="OpcodeInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="MAXIMUM_VALUE"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<interface name="Opcodes"
abstract="true"
static="false"
@@ -249711,7 +250606,7 @@
value="236"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250052,7 +250947,7 @@
value="238"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250063,7 +250958,7 @@
value="239"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250360,7 +251255,7 @@
value="244"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250371,7 +251266,7 @@
value="242"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250404,7 +251299,7 @@
value="243"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250415,7 +251310,7 @@
value="232"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250514,7 +251409,7 @@
value="240"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250591,7 +251486,7 @@
value="250"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250602,7 +251497,7 @@
value="251"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250635,7 +251530,7 @@
value="248"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250646,7 +251541,7 @@
value="249"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250723,7 +251618,7 @@
value="247"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250734,7 +251629,7 @@
value="245"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250767,7 +251662,7 @@
value="246"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -250778,7 +251673,7 @@
value="233"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -251526,7 +252421,7 @@
value="234"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -251735,7 +252630,7 @@
value="235"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -251845,7 +252740,7 @@
value="237"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -334572,7 +335467,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="minSize" type="int">
+<parameter name="numElements" type="int">
</parameter>
</constructor>
<constructor name="ArrayDeque"
@@ -334838,7 +335733,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="obj" type="java.lang.Object">
+<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="removeLast"
@@ -334862,7 +335757,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="obj" type="java.lang.Object">
+<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="size"
@@ -339620,6 +340515,19 @@
>
<implements name="java.util.Queue">
</implements>
+<method name="add"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="e" type="E">
+</parameter>
+</method>
<method name="addFirst"
return="void"
abstract="true"
@@ -339646,6 +340554,19 @@
<parameter name="e" type="E">
</parameter>
</method>
+<method name="contains"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
<method name="descendingIterator"
return="java.util.Iterator&lt;E&gt;"
abstract="true"
@@ -339657,6 +340578,17 @@
visibility="public"
>
</method>
+<method name="element"
+ return="E"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getFirst"
return="E"
abstract="true"
@@ -339679,6 +340611,30 @@
visibility="public"
>
</method>
+<method name="iterator"
+ return="java.util.Iterator&lt;E&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="offer"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="e" type="E">
+</parameter>
+</method>
<method name="offerFirst"
return="boolean"
abstract="true"
@@ -339705,6 +340661,17 @@
<parameter name="e" type="E">
</parameter>
</method>
+<method name="peek"
+ return="E"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="peekFirst"
return="E"
abstract="true"
@@ -339727,6 +340694,17 @@
visibility="public"
>
</method>
+<method name="poll"
+ return="E"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="pollFirst"
return="E"
abstract="true"
@@ -339773,6 +340751,30 @@
<parameter name="e" type="E">
</parameter>
</method>
+<method name="remove"
+ return="E"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="remove"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
<method name="removeFirst"
return="E"
abstract="true"
@@ -339821,6 +340823,17 @@
<parameter name="o" type="java.lang.Object">
</parameter>
</method>
+<method name="size"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
</interface>
<class name="Dictionary"
extends="java.lang.Object"
@@ -343745,11 +344758,24 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="endKey" type="K">
+<parameter name="toKey" type="K">
</parameter>
<parameter name="inclusive" type="boolean">
</parameter>
</method>
+<method name="headMap"
+ return="java.util.SortedMap&lt;K, V&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="toKey" type="K">
+</parameter>
+</method>
<method name="higherEntry"
return="java.util.Map.Entry&lt;K, V&gt;"
abstract="true"
@@ -343856,13 +344882,28 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="startKey" type="K">
+<parameter name="fromKey" type="K">
</parameter>
-<parameter name="startInclusive" type="boolean">
+<parameter name="fromInclusive" type="boolean">
</parameter>
-<parameter name="endKey" type="K">
+<parameter name="toKey" type="K">
</parameter>
-<parameter name="endInclusive" type="boolean">
+<parameter name="toInclusive" type="boolean">
+</parameter>
+</method>
+<method name="subMap"
+ return="java.util.SortedMap&lt;K, V&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fromKey" type="K">
+</parameter>
+<parameter name="toKey" type="K">
</parameter>
</method>
<method name="tailMap"
@@ -343875,11 +344916,24 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="startKey" type="K">
+<parameter name="fromKey" type="K">
</parameter>
<parameter name="inclusive" type="boolean">
</parameter>
</method>
+<method name="tailMap"
+ return="java.util.SortedMap&lt;K, V&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fromKey" type="K">
+</parameter>
+</method>
</interface>
<interface name="NavigableSet"
abstract="true"
@@ -343948,9 +345002,22 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="end" type="E">
+<parameter name="toElement" type="E">
</parameter>
-<parameter name="endInclusive" type="boolean">
+<parameter name="inclusive" type="boolean">
+</parameter>
+</method>
+<method name="headSet"
+ return="java.util.SortedSet&lt;E&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="toElement" type="E">
</parameter>
</method>
<method name="higher"
@@ -343966,6 +345033,17 @@
<parameter name="e" type="E">
</parameter>
</method>
+<method name="iterator"
+ return="java.util.Iterator&lt;E&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="lower"
return="E"
abstract="true"
@@ -344011,13 +345089,28 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="start" type="E">
+<parameter name="fromElement" type="E">
</parameter>
-<parameter name="startInclusive" type="boolean">
+<parameter name="fromInclusive" type="boolean">
</parameter>
-<parameter name="end" type="E">
+<parameter name="toElement" type="E">
</parameter>
-<parameter name="endInclusive" type="boolean">
+<parameter name="toInclusive" type="boolean">
+</parameter>
+</method>
+<method name="subSet"
+ return="java.util.SortedSet&lt;E&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fromElement" type="E">
+</parameter>
+<parameter name="toElement" type="E">
</parameter>
</method>
<method name="tailSet"
@@ -344030,9 +345123,22 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="start" type="E">
+<parameter name="fromElement" type="E">
</parameter>
-<parameter name="startInclusive" type="boolean">
+<parameter name="inclusive" type="boolean">
+</parameter>
+</method>
+<method name="tailSet"
+ return="java.util.SortedSet&lt;E&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fromElement" type="E">
</parameter>
</method>
</interface>
@@ -344694,6 +345800,19 @@
>
<implements name="java.util.Collection">
</implements>
+<method name="add"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="e" type="E">
+</parameter>
+</method>
<method name="element"
return="E"
abstract="true"
@@ -344715,7 +345834,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="peek"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 040421a..f62db1c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -341,9 +341,11 @@ public final class Pm {
if (nonLocalized != null) {
return nonLocalized.toString();
}
- Resources r = getResources(pii);
- if (r != null) {
- return r.getString(res);
+ if (res != 0) {
+ Resources r = getResources(pii);
+ if (r != null) {
+ return r.getString(res);
+ }
}
return null;
}
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index b96391a..6e589e4 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -21,6 +21,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.content.res.Resources.NotFoundException;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.util.Xml;
import android.view.animation.AnimationUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -195,72 +196,95 @@ public class AnimatorInflater {
anim = new ValueAnimator();
}
TypeEvaluator evaluator = null;
- boolean hasFrom = a.hasValue(com.android.internal.R.styleable.Animator_valueFrom);
- boolean hasTo = a.hasValue(com.android.internal.R.styleable.Animator_valueTo);
-
- switch (valueType) {
-
- case VALUE_TYPE_FLOAT: {
- float valueFrom;
- float valueTo;
- if (hasFrom) {
- valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- if (hasTo) {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setFloatValues(valueFrom, valueTo);
+
+ int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom;
+ int valueToIndex = com.android.internal.R.styleable.Animator_valueTo;
+
+ boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
+
+ TypedValue tvFrom = a.peekValue(valueFromIndex);
+ boolean hasFrom = (tvFrom != null);
+ int fromType = hasFrom ? tvFrom.type : 0;
+ TypedValue tvTo = a.peekValue(valueToIndex);
+ boolean hasTo = (tvTo != null);
+ int toType = hasTo ? tvTo.type : 0;
+
+ if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
+ (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
+ // special case for colors: ignore valueType and get ints
+ getFloats = false;
+ anim.setEvaluator(new RGBEvaluator());
+ }
+
+ if (getFloats) {
+ float valueFrom;
+ float valueTo;
+ if (hasFrom) {
+ if (fromType == TypedValue.TYPE_DIMENSION) {
+ valueFrom = a.getDimension(valueFromIndex, 0f);
+ } else {
+ valueFrom = a.getFloat(valueFromIndex, 0f);
+ }
+ if (hasTo) {
+ if (toType == TypedValue.TYPE_DIMENSION) {
+ valueTo = a.getDimension(valueToIndex, 0f);
} else {
- anim.setFloatValues(valueFrom);
+ valueTo = a.getFloat(valueToIndex, 0f);
}
+ anim.setFloatValues(valueFrom, valueTo);
+ } else {
+ anim.setFloatValues(valueFrom);
+ }
+ } else {
+ if (toType == TypedValue.TYPE_DIMENSION) {
+ valueTo = a.getDimension(valueToIndex, 0f);
} else {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setFloatValues(valueTo);
+ valueTo = a.getFloat(valueToIndex, 0f);
}
+ anim.setFloatValues(valueTo);
}
- break;
-
- case VALUE_TYPE_COLOR:
- evaluator = new RGBEvaluator();
- anim.setEvaluator(evaluator);
- // fall through to pick up values
- case VALUE_TYPE_INT: {
- int valueFrom;
- int valueTo;
- if (hasFrom) {
- valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0);
- if (hasTo) {
- valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
- anim.setIntValues(valueFrom, valueTo);
+ } else {
+ int valueFrom;
+ int valueTo;
+ if (hasFrom) {
+ if (fromType == TypedValue.TYPE_DIMENSION) {
+ valueFrom = (int) a.getDimension(valueFromIndex, 0f);
+ } else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
+ valueFrom = a.getColor(valueFromIndex, 0);
+ } else {
+ valueFrom = a.getInt(valueFromIndex, 0);
+ }
+ if (hasTo) {
+ if (toType == TypedValue.TYPE_DIMENSION) {
+ valueTo = (int) a.getDimension(valueToIndex, 0f);
+ } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
+ valueTo = a.getColor(valueToIndex, 0);
} else {
- anim.setIntValues(valueFrom);
+ valueTo = a.getInt(valueToIndex, 0);
}
+ anim.setIntValues(valueFrom, valueTo);
} else {
- valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
- anim.setIntValues(valueTo);
+ anim.setIntValues(valueFrom);
}
- }
- break;
-
- case VALUE_TYPE_CUSTOM: {
- // TODO: How to get an 'Object' value?
- float valueFrom;
- float valueTo;
- if (hasFrom) {
- valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- if (hasTo) {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setFloatValues(valueFrom, valueTo);
+ } else {
+ if (hasTo) {
+ if (toType == TypedValue.TYPE_DIMENSION) {
+ valueTo = (int) a.getDimension(valueToIndex, 0f);
+ } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
+ valueTo = a.getColor(valueToIndex, 0);
} else {
- anim.setFloatValues(valueFrom);
+ valueTo = a.getInt(valueToIndex, 0);
}
- } else {
- valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
- anim.setFloatValues(valueTo);
+ anim.setIntValues(valueTo);
}
}
- break;
}
-
anim.setDuration(duration);
anim.setStartDelay(startDelay);
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index f5420d1..154e084 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -17,7 +17,9 @@
package android.animation;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
/**
* This class plays a set of {@link Animator} objects in the specified order. Animations
@@ -117,10 +119,29 @@ public final class AnimatorSet extends Animator {
}
/**
+ * Sets up this AnimatorSet to play all of the supplied animations at the same time.
+ *
+ * @param items The animations that will be started simultaneously.
+ */
+ public void playTogether(Collection<Animator> items) {
+ if (items != null && items.size() > 0) {
+ mNeedsSort = true;
+ Builder builder = null;
+ for (Animator anim : items) {
+ if (builder == null) {
+ builder = play(anim);
+ } else {
+ builder.with(anim);
+ }
+ }
+ }
+ }
+
+ /**
* Sets up this AnimatorSet to play each of the supplied animations when the
* previous animation ends.
*
- * @param items The aniamtions that will be started one after another.
+ * @param items The animations that will be started one after another.
*/
public void playSequentially(Animator... items) {
if (items != null) {
@@ -136,6 +157,25 @@ public final class AnimatorSet extends Animator {
}
/**
+ * Sets up this AnimatorSet to play each of the supplied animations when the
+ * previous animation ends.
+ *
+ * @param items The animations that will be started one after another.
+ */
+ public void playSequentially(List<Animator> items) {
+ if (items != null && items.size() > 0) {
+ mNeedsSort = true;
+ if (items.size() == 1) {
+ play(items.get(0));
+ } else {
+ for (int i = 0; i < items.size() - 1; ++i) {
+ play(items.get(i)).before(items.get(i+1));
+ }
+ }
+ }
+ }
+
+ /**
* Returns the current list of child Animator objects controlled by this
* AnimatorSet. This is a copy of the internal list; modifications to the returned list
* will not affect the AnimatorSet, although changes to the underlying Animator objects
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 7a6ad0f..2f69520 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -168,13 +168,13 @@ public abstract class ActionBar {
* @param adapter An adapter that will provide views both to display
* the current navigation selection and populate views
* within the dropdown navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback A OnNavigationListener that will receive events when the user
* selects a navigation item.
* @deprecated See setListNavigationCallbacks.
*/
@Deprecated
public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
- NavigationCallback callback);
+ OnNavigationListener callback);
/**
* Set the adapter and navigation callback for list navigation mode.
@@ -182,17 +182,17 @@ public abstract class ActionBar {
* The supplied adapter will provide views for the expanded list as well as
* the currently selected item. (These may be displayed differently.)
*
- * The supplied NavigationCallback will alert the application when the user
+ * The supplied OnNavigationListener will alert the application when the user
* changes the current list selection.
*
* @param adapter An adapter that will provide views both to display
* the current navigation selection and populate views
* within the dropdown navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback An OnNavigationListener that will receive events when the user
* selects a navigation item.
*/
public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
- NavigationCallback callback);
+ OnNavigationListener callback);
/**
* Set the action bar into dropdown navigation mode and supply an adapter that will
@@ -201,7 +201,7 @@ public abstract class ActionBar {
* @param adapter An adapter that will provide views both to display the current
* navigation selection and populate views within the dropdown
* navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback A OnNavigationListener that will receive events when the user
* selects a navigation item.
* @param defaultSelectedPosition Position within the provided adapter that should be
* selected from the outset.
@@ -209,7 +209,7 @@ public abstract class ActionBar {
*/
@Deprecated
public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
- NavigationCallback callback, int defaultSelectedPosition);
+ OnNavigationListener callback, int defaultSelectedPosition);
/**
* Set the selected navigation item in list or tabbed navigation modes.
@@ -532,9 +532,24 @@ public abstract class ActionBar {
public abstract boolean isShowing();
/**
- * Callback interface for ActionBar navigation events.
+ * Add a listener that will respond to menu visibility change events.
+ *
+ * @param listener The new listener to add
+ */
+ public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Remove a menu visibility listener. This listener will no longer receive menu
+ * visibility change events.
+ *
+ * @param listener A listener to remove that was previously added
+ */
+ public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Listener interface for ActionBar navigation events.
*/
- public interface NavigationCallback {
+ public interface OnNavigationListener {
/**
* This method is called whenever a navigation item in your action bar
* is selected.
@@ -547,6 +562,21 @@ public abstract class ActionBar {
}
/**
+ * Listener for receiving events when action bar menus are shown or hidden.
+ */
+ public interface OnMenuVisibilityListener {
+ /**
+ * Called when an action bar menu is shown or hidden. Applications may want to use
+ * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
+ * gameplay, or other activity within the main content area.
+ *
+ * @param isVisible True if an action bar menu is now visible, false if no action bar
+ * menus are visible.
+ */
+ public void onMenuVisibilityChanged(boolean isVisible);
+ }
+
+ /**
* A tab in the action bar.
*
* <p>Tabs manage the hiding and showing of {@link Fragment}s.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d69a179..a0a6b42 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -42,6 +42,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.Selection;
@@ -78,6 +79,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* An activity is a single, focused thing that the user can do. Almost all
@@ -842,8 +844,6 @@ public class Activity extends ContextThemeWrapper
* @see #onPostCreate
*/
protected void onCreate(Bundle savedInstanceState) {
- mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, false);
if (mLastNonConfigurationInstances != null) {
mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
}
@@ -2362,6 +2362,13 @@ public class Activity extends ContextThemeWrapper
* @return The default implementation returns true.
*/
public boolean onMenuOpened(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ if (mActionBar != null) {
+ mActionBar.dispatchMenuVisibilityChanged(true);
+ } else {
+ Log.e(TAG, "Tried to open action bar menu with no action bar");
+ }
+ }
return true;
}
@@ -2392,7 +2399,7 @@ public class Activity extends ContextThemeWrapper
return true;
}
return mFragments.dispatchContextItemSelected(item);
-
+
default:
return false;
}
@@ -2417,6 +2424,10 @@ public class Activity extends ContextThemeWrapper
case Window.FEATURE_CONTEXT_MENU:
onContextMenuClosed(menu);
break;
+
+ case Window.FEATURE_ACTION_BAR:
+ mActionBar.dispatchMenuVisibilityChanged(false);
+ break;
}
}
@@ -3502,6 +3513,22 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Cause this Activity to be recreated with a new instance. This results
+ * in essentially the same flow as when the Activity is created due to
+ * a configuration change -- the current instance will go through its
+ * lifecycle to {@link #onDestroy} and a new instance then created after it.
+ */
+ public void recreate() {
+ if (mParent != null) {
+ throw new IllegalStateException("Can only be called on top-level activity");
+ }
+ if (Looper.myLooper() != mMainThread.getLooper()) {
+ throw new IllegalStateException("Must be called from main thread");
+ }
+ mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
+ }
+
+ /**
* Call this when your activity is done and should be closed. The
* ActivityResult is propagated back to whoever launched you via
* onActivityResult().
@@ -4255,6 +4282,8 @@ public class Activity extends ContextThemeWrapper
final void performCreate(Bundle icicle) {
onCreate(icicle);
+ mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
+ com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c0714e3..a8f08c2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -224,6 +224,11 @@ public final class ActivityThread {
boolean startsNotResumed;
boolean isForward;
+ int pendingConfigChanges;
+ boolean onlyLocalRequest;
+
+ View mPendingRemoveWindow;
+ WindowManager mPendingRemoveWindowManager;
ActivityClientRecord() {
parent = null;
@@ -444,19 +449,8 @@ public final class ActivityThread {
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config) {
- ActivityClientRecord r = new ActivityClientRecord();
-
- r.token = token;
- r.pendingResults = pendingResults;
- r.pendingIntents = pendingNewIntents;
- r.startsNotResumed = notResumed;
- r.createdConfig = config;
-
- synchronized (mPackages) {
- mRelaunchingActivities.add(r);
- }
-
- queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
+ requestRelaunchActivity(token, pendingResults, pendingNewIntents,
+ configChanges, notResumed, config, true);
}
public final void scheduleNewIntent(List<Intent> intents, IBinder token) {
@@ -981,7 +975,7 @@ public final class ActivityThread {
} break;
case RELAUNCH_ACTIVITY: {
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
- handleRelaunchActivity(r, msg.arg1);
+ handleRelaunchActivity(r);
} break;
case PAUSE_ACTIVITY:
handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
@@ -2183,6 +2177,19 @@ public final class ActivityThread {
return r;
}
+ final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
+ if (r.mPendingRemoveWindow != null) {
+ r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
+ IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
+ if (wtoken != null) {
+ WindowManagerImpl.getDefault().closeAll(wtoken,
+ r.activity.getClass().getName(), "Activity");
+ }
+ }
+ r.mPendingRemoveWindow = null;
+ r.mPendingRemoveWindowManager = null;
+ }
+
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
@@ -2235,6 +2242,9 @@ public final class ActivityThread {
r.hideForNow = true;
}
+ // Get rid of anything left hanging around.
+ cleanUpPendingRemoveWindows(r);
+
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
@@ -2267,11 +2277,14 @@ public final class ActivityThread {
}
}
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- if (localLOGV) Slog.v(
- TAG, "Scheduling idle handler for " + r);
- Looper.myQueue().addIdleHandler(new Idler());
+ if (!r.onlyLocalRequest) {
+ r.nextIdle = mNewActivities;
+ mNewActivities = r;
+ if (localLOGV) Slog.v(
+ TAG, "Scheduling idle handler for " + r);
+ Looper.myQueue().addIdleHandler(new Idler());
+ }
+ r.onlyLocalRequest = false;
} else {
// If an exception was thrown when trying to resume, then
@@ -2728,6 +2741,7 @@ public final class ActivityThread {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
+ cleanUpPendingRemoveWindows(r);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
@@ -2736,16 +2750,31 @@ public final class ActivityThread {
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
- wm.removeViewImmediate(v);
+ if (r.onlyLocalRequest) {
+ // Hold off on removing this until the new activity's
+ // window is being added.
+ r.mPendingRemoveWindow = v;
+ r.mPendingRemoveWindowManager = wm;
+ } else {
+ wm.removeViewImmediate(v);
+ }
}
- if (wtoken != null) {
+ if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerImpl.getDefault().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
- WindowManagerImpl.getDefault().closeAll(token,
- r.activity.getClass().getName(), "Activity");
+ if (r.mPendingRemoveWindow == null) {
+ // If we are delaying the removal of the activity window, then
+ // we can't clean up all windows here. Note that we can't do
+ // so later either, which means any windows that aren't closed
+ // by the app will leak. Well we try to warning them a lot
+ // about leaking windows, because that is a bug, so if they are
+ // using this recreate facility then they get to live with leaks.
+ WindowManagerImpl.getDefault().closeAll(token,
+ r.activity.getClass().getName(), "Activity");
+ }
// Mocked out contexts won't be participating in the normal
// process lifecycle, but if we're running with a proper
@@ -2766,17 +2795,70 @@ public final class ActivityThread {
}
}
- private final void handleRelaunchActivity(ActivityClientRecord tmp, int configChanges) {
+ public final void requestRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
+ int configChanges, boolean notResumed, Configuration config,
+ boolean fromServer) {
+ ActivityClientRecord target = null;
+
+ synchronized (mPackages) {
+ for (int i=0; i<mRelaunchingActivities.size(); i++) {
+ ActivityClientRecord r = mRelaunchingActivities.get(i);
+ if (r.token == token) {
+ target = r;
+ if (pendingResults != null) {
+ if (r.pendingResults != null) {
+ r.pendingResults.addAll(pendingResults);
+ } else {
+ r.pendingResults = pendingResults;
+ }
+ }
+ if (pendingNewIntents != null) {
+ if (r.pendingIntents != null) {
+ r.pendingIntents.addAll(pendingNewIntents);
+ } else {
+ r.pendingIntents = pendingNewIntents;
+ }
+ }
+ break;
+ }
+ }
+
+ if (target == null) {
+ target = new ActivityClientRecord();
+ target.token = token;
+ target.pendingResults = pendingResults;
+ target.pendingIntents = pendingNewIntents;
+ if (!fromServer) {
+ ActivityClientRecord existing = mActivities.get(token);
+ if (existing != null) {
+ target.startsNotResumed = existing.paused;
+ }
+ target.onlyLocalRequest = true;
+ }
+ mRelaunchingActivities.add(target);
+ queueOrSendMessage(H.RELAUNCH_ACTIVITY, target);
+ }
+
+ if (fromServer) {
+ target.startsNotResumed = notResumed;
+ target.onlyLocalRequest = false;
+ }
+ if (config != null) {
+ target.createdConfig = config;
+ }
+ target.pendingConfigChanges |= configChanges;
+ }
+ }
+
+ private final void handleRelaunchActivity(ActivityClientRecord tmp) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
Configuration changedConfig = null;
+ int configChanges = 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity "
- + tmp.token + " with configChanges=0x"
- + Integer.toHexString(configChanges));
-
// First: make sure we have the most recent configuration and most
// recent version of the activity, or skip it if some previous call
// had taken a more recent version.
@@ -2788,6 +2870,7 @@ public final class ActivityThread {
ActivityClientRecord r = mRelaunchingActivities.get(i);
if (r.token == token) {
tmp = r;
+ configChanges |= tmp.pendingConfigChanges;
mRelaunchingActivities.remove(i);
i--;
N--;
@@ -2799,6 +2882,10 @@ public final class ActivityThread {
return;
}
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity "
+ + tmp.token + " with configChanges=0x"
+ + Integer.toHexString(configChanges));
+
if (mPendingConfiguration != null) {
changedConfig = mPendingConfiguration;
mPendingConfiguration = null;
@@ -2834,6 +2921,7 @@ public final class ActivityThread {
}
r.activity.mConfigChangeFlags |= configChanges;
+ r.onlyLocalRequest = tmp.onlyLocalRequest;
Intent currentIntent = r.activity.mIntent;
Bundle savedState = null;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 64a4d7a..f90fc59 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -735,6 +735,9 @@ public class Dialog implements DialogInterface, Window.Callback,
* @see Activity#onMenuOpened(int, Menu)
*/
public boolean onMenuOpened(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ mActionBar.dispatchMenuVisibilityChanged(true);
+ }
return true;
}
@@ -749,6 +752,9 @@ public class Dialog implements DialogInterface, Window.Callback,
* @see Activity#onPanelClosed(int, Menu)
*/
public void onPanelClosed(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ mActionBar.dispatchMenuVisibilityChanged(false);
+ }
}
/**
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index e924c1c..139095f 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -110,6 +110,7 @@ public class FragmentBreadCrumbs extends ViewGroup
int maxHeight = 0;
int maxWidth = 0;
+ int measuredChildState = 0;
// Find rightmost and bottom-most child
for (int i = 0; i < count; i++) {
@@ -118,6 +119,8 @@ public class FragmentBreadCrumbs extends ViewGroup
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ measuredChildState = combineMeasuredStates(measuredChildState,
+ child.getMeasuredState());
}
}
@@ -129,8 +132,9 @@ public class FragmentBreadCrumbs extends ViewGroup
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
- resolveSize(maxHeight, heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, measuredChildState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ measuredChildState<<MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 0c280f9..4d19b62 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -17,12 +17,6 @@
package android.app;
-import static android.app.SuggestionsAdapter.getColumnString;
-
-import java.util.WeakHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -30,21 +24,15 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
-import android.provider.Browser;
import android.speech.RecognizerIntent;
-import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
-import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -55,18 +43,15 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.ListView;
+import android.widget.SearchView;
import android.widget.TextView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
+
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Search dialog. This is controlled by the
@@ -74,32 +59,28 @@ import android.widget.AdapterView.OnItemSelectedListener;
*
* @hide
*/
-public class SearchDialog extends Dialog implements OnItemClickListener, OnItemSelectedListener {
+public class SearchDialog extends Dialog {
// Debugging support
private static final boolean DBG = false;
private static final String LOG_TAG = "SearchDialog";
- private static final boolean DBG_LOG_TIMING = false;
private static final String INSTANCE_KEY_COMPONENT = "comp";
private static final String INSTANCE_KEY_APPDATA = "data";
- private static final String INSTANCE_KEY_STORED_APPDATA = "sData";
private static final String INSTANCE_KEY_USER_QUERY = "uQry";
-
+
// The string used for privateImeOptions to identify to the IME that it should not show
// a microphone button since one already exists in the search dialog.
private static final String IME_OPTION_NO_MICROPHONE = "nm";
- private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
// views & widgets
private TextView mBadgeLabel;
private ImageView mAppIcon;
- private SearchAutoComplete mSearchAutoComplete;
- private Button mGoButton;
- private ImageButton mVoiceButton;
+ private AutoCompleteTextView mSearchAutoComplete;
private View mSearchPlate;
+ private SearchView mSearchView;
private Drawable mWorkingSpinner;
// interaction with searchable application
@@ -107,30 +88,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private ComponentName mLaunchComponent;
private Bundle mAppSearchData;
private Context mActivityContext;
- private SearchManager mSearchManager;
// For voice searching
private final Intent mVoiceWebSearchIntent;
private final Intent mVoiceAppSearchIntent;
- // support for AutoCompleteTextView suggestions display
- private SuggestionsAdapter mSuggestionsAdapter;
-
- // Whether to rewrite queries when selecting suggestions
- private static final boolean REWRITE_QUERIES = true;
-
// The query entered by the user. This is not changed when selecting a suggestion
// that modifies the contents of the text field. But if the user then edits
// the suggestion, the resulting string is saved.
private String mUserQuery;
- // The query passed in when opening the SearchDialog. Used in the browser
- // case to determine whether the user has edited the query.
- private String mInitialQuery;
-
- // A weak map of drawables we've gotten from other packages, so we don't load them
- // more than once.
- private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
- new WeakHashMap<String, Drawable.ConstantState>();
// Last known IME options value for the search edit text.
private int mSearchAutoCompleteImeOptions;
@@ -160,7 +126,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mSearchManager = searchManager;
}
/**
@@ -196,30 +161,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// get the view elements for local access
SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar);
searchBar.setSearchDialog(this);
-
- mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
- mSearchAutoComplete = (SearchAutoComplete)
- findViewById(com.android.internal.R.id.search_src_text);
+ mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view);
+ mSearchView.setSubmitButtonEnabled(true);
+ mSearchView.setOnCloseListener(mOnCloseListener);
+ mSearchView.setOnQueryChangeListener(mOnQueryChangeListener);
+ mSearchView.setOnSuggestionSelectionListener(mOnSuggestionSelectionListener);
+
+ // TODO: Move the badge logic to SearchView or move the badge to search_bar.xml
+ mBadgeLabel = (TextView) mSearchView.findViewById(com.android.internal.R.id.search_badge);
+ mSearchAutoComplete = (AutoCompleteTextView)
+ mSearchView.findViewById(com.android.internal.R.id.search_src_text);
mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
- mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
- mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
- mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
+ mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate);
mWorkingSpinner = getContext().getResources().
getDrawable(com.android.internal.R.drawable.search_spinner);
mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
null, null, mWorkingSpinner, null);
setWorking(false);
- // attach listeners
- mSearchAutoComplete.addTextChangedListener(mTextWatcher);
- mSearchAutoComplete.setOnKeyListener(mTextKeyListener);
- mSearchAutoComplete.setOnItemClickListener(this);
- mSearchAutoComplete.setOnItemSelectedListener(this);
- mGoButton.setOnClickListener(mGoButtonClickListener);
- mGoButton.setOnKeyListener(mButtonsKeyListener);
- mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
- mVoiceButton.setOnKeyListener(mButtonsKeyListener);
-
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
@@ -256,7 +215,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return false;
}
- mInitialQuery = initialQuery == null ? "" : initialQuery;
// finally, load the user's initial text (which may trigger suggestions)
setUserQuery(initialQuery);
if (selectInitialQuery) {
@@ -296,6 +254,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// Recreate the search bar view every time the dialog is shown, to get rid
// of any bad state in the AutoCompleteTextView etc
createContentView();
+ mSearchView.setSearchableInfo(mSearchable);
show();
}
@@ -326,14 +285,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
getContext().unregisterReceiver(mConfChangeListener);
- closeSuggestionsAdapter();
-
// dump extra memory we're hanging on to
mLaunchComponent = null;
mAppSearchData = null;
mSearchable = null;
mUserQuery = null;
- mInitialQuery = null;
}
/**
@@ -349,20 +305,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * Closes and gets rid of the suggestions adapter.
- */
- private void closeSuggestionsAdapter() {
- // remove the adapter from the autocomplete first, to avoid any updates
- // when we drop the cursor
- mSearchAutoComplete.setAdapter((SuggestionsAdapter)null);
- // close any leftover cursor
- if (mSuggestionsAdapter != null) {
- mSuggestionsAdapter.close();
- }
- mSuggestionsAdapter = null;
- }
-
- /**
* Save the minimal set of data necessary to recreate the search
*
* @return A bundle with the state of the dialog, or {@code null} if the search
@@ -385,8 +327,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
/**
* Restore the state of the dialog from a previously saved bundle.
*
- * TODO: go through this and make sure that it saves everything that is saved
- *
* @param savedInstanceState The state of the dialog previously saved by
* {@link #onSaveInstanceState()}.
*/
@@ -404,21 +344,18 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return;
}
}
-
+
/**
* Called after resources have changed, e.g. after screen rotation or locale change.
*/
public void onConfigurationChanged() {
if (mSearchable != null && isShowing()) {
// Redraw (resources may have changed)
- updateSearchButton();
updateSearchAppIcon();
updateSearchBadge();
- updateQueryHint();
if (isLandscapeMode(getContext())) {
mSearchAutoComplete.ensureImeVisible(true);
}
- mSearchAutoComplete.showDropDownAfterLayout();
}
}
@@ -434,11 +371,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (mSearchable != null) {
mDecor.setVisibility(View.VISIBLE);
updateSearchAutoComplete();
- updateSearchButton();
updateSearchAppIcon();
updateSearchBadge();
- updateQueryHint();
- updateVoiceButton(TextUtils.isEmpty(mUserQuery));
// In order to properly configure the input method (if one is being used), we
// need to let it know if we'll be providing suggestions. Although it would be
@@ -474,68 +408,26 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* Updates the auto-complete text view.
*/
private void updateSearchAutoComplete() {
- // close any existing suggestions adapter
- closeSuggestionsAdapter();
-
- mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation
- mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold());
// we dismiss the entire dialog instead
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
-
- mSearchAutoComplete.setForceIgnoreOutsideTouch(true);
-
- // attach the suggestions adapter, if suggestions are available
- // The existence of a suggestions authority is the proxy for "suggestions available here"
- if (mSearchable.getSuggestAuthority() != null) {
- mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable,
- mOutsideDrawablesCache);
- mSearchAutoComplete.setAdapter(mSuggestionsAdapter);
- }
- }
-
- private void updateSearchButton() {
- String textLabel = null;
- Drawable iconLabel = null;
- int textId = mSearchable.getSearchButtonText();
- if (isBrowserSearch()){
- iconLabel = getContext().getResources()
- .getDrawable(com.android.internal.R.drawable.ic_btn_search_go);
- } else if (textId != 0) {
- textLabel = mActivityContext.getResources().getString(textId);
- } else {
- iconLabel = getContext().getResources().
- getDrawable(com.android.internal.R.drawable.ic_btn_search);
- }
- mGoButton.setText(textLabel);
- mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null);
+ mSearchAutoComplete.setForceIgnoreOutsideTouch(false);
}
private void updateSearchAppIcon() {
- if (isBrowserSearch()) {
- mAppIcon.setImageResource(0);
- mAppIcon.setVisibility(View.GONE);
- mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
- mSearchPlate.getPaddingTop(),
- mSearchPlate.getPaddingRight(),
- mSearchPlate.getPaddingBottom());
- } else {
- PackageManager pm = getContext().getPackageManager();
- Drawable icon;
- try {
- ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
- icon = pm.getApplicationIcon(info.applicationInfo);
- if (DBG) Log.d(LOG_TAG, "Using app-specific icon");
- } catch (NameNotFoundException e) {
- icon = pm.getDefaultActivityIcon();
- Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
- }
- mAppIcon.setImageDrawable(icon);
- mAppIcon.setVisibility(View.VISIBLE);
- mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL,
- mSearchPlate.getPaddingTop(),
- mSearchPlate.getPaddingRight(),
- mSearchPlate.getPaddingBottom());
+ PackageManager pm = getContext().getPackageManager();
+ Drawable icon;
+ try {
+ ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
+ icon = pm.getApplicationIcon(info.applicationInfo);
+ if (DBG)
+ Log.d(LOG_TAG, "Using app-specific icon");
+ } catch (NameNotFoundException e) {
+ icon = pm.getDefaultActivityIcon();
+ Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
}
+ mAppIcon.setImageDrawable(icon);
+ mAppIcon.setVisibility(View.VISIBLE);
+ mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), mSearchPlate.getPaddingBottom());
}
/**
@@ -546,7 +438,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
int visibility = View.GONE;
Drawable icon = null;
CharSequence text = null;
-
+
// optionally show one or the other.
if (mSearchable.useBadgeIcon()) {
icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
@@ -557,71 +449,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
visibility = View.VISIBLE;
if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
}
-
+
mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
mBadgeLabel.setText(text);
mBadgeLabel.setVisibility(visibility);
}
- /**
- * Update the hint in the query text field.
- */
- private void updateQueryHint() {
- if (isShowing()) {
- String hint = null;
- if (mSearchable != null) {
- int hintId = mSearchable.getHintId();
- if (hintId != 0) {
- hint = mActivityContext.getString(hintId);
- }
- }
- mSearchAutoComplete.setHint(hint);
- }
- }
-
- /**
- * Update the visibility of the voice button. There are actually two voice search modes,
- * either of which will activate the button.
- * @param empty whether the search query text field is empty. If it is, then the other
- * criteria apply to make the voice button visible. Otherwise the voice button will not
- * be visible - i.e., if the user has typed a query, remove the voice button.
- */
- private void updateVoiceButton(boolean empty) {
- int visibility = View.GONE;
- if ((mAppSearchData == null || !mAppSearchData.getBoolean(
- SearchManager.DISABLE_VOICE_SEARCH, false))
- && mSearchable.getVoiceSearchEnabled() && empty) {
- Intent testIntent = null;
- if (mSearchable.getVoiceSearchLaunchWebSearch()) {
- testIntent = mVoiceWebSearchIntent;
- } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
- testIntent = mVoiceAppSearchIntent;
- }
- if (testIntent != null) {
- ResolveInfo ri = getContext().getPackageManager().
- resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
- if (ri != null) {
- visibility = View.VISIBLE;
- }
- }
- }
- mVoiceButton.setVisibility(visibility);
- }
-
- /** Called by SuggestionsAdapter when the cursor contents changed. */
- void onDataSetChanged() {
- if (mSearchAutoComplete != null && mSuggestionsAdapter != null) {
- mSearchAutoComplete.onFilterComplete(mSuggestionsAdapter.getCount());
- }
- }
-
- /**
- * Hack to determine whether this is the browser, so we can adjust the UI.
- */
- private boolean isBrowserSearch() {
- return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
- }
-
/*
* Listeners of various types
*/
@@ -642,7 +475,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// Let Dialog handle events outside the window while the pop-up is showing.
return super.onTouchEvent(event);
}
-
+
private boolean isOutOfBounds(View v, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
@@ -651,336 +484,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|| (x > (v.getWidth()+slop))
|| (y > (v.getHeight()+slop));
}
-
- /**
- * Dialog's OnKeyListener implements various search-specific functionality
- *
- * @param keyCode This is the keycode of the typed key, and is the same value as
- * found in the KeyEvent parameter.
- * @param event The complete event record for the typed key
- *
- * @return Return true if the event was handled here, or false if not.
- */
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (DBG) Log.d(LOG_TAG, "onKeyDown(" + keyCode + "," + event + ")");
- if (mSearchable == null) {
- return false;
- }
-
- // if it's an action specified by the searchable activity, launch the
- // entered query with the action key
- SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
- if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
- return true;
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
- /**
- * Callback to watch the textedit field for empty/non-empty
- */
- private TextWatcher mTextWatcher = new TextWatcher() {
-
- public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
-
- public void onTextChanged(CharSequence s, int start,
- int before, int after) {
- if (DBG_LOG_TIMING) {
- dbgLogTiming("onTextChanged()");
- }
- if (mSearchable == null) {
- return;
- }
- if (!mSearchAutoComplete.isPerformingCompletion()) {
- // The user changed the query, remember it.
- mUserQuery = s == null ? "" : s.toString();
- }
- updateWidgetState();
- // Always want to show the microphone if the context is voice.
- // Also show the microphone if this is a browser search and the
- // query matches the initial query.
- updateVoiceButton(mSearchAutoComplete.isEmpty()
- || (isBrowserSearch() && mInitialQuery.equals(mUserQuery))
- || (mAppSearchData != null && mAppSearchData.getBoolean(
- SearchManager.CONTEXT_IS_VOICE)));
- }
-
- public void afterTextChanged(Editable s) {
- if (mSearchable == null) {
- return;
- }
- if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) {
- // The user changed the query, check if it is a URL and if so change the search
- // button in the soft keyboard to the 'Go' button.
- int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION))
- | EditorInfo.IME_ACTION_GO;
- if (options != mSearchAutoCompleteImeOptions) {
- mSearchAutoCompleteImeOptions = options;
- mSearchAutoComplete.setImeOptions(options);
- // This call is required to update the soft keyboard UI with latest IME flags.
- mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType());
- }
- }
- }
- };
-
- /**
- * Enable/Disable the go button based on edit text state (any text?)
- */
- private void updateWidgetState() {
- // enable the button if we have one or more non-space characters
- boolean enabled = !mSearchAutoComplete.isEmpty();
- if (isBrowserSearch()) {
- // In the browser, we hide the search button when there is no text,
- // or if the text matches the initial query.
- if (enabled && !mInitialQuery.equals(mUserQuery)) {
- mSearchAutoComplete.setBackgroundResource(
- com.android.internal.R.drawable.textfield_search);
- mGoButton.setVisibility(View.VISIBLE);
- // Just to be sure
- mGoButton.setEnabled(true);
- mGoButton.setFocusable(true);
- } else {
- mSearchAutoComplete.setBackgroundResource(
- com.android.internal.R.drawable.textfield_search_empty);
- mGoButton.setVisibility(View.GONE);
- }
- } else {
- // Elsewhere we just disable the button
- mGoButton.setEnabled(enabled);
- mGoButton.setFocusable(enabled);
- }
- }
-
- /**
- * React to typing in the GO search button by refocusing to EditText.
- * Continue typing the query.
- */
- View.OnKeyListener mButtonsKeyListener = new View.OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // guard against possible race conditions
- if (mSearchable == null) {
- return false;
- }
-
- if (!event.isSystem() &&
- (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
- (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
- (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
- (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
- // restore focus and give key to EditText ...
- if (mSearchAutoComplete.requestFocus()) {
- return mSearchAutoComplete.dispatchKeyEvent(event);
- }
- }
-
- return false;
- }
- };
-
- /**
- * React to a click in the GO button by launching a search.
- */
- View.OnClickListener mGoButtonClickListener = new View.OnClickListener() {
- public void onClick(View v) {
- // guard against possible race conditions
- if (mSearchable == null) {
- return;
- }
- launchQuerySearch();
- }
- };
-
- /**
- * React to a click in the voice search button.
- */
- View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() {
- public void onClick(View v) {
- // guard against possible race conditions
- if (mSearchable == null) {
- return;
- }
- SearchableInfo searchable = mSearchable;
- try {
- if (searchable.getVoiceSearchLaunchWebSearch()) {
- Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent,
- searchable);
- getContext().startActivity(webSearchIntent);
- } else if (searchable.getVoiceSearchLaunchRecognizer()) {
- Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
- searchable);
- getContext().startActivity(appSearchIntent);
- }
- } catch (ActivityNotFoundException e) {
- // Should not happen, since we check the availability of
- // voice search before showing the button. But just in case...
- Log.w(LOG_TAG, "Could not find voice search activity");
- }
- dismiss();
- }
- };
-
- /**
- * Create and return an Intent that can launch the voice search activity for web search.
- */
- private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) {
- Intent voiceIntent = new Intent(baseIntent);
- ComponentName searchActivity = searchable.getSearchActivity();
- voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
- searchActivity == null ? null : searchActivity.flattenToShortString());
- return voiceIntent;
- }
-
- /**
- * Create and return an Intent that can launch the voice search activity, perform a specific
- * voice transcription, and forward the results to the searchable activity.
- *
- * @param baseIntent The voice app search intent to start from
- * @return A completely-configured intent ready to send to the voice search activity
- */
- private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
- ComponentName searchActivity = searchable.getSearchActivity();
-
- // create the necessary intent to set up a search-and-forward operation
- // in the voice search system. We have to keep the bundle separate,
- // because it becomes immutable once it enters the PendingIntent
- Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
- queryIntent.setComponent(searchActivity);
- PendingIntent pending = PendingIntent.getActivity(
- getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
-
- // Now set up the bundle that will be inserted into the pending intent
- // when it's time to do the search. We always build it here (even if empty)
- // because the voice search activity will always need to insert "QUERY" into
- // it anyway.
- Bundle queryExtras = new Bundle();
- if (mAppSearchData != null) {
- queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData);
- }
-
- // Now build the intent to launch the voice search. Add all necessary
- // extras to launch the voice recognizer, and then all the necessary extras
- // to forward the results to the searchable activity
- Intent voiceIntent = new Intent(baseIntent);
-
- // Add all of the configuration options supplied by the searchable's metadata
- String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
- String prompt = null;
- String language = null;
- int maxResults = 1;
- Resources resources = mActivityContext.getResources();
- if (searchable.getVoiceLanguageModeId() != 0) {
- languageModel = resources.getString(searchable.getVoiceLanguageModeId());
- }
- if (searchable.getVoicePromptTextId() != 0) {
- prompt = resources.getString(searchable.getVoicePromptTextId());
- }
- if (searchable.getVoiceLanguageId() != 0) {
- language = resources.getString(searchable.getVoiceLanguageId());
- }
- if (searchable.getVoiceMaxResults() != 0) {
- maxResults = searchable.getVoiceMaxResults();
- }
- voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
- searchActivity == null ? null : searchActivity.flattenToShortString());
-
- // Add the values that configure forwarding the results
- voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
-
- return voiceIntent;
- }
-
- /**
- * Corrects http/https typo errors in the given url string, and if the protocol specifier was
- * not present defaults to http.
- *
- * @param inUrl URL to check and fix
- * @return fixed URL string.
- */
- private String fixUrl(String inUrl) {
- if (inUrl.startsWith("http://") || inUrl.startsWith("https://"))
- return inUrl;
-
- if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) {
- if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) {
- inUrl = inUrl.replaceFirst("/", "//");
- } else {
- inUrl = inUrl.replaceFirst(":", "://");
- }
- }
-
- if (inUrl.indexOf("://") == -1) {
- inUrl = "http://" + inUrl;
- }
-
- return inUrl;
- }
-
- /**
- * React to the user typing "enter" or other hardwired keys while typing in the search box.
- * This handles these special keys while the edit box has focus.
- */
- View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // guard against possible race conditions
- if (mSearchable == null) {
- return false;
- }
-
- if (DBG_LOG_TIMING) dbgLogTiming("doTextKey()");
- if (DBG) {
- Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event
- + "), selection: " + mSearchAutoComplete.getListSelection());
- }
-
- // If a suggestion is selected, handle enter, search key, and action keys
- // as presses on the selected suggestion
- if (mSearchAutoComplete.isPopupShowing() &&
- mSearchAutoComplete.getListSelection() != ListView.INVALID_POSITION) {
- return onSuggestionsKey(v, keyCode, event);
- }
-
- // If there is text in the query box, handle enter, and action keys
- // The search key is handled by the dialog's onKeyDown().
- if (!mSearchAutoComplete.isEmpty()) {
- if (keyCode == KeyEvent.KEYCODE_ENTER
- && event.getAction() == KeyEvent.ACTION_UP) {
- v.cancelLongPress();
-
- // If this is a url entered by the user & we displayed the 'Go' button which
- // the user clicked, launch the url instead of using it as a search query.
- if (mSearchable.autoUrlDetect() &&
- (mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION)
- == EditorInfo.IME_ACTION_GO) {
- Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString()));
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- launchIntent(intent);
- } else {
- // Launch as a regular search.
- launchQuerySearch();
- }
- return true;
- }
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
- if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
- return true;
- }
- }
- }
- return false;
- }
- };
@Override
public void hide() {
@@ -997,78 +500,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
super.hide();
}
-
- /**
- * React to the user typing while in the suggestions list. First, check for action
- * keys. If not handled, try refocusing regular characters into the EditText.
- */
- private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
- // guard against possible race conditions (late arrival after dismiss)
- if (mSearchable == null) {
- return false;
- }
- if (mSuggestionsAdapter == null) {
- return false;
- }
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (DBG_LOG_TIMING) {
- dbgLogTiming("onSuggestionsKey()");
- }
-
- // First, check for enter or search (both of which we'll treat as a "click")
- if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
- int position = mSearchAutoComplete.getListSelection();
- return launchSuggestion(position);
- }
-
- // Next, check for left/right moves, which we use to "return" the user to the edit view
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
- // give "focus" to text editor, with cursor at the beginning if
- // left key, at end if right key
- // TODO: Reverse left/right for right-to-left languages, e.g. Arabic
- int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ?
- 0 : mSearchAutoComplete.length();
- mSearchAutoComplete.setSelection(selPoint);
- mSearchAutoComplete.setListSelection(0);
- mSearchAutoComplete.clearListSelection();
- mSearchAutoComplete.ensureImeVisible(true);
-
- return true;
- }
-
- // Next, check for an "up and out" move
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP
- && 0 == mSearchAutoComplete.getListSelection()) {
- restoreUserQuery();
- // let ACTV complete the move
- return false;
- }
-
- // Next, check for an "action key"
- SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
- if ((actionKey != null) &&
- ((actionKey.getSuggestActionMsg() != null) ||
- (actionKey.getSuggestActionMsgColumn() != null))) {
- // launch suggestion using action key column
- int position = mSearchAutoComplete.getListSelection();
- if (position != ListView.INVALID_POSITION) {
- Cursor c = mSuggestionsAdapter.getCursor();
- if (c.moveToPosition(position)) {
- final String actionMsg = getActionKeyMessage(c, actionKey);
- if (actionMsg != null && (actionMsg.length() > 0)) {
- return launchSuggestion(position, keyCode, actionMsg);
- }
- }
- }
- }
- }
- return false;
- }
/**
* Launch a search for the text in the query text field.
*/
- public void launchQuerySearch() {
+ public void launchQuerySearch() {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
}
@@ -1080,50 +516,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
*/
- protected void launchQuerySearch(int actionKey, String actionMsg) {
+ protected void launchQuerySearch(int actionKey, String actionMsg) {
String query = mSearchAutoComplete.getText().toString();
String action = Intent.ACTION_SEARCH;
- Intent intent = createIntent(action, null, null, query,
- actionKey, actionMsg);
+ Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
launchIntent(intent);
}
-
- /**
- * Launches an intent based on a suggestion.
- *
- * @param position The index of the suggestion to create the intent from.
- * @return true if a successful launch, false if could not (e.g. bad position).
- */
- protected boolean launchSuggestion(int position) {
- return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
- }
-
- /**
- * Launches an intent based on a suggestion.
- *
- * @param position The index of the suggestion to create the intent from.
- * @param actionKey The key code of the action key that was pressed,
- * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
- * @param actionMsg The message for the action key that was pressed,
- * or <code>null</code> if none.
- * @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)) {
-
- Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
-
- // launch the intent
- launchIntent(intent);
-
- return true;
- }
- return false;
- }
/**
* Launches an intent, including any special intent handling.
@@ -1149,21 +547,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * If the intent is to open an HTTP or HTTPS URL, we set
- * {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that
- * has been opened by us for the same URL will be reused.
- */
- private void setBrowserApplicationId(Intent intent) {
- Uri data = intent.getData();
- if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null) {
- String scheme = data.getScheme();
- if (scheme != null && scheme.startsWith("http")) {
- intent.putExtra(Browser.EXTRA_APPLICATION_ID, data.toString());
- }
- }
- }
-
- /**
* Sets the list item selection in the AutoCompleteTextView's ListView.
*/
public void setListSelection(int index) {
@@ -1171,62 +554,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * When a particular suggestion has been selected, perform the various lookups required
- * to use the suggestion. This includes checking the cursor for suggestion-specific data,
- * and/or falling back to the XML for defaults; It also creates REST style Uri data when
- * the suggestion includes a data id.
- *
- * @param c The suggestions cursor, moved to the row of the user's selection
- * @param actionKey The key code of the action key that was pressed,
- * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
- * @param actionMsg The message for the action key that was pressed,
- * or <code>null</code> if none.
- * @return An intent for the suggestion at the cursor's position.
- */
- private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
- try {
- // use specific action if supplied, or default action if supplied, or fixed default
- String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
-
- if (action == null) {
- action = mSearchable.getSuggestIntentAction();
- }
- if (action == null) {
- action = Intent.ACTION_SEARCH;
- }
-
- // use specific data if supplied, or default data if supplied
- String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
- if (data == null) {
- data = mSearchable.getSuggestIntentData();
- }
- // then, if an ID was provided, append it.
- if (data != null) {
- String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
- if (id != null) {
- data = data + "/" + Uri.encode(id);
- }
- }
- Uri dataUri = (data == null) ? null : Uri.parse(data);
-
- String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
- String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
-
- return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
- } catch (RuntimeException e ) {
- int rowNum;
- try { // be really paranoid now
- rowNum = c.getPosition();
- } catch (RuntimeException e2 ) {
- rowNum = -1;
- }
- Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
- " returned exception" + e.toString());
- return null;
- }
- }
-
- /**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
@@ -1271,30 +598,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * For a given suggestion and a given cursor row, get the action message. If not provided
- * by the specific row/column, also check for a single definition (for the action key).
- *
- * @param c The cursor providing suggestions
- * @param actionKey The actionkey record being examined
- *
- * @return Returns a string, or null if no action key message for this suggestion
- */
- private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
- String result = null;
- // check first in the cursor data, for a suggestion-specific message
- final String column = actionKey.getSuggestActionMsgColumn();
- if (column != null) {
- result = SuggestionsAdapter.getColumnString(c, column);
- }
- // If the cursor didn't give us a message, see if there's a single message defined
- // for the actionkey (for all suggestions)
- if (result == null) {
- result = actionKey.getSuggestActionMsg();
- }
- return result;
- }
-
- /**
* The root element in the search bar layout. This is a custom view just to override
* the handling of the back button.
*/
@@ -1315,21 +618,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * Overrides the handling of the back key to move back to the previous sources or dismiss
- * the search dialog, instead of dismissing the input method.
+ * Overrides the handling of the back key to move back to the previous
+ * sources or dismiss the search dialog, instead of dismissing the input
+ * method.
*/
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
- if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + event + ")");
+ if (DBG)
+ Log.d(LOG_TAG, "onKeyPreIme(" + event + ")");
if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.getRepeatCount() == 0) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
state.startTracking(event, this);
return true;
- } else if (event.getAction() == KeyEvent.ACTION_UP
- && !event.isCanceled() && state.isTracking(event)) {
+ } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()
+ && state.isTracking(event)) {
mSearchDialog.onBackPressed();
return true;
}
@@ -1339,86 +643,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
}
- /**
- * Local subclass for AutoCompleteTextView.
- */
- public static class SearchAutoComplete extends AutoCompleteTextView {
-
- private int mThreshold;
-
- public SearchAutoComplete(Context context) {
- super(context);
- mThreshold = getThreshold();
- }
-
- public SearchAutoComplete(Context context, AttributeSet attrs) {
- super(context, attrs);
- mThreshold = getThreshold();
- }
-
- public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mThreshold = getThreshold();
- }
-
- @Override
- public void setThreshold(int threshold) {
- super.setThreshold(threshold);
- mThreshold = threshold;
- }
-
- /**
- * Returns true if the text field is empty, or contains only whitespace.
- */
- private boolean isEmpty() {
- return TextUtils.getTrimmedLength(getText()) == 0;
- }
-
- /**
- * We override this method to avoid replacing the query box text
- * when a suggestion is clicked.
- */
- @Override
- protected void replaceText(CharSequence text) {
- }
-
- /**
- * We override this method to avoid an extra onItemClick being called on the
- * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)}
- * when an item is clicked with the trackball.
- */
- @Override
- public void performCompletion() {
- }
-
- /**
- * We override this method to be sure and show the soft keyboard if appropriate when
- * the TextView has focus.
- */
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
-
- if (hasWindowFocus) {
- InputMethodManager inputManager = (InputMethodManager)
- getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- inputManager.showSoftInput(this, 0);
- // If in landscape mode, then make sure that
- // the ime is in front of the dropdown.
- if (isLandscapeMode(getContext())) {
- ensureImeVisible(true);
- }
- }
- }
-
- /**
- * We override this method so that we can allow a threshold of zero, which ACTV does not.
- */
- @Override
- public boolean enoughToFilter() {
- return mThreshold <= 0 || super.enoughToFilter();
- }
-
+ private boolean isEmpty(AutoCompleteTextView actv) {
+ return TextUtils.getTrimmedLength(actv.getText()) == 0;
}
@Override
@@ -1435,106 +661,58 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
cancel();
}
- /**
- * Implements OnItemClickListener
- */
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position);
- launchSuggestion(position);
+ private boolean onClosePressed() {
+ // Dismiss the dialog if close button is pressed when there's no query text
+ if (isEmpty(mSearchAutoComplete)) {
+ dismiss();
+ return true;
+ }
+
+ return false;
}
- /**
- * Implements OnItemSelectedListener
- */
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position);
- // A suggestion has been selected, rewrite the query if possible,
- // otherwise the restore the original query.
- if (REWRITE_QUERIES) {
- rewriteQueryFromSuggestion(position);
- }
- }
-
- /**
- * Implements OnItemSelectedListener
- */
- public void onNothingSelected(AdapterView<?> parent) {
- if (DBG) Log.d(LOG_TAG, "onNothingSelected()");
- }
-
- /**
- * Query rewriting.
- */
-
- private void rewriteQueryFromSuggestion(int position) {
- Cursor c = mSuggestionsAdapter.getCursor();
- if (c == null) {
- return;
- }
- if (c.moveToPosition(position)) {
- // Get the new query from the suggestion.
- CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
- if (newQuery != null) {
- // The suggestion rewrites the query.
- if (DBG) Log.d(LOG_TAG, "Rewriting query to '" + newQuery + "'");
- // Update the text field, without getting new suggestions.
- setQuery(newQuery);
- } else {
- // The suggestion does not rewrite the query, restore the user's query.
- if (DBG) Log.d(LOG_TAG, "Suggestion gives no rewrite, restoring user query.");
- restoreUserQuery();
- }
- } else {
- // We got a bad position, restore the user's query.
- Log.w(LOG_TAG, "Bad suggestion position: " + position);
- restoreUserQuery();
- }
- }
-
- /**
- * Restores the query entered by the user if needed.
- */
- private void restoreUserQuery() {
- if (DBG) Log.d(LOG_TAG, "Restoring query to '" + mUserQuery + "'");
- setQuery(mUserQuery);
- }
-
- /**
- * Sets the text in the query box, without updating the suggestions.
- */
- private void setQuery(CharSequence query) {
- mSearchAutoComplete.setText(query, false);
- if (query != null) {
- mSearchAutoComplete.setSelection(query.length());
- }
- }
-
- /**
- * Sets the text in the query box, updating the suggestions.
- */
- private void setUserQuery(String query) {
- if (query == null) {
- query = "";
- }
- mUserQuery = query;
- mSearchAutoComplete.setText(query);
- mSearchAutoComplete.setSelection(query.length());
- }
+ private final SearchView.OnCloseListener mOnCloseListener = new SearchView.OnCloseListener() {
- /**
- * Debugging Support
- */
+ public boolean onClose() {
+ return onClosePressed();
+ }
+ };
+
+ private final SearchView.OnQueryChangeListener mOnQueryChangeListener =
+ new SearchView.OnQueryChangeListener() {
+
+ public boolean onSubmitQuery(String query) {
+ dismiss();
+ return false;
+ }
+
+ public boolean onQueryTextChanged(String newText) {
+ return false;
+ }
+ };
+
+ private final SearchView.OnSuggestionSelectionListener mOnSuggestionSelectionListener =
+ new SearchView.OnSuggestionSelectionListener() {
+
+ public boolean onSuggestionSelected(int position) {
+ return false;
+ }
+
+ public boolean onSuggestionClicked(int position) {
+ dismiss();
+ return false;
+ }
+ };
/**
- * For debugging only, sample the millisecond clock and log it.
- * Uses AtomicLong so we can use in multiple threads
+ * Sets the text in the query box, updating the suggestions.
*/
- private AtomicLong mLastLogTime = new AtomicLong(SystemClock.uptimeMillis());
- private void dbgLogTiming(final String caller) {
- long millis = SystemClock.uptimeMillis();
- long oldTime = mLastLogTime.getAndSet(millis);
- long delta = millis - oldTime;
- final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller;
- Log.d(LOG_TAG,report);
+ private void setUserQuery(String query) {
+ if (query == null) {
+ query = "";
+ }
+ mUserQuery = query;
+ mSearchAutoComplete.setText(query);
+ mSearchAutoComplete.setSelection(query.length());
}
}
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
deleted file mode 100644
index 5705bff..0000000
--- a/core/java/android/app/SuggestionsAdapter.java
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * Copyright (C) 2009 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.app;
-
-import com.android.internal.R;
-
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContentResolver.OpenResourceIdResult;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Filter;
-import android.widget.ImageView;
-import android.widget.ResourceCursorAdapter;
-import android.widget.TextView;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.WeakHashMap;
-
-/**
- * Provides the contents for the suggestion drop-down list.in {@link SearchDialog}.
- *
- * @hide
- */
-class SuggestionsAdapter extends ResourceCursorAdapter {
-
- private static final boolean DBG = false;
- private static final String LOG_TAG = "SuggestionsAdapter";
- private static final int QUERY_LIMIT = 50;
-
- private SearchManager mSearchManager;
- private SearchDialog mSearchDialog;
- private SearchableInfo mSearchable;
- private Context mProviderContext;
- private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
- private boolean mClosed = false;
-
- // URL color
- private ColorStateList mUrlColor;
-
- // Cached column indexes, updated when the cursor changes.
- private int mText1Col;
- private int mText2Col;
- private int mText2UrlCol;
- private int mIconName1Col;
- private int mIconName2Col;
-
- static final int NONE = -1;
-
- private final Runnable mStartSpinnerRunnable;
- private final Runnable mStopSpinnerRunnable;
-
- /**
- * The amount of time we delay in the filter when the user presses the delete key.
- * @see Filter#setDelayer(android.widget.Filter.Delayer).
- */
- private static final long DELETE_KEY_POST_DELAY = 500L;
-
- public SuggestionsAdapter(Context context, SearchDialog searchDialog,
- SearchableInfo searchable,
- WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) {
- super(context,
- com.android.internal.R.layout.search_dropdown_item_icons_2line,
- null, // no initial cursor
- true); // auto-requery
- mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- mSearchDialog = searchDialog;
- mSearchable = searchable;
-
- // set up provider resources (gives us icons, etc.)
- Context activityContext = mSearchable.getActivityContext(mContext);
- mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
-
- mOutsideDrawablesCache = outsideDrawablesCache;
-
- mStartSpinnerRunnable = new Runnable() {
- public void run() {
- mSearchDialog.setWorking(true);
- }
- };
-
- mStopSpinnerRunnable = new Runnable() {
- public void run() {
- mSearchDialog.setWorking(false);
- }
- };
-
- // delay 500ms when deleting
- getFilter().setDelayer(new Filter.Delayer() {
-
- private int mPreviousLength = 0;
-
- public long getPostingDelay(CharSequence constraint) {
- if (constraint == null) return 0;
-
- long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
- mPreviousLength = constraint.length();
- return delay;
- }
- });
- }
-
- /**
- * Overridden to always return <code>false</code>, since we cannot be sure that
- * suggestion sources return stable IDs.
- */
- @Override
- public boolean hasStableIds() {
- return false;
- }
-
- /**
- * Use the search suggestions provider to obtain a live cursor. This will be called
- * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
- * The results will be processed in the UI thread and changeCursor() will be called.
- */
- @Override
- public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
- if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
- String query = (constraint == null) ? "" : constraint.toString();
- /**
- * for in app search we show the progress spinner until the cursor is returned with
- * the results.
- */
- Cursor cursor = null;
- mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
- try {
- cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
- // trigger fill window so the spinner stays up until the results are copied over and
- // closer to being ready
- if (cursor != null) {
- cursor.getCount();
- return cursor;
- }
- } catch (RuntimeException e) {
- Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
- }
- // If cursor is null or an exception was thrown, stop the spinner and return null.
- // changeCursor doesn't get called if cursor is null
- mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
- return null;
- }
-
- public void close() {
- if (DBG) Log.d(LOG_TAG, "close()");
- changeCursor(null);
- mClosed = true;
- }
-
- @Override
- public void notifyDataSetChanged() {
- if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
- super.notifyDataSetChanged();
-
- mSearchDialog.onDataSetChanged();
-
- updateSpinnerState(getCursor());
- }
-
- @Override
- public void notifyDataSetInvalidated() {
- if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
- super.notifyDataSetInvalidated();
-
- updateSpinnerState(getCursor());
- }
-
- private void updateSpinnerState(Cursor cursor) {
- Bundle extras = cursor != null ? cursor.getExtras() : null;
- if (DBG) {
- Log.d(LOG_TAG, "updateSpinnerState - extra = "
- + (extras != null
- ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
- : null));
- }
- // Check if the Cursor indicates that the query is not complete and show the spinner
- if (extras != null
- && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
- mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
- return;
- }
- // If cursor is null or is done, stop the spinner
- mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
- }
-
- /**
- * Cache columns.
- */
- @Override
- public void changeCursor(Cursor c) {
- if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")");
-
- if (mClosed) {
- Log.w(LOG_TAG, "Tried to change cursor after adapter was closed.");
- if (c != null) c.close();
- return;
- }
-
- try {
- super.changeCursor(c);
-
- if (c != null) {
- mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
- mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
- mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
- mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
- mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
- }
- } catch (Exception e) {
- Log.e(LOG_TAG, "error changing cursor and caching columns", e);
- }
- }
-
- /**
- * Tags the view with cached child view look-ups.
- */
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View v = super.newView(context, cursor, parent);
- v.setTag(new ChildViewCache(v));
- return v;
- }
-
- /**
- * Cache of the child views of drop-drown list items, to avoid looking up the children
- * each time the contents of a list item are changed.
- */
- private final static class ChildViewCache {
- public final TextView mText1;
- public final TextView mText2;
- public final ImageView mIcon1;
- public final ImageView mIcon2;
-
- public ChildViewCache(View v) {
- mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1);
- mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2);
- mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1);
- mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2);
- }
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- ChildViewCache views = (ChildViewCache) view.getTag();
-
- if (views.mText1 != null) {
- String text1 = getStringOrNull(cursor, mText1Col);
- setViewText(views.mText1, text1);
- }
- if (views.mText2 != null) {
- // First check TEXT_2_URL
- CharSequence text2 = getStringOrNull(cursor, mText2UrlCol);
- if (text2 != null) {
- text2 = formatUrl(text2);
- } else {
- text2 = getStringOrNull(cursor, mText2Col);
- }
-
- // If no second line of text is indicated, allow the first line of text
- // to be up to two lines if it wants to be.
- if (TextUtils.isEmpty(text2)) {
- if (views.mText1 != null) {
- views.mText1.setSingleLine(false);
- views.mText1.setMaxLines(2);
- }
- } else {
- if (views.mText1 != null) {
- views.mText1.setSingleLine(true);
- views.mText1.setMaxLines(1);
- }
- }
- setViewText(views.mText2, text2);
- }
-
- if (views.mIcon1 != null) {
- setViewDrawable(views.mIcon1, getIcon1(cursor));
- }
- if (views.mIcon2 != null) {
- setViewDrawable(views.mIcon2, getIcon2(cursor));
- }
- }
-
- private CharSequence formatUrl(CharSequence url) {
- if (mUrlColor == null) {
- // Lazily get the URL color from the current theme.
- TypedValue colorValue = new TypedValue();
- mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
- mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId);
- }
-
- SpannableString text = new SpannableString(url);
- text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null),
- 0, url.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- return text;
- }
-
- private void setViewText(TextView v, CharSequence text) {
- // Set the text even if it's null, since we need to clear any previous text.
- v.setText(text);
-
- if (TextUtils.isEmpty(text)) {
- v.setVisibility(View.GONE);
- } else {
- v.setVisibility(View.VISIBLE);
- }
- }
-
- private Drawable getIcon1(Cursor cursor) {
- if (mIconName1Col < 0) {
- return null;
- }
- String value = cursor.getString(mIconName1Col);
- Drawable drawable = getDrawableFromResourceValue(value);
- if (drawable != null) {
- return drawable;
- }
- return getDefaultIcon1(cursor);
- }
-
- private Drawable getIcon2(Cursor cursor) {
- if (mIconName2Col < 0) {
- return null;
- }
- String value = cursor.getString(mIconName2Col);
- return getDrawableFromResourceValue(value);
- }
-
- /**
- * Sets the drawable in an image view, makes sure the view is only visible if there
- * is a drawable.
- */
- private void setViewDrawable(ImageView v, Drawable drawable) {
- // Set the icon even if the drawable is null, since we need to clear any
- // previous icon.
- v.setImageDrawable(drawable);
-
- if (drawable == null) {
- v.setVisibility(View.GONE);
- } else {
- v.setVisibility(View.VISIBLE);
-
- // This is a hack to get any animated drawables (like a 'working' spinner)
- // to animate. You have to setVisible true on an AnimationDrawable to get
- // it to start animating, but it must first have been false or else the
- // call to setVisible will be ineffective. We need to clear up the story
- // about animated drawables in the future, see http://b/1878430.
- drawable.setVisible(false, false);
- drawable.setVisible(true, false);
- }
- }
-
- /**
- * Gets the text to show in the query field when a suggestion is selected.
- *
- * @param cursor The Cursor to read the suggestion data from. The Cursor should already
- * be moved to the suggestion that is to be read from.
- * @return The text to show, or <code>null</code> if the query should not be
- * changed when selecting this suggestion.
- */
- @Override
- public CharSequence convertToString(Cursor cursor) {
- if (cursor == null) {
- return null;
- }
-
- String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY);
- if (query != null) {
- return query;
- }
-
- if (mSearchable.shouldRewriteQueryFromData()) {
- String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
- if (data != null) {
- return data;
- }
- }
-
- if (mSearchable.shouldRewriteQueryFromText()) {
- String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
- if (text1 != null) {
- return text1;
- }
- }
-
- return null;
- }
-
- /**
- * This method is overridden purely to provide a bit of protection against
- * flaky content providers.
- *
- * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- try {
- return super.getView(position, convertView, parent);
- } catch (RuntimeException e) {
- Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
- // Put exception string in item title
- View v = newView(mContext, mCursor, parent);
- if (v != null) {
- ChildViewCache views = (ChildViewCache) v.getTag();
- TextView tv = views.mText1;
- tv.setText(e.toString());
- }
- return v;
- }
- }
-
- /**
- * Gets a drawable given a value provided by a suggestion provider.
- *
- * This value could be just the string value of a resource id
- * (e.g., "2130837524"), in which case we will try to retrieve a drawable from
- * the provider's resources. If the value is not an integer, it is
- * treated as a Uri and opened with
- * {@link ContentResolver#openOutputStream(android.net.Uri, String)}.
- *
- * All resources and URIs are read using the suggestion provider's context.
- *
- * If the string is not formatted as expected, or no drawable can be found for
- * the provided value, this method returns null.
- *
- * @param drawableId a string like "2130837524",
- * "android.resource://com.android.alarmclock/2130837524",
- * or "content://contacts/photos/253".
- * @return a Drawable, or null if none found
- */
- private Drawable getDrawableFromResourceValue(String drawableId) {
- if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
- return null;
- }
- try {
- // First, see if it's just an integer
- int resourceId = Integer.parseInt(drawableId);
- // It's an int, look for it in the cache
- String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE
- + "://" + mProviderContext.getPackageName() + "/" + resourceId;
- // Must use URI as cache key, since ints are app-specific
- Drawable drawable = checkIconCache(drawableUri);
- if (drawable != null) {
- return drawable;
- }
- // Not cached, find it by resource ID
- drawable = mProviderContext.getResources().getDrawable(resourceId);
- // Stick it in the cache, using the URI as key
- storeInIconCache(drawableUri, drawable);
- return drawable;
- } catch (NumberFormatException nfe) {
- // It's not an integer, use it as a URI
- Drawable drawable = checkIconCache(drawableId);
- if (drawable != null) {
- return drawable;
- }
- Uri uri = Uri.parse(drawableId);
- drawable = getDrawable(uri);
- storeInIconCache(drawableId, drawable);
- return drawable;
- } catch (Resources.NotFoundException nfe) {
- // It was an integer, but it couldn't be found, bail out
- Log.w(LOG_TAG, "Icon resource not found: " + drawableId);
- return null;
- }
- }
-
- /**
- * Gets a drawable by URI, without using the cache.
- *
- * @return A drawable, or {@code null} if the drawable could not be loaded.
- */
- private Drawable getDrawable(Uri uri) {
- try {
- String scheme = uri.getScheme();
- if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
- // Load drawables through Resources, to get the source density information
- OpenResourceIdResult r =
- mProviderContext.getContentResolver().getResourceId(uri);
- try {
- return r.r.getDrawable(r.id);
- } catch (Resources.NotFoundException ex) {
- throw new FileNotFoundException("Resource does not exist: " + uri);
- }
- } else {
- // Let the ContentResolver handle content and file URIs.
- InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
- if (stream == null) {
- throw new FileNotFoundException("Failed to open " + uri);
- }
- try {
- return Drawable.createFromStream(stream, null);
- } finally {
- try {
- stream.close();
- } catch (IOException ex) {
- Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
- }
- }
- }
- } catch (FileNotFoundException fnfe) {
- Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
- return null;
- }
- }
-
- private Drawable checkIconCache(String resourceUri) {
- Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri);
- if (cached == null) {
- return null;
- }
- if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri);
- return cached.newDrawable();
- }
-
- private void storeInIconCache(String resourceUri, Drawable drawable) {
- if (drawable != null) {
- mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState());
- }
- }
-
- /**
- * Gets the left-hand side icon that will be used for the current suggestion
- * if the suggestion contains an icon column but no icon or a broken icon.
- *
- * @param cursor A cursor positioned at the current suggestion.
- * @return A non-null drawable.
- */
- private Drawable getDefaultIcon1(Cursor cursor) {
- // Check the component that gave us the suggestion
- Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
- if (drawable != null) {
- return drawable;
- }
-
- // Fall back to a default icon
- return mContext.getPackageManager().getDefaultActivityIcon();
- }
-
- /**
- * Gets the activity or application icon for an activity.
- * Uses the local icon cache for fast repeated lookups.
- *
- * @param component Name of an activity.
- * @return A drawable, or {@code null} if neither the activity nor the application
- * has an icon set.
- */
- private Drawable getActivityIconWithCache(ComponentName component) {
- // First check the icon cache
- String componentIconKey = component.flattenToShortString();
- // Using containsKey() since we also store null values.
- if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
- Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
- return cached == null ? null : cached.newDrawable(mProviderContext.getResources());
- }
- // Then try the activity or application icon
- Drawable drawable = getActivityIcon(component);
- // Stick it in the cache so we don't do this lookup again.
- Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState();
- mOutsideDrawablesCache.put(componentIconKey, toCache);
- return drawable;
- }
-
- /**
- * Gets the activity or application icon for an activity.
- *
- * @param component Name of an activity.
- * @return A drawable, or {@code null} if neither the acitivy or the application
- * have an icon set.
- */
- private Drawable getActivityIcon(ComponentName component) {
- PackageManager pm = mContext.getPackageManager();
- final ActivityInfo activityInfo;
- try {
- activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA);
- } catch (NameNotFoundException ex) {
- Log.w(LOG_TAG, ex.toString());
- return null;
- }
- int iconId = activityInfo.getIconResource();
- if (iconId == 0) return null;
- String pkg = component.getPackageName();
- Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo);
- if (drawable == null) {
- Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for "
- + component.flattenToShortString());
- return null;
- }
- return drawable;
- }
-
- /**
- * Gets the value of a string column by name.
- *
- * @param cursor Cursor to read the value from.
- * @param columnName The name of the column to read.
- * @return The value of the given column, or <code>null</null>
- * if the cursor does not contain the given column.
- */
- public static String getColumnString(Cursor cursor, String columnName) {
- int col = cursor.getColumnIndex(columnName);
- return getStringOrNull(cursor, col);
- }
-
- private static String getStringOrNull(Cursor cursor, int col) {
- if (col == NONE) {
- return null;
- }
- try {
- return cursor.getString(col);
- } catch (Exception e) {
- Log.e(LOG_TAG,
- "unexpected error retrieving valid column from cursor, "
- + "did the remote process die?", e);
- return null;
- }
- }
-}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index d1ab8d5..abeeb74 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -110,16 +110,23 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
selectionArgs, sortOrder, observer, window);
- reply.writeNoException();
if (bulkCursor != null) {
- reply.writeStrongBinder(bulkCursor.asBinder());
-
+ final IBinder binder = bulkCursor.asBinder();
if (wantsCursorMetadata) {
- reply.writeInt(bulkCursor.count());
- reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
- bulkCursor.getColumnNames()));
+ final int count = bulkCursor.count();
+ final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
+ bulkCursor.getColumnNames());
+
+ reply.writeNoException();
+ reply.writeStrongBinder(binder);
+ reply.writeInt(count);
+ reply.writeInt(index);
+ } else {
+ reply.writeNoException();
+ reply.writeStrongBinder(binder);
}
} else {
+ reply.writeNoException();
reply.writeStrongBinder(null);
}
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 01ae1da..9893133 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,7 +16,6 @@
package android.content.res;
-import android.os.MemoryFile;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -184,14 +183,9 @@ public class AssetFileDescriptor implements Parcelable {
@Override
public int read() throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- int res = super.read();
- if (res >= 0) mRemaining--;
- return res;
- }
-
- return super.read();
+ byte[] buffer = new byte[1];
+ int result = read(buffer, 0, 1);
+ return result == -1 ? -1 : buffer[0] & 0xff;
}
@Override
@@ -209,16 +203,7 @@ public class AssetFileDescriptor implements Parcelable {
@Override
public int read(byte[] buffer) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- int count = buffer.length;
- if (count > mRemaining) count = (int)mRemaining;
- int res = super.read(buffer, 0, count);
- if (res >= 0) mRemaining -= res;
- return res;
- }
-
- return super.read(buffer);
+ return read(buffer, 0, buffer.length);
}
@Override
@@ -231,7 +216,6 @@ public class AssetFileDescriptor implements Parcelable {
return res;
}
- // TODO Auto-generated method stub
return super.skip(count);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index f3b2c81..fe4b900 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1021,6 +1021,9 @@ public class Camera {
private static final String KEY_ZOOM_SUPPORTED = "zoom-supported";
private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported";
private static final String KEY_FOCUS_DISTANCES = "focus-distances";
+ private static final String KEY_VIDEO_SIZE = "video-size";
+ private static final String KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO =
+ "preferred-preview-size-for-video";
// Parameter key suffix for supported values.
private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -1398,7 +1401,7 @@ public class Camera {
/**
* Returns the dimensions setting for preview pictures.
*
- * @return a Size object with the height and width setting
+ * @return a Size object with the width and height setting
* for the preview picture
*/
public Size getPreviewSize() {
@@ -1418,6 +1421,46 @@ public class Camera {
}
/**
+ * Gets the supported video frame sizes that can be used by
+ * MediaRecorder.
+ *
+ * If the returned list is not null, the returned list will contain at
+ * least one Size and one of the sizes in the returned list must be
+ * passed to MediaRecorder.setVideoSize() for camcorder application if
+ * camera is used as the video source. In this case, the size of the
+ * preview can be different from the resolution of the recorded video
+ * during video recording.
+ *
+ * @return a list of Size object if camera has separate preview and
+ * video output; otherwise, null is returned.
+ * @see #getPreferredPreviewSizeForVideo()
+ */
+ public List<Size> getSupportedVideoSizes() {
+ String str = get(KEY_VIDEO_SIZE + SUPPORTED_VALUES_SUFFIX);
+ return splitSize(str);
+ }
+
+ /**
+ * Returns the preferred or recommended preview size (width and height)
+ * in pixels for video recording. Camcorder applications should
+ * set the preview size to a value that is not larger than the
+ * preferred preview size. In other words, the product of the width
+ * and height of the preview size should not be larger than that of
+ * the preferred preview size. In addition, we recommend to choose a
+ * preview size that has the same aspect ratio as the resolution of
+ * video to be recorded.
+ *
+ * @return the preferred preview size (width and height) in pixels for
+ * video recording if getSupportedVideoSizes() does not return
+ * null; otherwise, null is returned.
+ * @see #getSupportedVideoSizes()
+ */
+ public Size getPreferredPreviewSizeForVideo() {
+ String pair = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO);
+ return strToSize(pair);
+ }
+
+ /**
* Sets the dimensions for EXIF thumbnail in Jpeg picture. If
* applications set both width and height to 0, EXIF will not contain
* thumbnail.
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 32ff3b3..8c55bf3 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -220,25 +220,47 @@ public class SensorEvent {
* </p>
*
* <h4>{@link android.hardware.Sensor#TYPE_GRAVITY Sensor.TYPE_GRAVITY}:</h4>
- * A three dimensional vector indicating the direction and magnitude of gravity. Units
- * are m/s^2. The coordinate system is the same as is used by the acceleration sensor.
+ * <p>A three dimensional vector indicating the direction and magnitude of gravity. Units
+ * are m/s^2. The coordinate system is the same as is used by the acceleration sensor.</p>
+ * <p><b>Note:</b> When the device is at rest, the output of the gravity sensor should be identical
+ * to that of the accelerometer.</p>
*
* <h4>{@link android.hardware.Sensor#TYPE_LINEAR_ACCELERATION Sensor.TYPE_LINEAR_ACCELERATION}:</h4>
* A three dimensional vector indicating acceleration along each device axis, not including
* gravity. All values have units of m/s^2. The coordinate system is the same as is used by the
- * acceleration sensor.
+ * acceleration sensor.
+ * <p>The output of the accelerometer, gravity and linear-acceleration sensors must obey the
+ * following relation:</p>
+ * <p><ul>acceleration = gravity + linear-acceleration</ul></p>
*
* <h4>{@link android.hardware.Sensor#TYPE_ROTATION_VECTOR Sensor.TYPE_ROTATION_VECTOR}:</h4>
- * The rotation vector represents the orientation of the device as a combination of an angle
- * and an axis, in which the device has rotated through an angle theta around an axis
- * &lt;x, y, z>. The three elements of the rotation vector are
- * &lt;x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>, such that the magnitude of the rotation
- * vector is equal to sin(theta/2), and the direction of the rotation vector is equal to the
- * direction of the axis of rotation. The three elements of the rotation vector are equal to
- * the last three components of a unit quaternion
- * &lt;cos(theta/2), x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>. Elements of the rotation
- * vector are unitless. The x,y, and z axis are defined in the same way as the acceleration
- * sensor.
+ * <p>The rotation vector represents the orientation of the device as a combination of an <i>angle</i>
+ * and an <i>axis</i>, in which the device has rotated through an angle &#952 around an axis
+ * &lt;x, y, z>.</p>
+ * <p>The three elements of the rotation vector are
+ * &lt;x*sin(&#952/2), y*sin(&#952/2), z*sin(&#952/2)>, such that the magnitude of the rotation
+ * vector is equal to sin(&#952/2), and the direction of the rotation vector is equal to the
+ * direction of the axis of rotation.</p>
+ * </p>The three elements of the rotation vector are equal to
+ * the last three components of a <b>unit</b> quaternion
+ * &lt;cos(&#952/2), x*sin(&#952/2), y*sin(&#952/2), z*sin(&#952/2)>.</p>
+ * <p>Elements of the rotation vector are unitless.
+ * The x,y, and z axis are defined in the same way as the acceleration
+ * sensor.</p>
+ * <ul>
+ * <p>
+ * values[0]: x*sin(&#952/2)
+ * </p>
+ * <p>
+ * values[1]: y*sin(&#952/2)
+ * </p>
+ * <p>
+ * values[2]: z*sin(&#952/2)
+ * </p>
+ * <p>
+ * values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i>
+ * </p>
+ * </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
* Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index c178aee..1b799ae 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1938,13 +1938,18 @@ public class SensorManager
* @param R an array of floats in which to store the rotation matrix
*/
public static void getRotationMatrixFromVector(float[] R, float[] rotationVector) {
- float q0 = (float)Math.sqrt(1 - rotationVector[0]*rotationVector[0] -
- rotationVector[1]*rotationVector[1] -
- rotationVector[2]*rotationVector[2]);
+
+ float q0;
float q1 = rotationVector[0];
float q2 = rotationVector[1];
float q3 = rotationVector[2];
+ if (rotationVector.length == 4) {
+ q0 = rotationVector[3];
+ } else {
+ q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3);
+ }
+
float sq_q1 = 2 * q1 * q1;
float sq_q2 = 2 * q2 * q2;
float sq_q3 = 2 * q3 * q3;
@@ -1995,10 +2000,12 @@ public class SensorManager
* @param Q an array of floats in which to store the computed quaternion
*/
public static void getQuaternionFromVector(float[] Q, float[] rv) {
- float w = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]);
- //In this case, the w component of the quaternion is known to be a positive number
-
- Q[0] = w;
+ if (rv.length == 4) {
+ Q[0] = rv[3];
+ } else {
+ //In this case, the w component of the quaternion is known to be a positive number
+ Q[0] = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]);
+ }
Q[1] = rv[0];
Q[2] = rv[1];
Q[3] = rv[2];
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 1c295a7..9c3bc9d 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -40,6 +40,7 @@ import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
+import dalvik.bytecode.OpcodeInfo;
import dalvik.bytecode.Opcodes;
import dalvik.system.VMDebug;
@@ -786,7 +787,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* @hide
*/
public static long countInstancesOfClass(Class cls) {
- return VMDebug.countInstancesOfClass(cls);
+ return VMDebug.countInstancesOfClass(cls, true);
}
/**
@@ -865,7 +866,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* </pre>
*/
public static class InstructionCount {
- private static final int NUM_INSTR = 256;
+ private static final int NUM_INSTR = OpcodeInfo.MAXIMUM_VALUE + 1;
private int[] mCounts;
@@ -909,8 +910,11 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
*/
public int globalTotal() {
int count = 0;
- for (int i = 0; i < NUM_INSTR; i++)
+
+ for (int i = 0; i < NUM_INSTR; i++) {
count += mCounts[i];
+ }
+
return count;
}
@@ -921,27 +925,16 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
public int globalMethodInvocations() {
int count = 0;
- //count += mCounts[Opcodes.OP_EXECUTE_INLINE];
- count += mCounts[Opcodes.OP_INVOKE_VIRTUAL];
- count += mCounts[Opcodes.OP_INVOKE_SUPER];
- count += mCounts[Opcodes.OP_INVOKE_DIRECT];
- count += mCounts[Opcodes.OP_INVOKE_STATIC];
- count += mCounts[Opcodes.OP_INVOKE_INTERFACE];
- count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_RANGE];
- count += mCounts[Opcodes.OP_INVOKE_SUPER_RANGE];
- count += mCounts[Opcodes.OP_INVOKE_DIRECT_RANGE];
- count += mCounts[Opcodes.OP_INVOKE_STATIC_RANGE];
- count += mCounts[Opcodes.OP_INVOKE_INTERFACE_RANGE];
- //count += mCounts[Opcodes.OP_INVOKE_DIRECT_EMPTY];
- count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK];
- count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK_RANGE];
- count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK];
- count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE];
+ for (int i = 0; i < NUM_INSTR; i++) {
+ if (OpcodeInfo.isInvoke(i)) {
+ count += mCounts[i];
+ }
+ }
+
return count;
}
}
-
/**
* A Map of typed debug properties.
*/
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 754d073..6414936 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -847,6 +847,13 @@ public final class StrictMode {
}
};
+ // Note: only access this once verifying the thread has a Looper.
+ private static final ThreadLocal<Handler> threadHandler = new ThreadLocal<Handler>() {
+ @Override protected Handler initialValue() {
+ return new Handler();
+ }
+ };
+
private static boolean tooManyViolationsThisLoop() {
return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP;
}
@@ -954,7 +961,6 @@ public final class StrictMode {
return;
}
- MessageQueue queue = Looper.myQueue();
final ArrayList<ViolationInfo> records = violationsBeingTimed.get();
if (records.size() >= MAX_OFFENSES_PER_LOOP) {
// Not worth measuring. Too many offenses in one loop.
@@ -977,9 +983,30 @@ public final class StrictMode {
}
}
- queue.addIdleHandler(new MessageQueue.IdleHandler() {
- public boolean queueIdle() {
+ // We post a runnable to a Handler (== delay 0 ms) for
+ // measuring the end time of a violation instead of using
+ // an IdleHandler (as was previously used) because an
+ // IdleHandler may not run for quite a long period of time
+ // if an ongoing animation is happening and continually
+ // posting ASAP (0 ms) animation steps. Animations are
+ // throttled back to 60fps via SurfaceFlinger/View
+ // invalidates, _not_ by posting frame updates every 16
+ // milliseconds.
+ threadHandler.get().post(new Runnable() {
+ public void run() {
long loopFinishTime = SystemClock.uptimeMillis();
+
+ // Note: we do this early, before handling the
+ // violation below, as handling the violation
+ // may include PENALTY_DEATH and we don't want
+ // to keep the red border on.
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(false);
+ } catch (RemoteException unused) {
+ }
+ }
+
for (int n = 0; n < records.size(); ++n) {
ViolationInfo v = records.get(n);
v.violationNumThisLoop = n + 1;
@@ -988,13 +1015,6 @@ 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
}
});
}
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
index 481859e..f6d01d3 100644
--- a/core/java/android/preference/PreferenceFrameLayout.java
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -20,16 +20,22 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
/**
* @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 static final int DEFAULT_BORDER_TOP = 0;
+ private static final int DEFAULT_BORDER_BOTTOM = 0;
+ private static final int DEFAULT_BORDER_LEFT = 0;
+ private static final int DEFAULT_BORDER_RIGHT = 0;
+ private final int mBorderTop;
+ private final int mBorderBottom;
+ private final int mBorderLeft;
+ private final int mBorderRight;
private boolean mPaddingApplied = false;
public PreferenceFrameLayout(Context context) {
@@ -46,45 +52,98 @@ public class PreferenceFrameLayout extends FrameLayout {
com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);
float density = context.getResources().getDisplayMetrics().density;
- int defaultTopPadding = (int) (density * DEFAULT_TOP_PADDING + 0.5f);
- int defaultBottomPadding = (int) (density * DEFAULT_BOTTOM_PADDING + 0.5f);
-
- mTopPadding = a.getDimensionPixelSize(
- com.android.internal.R.styleable.PreferenceFrameLayout_topPadding,
- defaultTopPadding);
- mBottomPadding = a.getDimensionPixelSize(
- com.android.internal.R.styleable.PreferenceFrameLayout_bottomPadding,
- defaultBottomPadding);
+ int defaultBorderTop = (int) (density * DEFAULT_BORDER_TOP + 0.5f);
+ int defaultBottomPadding = (int) (density * DEFAULT_BORDER_BOTTOM + 0.5f);
+ int defaultLeftPadding = (int) (density * DEFAULT_BORDER_LEFT + 0.5f);
+ int defaultRightPadding = (int) (density * DEFAULT_BORDER_RIGHT + 0.5f);
+ mBorderTop = a.getDimensionPixelSize(
+ com.android.internal.R.styleable.PreferenceFrameLayout_borderTop,
+ defaultBorderTop);
+ mBorderBottom = a.getDimensionPixelSize(
+ com.android.internal.R.styleable.PreferenceFrameLayout_borderBottom,
+ defaultBottomPadding);
+ mBorderLeft = a.getDimensionPixelSize(
+ com.android.internal.R.styleable.PreferenceFrameLayout_borderLeft,
+ defaultLeftPadding);
+ mBorderRight = a.getDimensionPixelSize(
+ com.android.internal.R.styleable.PreferenceFrameLayout_borderRight,
+ defaultRightPadding);
a.recycle();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
@Override
public void addView(View child) {
- int topPadding = getPaddingTop();
- int bottomPadding = getPaddingBottom();
+ int borderTop = getPaddingTop();
+ int borderBottom = getPaddingBottom();
+ int borderLeft = getPaddingLeft();
+ int borderRight = getPaddingRight();
+
+ LayoutParams layoutParams = (PreferenceFrameLayout.LayoutParams) child.getLayoutParams();
// 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 (layoutParams != null && layoutParams.removeBorders) {
if (mPaddingApplied) {
- topPadding -= mTopPadding;
- bottomPadding -= mBottomPadding;
+ borderTop -= mBorderTop;
+ borderBottom -= mBorderBottom;
+ borderLeft -= mBorderLeft;
+ borderRight -= mBorderRight;
mPaddingApplied = false;
}
+ } else {
+ // Add the padding to the view group after determining if the
+ // padding already exists.
+ if (!mPaddingApplied) {
+ borderTop += mBorderTop;
+ borderBottom += mBorderBottom;
+ borderLeft += mBorderLeft;
+ borderRight += mBorderRight;
+ mPaddingApplied = true;
+ }
}
+
int previousTop = getPaddingTop();
int previousBottom = getPaddingBottom();
- if (previousTop != topPadding || previousBottom != bottomPadding) {
- setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+ int previousLeft = getPaddingLeft();
+ int previousRight = getPaddingRight();
+ if (previousTop != borderTop || previousBottom != borderBottom
+ || previousLeft != borderLeft || previousRight != borderRight) {
+ setPadding(borderLeft, borderTop, borderRight, borderBottom);
}
+
super.addView(child);
}
+
+ public static class LayoutParams extends FrameLayout.LayoutParams {
+ public boolean removeBorders = false;
+ /**
+ * {@inheritDoc}
+ */
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ TypedArray a = c.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.PreferenceFrameLayout_Layout);
+ removeBorders = a.getBoolean(
+ com.android.internal.R.styleable.PreferenceFrameLayout_Layout_layout_removeBorders,
+ false);
+ a.recycle();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+ }
} \ No newline at end of file
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 683e603..b2b8c5a 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -51,7 +51,14 @@ public final class Downloads {
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
- * The permission to directly access the download manager's cache directory
+ * The permission to access the all the downloads in the manager.
+ */
+ public static final String PERMISSION_ACCESS_ALL =
+ "android.permission.ACCESS_ALL_DOWNLOADS";
+
+ /**
+ * The permission to directly access the download manager's cache
+ * directory
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
diff --git a/core/java/android/provider/Ptp.java b/core/java/android/provider/Ptp.java
index 2c54370..0f0919e 100644
--- a/core/java/android/provider/Ptp.java
+++ b/core/java/android/provider/Ptp.java
@@ -20,10 +20,13 @@ import android.content.ContentUris;
import android.net.Uri;
import android.util.Log;
-
/**
* The PTP provider supports accessing content on PTP devices.
- * @hide
+ * Currently the provider supports:
+ * - enumerating the storage units, files and directories on PTP devices
+ * - deleting files and directories on PTP devices
+ * - importing a file from PTP device into the host device's storage
+ * and adding it to the media provider
*/
public final class Ptp
{
@@ -36,6 +39,8 @@ public final class Ptp
/**
* Contains list of all PTP devices
+ * The BaseColumns._ID column contains a hardware specific identifier for the attached
+ * USB device, and is not guaranteed to be persistent across USB disconnects.
*/
public static final class Device implements BaseColumns {
@@ -59,7 +64,8 @@ public final class Ptp
}
/**
- * Contains list of storage units for an PTP device
+ * Contains list of storage units for an PTP device.
+ * The BaseColumns._ID column contains the PTP StorageID for the storage unit.
*/
public static final class Storage implements BaseColumns {
@@ -85,7 +91,10 @@ public final class Ptp
}
/**
- * Contains list of objects on an PTP device
+ * Contains list of objects on a PTP device.
+ * The columns in this table correspond directly to the ObjectInfo dataset
+ * described in the PTP specification (PIMA 15740:2000).
+ * The BaseColumns._ID column contains the object's PTP ObjectHandle.
*/
public static final class Object implements BaseColumns {
@@ -135,14 +144,14 @@ public final class Ptp
public static final String STORAGE_ID = "storage_id";
/**
- * The object's format. Can be one of the FORMAT_* symbols below,
- * or any of the valid PTP object formats as defined in the PTP specification.
+ * The object's format. Can be any of the valid PTP object formats
+ * as defined in the PTP specification.
* <P>Type: INTEGER</P>
*/
public static final String FORMAT = "format";
/**
- * The protection status of the object. See the PROTECTION_STATUS_*symbols below.
+ * The protection status of the object.
* <P>Type: INTEGER</P>
*/
public static final String PROTECTION_STATUS = "protection_status";
@@ -154,8 +163,8 @@ public final class Ptp
public static final String SIZE = "size";
/**
- * The object's thumbnail format. Can be one of the FORMAT_* symbols below,
- * or any of the valid PTP object formats as defined in the PTP specification.
+ * The object's thumbnail format. Can be 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";
@@ -211,7 +220,6 @@ public final class Ptp
/**
* The association type for a container object.
- * For folders this is typically {@link #ASSOCIATION_TYPE_GENERIC_FOLDER}
* <P>Type: INTEGER</P>
*/
public static final String ASSOCIATION_TYPE = "association_type";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ea4a16..ed71da2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2576,12 +2576,13 @@ public final class Settings {
public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
/**
- * A positive value indicates the frequency of SamplingProfiler
- * taking snapshots in hertz. Zero value means SamplingProfiler is disabled.
+ * A positive value indicates how often the SamplingProfiler
+ * should take snapshots. Zero value means SamplingProfiler
+ * is disabled.
*
* @hide
*/
- public static final String SAMPLING_PROFILER_HZ = "sampling_profiler_hz";
+ public static final String SAMPLING_PROFILER_MS = "sampling_profiler_ms";
/**
* Settings classname to launch when Settings is clicked from All
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 2f7482c..a6fd2f1 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -663,6 +663,25 @@ class TextLine {
}
/**
+ * @param wp
+ */
+ private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
+ final int previousTop = fmi.top;
+ final int previousAscent = fmi.ascent;
+ final int previousDescent = fmi.descent;
+ final int previousBottom = fmi.bottom;
+ final int previousLeading = fmi.leading;
+
+ wp.getFontMetricsInt(fmi);
+
+ fmi.top = Math.min(fmi.top, previousTop);
+ fmi.ascent = Math.min(fmi.ascent, previousAscent);
+ fmi.descent = Math.max(fmi.descent, previousDescent);
+ fmi.bottom = Math.max(fmi.bottom, previousBottom);
+ fmi.leading = Math.max(fmi.leading, previousLeading);
+ }
+
+ /**
* Utility function for measuring and rendering text. The text must
* not include a tab or emoji.
*
@@ -703,7 +722,7 @@ class TextLine {
}
if (fmi != null) {
- wp.getFontMetricsInt(fmi);
+ expandMetricsFromPaint(fmi, wp);
}
if (c != null) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 0d012d6..d724320 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -190,7 +190,9 @@ public class ArrowKeyMovementMethod implements MovementMethod {
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1, initialScrollY = -1;
- if (event.getAction() == MotionEvent.ACTION_UP) {
+ final int action = event.getAction();
+
+ if (action == MotionEvent.ACTION_UP) {
initialScrollX = Touch.getInitialScrollX(widget, buffer);
initialScrollY = Touch.getInitialScrollY(widget, buffer);
}
@@ -198,7 +200,7 @@ public class ArrowKeyMovementMethod implements MovementMethod {
boolean handled = Touch.onTouchEvent(widget, buffer, event);
if (widget.isFocused() && !widget.didTouchFocusSelect()) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (action == MotionEvent.ACTION_DOWN) {
boolean cap = isCap(buffer);
if (cap) {
int offset = widget.getOffset((int) event.getX(), (int) event.getY());
@@ -211,7 +213,7 @@ public class ArrowKeyMovementMethod implements MovementMethod {
// mode once the view detected it needed to scroll.
widget.getParent().requestDisallowInterceptTouchEvent(true);
}
- } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ } else if (action == MotionEvent.ACTION_MOVE) {
boolean cap = isCap(buffer);
if (cap && handled) {
@@ -231,7 +233,7 @@ public class ArrowKeyMovementMethod implements MovementMethod {
Selection.extendSelection(buffer, offset);
return true;
}
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ } else if (action == MotionEvent.ACTION_UP) {
// If we have scrolled, then the up shouldn't move the cursor,
// but we do need to make sure the cursor is still visible at
// the current scroll offset to avoid the scroll jumping later
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index 3308172..09388c0 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -31,27 +31,48 @@ import android.view.View;
public class QwertyKeyListener extends BaseKeyListener {
private static QwertyKeyListener[] sInstance =
new QwertyKeyListener[Capitalize.values().length * 2];
+ private static QwertyKeyListener sFullKeyboardInstance;
- public QwertyKeyListener(Capitalize cap, boolean autotext) {
+ private Capitalize mAutoCap;
+ private boolean mAutoText;
+ private boolean mFullKeyboard;
+
+ private QwertyKeyListener(Capitalize cap, boolean autoText, boolean fullKeyboard) {
mAutoCap = cap;
- mAutoText = autotext;
+ mAutoText = autoText;
+ mFullKeyboard = fullKeyboard;
+ }
+
+ public QwertyKeyListener(Capitalize cap, boolean autoText) {
+ this(cap, autoText, false);
}
/**
* Returns a new or existing instance with the specified capitalization
* and correction properties.
*/
- public static QwertyKeyListener getInstance(boolean autotext,
- Capitalize cap) {
- int off = cap.ordinal() * 2 + (autotext ? 1 : 0);
+ public static QwertyKeyListener getInstance(boolean autoText, Capitalize cap) {
+ int off = cap.ordinal() * 2 + (autoText ? 1 : 0);
if (sInstance[off] == null) {
- sInstance[off] = new QwertyKeyListener(cap, autotext);
+ sInstance[off] = new QwertyKeyListener(cap, autoText);
}
return sInstance[off];
}
+ /**
+ * Gets an instance of the listener suitable for use with full keyboards.
+ * Disables auto-capitalization, auto-text and long-press initiated on-screen
+ * character pickers.
+ */
+ public static QwertyKeyListener getInstanceForFullKeyboard() {
+ if (sFullKeyboardInstance == null) {
+ sFullKeyboardInstance = new QwertyKeyListener(Capitalize.NONE, false, true);
+ }
+ return sFullKeyboardInstance;
+ }
+
public int getInputType() {
return makeTextContentType(mAutoCap, mAutoText);
}
@@ -85,14 +106,16 @@ public class QwertyKeyListener extends BaseKeyListener {
int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content));
- int count = event.getRepeatCount();
- if (count > 0 && selStart == selEnd && selStart > 0) {
- char c = content.charAt(selStart - 1);
+ if (!mFullKeyboard) {
+ int count = event.getRepeatCount();
+ if (count > 0 && selStart == selEnd && selStart > 0) {
+ char c = content.charAt(selStart - 1);
- if (c == i || c == Character.toUpperCase(i) && view != null) {
- if (showCharacterPicker(view, content, c, false, count)) {
- resetMetaState(content);
- return true;
+ if (c == i || c == Character.toUpperCase(i) && view != null) {
+ if (showCharacterPicker(view, content, c, false, count)) {
+ resetMetaState(content);
+ return true;
+ }
}
}
}
@@ -490,8 +513,5 @@ public class QwertyKeyListener extends BaseKeyListener {
private char[] mText;
}
-
- private Capitalize mAutoCap;
- private boolean mAutoText;
}
diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java
index 8ad6f50..8312fe1 100644
--- a/core/java/android/text/method/TextKeyListener.java
+++ b/core/java/android/text/method/TextKeyListener.java
@@ -189,7 +189,12 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
return MultiTapKeyListener.getInstance(mAutoText, mAutoCap);
} else if (kind == KeyCharacterMap.FULL
|| kind == KeyCharacterMap.SPECIAL_FUNCTION) {
- return QwertyKeyListener.getInstance(false, Capitalize.NONE);
+ // We consider special function keyboards full keyboards as a workaround for
+ // devices that do not have built-in keyboards. Applications may try to inject
+ // key events using the built-in keyboard device id which may be configured as
+ // a special function keyboard using a default key map. Ideally, as of Honeycomb,
+ // these applications should be modified to use KeyCharacterMap.VIRTUAL_KEYBOARD.
+ return QwertyKeyListener.getInstanceForFullKeyboard();
}
return NullKeyListener.getInstance();
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 07e87d6..6634f00 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -29,6 +29,7 @@ public class DragEvent implements Parcelable {
float mX, mY;
ClipDescription mClipDescription;
ClipData mClipData;
+ Object mLocalState;
boolean mDragResult;
private DragEvent mNext;
@@ -139,11 +140,11 @@ public static final int ACTION_DRAG_EXITED = 6;
}
static DragEvent obtain() {
- return DragEvent.obtain(0, 0f, 0f, null, null, false);
+ return DragEvent.obtain(0, 0f, 0f, null, null, null, false);
}
/** @hide */
- public static DragEvent obtain(int action, float x, float y,
+ public static DragEvent obtain(int action, float x, float y, Object localState,
ClipDescription description, ClipData data, boolean result) {
final DragEvent ev;
synchronized (gRecyclerLock) {
@@ -167,7 +168,7 @@ public static final int ACTION_DRAG_EXITED = 6;
/** @hide */
public static DragEvent obtain(DragEvent source) {
- return obtain(source.mAction, source.mX, source.mY,
+ return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
source.mClipDescription, source.mClipData, source.mDragResult);
}
@@ -218,6 +219,15 @@ public static final int ACTION_DRAG_EXITED = 6;
}
/**
+ * Provides the local state object passed as the {@code myLocalState} parameter to
+ * View.startDrag(). The object will always be null here if the application receiving
+ * the DragEvent is not the one that started the drag.
+ */
+ public Object getLocalState() {
+ return mLocalState;
+ }
+
+ /**
* Provides an indication of whether the drag operation concluded successfully.
* This method is only available on ACTION_DRAG_ENDED events.
* @return {@code true} if the drag operation ended with an accepted drop; {@code false}
@@ -249,6 +259,7 @@ public static final int ACTION_DRAG_EXITED = 6;
mClipData = null;
mClipDescription = null;
+ mLocalState = null;
synchronized (gRecyclerLock) {
if (gRecyclerUsed < MAX_RECYCLED) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 6f4abef..24a9f87 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -94,13 +94,18 @@ public abstract class HardwareRenderer {
*/
abstract void setup(int width, int height);
+ interface HardwareDrawCallbacks {
+ void onHardwarePreDraw(Canvas canvas);
+ void onHardwarePostDraw(Canvas canvas);
+ }
+
/**
* Draws the specified view.
*
* @param view The view to draw.
* @param attachInfo AttachInfo tied to the specified view.
*/
- abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
+ abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
/**
* Creates a new display list that can be used to record batches of
@@ -456,7 +461,7 @@ public abstract class HardwareRenderer {
}
@Override
- void draw(View view, View.AttachInfo attachInfo, int yOffset) {
+ void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
if (canDraw()) {
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
attachInfo.mIgnoreDirtyState = true;
@@ -473,11 +478,12 @@ public abstract class HardwareRenderer {
Canvas canvas = mCanvas;
int saveCount = canvas.save();
- canvas.translate(0, -yOffset);
+ callbacks.onHardwarePreDraw(canvas);
try {
view.draw(canvas);
} finally {
+ callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index fbd9eac..97bd8dd 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -31,7 +31,19 @@ import java.lang.Character;
public class KeyCharacterMap {
/**
* The id of the device's primary built in keyboard is always 0.
+ *
+ * @deprecated This constant should no longer be used because there is no
+ * guarantee that a device has a built-in keyboard that can be used for
+ * typing text. There might not be a built-in keyboard, the built-in keyboard
+ * might be a {@link #NUMERIC} or {@link #SPECIAL_FUNCTION} keyboard, or there
+ * might be multiple keyboards installed including external keyboards.
+ * When interpreting key presses received from the framework, applications should
+ * use the device id specified in the {@link KeyEvent} received.
+ * When synthesizing key presses for delivery elsewhere or when translating key presses
+ * from unknown keyboards, applications should use the special {@link #VIRTUAL_KEYBOARD}
+ * device id.
*/
+ @Deprecated
public static final int BUILT_IN_KEYBOARD = 0;
/**
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index b3277e4..03407a3 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1081,7 +1081,15 @@ public class KeyEvent extends InputEvent implements Parcelable {
static final boolean DEBUG = false;
static final String TAG = "KeyEvent";
-
+
+ private static final int MAX_RECYCLED = 10;
+ private static final Object gRecyclerLock = new Object();
+ private static int gRecyclerUsed;
+ private static KeyEvent gRecyclerTop;
+
+ private KeyEvent mNext;
+ private boolean mRecycled;
+
private int mMetaState;
private int mAction;
private int mKeyCode;
@@ -1160,6 +1168,9 @@ public class KeyEvent extends InputEvent implements Parcelable {
}
}
+ private KeyEvent() {
+ }
+
/**
* Create a new key event.
*
@@ -1382,6 +1393,67 @@ public class KeyEvent extends InputEvent implements Parcelable {
mCharacters = origEvent.mCharacters;
}
+ private static KeyEvent obtain() {
+ final KeyEvent ev;
+ synchronized (gRecyclerLock) {
+ ev = gRecyclerTop;
+ if (ev == null) {
+ return new KeyEvent();
+ }
+ gRecyclerTop = ev.mNext;
+ gRecyclerUsed -= 1;
+ }
+ ev.mRecycled = false;
+ ev.mNext = null;
+ return ev;
+ }
+
+ /**
+ * Obtains a (potentially recycled) key event.
+ *
+ * @hide
+ */
+ public static KeyEvent obtain(long downTime, long eventTime, int action,
+ int code, int repeat, int metaState,
+ int deviceId, int scancode, int flags, int source, String characters) {
+ KeyEvent ev = obtain();
+ ev.mDownTime = downTime;
+ ev.mEventTime = eventTime;
+ ev.mAction = action;
+ ev.mKeyCode = code;
+ ev.mRepeatCount = repeat;
+ ev.mMetaState = metaState;
+ ev.mDeviceId = deviceId;
+ ev.mScanCode = scancode;
+ ev.mFlags = flags;
+ ev.mSource = source;
+ ev.mCharacters = characters;
+ return ev;
+ }
+
+ /**
+ * Recycles a key event.
+ * Key events should only be recycled if they are owned by the system since user
+ * code expects them to be essentially immutable, "tracking" notwithstanding.
+ *
+ * @hide
+ */
+ public final void recycle() {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
+ mCharacters = null;
+
+ synchronized (gRecyclerLock) {
+ if (gRecyclerUsed < MAX_RECYCLED) {
+ gRecyclerUsed++;
+ mNext = gRecyclerTop;
+ gRecyclerTop = this;
+ }
+ }
+ }
+
/**
* Create a new key event that is the same as the given one, but whose
* event time and repeat count are replaced with the given value.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 195d689..e81aa98 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -314,10 +314,10 @@ public final class MotionEvent extends InputEvent implements Parcelable {
*/
static private final int BASE_AVAIL_SAMPLES = 8;
- static private final int MAX_RECYCLED = 10;
- static private Object gRecyclerLock = new Object();
- static private int gRecyclerUsed = 0;
- static private MotionEvent gRecyclerTop = null;
+ private static final int MAX_RECYCLED = 10;
+ private static final Object gRecyclerLock = new Object();
+ private static int gRecyclerUsed;
+ private static MotionEvent gRecyclerTop;
private long mDownTimeNano;
private int mAction;
@@ -361,7 +361,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
static private MotionEvent obtain(int pointerCount, int sampleCount) {
final MotionEvent ev;
synchronized (gRecyclerLock) {
- if (gRecyclerTop == null) {
+ ev = gRecyclerTop;
+ if (ev == null) {
if (pointerCount < BASE_AVAIL_POINTERS) {
pointerCount = BASE_AVAIL_POINTERS;
}
@@ -370,7 +371,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
return new MotionEvent(pointerCount, sampleCount);
}
- ev = gRecyclerTop;
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b45aa99..b9a9674 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -282,8 +282,12 @@ public class SurfaceView extends View {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
- int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
+ int width = mRequestedWidth >= 0
+ ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
+ : getDefaultSize(0, widthMeasureSpec);
+ int height = mRequestedHeight >= 0
+ ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
+ : getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1d0f185..daf9ac4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -986,6 +986,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public static final int FOCUS_DOWN = 0x00000082;
/**
+ * Bits of {@link #getMeasuredWidthAndState()} and
+ * {@link #getMeasuredWidthAndState()} that provide the actual measured size.
+ */
+ public static final int MEASURED_SIZE_MASK = 0x00ffffff;
+
+ /**
+ * Bits of {@link #getMeasuredWidthAndState()} and
+ * {@link #getMeasuredWidthAndState()} that provide the additional state bits.
+ */
+ public static final int MEASURED_STATE_MASK = 0xff000000;
+
+ /**
+ * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
+ * for functions that combine both width and height into a single int,
+ * such as {@link #getMeasuredState()} and the childState argument of
+ * {@link #resolveSizeAndState(int, int, int)}.
+ */
+ public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
+
+ /**
+ * Bit of {@link #getMeasuredWidthAndState()} and
+ * {@link #getMeasuredWidthAndState()} that indicates the measured size
+ * is smaller that the space the view would like to have.
+ */
+ public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
+
+ /**
* Base View state sets
*/
// Singles
@@ -1463,14 +1490,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "measurement")
- protected int mMeasuredWidth;
+ /*package*/ int mMeasuredWidth;
/**
* Height as measured during measure pass.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "measurement")
- protected int mMeasuredHeight;
+ /*package*/ int mMeasuredHeight;
/**
* The view's identifier.
@@ -5212,28 +5239,67 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * The width of this view as measured in the most recent call to measure().
+ * Like {@link #getMeasuredWidthAndState()}, but only returns the
+ * raw width component (that is the result is masked by
+ * {@link #MEASURED_SIZE_MASK}).
+ *
+ * @return The raw measured width of this view.
+ */
+ public final int getMeasuredWidth() {
+ return mMeasuredWidth & MEASURED_SIZE_MASK;
+ }
+
+ /**
+ * Return the full width measurement information for this view as computed
+ * by the most recent call to {@link #measure}. This result is a bit mask
+ * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getWidth()} to see how wide a view is after layout.
*
- * @return The measured width of this view.
+ * @return The measured width of this view as a bit mask.
*/
- public final int getMeasuredWidth() {
+ public final int getMeasuredWidthAndState() {
return mMeasuredWidth;
}
/**
- * The height of this view as measured in the most recent call to measure().
- * This should be used during measurement and layout calculations only. Use
- * {@link #getHeight()} to see how tall a view is after layout.
+ * Like {@link #getMeasuredHeightAndState()}, but only returns the
+ * raw width component (that is the result is masked by
+ * {@link #MEASURED_SIZE_MASK}).
*
- * @return The measured height of this view.
+ * @return The raw measured height of this view.
*/
public final int getMeasuredHeight() {
+ return mMeasuredHeight & MEASURED_SIZE_MASK;
+ }
+
+ /**
+ * Return the full height measurement information for this view as computed
+ * by the most recent call to {@link #measure}. This result is a bit mask
+ * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
+ * This should be used during measurement and layout calculations only. Use
+ * {@link #getHeight()} to see how wide a view is after layout.
+ *
+ * @return The measured width of this view as a bit mask.
+ */
+ public final int getMeasuredHeightAndState() {
return mMeasuredHeight;
}
/**
+ * Return only the state bits of {@link #getMeasuredWidthAndState()}
+ * and {@link #getMeasuredHeightAndState()}, combined into one integer.
+ * The width component is in the regular bits {@link #MEASURED_STATE_MASK}
+ * and the height component is at the shifted bits
+ * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
+ */
+ public final int getMeasuredState() {
+ return (mMeasuredWidth&MEASURED_STATE_MASK)
+ | ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)
+ & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ /**
* The transform matrix of this view, which is calculated based on the current
* roation, scale, and pivot properties.
*
@@ -9690,8 +9756,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* measured width and measured height. Failing to do so will trigger an
* exception at measurement time.</p>
*
- * @param measuredWidth the measured width of this view
- * @param measuredHeight the measured height of this view
+ * @param measuredWidth The measured width of this view. May be a complex
+ * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
+ * @param measuredHeight The measured height of this view. May be a complex
+ * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
*/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
@@ -9701,14 +9771,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * Utility to reconcile a desired size with constraints imposed by a MeasureSpec.
- * Will take the desired size, unless a different size is imposed by the constraints.
+ * Merge two states as returned by {@link #getMeasuredState()}.
+ * @param curState The current state as returned from a view or the result
+ * of combining multiple views.
+ * @param newState The new view state to combine.
+ * @return Returns a new integer reflecting the combination of the two
+ * states.
+ */
+ public static int combineMeasuredStates(int curState, int newState) {
+ return curState | newState;
+ }
+
+ /**
+ * Version of {@link #resolveSizeAndState(int, int, int)}
+ * returning only the {@link #MEASURED_SIZE_MASK} bits of the result.
+ */
+ public static int resolveSize(int size, int measureSpec) {
+ return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
+ }
+
+ /**
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Will take the desired size, unless a different size
+ * is imposed by the constraints. The returned value is a compound integer,
+ * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
+ * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
+ * size is smaller than the size the view wants to be.
*
* @param size How big the view wants to be
* @param measureSpec Constraints imposed by the parent
- * @return The size this view should be.
+ * @return Size information bit mask as defined by
+ * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
*/
- public static int resolveSize(int size, int measureSpec) {
+ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
@@ -9717,13 +9812,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
result = size;
break;
case MeasureSpec.AT_MOST:
- result = Math.min(size, specSize);
+ if (specSize < size) {
+ result = specSize | MEASURED_STATE_TOO_SMALL;
+ } else {
+ result = size;
+ }
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
- return result;
+ return result | (childMeasuredState&MEASURED_STATE_MASK);
}
/**
@@ -10075,9 +10174,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag
* operation is handed over to the OS.
* !!! TODO: real docs
+ *
+ * @param data !!! TODO
+ * @param thumbBuilder !!! TODO
+ * @param myWindowOnly When {@code true}, indicates that the drag operation should be
+ * restricted to the calling application. In this case only the calling application
+ * will see any DragEvents related to this drag operation.
+ * @param myLocalState An arbitrary object that will be passed as part of every DragEvent
+ * delivered to the calling application during the course of the current drag operation.
+ * This object is private to the application that called startDrag(), and is not
+ * visible to other applications. It provides a lightweight way for the application to
+ * propagate information from the initiator to the recipient of a drag within its own
+ * application; for example, to help disambiguate between 'copy' and 'move' semantics.
+ * @return {@code true} if the drag operation was initiated successfully; {@code false} if
+ * an error prevented the drag from taking place.
*/
public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder,
- boolean myWindowOnly) {
+ boolean myWindowOnly, Object myLocalState) {
if (ViewDebug.DEBUG_DRAG) {
Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " local=" + myWindowOnly);
}
@@ -10111,8 +10224,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
surface.unlockCanvasAndPost(canvas);
}
+ final ViewRoot root = getViewRoot();
+
+ // Cache the local state object for delivery with DragEvents
+ root.setLocalDragState(myLocalState);
+
// repurpose 'thumbSize' for the last touch point
- getViewRoot().getLastTouchPoint(thumbSize);
+ root.getLastTouchPoint(thumbSize);
okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token,
(float) thumbSize.x, (float) thumbSize.y,
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index bb85894..6b41ce5 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -166,7 +166,7 @@ public class ViewConfiguration {
/**
* Max distance to overfling for edge effects
*/
- private static final int OVERFLING_DISTANCE = 4;
+ private static final int OVERFLING_DISTANCE = 12;
private final int mEdgeSlop;
private final int mFadingEdgeLength;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index cb7d0e2..a34fe87 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -25,7 +25,9 @@ import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
@@ -57,6 +59,8 @@ import android.util.TypedValue;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -79,7 +83,8 @@ import java.util.ArrayList;
* {@hide}
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
-public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks {
+public final class ViewRoot extends Handler implements ViewParent,
+ View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
private static final String TAG = "ViewRoot";
private static final boolean DBG = false;
private static final boolean SHOW_FPS = false;
@@ -213,12 +218,17 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
int mScrollY;
int mCurScrollY;
Scroller mScroller;
+ Bitmap mResizeBitmap;
+ long mResizeBitmapStartTime;
+ int mResizeBitmapDuration;
+ static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
final ViewConfiguration mViewConfiguration;
/* Drag/drop */
ClipDescription mDragDescription;
View mCurrentDragView;
+ Object mLocalDragState;
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
@@ -576,6 +586,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
dirty.inset(-1, -1);
}
}
+ if (!mDirty.isEmpty()) {
+ mAttachInfo.mIgnoreDirtyState = true;
+ }
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
@@ -626,6 +639,13 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
return mAppVisible ? mView.getVisibility() : View.GONE;
}
+ void disposeResizeBitmap() {
+ if (mResizeBitmap != null) {
+ mResizeBitmap.recycle();
+ mResizeBitmap = null;
+ }
+ }
+
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
@@ -734,6 +754,48 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
+ if (mWidth > 0 && mHeight > 0 &&
+ mSurface != null && mSurface.isValid() &&
+ mAttachInfo.mHardwareRenderer != null &&
+ mAttachInfo.mHardwareRenderer.isEnabled() &&
+ lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
+
+ disposeResizeBitmap();
+
+ boolean completed = false;
+ try {
+ mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight,
+ Bitmap.Config.ARGB_8888);
+ mResizeBitmap.setHasAlpha(false);
+ Canvas canvas = new Canvas(mResizeBitmap);
+ int yoff;
+ final boolean scrolling = mScroller != null
+ && mScroller.computeScrollOffset();
+ if (scrolling) {
+ yoff = mScroller.getCurrY();
+ mScroller.abortAnimation();
+ } else {
+ yoff = mScrollY;
+ }
+ canvas.translate(0, -yoff);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
+ }
+ canvas.setScreenDensity(mAttachInfo.mScalingRequired
+ ? DisplayMetrics.DENSITY_DEVICE : 0);
+ mView.draw(canvas);
+ mResizeBitmapStartTime = SystemClock.uptimeMillis();
+ mResizeBitmapDuration = mView.getResources().getInteger(
+ com.android.internal.R.integer.config_mediumAnimTime);
+ completed = true;
+ } catch (OutOfMemoryError e) {
+ Log.w(TAG, "Not enough memory for content change anim buffer", e);
+ } finally {
+ if (!completed) {
+ mResizeBitmap = null;
+ }
+ }
+ }
mAttachInfo.mContentInsets.set(mPendingContentInsets);
host.fitSystemWindows(mAttachInfo.mContentInsets);
insetsChanged = true;
@@ -775,29 +837,23 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
- int maxHeight = (desiredWindowHeight*2)/3;
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
- + host.getWidth() + "," + host.getHeight() + ")");
- // Note: for now we are not taking into account height, since we
- // can't distinguish between places where it would be useful to
- // increase the width (text) vs. where it would not (a list).
- // Maybe we can just try the next size up, and see if that reduces
- // the height?
- if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) {
- Log.v(TAG, "Good!");
+ + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
+ if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
+ childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
- + host.getWidth() + "," + host.getHeight() + ")");
- if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) {
+ + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
+ if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
@@ -863,7 +919,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
boolean windowShouldResize = mLayoutRequested && windowSizeMayChange
- && ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
+ && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
@@ -910,8 +966,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
}
if (DEBUG_LAYOUT) {
- Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
- host.mMeasuredHeight + ", params=" + params);
+ Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
+ host.getMeasuredHeight() + ", params=" + params);
}
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
@@ -972,6 +1028,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
if (mScroller != null) {
mScroller.abortAnimation();
}
+ disposeResizeBitmap();
}
} catch (RemoteException e) {
}
@@ -1050,15 +1107,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
- if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
- || mHeight != host.mMeasuredHeight || contentInsetsChanged) {
+ if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
+ || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth="
- + mWidth + " measuredWidth=" + host.mMeasuredWidth
+ + mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
- + " measuredHeight" + host.mMeasuredHeight
+ + " measuredHeight" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
@@ -1067,8 +1124,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
- int width = host.mMeasuredWidth;
- int height = host.mMeasuredHeight;
+ int width = host.getMeasuredWidth();
+ int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
@@ -1103,12 +1160,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mScrollMayChange = true;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
TAG, "Laying out " + host + " to (" +
- host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
+ host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
long startTime = 0L;
if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
startTime = SystemClock.elapsedRealtime();
}
- host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
+ host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
@@ -1310,6 +1367,22 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
return measureSpec;
}
+ int mHardwareYOffset;
+ int mResizeAlpha;
+ final Paint mResizePaint = new Paint();
+
+ public void onHardwarePreDraw(Canvas canvas) {
+ canvas.translate(0, -mHardwareYOffset);
+ }
+
+ public void onHardwarePostDraw(Canvas canvas) {
+ if (mResizeBitmap != null) {
+ canvas.translate(0, mHardwareYOffset);
+ mResizePaint.setAlpha(mResizeAlpha);
+ canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint);
+ }
+ }
+
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (surface == null || !surface.isValid()) {
@@ -1334,8 +1407,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
int yoff;
- final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
- if (scrolling) {
+ boolean animating = mScroller != null && mScroller.computeScrollOffset();
+ if (animating) {
yoff = mScroller.getCurrY();
} else {
yoff = mScrollY;
@@ -1347,10 +1420,29 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
float appScale = mAttachInfo.mApplicationScale;
boolean scalingRequired = mAttachInfo.mScalingRequired;
+ int resizeAlpha = 0;
+ if (mResizeBitmap != null) {
+ long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime;
+ if (deltaTime < mResizeBitmapDuration) {
+ float amt = deltaTime/(float)mResizeBitmapDuration;
+ amt = mResizeInterpolator.getInterpolation(amt);
+ animating = true;
+ resizeAlpha = 255 - (int)(amt*255);
+ } else {
+ disposeResizeBitmap();
+ }
+ }
+
Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
dirty.setEmpty();
+ if (animating) {
+ if (mScroller != null) {
+ mScroller.abortAnimation();
+ }
+ disposeResizeBitmap();
+ }
return;
}
@@ -1363,10 +1455,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
if (!dirty.isEmpty() || mIsAnimating) {
mIsAnimating = false;
dirty.setEmpty();
- mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff);
+ mHardwareYOffset = yoff;
+ mResizeAlpha = resizeAlpha;
+ mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
}
- if (scrolling) {
+ if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
@@ -1486,7 +1580,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
}
- if (scrolling) {
+ if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
@@ -1600,7 +1694,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
if (scrollY != mScrollY) {
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+ mScrollY + " , new=" + scrollY);
- if (!immediate) {
+ if (!immediate && mResizeBitmap == null) {
if (mScroller == null) {
mScroller = new Scroller(mView.getContext());
}
@@ -2512,6 +2606,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
captureKeyLog("captureDispatchKeyEvent", event);
}
+ // Make sure the fallback event policy sees all keys that will be delivered to the
+ // view hierarchy.
+ mFallbackEventHandler.preDispatchKeyEvent(event);
+
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
finishKeyEvent(event, sendDone, true);
@@ -2585,6 +2683,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
/* drag/drop */
+ void setLocalDragState(Object obj) {
+ mLocalDragState = obj;
+ }
+
private void handleDragEvent(DragEvent event) {
// From the root, only drag start/end/location are dispatched. entered/exited
// are determined and dispatched by the viewgroup hierarchy, who then report
@@ -2643,7 +2745,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
}
- // Report the drop result if necessary
+ // Report the drop result when we're done
if (what == DragEvent.ACTION_DROP) {
try {
Log.i(TAG, "Reporting drop result: " + result);
@@ -2652,6 +2754,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
Log.e(TAG, "Unable to report drop result");
}
}
+
+ // When the drag operation ends, release any local state object
+ // that may have been in use
+ if (what == DragEvent.ACTION_DRAG_ENDED) {
+ setLocalDragState(null);
+ }
}
}
event.recycle();
@@ -2695,8 +2803,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
//Log.d(TAG, ">>>>>> CALLING relayout");
int relayoutResult = sWindowSession.relayout(
mWindow, params,
- (int) (mView.mMeasuredWidth * appScale + 0.5f),
- (int) (mView.mMeasuredHeight * appScale + 0.5f),
+ (int) (mView.getMeasuredWidth() * appScale + 0.5f),
+ (int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);
@@ -2968,6 +3076,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
} else {
what = DISPATCH_DRAG_EVENT;
}
+ event.mLocalState = mLocalDragState; // only present when this app called startDrag()
Message msg = obtainMessage(what, event);
sendMessage(msg);
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 5a9cd97..af36d80 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -551,19 +551,14 @@ public interface WindowManagerPolicy {
* affect the power state of the device, for example, the power keys.
* Generally, it's best to keep as little as possible in the queue thread
* because it's the most fragile.
- * @param whenNanos The event time in uptime nanoseconds.
- * @param action The key event action.
- * @param flags The key event flags.
- * @param keyCode The key code.
- * @param scanCode The key's scan code.
+ * @param event The key event.
* @param policyFlags The policy flags associated with the key.
* @param isScreenOn True if the screen is already on
*
* @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
* {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
*/
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn);
+ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
/**
* Called from the input dispatcher thread before a key is dispatched to a window.
@@ -574,18 +569,12 @@ public interface WindowManagerPolicy {
*
* @param win The window that currently has focus. This is where the key
* event will normally go.
- * @param action The key event action.
- * @param flags The key event flags.
- * @param keyCode The key code.
- * @param scanCode The key's scan code.
- * @param metaState bit mask of meta keys that are held.
- * @param repeatCount Number of times a key down has repeated.
+ * @param event The key event.
* @param policyFlags The policy flags associated with the key.
* @return Returns true if the policy consumed the event and it should
* not be further dispatched.
*/
- public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
- int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
+ public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
/**
* Called from the input dispatcher thread when an application did not handle
@@ -596,17 +585,11 @@ public interface WindowManagerPolicy {
*
* @param win The window that currently has focus. This is where the key
* event will normally go.
- * @param action The key event action.
- * @param flags The key event flags.
- * @param keyCode The key code.
- * @param scanCode The key's scan code.
- * @param metaState bit mask of meta keys that are held.
- * @param repeatCount Number of times a key down has repeated.
+ * @param event The key event.
* @param policyFlags The policy flags associated with the key.
* @return Returns true if the policy consumed the event.
*/
- public boolean dispatchUnhandledKey(WindowState win, int action, int flags,
- int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
+ public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
/**
* Called when layout of the windows is about to start.
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9568e4f..8f92458 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -571,8 +571,11 @@ class BrowserFrame extends Handler {
Iterator iter = mJSInterfaceMap.keySet().iterator();
while (iter.hasNext()) {
String interfaceName = (String) iter.next();
- nativeAddJavascriptInterface(nativeFramePointer,
- mJSInterfaceMap.get(interfaceName), interfaceName);
+ Object object = mJSInterfaceMap.get(interfaceName);
+ if (object != null) {
+ nativeAddJavascriptInterface(nativeFramePointer,
+ mJSInterfaceMap.get(interfaceName), interfaceName);
+ }
}
}
}
@@ -595,6 +598,7 @@ class BrowserFrame extends Handler {
}
public void addJavascriptInterface(Object obj, String interfaceName) {
+ assert obj != null;
if (mJSInterfaceMap == null) {
mJSInterfaceMap = new HashMap<String, Object>();
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 0e9d9b7..1647540 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -621,6 +621,20 @@ public final class CookieManager {
}
/**
+ * Tell the cookie store that this is a good time to flush cookies to flash.
+ *
+ * This should be called when the app is paused. Note that this method only
+ * acts as a hint, and may not have any effect. Flushing is asynchronous.
+ *
+ * @hide pending API council approval.
+ */
+ public void flushCookieStore() {
+ if (useChromiumHttpStack()) {
+ nativeFlushCookieStore();
+ }
+ }
+
+ /**
* Package level api, called from CookieSyncManager
*
* Get a list of cookies which are updated since a given time.
@@ -1078,4 +1092,5 @@ public final class CookieManager {
private static native void nativeRemoveSessionCookie();
private static native void nativeSetAcceptCookie(boolean accept);
private static native void nativeSetCookie(String url, String value);
+ private static native void nativeFlushCookieStore();
}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 27043e0..641604e 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -43,7 +43,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
private Resources mResources;
private boolean mMatchesFound;
private int mNumberOfMatches;
- private View mTitleBar;
private ActionMode mActionMode;
FindActionModeCallback(Context context) {
@@ -62,8 +61,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
mResources = context.getResources();
}
- void setTitleBar(View v) { mTitleBar = v; }
-
void finish() {
mActionMode.finish();
}
@@ -174,7 +171,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
@Override
public void onDestroyActionMode(ActionMode mode) {
- if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
mWebView.notifyFindDialogDismissed();
mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 6769563..85bff4f 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -138,6 +138,10 @@ class HTML5VideoViewProxy extends Handler
mCurrentProxy.dispatchOnEnded();
else
mCurrentProxy.dispatchOnPaused();
+
+ // Re enable plugin views.
+ mCurrentProxy.getWebView().getViewManager().showAll();
+
isVideoSelfEnded = false;
mCurrentProxy = null;
mLayout.removeView(mVideoView);
@@ -199,6 +203,9 @@ class HTML5VideoViewProxy extends Handler
mTimer = new Timer();
mVideoView.start();
client.onShowCustomView(mLayout, mCallback);
+ // Plugins like Flash will draw over the video so hide
+ // them while we're playing.
+ mCurrentProxy.getWebView().getViewManager().hideAll();
}
public static boolean isPlaying(HTML5VideoViewProxy proxy) {
@@ -599,6 +606,10 @@ class HTML5VideoViewProxy extends Handler
return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
}
+ /* package */ WebView getWebView() {
+ return mWebView;
+ }
+
private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
private native void nativeOnEnded(int nativePointer);
private native void nativeOnPaused(int nativePointer);
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index cf91902..2f3dc7c 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -25,15 +25,12 @@ import android.view.View;
class SelectActionModeCallback implements ActionMode.Callback {
private WebView mWebView;
- private View mTitleBar;
private ActionMode mActionMode;
void setWebView(WebView webView) {
mWebView = webView;
}
- void setTitleBar(View v) { mTitleBar = v; }
-
void finish() {
mActionMode.finish();
}
@@ -86,7 +83,6 @@ class SelectActionModeCallback implements ActionMode.Callback {
@Override
public void onDestroyActionMode(ActionMode mode) {
- if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
mWebView.selectionDone();
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 911b0b0..9becb6d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -314,6 +314,17 @@ public class WebView extends AbsoluteLayout
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener {
+ private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
+ public void onGlobalLayout() {
+ if (isShown()) {
+ setGLRectViewport();
+ }
+ }
+ }
+
+ // The listener to capture global layout change event.
+ private InnerGlobalLayoutListener mListener = null;
+
// if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
// the screen all-the-time. Good for profiling our drawing code
static private final boolean AUTO_REDRAW_HACK = false;
@@ -2183,14 +2194,13 @@ public class WebView extends AbsoluteLayout
// look at the cursor node, and not the focus node. Also, what is
// getFocusNodePath?
public void requestFocusNodeHref(Message hrefMsg) {
- if (hrefMsg == null || mNativeClass == 0) {
+ if (hrefMsg == null) {
return;
}
- if (nativeCursorIsAnchor()) {
- mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
- nativeCursorFramePointer(), nativeCursorNodePointer(),
- hrefMsg);
- }
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
+ mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
+ contentX, contentY, hrefMsg);
}
/**
@@ -2254,16 +2264,6 @@ public class WebView extends AbsoluteLayout
* @hide
*/
public void setEmbeddedTitleBar(View v) {
- if (null == v) {
- // If one of our callbacks is holding onto the titlebar to replace
- // it when its ActionMode ends, remove it.
- if (mSelectCallback != null) {
- mSelectCallback.setTitleBar(null);
- }
- if (mFindCallback != null) {
- mFindCallback.setTitleBar(null);
- }
- }
if (mTitleBar == v) return;
if (mTitleBar != null) {
removeView(mTitleBar);
@@ -2444,6 +2444,7 @@ public class WebView extends AbsoluteLayout
mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
nativeMoveGeneration(), mUserScroll ? 1 : 0, pos);
mLastVisibleRectSent = rect;
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
}
Rect globalRect = new Rect();
if (getGlobalVisibleRect(globalRect)
@@ -2472,15 +2473,14 @@ public class WebView extends AbsoluteLayout
// Sets r to be our visible rectangle in content coordinates
private void calcOurContentVisibleRect(Rect r) {
calcOurVisibleRect(r);
- // since we might overscroll, pin the rect to the bounds of the content
- r.left = Math.max(viewToContentX(r.left), 0);
+ r.left = viewToContentX(r.left);
// 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
// bar is partially visible, the part of the visible rect which is
// displaying our content is displaced by that amount.
- r.top = Math.max(viewToContentY(r.top + getVisibleTitleHeight()), 0);
- r.right = Math.min(viewToContentX(r.right), mContentWidth);
- r.bottom = Math.min(viewToContentY(r.bottom), mContentHeight);
+ r.top = viewToContentY(r.top + getVisibleTitleHeight());
+ r.right = viewToContentX(r.right);
+ r.bottom = viewToContentY(r.bottom);
}
// Sets r to be our visible rectangle in content coordinates. We use this
@@ -2490,15 +2490,14 @@ public class WebView extends AbsoluteLayout
private void calcOurContentVisibleRectF(RectF r) {
Rect ri = new Rect(0,0,0,0);
calcOurVisibleRect(ri);
- // pin the rect to the bounds of the content
- r.left = Math.max(viewToContentXf(ri.left), 0.0f);
+ r.left = viewToContentXf(ri.left);
// 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
// bar is partially visible, the part of the visible rect which is
// displaying our content is displaced by that amount.
- r.top = Math.max(viewToContentYf(ri.top + getVisibleTitleHeight()), 0.0f);
- r.right = Math.min(viewToContentXf(ri.right), (float)mContentWidth);
- r.bottom = Math.min(viewToContentYf(ri.bottom), (float)mContentHeight);
+ r.top = viewToContentYf(ri.top + getVisibleTitleHeight());
+ r.right = viewToContentXf(ri.right);
+ r.bottom = viewToContentYf(ri.bottom);
}
static class ViewSizeData {
@@ -2898,11 +2897,6 @@ public class WebView extends AbsoluteLayout
setFindIsUp(true);
mFindCallback.setWebView(this);
View titleBar = mTitleBar;
- // We do not want to show the embedded title bar during find or
- // select, but keep track of it so that it can be replaced when the
- // mode is exited.
- setEmbeddedTitleBar(null);
- mFindCallback.setTitleBar(titleBar);
startActionMode(mFindCallback);
if (text == null) {
text = mLastFind;
@@ -3497,10 +3491,14 @@ public class WebView extends AbsoluteLayout
* <li> The Java object that is bound runs in another thread and not in
* the thread that it was constructed in.</li>
* </ul></p>
- * @param obj The class instance to bind to Javascript
- * @param interfaceName The name to used to expose the class in Javascript
+ * @param obj The class instance to bind to Javascript, null instances are
+ * ignored.
+ * @param interfaceName The name to used to expose the class in JavaScript.
*/
public void addJavascriptInterface(Object obj, String interfaceName) {
+ if (obj == null) {
+ return;
+ }
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mObject = obj;
arg.mInterfaceName = interfaceName;
@@ -3742,6 +3740,8 @@ public class WebView extends AbsoluteLayout
if (mNativeClass != 0 && nativeWordSelection(x, y)) {
nativeSetExtendSelection();
mDrawSelectionPointer = false;
+ mSelectionStarted = true;
+ mTouchMode = TOUCH_DRAG_MODE;
return true;
}
selectionDone();
@@ -4626,12 +4626,6 @@ public class WebView extends AbsoluteLayout
nativeHideCursor();
mSelectCallback = new SelectActionModeCallback();
mSelectCallback.setWebView(this);
- View titleBar = mTitleBar;
- // We do not want to show the embedded title bar during find or
- // select, but keep track of it so that it can be replaced when the
- // mode is exited.
- setEmbeddedTitleBar(null);
- mSelectCallback.setTitleBar(titleBar);
startActionMode(mSelectCallback);
}
@@ -4709,6 +4703,11 @@ public class WebView extends AbsoluteLayout
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (hasWindowFocus()) setActive(true);
+ final ViewTreeObserver treeObserver = getViewTreeObserver();
+ if (treeObserver != null && mListener == null) {
+ mListener = new InnerGlobalLayoutListener();
+ treeObserver.addOnGlobalLayoutListener(mListener);
+ }
}
@Override
@@ -4716,6 +4715,13 @@ public class WebView extends AbsoluteLayout
clearHelpers();
mZoomManager.dismissZoomPicker();
if (hasWindowFocus()) setActive(false);
+
+ final ViewTreeObserver treeObserver = getViewTreeObserver();
+ if (treeObserver != null && mListener != null) {
+ treeObserver.removeGlobalOnLayoutListener(mListener);
+ mListener = null;
+ }
+
super.onDetachedFromWindow();
}
@@ -4856,13 +4862,19 @@ public class WebView extends AbsoluteLayout
}
void setGLRectViewport() {
- View window = getRootView();
- int[] location = new int[2];
- getLocationInWindow(location);
- mGLRectViewport = new Rect(location[0], window.getHeight()
- - (location[1] + getHeight()),
- location[0] + getWidth(),
- window.getHeight() - location[1]);
+ // Use the getGlobalVisibleRect() to get the intersection among the parents
+ Rect webViewRect = new Rect();
+ boolean visible = getGlobalVisibleRect(webViewRect);
+
+ // Then need to invert the Y axis, just for GL
+ View rootView = getRootView();
+ int rootViewHeight = rootView.getHeight();
+ int savedWebViewBottom = webViewRect.bottom;
+ webViewRect.bottom = rootViewHeight - webViewRect.top;
+ webViewRect.top = rootViewHeight - savedWebViewBottom;
+
+ // Store the viewport
+ mGLRectViewport = webViewRect;
}
/**
@@ -4998,10 +5010,10 @@ public class WebView extends AbsoluteLayout
startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
}
- private void startScrollingLayer(float gestureX, float gestureY) {
+ private void startScrollingLayer(float x, float y) {
if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
- int contentX = viewToContentX((int) gestureX + mScrollX);
- int contentY = viewToContentY((int) gestureY + mScrollY);
+ int contentX = viewToContentX((int) x + mScrollX);
+ int contentY = viewToContentY((int) y + mScrollY);
mScrollingLayer = nativeScrollableLayer(contentX, contentY);
if (mScrollingLayer != 0) {
mTouchMode = TOUCH_DRAG_LAYER_MODE;
@@ -5034,52 +5046,12 @@ public class WebView extends AbsoluteLayout
float y = ev.getY();
long eventTime = ev.getEventTime();
- final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
- 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;
- float gestureY = y;
- if (detector == null || !detector.isInProgress()) {
- // The gesture for scrolling a layer is two fingers close together.
- // FIXME: we may consider giving WebKit an option to handle
- // multi-touch events later.
- if (ev.getPointerCount() > 1) {
- float dx = ev.getX(1) - ev.getX(0);
- float dy = ev.getY(1) - ev.getY(0);
- float dist = (dx * dx + dy * dy) *
- DRAG_LAYER_INVERSE_DENSITY_SQUARED;
- // Use the approximate center to determine if the gesture is in
- // a layer.
- gestureX = ev.getX(0) + (dx * .5f);
- gestureY = ev.getY(0) + (dy * .5f);
- // Now use a consistent point for tracking movement.
- if (ev.getX(0) < ev.getX(1)) {
- x = ev.getX(0);
- y = ev.getY(0);
- } else {
- x = ev.getX(1);
- y = ev.getY(1);
- }
- action = ev.getActionMasked();
- if (dist < DRAG_LAYER_FINGER_DISTANCE) {
- isScrollGesture = true;
- } else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
- // Fingers moved too far apart while dragging, the user
- // might be trying to zoom.
- mTouchMode = TOUCH_INIT_MODE;
- }
- }
- }
-
- // 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()))) {
+ (mDeferMultitouch || mZoomManager.isZoomScaleFixed())) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit");
}
@@ -5087,8 +5059,11 @@ public class WebView extends AbsoluteLayout
return true;
}
+ final ScaleGestureDetector detector =
+ mZoomManager.getMultiTouchGestureDetector();
+
if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
- mTouchMode != TOUCH_DRAG_LAYER_MODE && !isScrollGesture) {
+ mTouchMode != TOUCH_DRAG_LAYER_MODE) {
if (!detector.isInProgress() &&
ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
// Insert a fake pointer down event in order to start
@@ -5378,9 +5353,6 @@ public class WebView extends AbsoluteLayout
deltaX = 0;
deltaY = 0;
- if (isScrollGesture) {
- startScrollingLayer(gestureX, gestureY);
- }
startDrag();
}
@@ -5449,6 +5421,7 @@ public class WebView extends AbsoluteLayout
mUserScroll = true;
}
+ startScrollingLayer(x, y);
doDrag(deltaX, deltaY);
// Turn off scrollbars when dragging a layer.
@@ -5555,6 +5528,7 @@ public class WebView extends AbsoluteLayout
break;
}
case TOUCH_DRAG_MODE:
+ case TOUCH_DRAG_LAYER_MODE:
mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
// if the user waits a while w/o moving before the
@@ -5586,7 +5560,6 @@ public class WebView extends AbsoluteLayout
invalidate();
// fall through
case TOUCH_DRAG_START_MODE:
- case TOUCH_DRAG_LAYER_MODE:
// TOUCH_DRAG_START_MODE should not happen for the real
// device as we almost certain will get a MOVE. But this
// is possible on emulator.
@@ -5676,8 +5649,10 @@ public class WebView extends AbsoluteLayout
deltaY = viewToContentDimension(deltaY);
if (nativeScrollLayer(mScrollingLayer, deltaX, deltaY)) {
invalidate();
+ return;
}
- return;
+ // Switch to drag mode and fall through.
+ mTouchMode = TOUCH_DRAG_MODE;
}
final int oldX = mScrollX;
@@ -6139,10 +6114,12 @@ public class WebView extends AbsoluteLayout
}
if (mOverscrollDistance < mOverflingDistance) {
- if (mScrollX == -mOverscrollDistance || mScrollX == maxX + mOverscrollDistance) {
+ if ((vx > 0 && mScrollX == -mOverscrollDistance) ||
+ (vx < 0 && mScrollX == maxX + mOverscrollDistance)) {
vx = 0;
}
- if (mScrollY == -mOverscrollDistance || mScrollY == maxY + mOverscrollDistance) {
+ if ((vy > 0 && mScrollY == -mOverscrollDistance) ||
+ (vy < 0 && mScrollY == maxY + mOverscrollDistance)) {
vy = 0;
}
}
@@ -6493,6 +6470,8 @@ public class WebView extends AbsoluteLayout
if (measuredHeight > heightSize) {
measuredHeight = heightSize;
mHeightCanMeasure = false;
+ } else if (measuredHeight < heightSize) {
+ measuredHeight |= MEASURED_STATE_TOO_SMALL;
}
}
} else {
@@ -6506,6 +6485,9 @@ public class WebView extends AbsoluteLayout
mWidthCanMeasure = true;
measuredWidth = contentWidth;
} else {
+ if (measuredWidth < contentWidth) {
+ measuredWidth |= MEASURED_STATE_TOO_SMALL;
+ }
mWidthCanMeasure = false;
}
@@ -6961,6 +6943,7 @@ public class WebView extends AbsoluteLayout
int deltaY = pinLocY((int) (mScrollY
+ mLastDeferTouchY - y))
- mScrollY;
+ startScrollingLayer(x, y);
doDrag(deltaX, deltaY);
if (deltaX != 0) mLastDeferTouchX = x;
if (deltaY != 0) mLastDeferTouchY = y;
@@ -7595,6 +7578,7 @@ public class WebView extends AbsoluteLayout
if (mNativeClass == 0) {
return false;
}
+ mInitialHitTestResult = null;
mLastCursorTime = time;
mLastCursorBounds = nativeGetCursorRingBounds();
boolean keyHandled
@@ -7715,6 +7699,10 @@ public class WebView extends AbsoluteLayout
mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
}
+ /* package */ ViewManager getViewManager() {
+ return mViewManager;
+ }
+
private native int nativeCacheHitFramePointer();
private native Rect nativeCacheHitNodeBounds();
private native int nativeCacheHitNodePointer();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b7d20b4..a7a839d 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -500,7 +500,7 @@ final class WebViewCore {
/**
* Update the layers' content
*/
- private native int nativeUpdateLayers();
+ private native int nativeUpdateLayers(Region invalRegion);
private native boolean nativeFocusBoundsChanged();
@@ -553,8 +553,8 @@ final class WebViewCore {
private native void nativeMoveMouseIfLatest(int moveGeneration,
int framePtr, int x, int y);
- private native String nativeRetrieveHref(int framePtr, int nodePtr);
- private native String nativeRetrieveAnchorText(int framePtr, int nodePtr);
+ private native String nativeRetrieveHref(int x, int y);
+ private native String nativeRetrieveAnchorText(int x, int y);
private native void nativeTouchUp(int touchGeneration,
int framePtr, int nodePtr, int x, int y);
@@ -1914,7 +1914,7 @@ final class WebViewCore {
return;
}
DrawData draw = new DrawData();
- draw.mBaseLayer = nativeUpdateLayers();
+ draw.mBaseLayer = nativeUpdateLayers(draw.mInvalRegion);
webkitDraw(draw);
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index dd71b3f..0da73a4 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -85,6 +85,16 @@ public abstract class AbsSeekBar extends ProgressBar {
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
+ boolean needUpdate;
+ // This way, calling setThumb again with the same bitmap will result in
+ // it recalcuating mThumbOffset (if for example it the bounds of the
+ // drawable changed)
+ if (mThumb != null && thumb != mThumb) {
+ mThumb.setCallback(null);
+ needUpdate = true;
+ } else {
+ needUpdate = false;
+ }
if (thumb != null) {
thumb.setCallback(this);
@@ -92,9 +102,25 @@ public abstract class AbsSeekBar extends ProgressBar {
// such that the thumb will hang halfway off either edge of the
// progress bar.
mThumbOffset = thumb.getIntrinsicWidth() / 2;
+
+ // If we're updating get the new states
+ if (needUpdate &&
+ (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth()
+ || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) {
+ requestLayout();
+ }
}
mThumb = thumb;
invalidate();
+ if (needUpdate) {
+ updateThumbPos(getWidth(), getHeight());
+ if (thumb.isStateful()) {
+ // Note that if the states are different this won't work.
+ // For now, let's consider that an app bug.
+ int[] state = getDrawableState();
+ thumb.setState(state);
+ }
+ }
}
/**
@@ -191,6 +217,10 @@ public abstract class AbsSeekBar extends ProgressBar {
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateThumbPos(w, h);
+ }
+
+ private void updateThumbPos(int w, int h) {
Drawable d = getCurrentDrawable();
Drawable thumb = mThumb;
int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
@@ -281,8 +311,8 @@ public abstract class AbsSeekBar extends ProgressBar {
dw += mPaddingLeft + mPaddingRight;
dh += mPaddingTop + mPaddingBottom;
- setMeasuredDimension(resolveSize(dw, widthMeasureSpec),
- resolveSize(dh, heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
+ resolveSizeAndState(dh, heightMeasureSpec, 0));
}
@Override
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 2b3b98d..3d79205 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -223,8 +223,8 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
- heightSize = resolveSize(preferredHeight, heightMeasureSpec);
- widthSize = resolveSize(preferredWidth, widthMeasureSpec);
+ heightSize = resolveSizeAndState(preferredHeight, heightMeasureSpec, 0);
+ widthSize = resolveSizeAndState(preferredWidth, widthMeasureSpec, 0);
setMeasuredDimension(widthSize, heightSize);
mHeightMeasureSpec = heightMeasureSpec;
diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java
index 970cbe3..ac82af7 100644
--- a/core/java/android/widget/AbsoluteLayout.java
+++ b/core/java/android/widget/AbsoluteLayout.java
@@ -88,8 +88,8 @@ public class AbsoluteLayout extends ViewGroup {
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
- resolveSize(maxHeight, heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
+ resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 695ea6b..162b030 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -648,8 +648,8 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
private void measureChildren() {
final int count = getChildCount();
- final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
- final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom;
+ final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
+ final int childHeight = getMeasuredHeight() - mPaddingTop - mPaddingBottom;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -674,16 +674,28 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
mPaddingBottom : 0;
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
- heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop +
- mPaddingBottom, heightSpecSize) : 0;
+ if (haveChildRefSize) {
+ int height = mReferenceChildHeight + mPaddingTop + mPaddingBottom;
+ if (height > heightSpecSize) {
+ heightSpecSize |= MEASURED_STATE_TOO_SMALL;
+ } else {
+ heightSpecSize = height;
+ }
+ }
}
if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
mPaddingRight : 0;
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
- widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
- mPaddingRight, widthSpecSize) : 0;
+ if (haveChildRefSize) {
+ int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight;
+ if (width > widthSpecSize) {
+ widthSpecSize |= MEASURED_STATE_TOO_SMALL;
+ } else {
+ widthSpecSize = width;
+ }
+ }
}
setMeasuredDimension(widthSpecSize, heightSpecSize);
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index f847bc3..84ebec3 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -146,8 +146,8 @@ public class AnalogClock extends View {
float scale = Math.min(hScale, vScale);
- setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
- resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0),
+ resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0));
}
@Override
diff --git a/core/java/android/widget/ButtonGroup.java b/core/java/android/widget/ButtonGroup.java
index 6af1c7e..7548ef6 100644
--- a/core/java/android/widget/ButtonGroup.java
+++ b/core/java/android/widget/ButtonGroup.java
@@ -167,12 +167,14 @@ public class ButtonGroup extends LinearLayout {
if (getOrientation() == VERTICAL) {
final int dividerSize = mDividerHeight * dividerCount;
- setMeasuredDimension(getMeasuredWidth(),
- resolveSize(getMeasuredHeight() + dividerSize, heightMeasureSpec));
+ setMeasuredDimension(getMeasuredWidthAndState(),
+ resolveSizeAndState(getMeasuredHeight() + dividerSize, heightMeasureSpec,
+ getMeasuredHeightAndState()));
} else {
final int dividerSize = mDividerWidth * dividerCount;
- setMeasuredDimension(resolveSize(getMeasuredWidth() + dividerSize, widthMeasureSpec),
- getMeasuredHeight());
+ setMeasuredDimension(resolveSizeAndState(getMeasuredWidth() + dividerSize,
+ widthMeasureSpec, getMeasuredWidthAndState()),
+ getMeasuredHeightAndState());
}
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index bcab7a9..940fec1 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -248,6 +248,7 @@ public class FrameLayout extends ViewGroup {
int maxHeight = 0;
int maxWidth = 0;
+ int childState = 0;
// Find rightmost and bottommost child
for (int i = 0; i < count; i++) {
@@ -256,6 +257,7 @@ public class FrameLayout extends ViewGroup {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
@@ -274,8 +276,9 @@ public class FrameLayout extends ViewGroup {
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
- resolveSize(maxHeight, heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState<<MEASURED_HEIGHT_STATE_SHIFT));
}
/**
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 9789658..ce76bee 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -814,7 +814,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
* @return Where the top of the child should be
*/
private int calculateTop(View child, boolean duringLayout) {
- int myHeight = duringLayout ? mMeasuredHeight : getHeight();
+ int myHeight = duringLayout ? getMeasuredHeight() : getHeight();
int childHeight = duringLayout ? child.getMeasuredHeight() : child.getHeight();
int childTop = 0;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 4146460..84bc5f2 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -979,6 +979,7 @@ public class GridView extends AbsListView {
determineColumns(childWidth);
int childHeight = 0;
+ int childState = 0;
mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
final int count = mItemCount;
@@ -1001,6 +1002,7 @@ public class GridView extends AbsListView {
child.measure(childWidthSpec, childHeightSpec);
childHeight = child.getMeasuredHeight();
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
if (mRecycler.shouldRecycleViewType(p.viewType)) {
mRecycler.addScrapView(child, -1);
@@ -1029,6 +1031,15 @@ public class GridView extends AbsListView {
heightSize = ourSize;
}
+ if (widthMode == MeasureSpec.AT_MOST && mRequestedNumColumns != AUTO_FIT) {
+ int ourSize = (mRequestedNumColumns*mColumnWidth)
+ + ((mRequestedNumColumns-1)*mHorizontalSpacing)
+ + mListPadding.left + mListPadding.right;
+ if (ourSize > widthSize) {
+ widthSize |= MEASURED_STATE_TOO_SMALL;
+ }
+ }
+
setMeasuredDimension(widthSize, heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 555d993..1fe6f4b 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -696,7 +696,7 @@ public class ImageView extends View {
/ desiredAspect) + ptop + pbottom;
if (newHeight <= heightSize) {
heightSize = newHeight;
- }
+ }
}
}
}
@@ -711,8 +711,8 @@ public class ImageView extends View {
w = Math.max(w, getSuggestedMinimumWidth());
h = Math.max(h, getSuggestedMinimumHeight());
- widthSize = resolveSize(w, widthMeasureSpec);
- heightSize = resolveSize(h, heightMeasureSpec);
+ widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
+ heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);
}
setMeasuredDimension(widthSize, heightSize);
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 1e5489a..a09e5c6 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -20,6 +20,8 @@ import com.android.internal.R;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -57,6 +59,23 @@ public class LinearLayout extends ViewGroup {
public static final int VERTICAL = 1;
/**
+ * Don't show any dividers.
+ */
+ public static final int SHOW_DIVIDER_NONE = 0;
+ /**
+ * Show a divider at the beginning of the group.
+ */
+ public static final int SHOW_DIVIDER_BEGINNING = 1;
+ /**
+ * Show dividers between each item in the group.
+ */
+ public static final int SHOW_DIVIDER_MIDDLE = 2;
+ /**
+ * Show a divider at the end of the group.
+ */
+ public static final int SHOW_DIVIDER_END = 4;
+
+ /**
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
@@ -119,6 +138,12 @@ public class LinearLayout extends ViewGroup {
private static final int INDEX_BOTTOM = 2;
private static final int INDEX_FILL = 3;
+ private Drawable mDivider;
+ private int mDividerWidth;
+ private int mDividerHeight;
+ private int mShowDividers;
+ private int mDividerPadding;
+
public LinearLayout(Context context) {
super(context);
}
@@ -155,10 +180,158 @@ public class LinearLayout extends ViewGroup {
mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
+ setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
+ mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
+ mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
+
a.recycle();
}
/**
+ * Set how dividers should be shown between items in this layout
+ *
+ * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
+ * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
+ * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
+ */
+ public void setShowDividers(int showDividers) {
+ if (showDividers != mShowDividers) {
+ requestLayout();
+ }
+ mShowDividers = showDividers;
+ }
+
+ /**
+ * @return A flag set indicating how dividers should be shown around items.
+ * @see #setShowDividers(int)
+ */
+ public int getShowDividers() {
+ return mShowDividers;
+ }
+
+ /**
+ * Set a drawable to be used as a divider between items.
+ * @param divider Drawable that will divide each item.
+ * @see #setShowDividers(int)
+ */
+ public void setDividerDrawable(Drawable divider) {
+ if (divider == mDivider) {
+ return;
+ }
+ mDivider = divider;
+ if (divider != null) {
+ mDividerWidth = divider.getIntrinsicWidth();
+ mDividerHeight = divider.getIntrinsicHeight();
+ } else {
+ mDividerWidth = 0;
+ mDividerHeight = 0;
+ }
+ setWillNotDraw(divider == null);
+ requestLayout();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDivider == null) {
+ return;
+ }
+
+ if (mOrientation == VERTICAL) {
+ drawDividersVertical(canvas);
+ } else {
+ drawDividersHorizontal(canvas);
+ }
+ }
+
+ void drawDividersVertical(Canvas canvas) {
+ final boolean showDividerBeginning =
+ (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+ final boolean showDividerEnd =
+ (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END;
+
+ final int count = getVirtualChildCount();
+ int top = getPaddingTop();
+ boolean firstVisible = true;
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null) {
+ top += measureNullChild(i);
+ } else if (child.getVisibility() != GONE) {
+ if (firstVisible) {
+ firstVisible = false;
+ if (showDividerBeginning) {
+ drawHorizontalDivider(canvas, top);
+ top += mDividerHeight;
+ }
+ } else if (showDividerMiddle) {
+ drawHorizontalDivider(canvas, top);
+ top += mDividerHeight;
+ }
+
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ top += lp.topMargin + child.getHeight() + lp.bottomMargin;
+ }
+ }
+
+ if (showDividerEnd) {
+ drawHorizontalDivider(canvas, top);
+ }
+ }
+
+ void drawDividersHorizontal(Canvas canvas) {
+ final boolean showDividerBeginning =
+ (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+ final boolean showDividerEnd =
+ (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END;
+
+ final int count = getVirtualChildCount();
+ int left = getPaddingLeft();
+ boolean firstVisible = true;
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null) {
+ left += measureNullChild(i);
+ } else if (child.getVisibility() != GONE) {
+ if (firstVisible) {
+ firstVisible = false;
+ if (showDividerBeginning) {
+ drawVerticalDivider(canvas, left);
+ left += mDividerWidth;
+ }
+ } else if (showDividerMiddle) {
+ drawVerticalDivider(canvas, left);
+ left += mDividerWidth;
+ }
+
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ left += lp.leftMargin + child.getWidth() + lp.rightMargin;
+ }
+ }
+
+ if (showDividerEnd) {
+ drawVerticalDivider(canvas, left);
+ }
+ }
+
+ void drawHorizontalDivider(Canvas canvas, int top) {
+ mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
+ getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
+ mDivider.draw(canvas);
+ }
+
+ void drawVerticalDivider(Canvas canvas, int left) {
+ mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
+ left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
+ mDivider.draw(canvas);
+ }
+
+ /**
* <p>Indicates whether widgets contained within this layout are aligned
* on their baseline or not.</p>
*
@@ -363,6 +536,7 @@ public class LinearLayout extends ViewGroup {
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0;
int maxWidth = 0;
+ int childState = 0;
int alternativeMaxWidth = 0;
int weightedMaxWidth = 0;
boolean allFillParent = true;
@@ -380,7 +554,14 @@ public class LinearLayout extends ViewGroup {
int largestChildHeight = Integer.MIN_VALUE;
+ // A divider at the end will change how much space views can consume.
+ final boolean showDividerBeginning =
+ (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+
// See how tall everyone is. Also remember max width.
+ boolean firstVisible = true;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -394,6 +575,15 @@ public class LinearLayout extends ViewGroup {
continue;
}
+ if (firstVisible) {
+ firstVisible = false;
+ if (showDividerBeginning) {
+ mTotalLength += mDividerHeight;
+ }
+ } else if (showDividerMiddle) {
+ mTotalLength += mDividerHeight;
+ }
+
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
@@ -469,6 +659,7 @@ public class LinearLayout extends ViewGroup {
final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
@@ -486,6 +677,10 @@ public class LinearLayout extends ViewGroup {
i += getChildrenSkipCount(child, i);
}
+ if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) {
+ mTotalLength += mDividerHeight;
+ }
+
if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
mTotalLength = 0;
@@ -520,7 +715,8 @@ public class LinearLayout extends ViewGroup {
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
// Reconcile our calculated size with the heightMeasureSpec
- heightSize = resolveSize(heightSize, heightMeasureSpec);
+ int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
+ heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds
@@ -569,6 +765,10 @@ public class LinearLayout extends ViewGroup {
MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
MeasureSpec.EXACTLY));
}
+
+ // Child may now not fit in vertical dimension.
+ childState = combineMeasuredStates(childState, child.getMeasuredState()
+ & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
}
final int margin = lp.leftMargin + lp.rightMargin;
@@ -605,7 +805,8 @@ public class LinearLayout extends ViewGroup {
// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ heightSizeAndState);
if (matchWidth) {
forceUniformWidth(count, heightMeasureSpec);
@@ -649,6 +850,7 @@ public class LinearLayout extends ViewGroup {
void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0;
int maxHeight = 0;
+ int childState = 0;
int alternativeMaxHeight = 0;
int weightedMaxHeight = 0;
boolean allFillParent = true;
@@ -679,7 +881,14 @@ public class LinearLayout extends ViewGroup {
int largestChildWidth = Integer.MIN_VALUE;
+ // A divider at the end will change how much space views can consume.
+ final boolean showDividerBeginning =
+ (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+
// See how wide everyone is. Also remember max height.
+ boolean firstVisible = true;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -693,6 +902,15 @@ public class LinearLayout extends ViewGroup {
continue;
}
+ if (firstVisible) {
+ firstVisible = false;
+ if (showDividerBeginning) {
+ mTotalLength += mDividerWidth;
+ }
+ } else if (showDividerMiddle) {
+ mTotalLength += mDividerWidth;
+ }
+
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
@@ -769,6 +987,7 @@ public class LinearLayout extends ViewGroup {
final int margin = lp.topMargin + lp.bottomMargin;
final int childHeight = child.getMeasuredHeight() + margin;
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
if (baselineAligned) {
final int childBaseline = child.getBaseline();
@@ -803,6 +1022,10 @@ public class LinearLayout extends ViewGroup {
i += getChildrenSkipCount(child, i);
}
+ if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) {
+ mTotalLength += mDividerWidth;
+ }
+
// Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
// the most common case
if (maxAscent[INDEX_TOP] != -1 ||
@@ -856,7 +1079,8 @@ public class LinearLayout extends ViewGroup {
widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
// Reconcile our calculated size with the widthMeasureSpec
- widthSize = resolveSize(widthSize, widthMeasureSpec);
+ int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
+ widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds
@@ -911,6 +1135,10 @@ public class LinearLayout extends ViewGroup {
share > 0 ? share : 0, MeasureSpec.EXACTLY),
childHeightMeasureSpec);
}
+
+ // Child may now not fit in horizontal dimension.
+ childState = combineMeasuredStates(childState,
+ child.getMeasuredState() & MEASURED_STATE_MASK);
}
if (isExactly) {
@@ -980,7 +1208,9 @@ public class LinearLayout extends ViewGroup {
// Check against our minimum height
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- setMeasuredDimension(widthSize, resolveSize(maxHeight, heightMeasureSpec));
+ setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
if (matchHeight) {
forceUniformHeight(count, widthMeasureSpec);
@@ -1127,7 +1357,14 @@ public class LinearLayout extends ViewGroup {
}
}
-
+
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+
+ if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) {
+ childTop += mDividerHeight;
+ }
+
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
@@ -1162,12 +1399,15 @@ public class LinearLayout extends ViewGroup {
break;
}
-
childTop += lp.topMargin;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
+ if (showDividerMiddle) {
+ childTop += mDividerHeight;
+ }
+
i += getChildrenSkipCount(child, i);
}
}
@@ -1216,7 +1456,14 @@ public class LinearLayout extends ViewGroup {
childLeft += ((mRight - mLeft) - mTotalLength) / 2;
break;
}
- }
+ }
+
+ final boolean showDividerMiddle =
+ (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+
+ if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) {
+ childLeft += mDividerWidth;
+ }
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
@@ -1282,6 +1529,10 @@ public class LinearLayout extends ViewGroup {
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
+ if (showDividerMiddle) {
+ childLeft += mDividerWidth;
+ }
+
i += getChildrenSkipCount(child, i);
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index fd4f950..3703846 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1102,6 +1102,7 @@ public class ListView extends AbsListView {
int childWidth = 0;
int childHeight = 0;
+ int childState = 0;
mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
@@ -1112,6 +1113,7 @@ public class ListView extends AbsListView {
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
((LayoutParams) child.getLayoutParams()).viewType)) {
@@ -1122,6 +1124,8 @@ public class ListView extends AbsListView {
if (widthMode == MeasureSpec.UNSPECIFIED) {
widthSize = mListPadding.left + mListPadding.right + childWidth +
getVerticalScrollbarWidth();
+ } else {
+ widthSize |= (childState&MEASURED_STATE_MASK);
}
if (heightMode == MeasureSpec.UNSPECIFIED) {
@@ -1134,7 +1138,7 @@ public class ListView extends AbsListView {
heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
}
- setMeasuredDimension(widthSize, heightSize);
+ setMeasuredDimension(widthSize , heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}
@@ -3196,7 +3200,8 @@ public class ListView extends AbsListView {
}
mDivider = divider;
mDividerIsOpaque = divider == null || divider.getOpacity() == PixelFormat.OPAQUE;
- requestLayoutIfNecessary();
+ requestLayout();
+ invalidate();
}
/**
@@ -3214,7 +3219,8 @@ public class ListView extends AbsListView {
*/
public void setDividerHeight(int height) {
mDividerHeight = height;
- requestLayoutIfNecessary();
+ requestLayout();
+ invalidate();
}
/**
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index cd81e31..b9e59d6 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -17,9 +17,12 @@
package android.widget;
import android.content.Context;
-import android.graphics.Interpolator;
+import android.hardware.SensorManager;
+import android.util.FloatMath;
+import android.util.Log;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
/**
* This class encapsulates scrolling with the ability to overshoot the bounds
@@ -27,65 +30,51 @@ import android.view.animation.AnimationUtils;
* {@link android.widget.Scroller} in most cases.
*/
public class OverScroller {
- int mMode;
+ private int mMode;
- private final MagneticOverScroller mScrollerX;
- private final MagneticOverScroller mScrollerY;
+ private final SplineOverScroller mScrollerX;
+ private final SplineOverScroller mScrollerY;
- private float mDeceleration;
- private final float mPpi;
- private final boolean mFlywheel;
+ private final Interpolator mInterpolator;
- 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 final boolean mFlywheel;
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;
- }
+ /**
+ * Creates an OverScroller with a viscous fluid scroll interpolator.
+ * @param context
+ */
+ public OverScroller(Context context) {
+ this(context, null);
+ }
- {
- 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;
+ /**
+ * Creates an OverScroller with default edge bounce coefficients and flywheel enabled.
+ * @param context The context of this application.
+ * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
+ * be used.
+ */
+ public OverScroller(Context context, Interpolator interpolator) {
+ this(context, interpolator, SplineOverScroller.DEFAULT_BOUNCE_COEFFICIENT,
+ SplineOverScroller.DEFAULT_BOUNCE_COEFFICIENT);
}
- public OverScroller(Context context) {
- this(context, null, 0.f, 0.f, true);
+ /**
+ * Creates an OverScroller with flywheel enabled.
+ * @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) {
+ this(context, interpolator, bounceCoefficientX, bounceCoefficientY, true);
}
/**
@@ -97,20 +86,21 @@ public class OverScroller {
* 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.
+ * @param flywheel If true, successive fling motions will keep on increasing scroll speed.
*/
public OverScroller(Context context, Interpolator interpolator,
float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
+ mInterpolator = interpolator;
mFlywheel = flywheel;
- mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
- mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
- mScrollerX = new MagneticOverScroller();
- mScrollerY = new MagneticOverScroller();
+ mScrollerX = new SplineOverScroller();
+ mScrollerY = new SplineOverScroller();
+
+ SplineOverScroller.initFromContext(context);
mScrollerX.setBounceCoefficient(bounceCoefficientX);
mScrollerY.setBounceCoefficient(bounceCoefficientY);
}
-
/**
* The amount of friction applied to flings. The default value
* is {@link ViewConfiguration#getScrollFriction}.
@@ -119,14 +109,8 @@ public class OverScroller {
* 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;
+ mScrollerX.setFriction(friction);
+ mScrollerY.setFriction(friction);
}
/**
@@ -178,7 +162,7 @@ public class OverScroller {
public float getCurrVelocity() {
float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
- return (float) Math.sqrt(squaredNorm);
+ return FloatMath.sqrt(squaredNorm);
}
/**
@@ -307,7 +291,11 @@ public class OverScroller {
if (elapsedTime < duration) {
float q = (float) (elapsedTime) / duration;
- q = Scroller.viscousFluid(q);
+ if (mInterpolator == null) {
+ q = Scroller.viscousFluid(q);
+ } else {
+ q = mInterpolator.getInterpolation(q);
+ }
mScrollerX.updateScroll(q);
mScrollerY.updateScroll(q);
@@ -496,9 +484,9 @@ public class OverScroller {
*/
public boolean isOverScrolled() {
return ((!mScrollerX.mFinished &&
- mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+ mScrollerX.mState != SplineOverScroller.TO_EDGE) ||
(!mScrollerY.mFinished &&
- mScrollerY.mState != MagneticOverScroller.TO_EDGE));
+ mScrollerY.mState != SplineOverScroller.TO_EDGE));
}
/**
@@ -536,60 +524,126 @@ public class OverScroller {
Math.signum(yvel) == Math.signum(dy);
}
- class MagneticOverScroller {
+ static class SplineOverScroller {
// Initial position
- int mStart;
+ private int mStart;
// Current position
- int mCurrentPosition;
+ private int mCurrentPosition;
// Final position
- int mFinal;
+ private int mFinal;
// Initial velocity
- int mVelocity;
+ private int mVelocity;
// Current velocity
- float mCurrVelocity;
+ private float mCurrVelocity;
// Constant current deceleration
- float mDeceleration;
+ private float mDeceleration;
// Animation starting time, in system milliseconds
- long mStartTime;
+ private long mStartTime;
// Animation duration, in milliseconds
- int mDuration;
+ private int mDuration;
// Duration to complete spline component of animation
- int mSplineDuration;
+ private int mSplineDuration;
// Distance to travel along spline animation
- int mSplineDistance;
+ private int mSplineDistance;
// Whether the animation is currently in progress
- boolean mFinished;
+ private boolean mFinished;
- private static final int TO_EDGE = 0;
- private static final int TO_BOUNDARY = 1;
- private static final int TO_BOUNCE = 2;
+ // The allowed overshot distance before boundary is reached.
+ private int mOver;
+ // Fling friction
+ private float mFlingFriction = ViewConfiguration.getScrollFriction();
+
+ // Proportion of velocity preserved at the end of a bounce animation.
+ private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
+
+ // Current state of the animation.
private int mState = TO_EDGE;
- // The allowed overshot distance before boundary is reached.
- private int mOver;
+ // Constant gravity value, used in the deceleration phase.
+ private static final float GRAVITY = 2000.0f;
+
+ // A device specific coefficient adjusted to physical values.
+ private static float PHYSICAL_COEF;
+
+ private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
+ private static final float INFLEXION = 0.3f; // Tension lines cross at (INFLEXION, 1)
+ private static final float START_TENSION = 0.7f;
+ private static final float END_TENSION = 0.8f;
+ private static final float P1 = START_TENSION * INFLEXION;
+ private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
+
+ 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 TO_EDGE = 0;
+ private static final int TO_BOUNDARY = 1;
+ private static final int TO_BOUNCE = 2;
// 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;
+ // when the edge limits are reached.
+ private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 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 static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f;
- private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
+ 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;
- MagneticOverScroller() {
+ 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) * P1 + x * P2) + 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 * ((1.0f - x) * START_TENSION + x) + x * x * x;
+
+ float y_max = 1.0f;
+ float y, dy;
+ while (true) {
+ y = y_min + (y_max - y_min) / 2.0f;
+ coef = 3.0f * y * (1.0f - y);
+ dy = coef * ((1.0f - y) * START_TENSION + y) + 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) * P1 + y * P2) + y * y * y;
+ }
+ SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
+ }
+
+ static void initFromContext(Context context) {
+ final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
+ PHYSICAL_COEF = SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * ppi
+ * 0.84f; // look and feel tuning
+ }
+
+ void setFriction(float friction) {
+ mFlingFriction = friction;
+ }
+
+ SplineOverScroller() {
mFinished = true;
}
@@ -600,18 +654,18 @@ public class OverScroller {
/*
* Get a signed deceleration that will reduce the velocity.
*/
- float getDeceleration(int velocity) {
- return velocity > 0 ? -OverScroller.this.mDeceleration : OverScroller.this.mDeceleration;
+ static private float getDeceleration(int velocity) {
+ return velocity > 0 ? -GRAVITY : GRAVITY;
}
/*
* 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) {
+ private 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 float x = Math.abs((float) newDistance / oldDistance);
final int index = (int) (NB_SAMPLES * x);
if (index < NB_SAMPLES) {
final float x_inf = (float) index / NB_SAMPLES;
@@ -619,7 +673,6 @@ public class OverScroller {
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;
}
}
@@ -696,7 +749,7 @@ public class OverScroller {
mCurrVelocity = mVelocity = velocity;
mDuration = mSplineDuration = 0;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStart = start;
+ mCurrentPosition = mStart = start;
if (start > max || start < min) {
startAfterEdge(start, min, max, velocity);
@@ -707,10 +760,8 @@ public class OverScroller {
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));
+ mDuration = mSplineDuration = getSplineFlingDuration(velocity);
+ totalDistance = getSplineFlingDistance(velocity);
}
mSplineDistance = (int) (totalDistance * Math.signum(velocity));
@@ -728,6 +779,23 @@ public class OverScroller {
}
}
+ private double getSplineDeceleration(int velocity) {
+ return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * PHYSICAL_COEF));
+ }
+
+ private double getSplineFlingDistance(int velocity) {
+ final double l = getSplineDeceleration(velocity);
+ final double decelMinusOne = DECELERATION_RATE - 1.0;
+ return mFlingFriction * PHYSICAL_COEF * Math.exp(DECELERATION_RATE / decelMinusOne * l);
+ }
+
+ /* Returns the duration, expressed in milliseconds */
+ private int getSplineFlingDuration(int velocity) {
+ final double l = getSplineDeceleration(velocity);
+ final double decelMinusOne = DECELERATION_RATE - 1.0;
+ return (int) (1000.0 * Math.exp(l / decelMinusOne));
+ }
+
private void fitOnBounceCurve(int start, int end, int velocity) {
// Simulate a bounce that started from edge
final float durationToApex = - velocity / mDeceleration;
@@ -748,6 +816,7 @@ public class OverScroller {
private void startAfterEdge(int start, int min, int max, int velocity) {
if (start > min && start < max) {
+ Log.e("OverScroller", "startAfterEdge called from a valid position");
mFinished = true;
return;
}
@@ -759,9 +828,7 @@ public class OverScroller {
// 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));
+ final double totalDistance = getSplineFlingDistance(velocity);
if (totalDistance > Math.abs(overDistance)) {
fling(start, velocity, positive ? min : start, positive ? start : max, mOver);
} else {
@@ -771,11 +838,13 @@ public class OverScroller {
}
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);
+ if (mState == TO_EDGE) {
+ 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() {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 63fb3e9..85ca5f3 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -891,8 +891,8 @@ public class ProgressBar extends View {
dw += mPaddingLeft + mPaddingRight;
dh += mPaddingTop + mPaddingBottom;
- setMeasuredDimension(resolveSize(dw, widthMeasureSpec),
- resolveSize(dh, heightMeasureSpec));
+ setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
+ resolveSizeAndState(dh, heightMeasureSpec, 0));
}
@Override
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 28499d0..9e6ff4b 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -280,7 +280,8 @@ public class RatingBar extends AbsSeekBar {
// TODO: Once ProgressBar's TODOs are gone, this can be done more
// cleanly than mSampleTile
final int width = mSampleTile.getWidth() * mNumStars;
- setMeasuredDimension(resolveSize(width, widthMeasureSpec), mMeasuredHeight);
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
+ getMeasuredHeight());
}
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 029aebf..b296b77 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -18,8 +18,6 @@ package android.widget;
import static android.widget.SuggestionsAdapter.getColumnString;
-import com.android.internal.R;
-
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.SearchableInfo;
@@ -29,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
@@ -50,6 +49,8 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.R;
+
import java.util.WeakHashMap;
/**
@@ -84,7 +85,7 @@ public class SearchView extends LinearLayout {
private View mCloseButton;
private View mSearchEditFrame;
private View mVoiceButton;
- private AutoCompleteTextView mQueryTextView;
+ private SearchAutoComplete mQueryTextView;
private boolean mSubmitButtonEnabled;
private CharSequence mQueryHint;
private boolean mQueryRefinement;
@@ -181,7 +182,9 @@ public class SearchView extends LinearLayout {
inflater.inflate(R.layout.search_view, this, true);
mSearchButton = findViewById(R.id.search_button);
- mQueryTextView = (AutoCompleteTextView) findViewById(R.id.search_src_text);
+ mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+ mQueryTextView.setSearchView(this);
+
mSearchEditFrame = findViewById(R.id.search_edit_frame);
mSubmitButton = findViewById(R.id.search_go_btn);
mCloseButton = findViewById(R.id.search_close_btn);
@@ -196,6 +199,7 @@ public class SearchView extends LinearLayout {
mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
mQueryTextView.setOnItemClickListener(mOnItemClickListener);
mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+ mQueryTextView.setOnKeyListener(mTextKeyListener);
// Inform any listener of focus changes
mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
@@ -558,6 +562,148 @@ public class SearchView extends LinearLayout {
return super.onKeyDown(keyCode, event);
}
+ /**
+ * React to the user typing "enter" or other hardwired keys while typing in
+ * the search box. This handles these special keys while the edit box has
+ * focus.
+ */
+ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // guard against possible race conditions
+ if (mSearchable == null) {
+ return false;
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
+ + mQueryTextView.getListSelection());
+ }
+
+ // If a suggestion is selected, handle enter, search key, and action keys
+ // as presses on the selected suggestion
+ if (mQueryTextView.isPopupShowing()
+ && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+ return onSuggestionsKey(v, keyCode, event);
+ }
+
+ // If there is text in the query box, handle enter, and action keys
+ // The search key is handled by the dialog's onKeyDown().
+ if (!mQueryTextView.isEmpty()) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+ v.cancelLongPress();
+
+ // Launch as a regular search.
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+ .toString());
+ return true;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView
+ .getText().toString());
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ };
+
+ /**
+ * React to the user typing while in the suggestions list. First, check for
+ * action keys. If not handled, try refocusing regular characters into the
+ * EditText.
+ */
+ private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
+ // guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable == null) {
+ return false;
+ }
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+
+ // First, check for enter or search (both of which we'll treat as a
+ // "click")
+ if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
+ int position = mQueryTextView.getListSelection();
+ return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
+ }
+
+ // Next, check for left/right moves, which we use to "return" the
+ // user to the edit view
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ // give "focus" to text editor, with cursor at the beginning if
+ // left key, at end if right key
+ // TODO: Reverse left/right for right-to-left languages, e.g.
+ // Arabic
+ int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+ .length();
+ mQueryTextView.setSelection(selPoint);
+ mQueryTextView.setListSelection(0);
+ mQueryTextView.clearListSelection();
+ mQueryTextView.ensureImeVisible(true);
+
+ return true;
+ }
+
+ // Next, check for an "up and out" move
+ if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+ // TODO: restoreUserQuery();
+ // let ACTV complete the move
+ return false;
+ }
+
+ // Next, check for an "action key"
+ SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ if ((actionKey != null)
+ && ((actionKey.getSuggestActionMsg() != null) || (actionKey
+ .getSuggestActionMsgColumn() != null))) {
+ // launch suggestion using action key column
+ int position = mQueryTextView.getListSelection();
+ if (position != ListView.INVALID_POSITION) {
+ Cursor c = mSuggestionsAdapter.getCursor();
+ if (c.moveToPosition(position)) {
+ final String actionMsg = getActionKeyMessage(c, actionKey);
+ if (actionMsg != null && (actionMsg.length() > 0)) {
+ return onItemClicked(position, keyCode, actionMsg);
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * For a given suggestion and a given cursor row, get the action message. If
+ * not provided by the specific row/column, also check for a single
+ * definition (for the action key).
+ *
+ * @param c The cursor providing suggestions
+ * @param actionKey The actionkey record being examined
+ *
+ * @return Returns a string, or null if no action key message for this
+ * suggestion
+ */
+ private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
+ String result = null;
+ // check first in the cursor data, for a suggestion-specific message
+ final String column = actionKey.getSuggestActionMsgColumn();
+ if (column != null) {
+ result = SuggestionsAdapter.getColumnString(c, column);
+ }
+ // If the cursor didn't give us a message, see if there's a single
+ // message defined
+ // for the actionkey (for all suggestions)
+ if (result == null) {
+ result = actionKey.getSuggestActionMsg();
+ }
+ return result;
+ }
+
private void updateQueryHint() {
if (mQueryHint != null) {
mQueryTextView.setHint(mQueryHint);
@@ -710,20 +856,34 @@ public class SearchView extends LinearLayout {
}
}
+ private boolean onItemClicked(int position, int actionKey, String actionMsg) {
+ if (mOnSuggestionListener == null
+ || !mOnSuggestionListener.onSuggestionClicked(position)) {
+ launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
+ setImeVisibility(false);
+ dismissSuggestions();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onItemSelected(int position) {
+ if (mOnSuggestionListener == null
+ || !mOnSuggestionListener.onSuggestionSelected(position)) {
+ rewriteQueryFromSuggestion(position);
+ return true;
+ }
+ return false;
+ }
+
private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
/**
* Implements OnItemClickListener
*/
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (DBG)
- Log.d(LOG_TAG, "onItemClick() position " + position);
- if (mOnSuggestionListener == null
- || !mOnSuggestionListener.onSuggestionClicked(position)) {
- launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
- setImeVisibility(false);
- dismissSuggestions();
- }
+ if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position);
+ onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
};
@@ -733,14 +893,8 @@ public class SearchView extends LinearLayout {
* Implements OnItemSelectedListener
*/
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (DBG)
- Log.d(LOG_TAG, "onItemSelected() position " + position);
- // A suggestion has been selected, rewrite the query if possible,
- // otherwise the restore the original query.
- if (mOnSuggestionListener == null
- || !mOnSuggestionListener.onSuggestionSelected(position)) {
- rewriteQueryFromSuggestion(position);
- }
+ if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position);
+ SearchView.this.onItemSelected(position);
}
/**
@@ -1003,6 +1157,11 @@ public class SearchView extends LinearLayout {
}
}
+ static boolean isLandscapeMode(Context context) {
+ return context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
/**
* Callback to watch the text field for empty/non-empty
*/
@@ -1018,4 +1177,93 @@ public class SearchView extends LinearLayout {
public void afterTextChanged(Editable s) {
}
};
+
+ /**
+ * Local subclass for AutoCompleteTextView.
+ * @hide
+ */
+ public static class SearchAutoComplete extends AutoCompleteTextView {
+
+ private int mThreshold;
+ private SearchView mSearchView;
+
+ public SearchAutoComplete(Context context) {
+ super(context);
+ mThreshold = getThreshold();
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mThreshold = getThreshold();
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mThreshold = getThreshold();
+ }
+
+ void setSearchView(SearchView searchView) {
+ mSearchView = searchView;
+ }
+
+ @Override
+ public void setThreshold(int threshold) {
+ super.setThreshold(threshold);
+ mThreshold = threshold;
+ }
+
+ /**
+ * Returns true if the text field is empty, or contains only whitespace.
+ */
+ private boolean isEmpty() {
+ return TextUtils.getTrimmedLength(getText()) == 0;
+ }
+
+ /**
+ * We override this method to avoid replacing the query box text when a
+ * suggestion is clicked.
+ */
+ @Override
+ protected void replaceText(CharSequence text) {
+ }
+
+ /**
+ * We override this method to avoid an extra onItemClick being called on
+ * the drop-down's OnItemClickListener by
+ * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is
+ * clicked with the trackball.
+ */
+ @Override
+ public void performCompletion() {
+ }
+
+ /**
+ * We override this method to be sure and show the soft keyboard if
+ * appropriate when the TextView has focus.
+ */
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ InputMethodManager inputManager = (InputMethodManager) getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputManager.showSoftInput(this, 0);
+ // If in landscape mode, then make sure that
+ // the ime is in front of the dropdown.
+ if (isLandscapeMode(getContext())) {
+ ensureImeVisible(true);
+ }
+ }
+ }
+
+ /**
+ * We override this method so that we can allow a threshold of zero,
+ * which ACTV does not.
+ */
+ @Override
+ public boolean enoughToFilter() {
+ return mThreshold <= 0 || super.enoughToFilter();
+ }
+ }
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 60422ae..006473e 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -357,7 +357,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
// Position vertically based on gravity setting
int childTop = mSpinnerPadding.top
- + ((mMeasuredHeight - mSpinnerPadding.bottom -
+ + ((getMeasuredHeight() - mSpinnerPadding.bottom -
mSpinnerPadding.top - child.getMeasuredHeight()) / 2);
int childBottom = childTop + child.getMeasuredHeight();
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index aee48c6..64fb985 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -281,7 +281,7 @@ public class StackView extends AdapterViewAnimator {
}
private void transformViewAtIndex(int index, View view) {
- float maxPerpectiveShift = mMeasuredHeight * PERSPECTIVE_SHIFT_FACTOR;
+ float maxPerpectiveShift = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR;
index = mMaxNumActiveViews - index - 1;
if (index == mMaxNumActiveViews - 1) index--;
@@ -297,7 +297,7 @@ public class StackView extends AdapterViewAnimator {
int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
float perspectiveTranslation = -stackDirection * r * maxPerpectiveShift;
float scaleShiftCorrection = stackDirection * (1 - scale) *
- (mMeasuredHeight * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f);
+ (getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f);
float transY = perspectiveTranslation + scaleShiftCorrection;
PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
@@ -897,8 +897,8 @@ public class StackView extends AdapterViewAnimator {
private void measureChildren() {
final int count = getChildCount();
- final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
- final int childHeight = Math.round(mMeasuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR))
+ final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
+ final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR))
- mPaddingTop - mPaddingBottom;
for (int i = 0; i < count; i++) {
@@ -925,17 +925,33 @@ public class StackView extends AdapterViewAnimator {
Math.round(mReferenceChildHeight * (1 + factor)) +
mPaddingTop + mPaddingBottom : 0;
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
- heightSpecSize = haveChildRefSize ? Math.min(
- Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop +
- mPaddingBottom, heightSpecSize) : 0;
+ if (haveChildRefSize) {
+ int height = Math.round(mReferenceChildHeight * (1 + factor))
+ + mPaddingTop + mPaddingBottom;
+ if (height <= heightSpecSize) {
+ heightSpecSize = height;
+ } else {
+ heightSpecSize |= MEASURED_STATE_TOO_SMALL;
+ }
+ } else {
+ heightSpecSize = 0;
+ }
}
if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
mPaddingRight : 0;
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
- widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
- mPaddingRight, widthSpecSize) : 0;
+ if (haveChildRefSize) {
+ int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight;
+ if (width <= widthSpecSize) {
+ widthSpecSize = width;
+ } else {
+ widthSpecSize |= MEASURED_STATE_TOO_SMALL;
+ }
+ } else {
+ widthSpecSize = 0;
+ }
}
setMeasuredDimension(widthSpecSize, heightSpecSize);
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 73a8c66..4223040 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -359,10 +359,9 @@ public class Switch extends CompoundButton {
mSwitchHeight = switchHeight;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- final int measuredWidth = getMeasuredWidth();
final int measuredHeight = getMeasuredHeight();
if (measuredHeight < switchHeight) {
- setMeasuredDimension(measuredWidth, switchHeight);
+ setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 57af643..ced8e9b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -89,6 +89,7 @@ import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
+import android.view.ActionMode.Callback;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
@@ -220,8 +221,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mTemporaryDetach;
private boolean mDispatchTemporaryDetach;
- private boolean mEatTouchRelease = false;
- private boolean mScrolled = false;
+ private boolean mDiscardNextActionUp = false;
+ private boolean mIgnoreActionUpEvent = false;
private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
@@ -314,6 +315,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Drawable mSelectHandleCenter;
private int mLastDownPositionX, mLastDownPositionY;
+ private Callback mCustomSelectionActionModeCallback;
/*
* Kick-start the font cache for the zygote process (to pay the cost of
@@ -6869,7 +6871,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (mSelectAllOnFocus) {
- Selection.setSelection((Spannable) mText, 0, mText.length());
+ selectAll();
}
mTouchFocusSelected = true;
@@ -7002,26 +7004,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void onTapUpEvent(int prevStart, int prevEnd) {
- final int start = getSelectionStart();
- final int end = getSelectionEnd();
-
- if (start == end) {
- if (start >= prevStart && start < prevEnd) {
- // Restore previous selection
- Selection.setSelection((Spannable)mText, prevStart, prevEnd);
- return;
- } else {
- // Tapping outside stops selection mode, if any
- stopSelectionActionMode();
-
- if (hasInsertionController()) {
- getInsertionController().show();
- }
- }
- }
- }
-
class CommitSelectionReceiver extends ResultReceiver {
private final int mPrevStart, mPrevEnd;
@@ -7066,7 +7048,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Reset this state; it will be re-set if super.onTouchEvent
// causes focus to move to the view.
mTouchFocusSelected = false;
- mScrolled = false;
+ mIgnoreActionUpEvent = false;
}
final boolean superResult = super.onTouchEvent(event);
@@ -7076,8 +7058,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* move the selection away from whatever the menu action was
* trying to affect.
*/
- if (mEatTouchRelease && action == MotionEvent.ACTION_UP) {
- mEatTouchRelease = false;
+ if (mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
+ mDiscardNextActionUp = false;
return superResult;
}
@@ -7106,7 +7088,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mSelectionModifierCursorController.updatePosition();
}
}
- if (action == MotionEvent.ACTION_UP && !mScrolled && isFocused()) {
+ if (action == MotionEvent.ACTION_UP && !mIgnoreActionUpEvent && isFocused()) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -7117,13 +7099,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (!mTextIsSelectable) {
- // Selection in read-only text should not bring up the IME.
+ // Show the IME, except when selecting in read-only text.
handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
}
- // Cannot be done by CommitSelectionReceiver, which might not always be called,
- // for instance when dealing with an ExtractEditText.
- onTapUpEvent(oldSelStart, oldSelEnd);
+ stopSelectionActionMode();
+ if (hasInsertionController()) {
+ getInsertionController().show();
+ }
}
}
@@ -7181,7 +7164,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void cancelLongPress() {
super.cancelLongPress();
- mScrolled = true;
+ mIgnoreActionUpEvent = true;
}
@Override
@@ -7420,10 +7403,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- if (mText.length() > 0 && hasSelection()) {
- if (mText instanceof Editable && mInput != null) {
- return true;
- }
+ if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mInput != null) {
+ return true;
}
return false;
@@ -7543,13 +7524,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return (int) (range & 0x00000000FFFFFFFFL);
}
+ private void selectAll() {
+ Selection.setSelection((Spannable) mText, 0, mText.length());
+ }
+
private void selectCurrentWord() {
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());
+ selectAll();
return;
}
@@ -7815,7 +7800,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean performLongClick() {
if (super.performLongClick()) {
- mEatTouchRelease = true;
+ mDiscardNextActionUp = true;
return true;
}
@@ -7826,7 +7811,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
stopSelectionActionMode();
Selection.setSelection((Spannable)mText, offset);
getInsertionController().show(0);
- mEatTouchRelease = true;
+ mDiscardNextActionUp = true;
return true;
}
@@ -7837,7 +7822,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int end = getSelectionEnd();
CharSequence selectedText = mTransformed.subSequence(start, end);
ClipData data = ClipData.newPlainText(null, null, selectedText);
- startDrag(data, getTextThumbnailBuilder(selectedText), false);
+ startDrag(data, getTextThumbnailBuilder(selectedText), false, null);
mDragSourcePositions = packRangeInLong(start, end);
stopSelectionActionMode();
} else {
@@ -7845,13 +7830,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
getSelectionController().show();
}
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- mEatTouchRelease = true;
+ mDiscardNextActionUp = true;
return true;
}
if (startSelectionActionMode()) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- mEatTouchRelease = true;
+ mDiscardNextActionUp = true;
return true;
}
@@ -7881,16 +7866,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Provides the callback used to start a selection action mode.
+ * If provided, this ActionMode.Callback will be used to create the ActionMode when text
+ * selection is initiated in this View.
*
- * @return A callback instance that will be used to start selection mode, or null if selection
- * mode is not available.
+ * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
+ * Paste actions, depending on what this View supports.
+ *
+ * A custom implementation can add new entries in the default menu in its
+ * {@link ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The default actions
+ * can also be removed from the menu using {@link Menu#removeItem(int)} and passing
+ * {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy} or
+ * {@link android.R.id#paste} ids as parameters.
+ *
+ * Action click events should be handled by the custom implementation of
+ * {@link ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
+ *
+ * Note that text selection mode is not started when a TextView receives focus and the
+ * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
+ * that case, to allow for quick replacement.
*/
- private ActionMode.Callback getActionModeCallback() {
- if (canSelectText()) {
- return new SelectionActionModeCallback();
- }
- return null;
+ public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
+ mCustomSelectionActionModeCallback = actionModeCallback;
+ }
+
+ /**
+ * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null.
+ *
+ * @return The current custom selection callback.
+ */
+ public ActionMode.Callback getCustomSelectionActionModeCallback() {
+ return mCustomSelectionActionModeCallback;
}
/**
@@ -7903,7 +7908,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- ActionMode.Callback actionModeCallback = getActionModeCallback();
+ selectCurrentWord();
+ ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
if (actionModeCallback != null) {
mSelectionActionMode = startActionMode(actionModeCallback);
return mSelectionActionMode != null;
@@ -7969,6 +7975,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sLastCutOrCopyTime = SystemClock.uptimeMillis();
}
+ /**
+ * An ActionMode Callback class that is used to provide actions while in text selection mode.
+ *
+ * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
+ * on which of these this TextView supports.
+ */
private class SelectionActionModeCallback implements ActionMode.Callback {
@Override
@@ -7987,48 +7999,47 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
mode.setSubtitle(null);
- boolean atLeastOne = false;
-
if (canSelectText()) {
- selectCurrentWord();
-
menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
setAlphabeticShortcut('a').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- atLeastOne = true;
}
if (canCut()) {
menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
- setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCutDrawable, 0)).
+ setIcon(styledAttributes.getResourceId(
+ R.styleable.Theme_actionModeCutDrawable, 0)).
setAlphabeticShortcut('x').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- atLeastOne = true;
}
if (canCopy()) {
menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
- setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCopyDrawable, 0)).
+ setIcon(styledAttributes.getResourceId(
+ R.styleable.Theme_actionModeCopyDrawable, 0)).
setAlphabeticShortcut('c').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- atLeastOne = true;
}
if (canPaste()) {
menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
- setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModePasteDrawable, 0)).
+ setIcon(styledAttributes.getResourceId(
+ R.styleable.Theme_actionModePasteDrawable, 0)).
setAlphabeticShortcut('v').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- atLeastOne = true;
}
styledAttributes.recycle();
- if (atLeastOne) {
+ if (mCustomSelectionActionModeCallback != null) {
+ mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu);
+ }
+
+ if (menu.hasVisibleItems() || mode.getCustomView() != null) {
getSelectionController().show();
return true;
} else {
@@ -8038,15 +8049,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ if (mCustomSelectionActionModeCallback != null) {
+ return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu);
+ }
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (mCustomSelectionActionModeCallback != null &&
+ mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
+ return true;
+ }
+
final int itemId = item.getItemId();
if (itemId == ID_SELECT_ALL) {
- Selection.setSelection((Spannable) mText, 0, mText.length());
+ selectAll();
// Update controller positions after selection change.
if (hasSelectionController()) {
getSelectionController().show();
@@ -8089,6 +8108,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void onDestroyActionMode(ActionMode mode) {
+ if (mCustomSelectionActionModeCallback != null) {
+ mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
+ }
Selection.setSelection((Spannable) mText, getSelectionStart());
hideSelectionModifierCursorController();
mSelectionActionMode = null;
@@ -8763,7 +8785,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Double tap detection
long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
- if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
+ if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
+ isPositionOnText(x, y)) {
final int deltaX = x - mPreviousTapPositionX;
final int deltaY = y - mPreviousTapPositionY;
final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
@@ -8772,6 +8795,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int slopSquared = doubleTapSlop * doubleTapSlop;
if (distanceSquared < slopSquared) {
startSelectionActionMode();
+ mDiscardNextActionUp = true;
}
}
@@ -8941,66 +8965,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
TextView.this.requestFocus();
return true;
- case DragEvent.ACTION_DRAG_LOCATION: {
+ case DragEvent.ACTION_DRAG_LOCATION:
final int offset = getOffset((int) event.getX(), (int) event.getY());
Selection.setSelection((Spannable)mText, offset);
return true;
- }
-
- case DragEvent.ACTION_DROP: {
- StringBuilder content = new StringBuilder("");
- ClipData clipData = event.getClipData();
- final int itemCount = clipData.getItemCount();
- for (int i=0; i < itemCount; i++) {
- Item item = clipData.getItem(i);
- content.append(item.coerceToText(TextView.this.mContext));
- }
-
- final int offset = getOffset((int) event.getX(), (int) event.getY());
-
- if (mDragSourcePositions != -1) {
- final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
- final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
- if (offset >= dragSourceStart && offset < dragSourceEnd) {
- // A drop inside the original selection discards the drop.
- return true;
- }
- }
-
- final int originalLength = mText.length();
- long minMax = prepareSpacesAroundPaste(offset, offset, content);
- int min = extractRangeStartFromLong(minMax);
- int max = extractRangeEndFromLong(minMax);
-
- Selection.setSelection((Spannable) mText, max);
- ((Editable) mText).replace(min, max, content);
-
- if (mDragSourcePositions != -1) {
- int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
- int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
- if (max <= dragSourceStart) {
- // Inserting text before selection has shifted positions
- final int shift = mText.length() - originalLength;
- dragSourceStart += shift;
- dragSourceEnd += shift;
- }
-
- // Delete original selection
- ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
-
- // Make sure we do not leave two adjacent spaces.
- if ((dragSourceStart == 0 ||
- Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
- (dragSourceStart == mText.length() ||
- Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
- final int pos = dragSourceStart == mText.length() ?
- dragSourceStart - 1 : dragSourceStart;
- ((Editable) mText).delete(pos, pos + 1);
- }
- }
+ case DragEvent.ACTION_DROP:
+ onDrop(event);
return true;
- }
case DragEvent.ACTION_DRAG_ENDED:
mDragSourcePositions = -1;
@@ -9012,6 +8984,59 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ private void onDrop(DragEvent event) {
+ StringBuilder content = new StringBuilder("");
+ ClipData clipData = event.getClipData();
+ final int itemCount = clipData.getItemCount();
+ for (int i=0; i < itemCount; i++) {
+ Item item = clipData.getItem(i);
+ content.append(item.coerceToText(TextView.this.mContext));
+ }
+
+ final int offset = getOffset((int) event.getX(), (int) event.getY());
+
+ if (mDragSourcePositions != -1) {
+ final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+ final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+ if (offset >= dragSourceStart && offset < dragSourceEnd) {
+ // A drop inside the original selection discards the drop.
+ return;
+ }
+ }
+
+ final int originalLength = mText.length();
+ long minMax = prepareSpacesAroundPaste(offset, offset, content);
+ int min = extractRangeStartFromLong(minMax);
+ int max = extractRangeEndFromLong(minMax);
+
+ Selection.setSelection((Spannable) mText, max);
+ ((Editable) mText).replace(min, max, content);
+
+ if (mDragSourcePositions != -1) {
+ int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+ int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+ if (max <= dragSourceStart) {
+ // Inserting text before selection has shifted positions
+ final int shift = mText.length() - originalLength;
+ dragSourceStart += shift;
+ dragSourceEnd += shift;
+ }
+
+ // Delete original selection
+ ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
+
+ // Make sure we do not leave two adjacent spaces.
+ if ((dragSourceStart == 0 ||
+ Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
+ (dragSourceStart == mText.length() ||
+ Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
+ final int pos = dragSourceStart == mText.length() ?
+ dragSourceStart - 1 : dragSourceStart;
+ ((Editable) mText).delete(pos, pos + 1);
+ }
+ }
+ }
+
/**
* @return True if this view supports insertion handles.
*/
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 20402a3..447a062 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -70,6 +70,10 @@ public class ActionBarImpl extends ActionBar {
private ActionMode mActionMode;
+ private boolean mLastMenuVisibility;
+ private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
+ new ArrayList<OnMenuVisibilityListener>();
+
private static final int CONTEXT_DISPLAY_NORMAL = 0;
private static final int CONTEXT_DISPLAY_SPLIT = 1;
@@ -120,6 +124,26 @@ public class ActionBarImpl extends ActionBar {
CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
}
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.add(listener);
+ }
+
+ public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.remove(listener);
+ }
+
+ public void dispatchMenuVisibilityChanged(boolean isVisible) {
+ if (isVisible == mLastMenuVisibility) {
+ return;
+ }
+ mLastMenuVisibility = isVisible;
+
+ final int count = mMenuVisibilityListeners.size();
+ for (int i = 0; i < count; i++) {
+ mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
+ }
+ }
+
@Override
public void setTitle(int resId) {
setTitle(mContext.getString(resId));
@@ -138,11 +162,11 @@ public class ActionBarImpl extends ActionBar {
mActionView.setCallback(null);
}
- public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+ public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) {
setDropdownNavigationMode(adapter, callback, -1);
}
- public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
+ public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback,
int defaultSelectedPosition) {
cleanupTabs();
setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
@@ -516,7 +540,7 @@ public class ActionBarImpl extends ActionBar {
public void onMenuModeChange(MenuBuilder menu) {
invalidate();
- mUpperContextView.showOverflowMenu();
+ mUpperContextView.openOverflowMenu();
}
}
@@ -627,7 +651,7 @@ public class ActionBarImpl extends ActionBar {
}
@Override
- public void setListNavigationCallbacks(SpinnerAdapter adapter, NavigationCallback callback) {
+ public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
mActionView.setDropdownAdapter(adapter);
mActionView.setCallback(callback);
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index b2810b1..e384320 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -118,6 +118,12 @@ public class AlertController {
private int mCheckedItem = -1;
+ private int mAlertDialogLayout;
+ private int mListLayout;
+ private int mMultiChoiceItemLayout;
+ private int mSingleChoiceItemLayout;
+ private int mListItemLayout;
+
private Handler mHandler;
View.OnClickListener mButtonHandler = new View.OnClickListener() {
@@ -178,6 +184,27 @@ public class AlertController {
mDialogInterface = di;
mWindow = window;
mHandler = new ButtonHandler(di);
+
+ TypedArray a = context.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.AlertDialog,
+ com.android.internal.R.attr.alertDialogStyle, 0);
+
+ mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,
+ com.android.internal.R.layout.alert_dialog);
+ mListLayout = a.getResourceId(
+ com.android.internal.R.styleable.AlertDialog_listLayout,
+ com.android.internal.R.layout.select_dialog);
+ mMultiChoiceItemLayout = a.getResourceId(
+ com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout,
+ com.android.internal.R.layout.select_dialog_multichoice);
+ mSingleChoiceItemLayout = a.getResourceId(
+ com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
+ com.android.internal.R.layout.select_dialog_singlechoice);
+ mListItemLayout = a.getResourceId(
+ com.android.internal.R.styleable.AlertDialog_listItemLayout,
+ com.android.internal.R.layout.select_dialog_item);
+
+ a.recycle();
}
static boolean canTextInput(View v) {
@@ -210,7 +237,7 @@ public class AlertController {
mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
- mWindow.setContentView(com.android.internal.R.layout.alert_dialog);
+ mWindow.setContentView(mAlertDialogLayout);
setupView();
}
@@ -810,13 +837,13 @@ public class AlertController {
private void createListView(final AlertController dialog) {
final RecycleListView listView = (RecycleListView)
- mInflater.inflate(R.layout.select_dialog, null);
+ mInflater.inflate(dialog.mListLayout, null);
ListAdapter adapter;
if (mIsMultiChoice) {
if (mCursor == null) {
adapter = new ArrayAdapter<CharSequence>(
- mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) {
+ mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
@@ -850,7 +877,7 @@ public class AlertController {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.select_dialog_multichoice,
+ return mInflater.inflate(dialog.mMultiChoiceItemLayout,
parent, false);
}
@@ -858,7 +885,7 @@ public class AlertController {
}
} else {
int layout = mIsSingleChoice
- ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item;
+ ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
if (mCursor == null) {
adapter = (mAdapter != null) ? mAdapter
: new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index c930c57..bfef275 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -27,6 +27,7 @@ import java.io.PrintStream;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.ThreadFactory;
import android.content.pm.PackageInfo;
import android.util.Log;
@@ -43,27 +44,40 @@ public class SamplingProfilerIntegration {
private static final boolean enabled;
private static final Executor snapshotWriter;
- private static final int samplingProfilerHz;
-
- /** Whether or not we've created the snapshots dir. */
- private static boolean dirMade = false;
+ private static final int samplingProfilerMilliseconds;
+ private static final int samplingProfilerDepth;
/** Whether or not a snapshot is being persisted. */
private static final AtomicBoolean pending = new AtomicBoolean(false);
static {
- samplingProfilerHz = SystemProperties.getInt("persist.sys.profiler_hz", 0);
- // Disabling this for now, as it crashes when enabled server-side. So adding
- // a new property ("REALLY") for those wanting to test and fix it.
- boolean really = SystemProperties.getInt("persist.sys.profiler_hz_REALLY", 0) > 0;
- if (samplingProfilerHz > 0 && really) {
- snapshotWriter = Executors.newSingleThreadExecutor();
- enabled = true;
- Log.i(TAG, "Profiler is enabled. Sampling Profiler Hz: " + samplingProfilerHz);
+ samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0);
+ samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4);
+ if (samplingProfilerMilliseconds > 0) {
+ File dir = new File(SNAPSHOT_DIR);
+ dir.mkdirs();
+ // the directory needs to be writable to anybody to allow file writing
+ dir.setWritable(true, false);
+ // the directory needs to be executable to anybody to allow file creation
+ dir.setExecutable(true, false);
+ if (dir.isDirectory()) {
+ snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ public Thread newThread(Runnable r) {
+ return new Thread(r, TAG);
+ }
+ });
+ enabled = true;
+ Log.i(TAG, "Profiling enabled. Sampling interval ms: "
+ + samplingProfilerMilliseconds);
+ } else {
+ snapshotWriter = null;
+ enabled = true;
+ Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR);
+ }
} else {
snapshotWriter = null;
enabled = false;
- Log.i(TAG, "Profiler is disabled.");
+ Log.i(TAG, "Profiling disabled.");
}
}
@@ -85,8 +99,8 @@ public class SamplingProfilerIntegration {
}
ThreadGroup group = Thread.currentThread().getThreadGroup();
SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
- INSTANCE = new SamplingProfiler(4, threadSet); // TODO parameter for depth
- INSTANCE.start(1000/samplingProfilerHz);
+ INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet);
+ INSTANCE.start(samplingProfilerMilliseconds);
}
/**
@@ -106,25 +120,8 @@ public class SamplingProfilerIntegration {
if (pending.compareAndSet(false, true)) {
snapshotWriter.execute(new Runnable() {
public void run() {
- if (!dirMade) {
- File dir = new File(SNAPSHOT_DIR);
- dir.mkdirs();
- // the directory needs to be writable to anybody
- dir.setWritable(true, false);
- // the directory needs to be executable to anybody
- // don't know why yet, but mode 723 would work, while
- // mode 722 throws FileNotFoundExecption at line 151
- dir.setExecutable(true, false);
- if (new File(SNAPSHOT_DIR).isDirectory()) {
- dirMade = true;
- } else {
- Log.w(TAG, "Creation of " + SNAPSHOT_DIR + " failed.");
- pending.set(false);
- return;
- }
- }
try {
- writeSnapshot(SNAPSHOT_DIR, processName, packageInfo);
+ writeSnapshotFile(processName, packageInfo);
} finally {
pending.set(false);
}
@@ -140,7 +137,7 @@ public class SamplingProfilerIntegration {
if (!enabled) {
return;
}
- writeSnapshot("zygote", null);
+ writeSnapshotFile("zygote", null);
INSTANCE.shutdown();
INSTANCE = null;
}
@@ -148,7 +145,7 @@ public class SamplingProfilerIntegration {
/**
* pass in PackageInfo to retrieve various values for snapshot header
*/
- private static void writeSnapshot(String dir, String processName, PackageInfo packageInfo) {
+ private static void writeSnapshotFile(String processName, PackageInfo packageInfo) {
if (!enabled) {
return;
}
@@ -161,7 +158,7 @@ public class SamplingProfilerIntegration {
*/
long start = System.currentTimeMillis();
String name = processName.replaceAll(":", ".");
- String path = dir + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
+ String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
PrintStream out = null;
try {
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 0891acc..101dd91 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -95,7 +95,7 @@ public class AsyncChannel {
*
* msg.arg1 == 0 : STATUS_SUCCESSFUL
* 1 : STATUS_BINDING_UNSUCCESSFUL
- * msg.arg2 == token parameter
+ * msg.obj == the AsyncChannel
* msg.replyTo == dstMessenger if successful
*/
public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
@@ -136,7 +136,7 @@ public class AsyncChannel {
*
* msg.arg1 == 0 : STATUS_SUCCESSFUL
* : All other values signify failure and the channel state is indeterminate
- * msg.arg2 == token parameter
+ * msg.obj == the AsyncChannel
* msg.replyTo = messenger disconnecting or null if it was never connected.
*/
public static final int CMD_CHANNEL_DISCONNECTED = -5;
@@ -179,14 +179,12 @@ public class AsyncChannel {
* @param dstPackageName is the destination package name
* @param dstClassName is the fully qualified class name (i.e. contains
* package name)
- * @param token unique id for this connection
*/
private void connectSrcHandlerToPackage(
- Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
- int token) {
+ Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
if (DBG) log("connect srcHandler to dst Package & class E");
- mConnection = new AsyncChannelConnection(token);
+ mConnection = new AsyncChannelConnection();
/* Initialize the source information */
mSrcContext = srcContext;
@@ -205,7 +203,7 @@ public class AsyncChannel {
intent.setClassName(dstPackageName, dstClassName);
boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if (!result) {
- replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token);
+ replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL);
}
if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
@@ -216,7 +214,7 @@ public class AsyncChannel {
*
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
* msg.arg1 = status
- * msg.arg2 = token
+ * msg.obj = the AsyncChannel
*
* @param srcContext is the context of the source
* @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -224,10 +222,9 @@ public class AsyncChannel {
* @param dstPackageName is the destination package name
* @param dstClassName is the fully qualified class name (i.e. contains
* package name)
- * @param token returned in msg.arg2
*/
public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
- String dstClassName, int token) {
+ String dstClassName) {
if (DBG) log("connect srcHandler to dst Package & class E");
final class ConnectAsync implements Runnable {
@@ -235,25 +232,21 @@ public class AsyncChannel {
Handler mSrcHdlr;
String mDstPackageName;
String mDstClassName;
- int mToken;
ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
- String dstClassName, int token) {
+ String dstClassName) {
mSrcCtx = srcContext;
mSrcHdlr = srcHandler;
mDstPackageName = dstPackageName;
mDstClassName = dstClassName;
- mToken = token;
}
public void run() {
- connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
- mToken);
+ connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
}
}
- ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
- token);
+ ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
new Thread(ca).start();
if (DBG) log("connect srcHandler to dst Package & class X");
@@ -264,15 +257,14 @@ public class AsyncChannel {
*
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
* msg.arg1 = status
- * msg.arg2 = token
+ * msg.obj = the AsyncChannel
*
* @param srcContext
* @param srcHandler
* @param klass is the class to send messages to.
- * @param token returned in msg.arg2
*/
- public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
- connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
+ public void connect(Context srcContext, Handler srcHandler, Class<?> klass) {
+ connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName());
}
/**
@@ -280,14 +272,13 @@ public class AsyncChannel {
*
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
* msg.arg1 = status
- * msg.arg2 = token
+ * msg.obj = the AsyncChannel
*
* @param srcContext
* @param srcHandler
* @param dstMessenger
- * @param token returned in msg.arg2
*/
- public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
+ public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
if (DBG) log("connect srcHandler to the dstMessenger E");
// Initialize source fields
@@ -301,7 +292,7 @@ public class AsyncChannel {
if (DBG) log("tell source we are half connected");
// Tell source we are half connected
- replyHalfConnected(STATUS_SUCCESSFUL, token);
+ replyHalfConnected(STATUS_SUCCESSFUL);
if (DBG) log("connect srcHandler to the dstMessenger X");
}
@@ -311,16 +302,15 @@ public class AsyncChannel {
*
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
* msg.arg1 = status
- * msg.arg2 = token
+ * msg.obj = the AsyncChannel
*
* @param srcContext is the context of the source
* @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
* messages
* @param dstHandler is the hander to send messages to.
- * @param token returned in msg.arg2
*/
- public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
- connect(srcContext, srcHandler, new Messenger(dstHandler), token);
+ public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
+ connect(srcContext, srcHandler, new Messenger(dstHandler));
}
/**
@@ -328,14 +318,13 @@ public class AsyncChannel {
*
* Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
* msg.arg1 = status
- * msg.arg2 = token
+ * msg.obj = the AsyncChannel
*
* @param srcAsyncService
* @param dstMessenger
- * @param token returned in msg.arg2
*/
- public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
- connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
+ public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
+ connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
}
/**
@@ -351,15 +340,14 @@ public class AsyncChannel {
/**
* Disconnect
*/
- public void disconnect(int token) {
+ public void disconnect() {
if (mConnection != null) {
- mConnection.setToken(token);
mSrcContext.unbindService(mConnection);
}
if (mSrcHandler != null) {
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
msg.arg1 = STATUS_SUCCESSFUL;
- msg.arg2 = token;
+ msg.obj = this;
msg.replyTo = mDstMessenger;
mSrcHandler.sendMessage(msg);
}
@@ -727,10 +715,10 @@ public class AsyncChannel {
*
* @param status to be stored in msg.arg1
*/
- private void replyHalfConnected(int status, int token) {
+ private void replyHalfConnected(int status) {
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
msg.arg1 = status;
- msg.arg2 = token;
+ msg.obj = this;
msg.replyTo = mDstMessenger;
mSrcHandler.sendMessage(msg);
}
@@ -739,28 +727,18 @@ public class AsyncChannel {
* ServiceConnection to receive call backs.
*/
class AsyncChannelConnection implements ServiceConnection {
- private int mToken;
-
- AsyncChannelConnection(int token) {
- mToken = token;
- }
-
- /**
- * @param token
- */
- public void setToken(int token) {
- mToken = token;
+ AsyncChannelConnection() {
}
public void onServiceConnected(ComponentName className, IBinder service) {
mDstMessenger = new Messenger(service);
- replyHalfConnected(STATUS_SUCCESSFUL, mToken);
+ replyHalfConnected(STATUS_SUCCESSFUL);
}
public void onServiceDisconnected(ComponentName className) {
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
msg.arg1 = STATUS_SUCCESSFUL;
- msg.arg2 = mToken;
+ msg.obj = AsyncChannel.this;
msg.replyTo = mDstMessenger;
mSrcHandler.sendMessage(msg);
}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index b54daba..2d067da 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -135,6 +135,6 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
public void onMenuModeChange(MenuBuilder menu) {
invalidate();
- mContextView.showOverflowMenu();
+ mContextView.openOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 621defe..84067d0 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -59,6 +59,22 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
};
+ private class OpenOverflowRunnable implements Runnable {
+ private MenuPopupHelper mPopup;
+
+ public OpenOverflowRunnable(MenuPopupHelper popup) {
+ mPopup = popup;
+ }
+
+ public void run() {
+ mOverflowPopup = mPopup;
+ mPopup.show();
+ mPostedOpenRunnable = null;
+ }
+ }
+
+ private OpenOverflowRunnable mPostedOpenRunnable;
+
public ActionMenuView(Context context) {
this(context, null);
}
@@ -100,6 +116,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
final int screen = newConfig.screenLayout;
mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
Configuration.SCREENLAYOUT_SIZE_XLARGE;
@@ -115,6 +132,14 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+ mOverflowPopup.dismiss();
+ }
+ }
+
private int getMaxActionButtons() {
return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
}
@@ -193,30 +218,34 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
public boolean showOverflowMenu() {
- if (mOverflowButton != null) {
- final MenuPopupHelper popup =
- new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
- // Post this for later; we might still need a layout for the anchor to be right.
- post(new Runnable() {
- public void run() {
- popup.show();
- }
- });
- mOverflowPopup = popup;
+ if (mOverflowButton != null && !isOverflowMenuShowing()) {
+ mMenu.getCallback().onMenuModeChange(mMenu);
return true;
}
return false;
}
+ public void openOverflowMenu() {
+ OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true);
+ mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ post(mPostedOpenRunnable);
+ }
+
public boolean isOverflowMenuShowing() {
- MenuPopupHelper popup = mOverflowPopup;
- if (popup != null) {
- return popup.isShowing();
- }
- return false;
+ return mOverflowPopup != null && mOverflowPopup.isShowing();
+ }
+
+ public boolean isOverflowMenuOpen() {
+ return mOverflowPopup != null;
}
public boolean hideOverflowMenu() {
+ if (mPostedOpenRunnable != null) {
+ removeCallbacks(mPostedOpenRunnable);
+ return true;
+ }
+
MenuPopupHelper popup = mOverflowPopup;
if (popup != null) {
popup.dismiss();
@@ -274,9 +303,22 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
return true;
}
- // Change to overflow mode
- mMenu.getCallback().onMenuModeChange(mMenu);
+ showOverflowMenu();
return true;
}
}
+
+ private class OverflowPopup extends MenuPopupHelper {
+ public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly) {
+ super(context, menu, anchorView, overflowOnly);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mMenu.getCallback().onCloseMenu(mMenu, true);
+ mOverflowPopup = null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index 178dcde..d18c9727 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -486,7 +486,7 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
// Position the children
if (layoutNumRows > 0) {
- positionChildren(mMeasuredWidth, mMeasuredHeight);
+ positionChildren(getMeasuredWidth(), getMeasuredHeight());
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 1406e4e..2cb78a5 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -35,7 +35,7 @@ import java.lang.ref.WeakReference;
* @hide
*/
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
- ViewTreeObserver.OnGlobalLayoutListener {
+ ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener {
private static final String TAG = "MenuPopupHelper";
private Context mContext;
@@ -46,12 +46,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
private boolean mOverflowOnly;
private ViewTreeObserver mTreeObserver;
- private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
- public void onDismiss() {
- mPopup = null;
- }
- };
-
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false);
}
@@ -77,7 +71,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
public void show() {
mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
mPopup.setOnItemClickListener(this);
- mPopup.setOnDismissListener(mDismissListener);
+ mPopup.setOnDismissListener(this);
final MenuAdapter adapter = mOverflowOnly ?
mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
@@ -110,8 +104,12 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
if (isShowing()) {
mPopup.dismiss();
}
+ }
+
+ public void onDismiss() {
+ mPopup = null;
if (mTreeObserver != null) {
- mTreeObserver.removeGlobalOnLayoutListener(this);
+ mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this);
mTreeObserver = null;
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index e93c414..cbf12bf 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -180,6 +180,12 @@ public class ActionBarContextView extends ViewGroup {
return false;
}
+ public void openOverflowMenu() {
+ if (mMenuView != null) {
+ mMenuView.openOverflowMenu();
+ }
+ }
+
public boolean hideOverflowMenu() {
if (mMenuView != null) {
return mMenuView.hideOverflowMenu();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index f931217..07a65fc 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -22,7 +22,7 @@ import com.android.internal.view.menu.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
import android.app.ActionBar;
-import android.app.ActionBar.NavigationCallback;
+import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -114,7 +114,7 @@ public class ActionBarView extends ViewGroup {
private ActionMenuItem mLogoNavItem;
private SpinnerAdapter mSpinnerAdapter;
- private NavigationCallback mCallback;
+ private OnNavigationListener mCallback;
private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
new AdapterView.OnItemSelectedListener() {
@@ -243,7 +243,7 @@ public class ActionBarView extends ViewGroup {
return null;
}
- public void setCallback(NavigationCallback callback) {
+ public void setCallback(OnNavigationListener callback) {
mCallback = callback;
}
@@ -269,6 +269,12 @@ public class ActionBarView extends ViewGroup {
return false;
}
+ public void openOverflowMenu() {
+ if (mMenuView != null) {
+ mMenuView.openOverflowMenu();
+ }
+ }
+
public void postShowOverflowMenu() {
post(new Runnable() {
public void run() {
@@ -291,6 +297,13 @@ public class ActionBarView extends ViewGroup {
return false;
}
+ public boolean isOverflowMenuOpen() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuOpen();
+ }
+ return false;
+ }
+
public boolean isOverflowReserved() {
return mMenuView != null && mMenuView.isOverflowReserved();
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 695d50a4..b033878 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -579,20 +579,30 @@ static int mainWorkCallback(int fd, int events, void* data) {
while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
- jboolean handled = code->env->CallBooleanMethod(code->clazz,
- gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
- checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
- code->env->DeleteLocalRef(inputEventObj);
+ jboolean handled;
+ if (inputEventObj) {
+ handled = code->env->CallBooleanMethod(code->clazz,
+ gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+ checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
+ code->env->DeleteLocalRef(inputEventObj);
+ } else {
+ LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
+ handled = false;
+ }
code->nativeInputQueue->finishEvent(keyEvent, handled, true);
}
int seq;
while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
- code->env->CallVoidMethod(code->clazz,
- gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
- checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
- code->env->DeleteLocalRef(inputEventObj);
+ if (inputEventObj) {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
+ checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
+ code->env->DeleteLocalRef(inputEventObj);
+ } else {
+ LOGE("Failed to obtain key event for preDispatchKeyEvent.");
+ }
}
} break;
case CMD_FINISH: {
@@ -987,7 +997,12 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL) {
KeyEvent* event = code->nativeInputQueue->createKeyEvent();
- android_view_KeyEvent_toNative(env, eventObj, event);
+ status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
+ if (status) {
+ delete event;
+ jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
+ return;
+ }
code->nativeInputQueue->dispatchEvent(event);
}
}
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 7e7583c..4b04b8b 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -30,7 +30,8 @@ namespace android {
static struct {
jclass clazz;
- jmethodID ctor;
+ jmethodID obtain;
+ jmethodID recycle;
jfieldID mDeviceId;
jfieldID mSource;
@@ -48,7 +49,8 @@ static struct {
// ----------------------------------------------------------------------------
jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
- return env->NewObject(gKeyEventClassInfo.clazz, gKeyEventClassInfo.ctor,
+ jobject eventObj = env->CallStaticObjectMethod(gKeyEventClassInfo.clazz,
+ gKeyEventClassInfo.obtain,
nanoseconds_to_milliseconds(event->getDownTime()),
nanoseconds_to_milliseconds(event->getEventTime()),
event->getAction(),
@@ -58,10 +60,18 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
event->getDeviceId(),
event->getScanCode(),
event->getFlags(),
- event->getSource());
+ event->getSource(),
+ NULL);
+ if (env->ExceptionCheck()) {
+ LOGE("An exception occurred while obtaining a key event.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return NULL;
+ }
+ return eventObj;
}
-void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
+status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
KeyEvent* event) {
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
@@ -77,6 +87,18 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
event->initialize(deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
milliseconds_to_nanoseconds(downTime),
milliseconds_to_nanoseconds(eventTime));
+ return OK;
+}
+
+status_t android_view_KeyEvent_recycle(JNIEnv* env, jobject eventObj) {
+ env->CallVoidMethod(eventObj, gKeyEventClassInfo.recycle);
+ if (env->ExceptionCheck()) {
+ LOGW("An exception occurred while recycling a key event.");
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return UNKNOWN_ERROR;
+ }
+ return OK;
}
static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) {
@@ -87,6 +109,7 @@ static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode
return KeyEvent::hasDefaultAction(keyCode);
}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod g_methods[] = {
@@ -99,6 +122,10 @@ static const JNINativeMethod g_methods[] = {
LOG_FATAL_IF(! var, "Unable to find class " className); \
var = jclass(env->NewGlobalRef(var));
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find static method" methodName);
+
#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method" methodName);
@@ -109,9 +136,11 @@ static const JNINativeMethod g_methods[] = {
int register_android_view_KeyEvent(JNIEnv* env) {
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
-
- GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz,
- "<init>", "(JJIIIIIIII)V");
+
+ GET_STATIC_METHOD_ID(gKeyEventClassInfo.obtain, gKeyEventClassInfo.clazz,
+ "obtain", "(JJIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
+ GET_METHOD_ID(gKeyEventClassInfo.recycle, gKeyEventClassInfo.clazz,
+ "recycle", "()V");
GET_FIELD_ID(gKeyEventClassInfo.mDeviceId, gKeyEventClassInfo.clazz,
"mDeviceId", "I");
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
index 0bd410c..586eb2f 100644
--- a/core/jni/android_view_KeyEvent.h
+++ b/core/jni/android_view_KeyEvent.h
@@ -18,18 +18,28 @@
#define _ANDROID_VIEW_KEYEVENT_H
#include "jni.h"
+#include <utils/Errors.h>
+#include <utils/threads.h>
namespace android {
class KeyEvent;
-/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance. */
+/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance.
+ * Returns NULL on error. */
extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event);
-/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance. */
-extern void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
+/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance.
+ * Returns non-zero on error. */
+extern status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
KeyEvent* event);
+/* Recycles a DVM KeyEvent object.
+ * Key events should only be recycled if they are owned by the system since user
+ * code expects them to be essentially immutable, "tracking" notwithstanding.
+ * Returns non-zero on error. */
+extern status_t android_view_KeyEvent_recycle(JNIEnv* env, jobject eventObj);
+
} // namespace android
#endif // _ANDROID_OS_KEYEVENT_H
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 537ac72..f32f0ff 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -169,7 +169,7 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even
return eventObj;
}
-void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
+status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
MotionEvent* event) {
jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gMotionEventClassInfo.mSource);
@@ -184,6 +184,16 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
jint flags = env->GetIntField(eventObj, gMotionEventClassInfo.mFlags);
jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
+
+ if (numPointers == 0) {
+ LOGE("Malformed MotionEvent: mNumPointers was zero");
+ return BAD_VALUE;
+ }
+ if (numSamples == 0) {
+ LOGE("Malformed MotionEvent: mNumSamples was zero");
+ return BAD_VALUE;
+ }
+
jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
gMotionEventClassInfo.mPointerIdentifiers));
jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
@@ -191,9 +201,6 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj,
gMotionEventClassInfo.mEventTimeNanoSamples));
- LOG_FATAL_IF(numPointers == 0, "numPointers was zero");
- LOG_FATAL_IF(numSamples == 0, "numSamples was zero");
-
jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical(
@@ -236,22 +243,25 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
event->addSample(sampleEventTime, samplePointerCoords);
}
- env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT);
- env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT);
env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT);
- env->DeleteLocalRef(pointerIdentifierArray);
- env->DeleteLocalRef(dataSampleArray);
env->DeleteLocalRef(eventTimeNanoSampleArray);
+ env->DeleteLocalRef(dataSampleArray);
+ env->DeleteLocalRef(pointerIdentifierArray);
+ return OK;
}
-void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
+status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
if (env->ExceptionCheck()) {
LOGW("An exception occurred while recycling a motion event.");
LOGW_EX(env);
env->ExceptionClear();
+ return UNKNOWN_ERROR;
}
+ return OK;
}
static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index 86e4bde..80dc861 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -18,20 +18,24 @@
#define _ANDROID_VIEW_MOTIONEVENT_H
#include "jni.h"
+#include <utils/Errors.h>
namespace android {
class MotionEvent;
-/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance. */
+/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance.
+ * Returns NULL on error. */
extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event);
-/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance. */
-extern void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
+/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance.
+ * Returns non-zero on error. */
+extern status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
MotionEvent* event);
-/* Recycles a DVM MotionEvent object. */
-extern void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj);
+/* Recycles a DVM MotionEvent object.
+ * Returns non-zero on error. */
+extern status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj);
} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ddc63dd..a2666e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1013,7 +1013,7 @@
<permission android:name="android.permission.SHUTDOWN"
android:label="@string/permlab_shutdown"
android:description="@string/permdesc_shutdown"
- android:protectionLevel="signature" />
+ android:protectionLevel="signatureOrSystem" />
<!-- Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png
new file mode 100644
index 0000000..8bb4048
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png
new file mode 100644
index 0000000..fdd3ee7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
index 87d9c21..ab6abdc 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
index 720ee78..dbdfc79 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
index 4275da0..4eba040 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
index 3ec9c1f..b186730 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
index 227bde2..06190a1 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
index 6ddfab0..8c16566 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png
new file mode 100644
index 0000000..0ce5d13
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png
new file mode 100644
index 0000000..945516e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png b/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png
new file mode 100644
index 0000000..2993b44
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
new file mode 100644
index 0000000..33e6dc8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
new file mode 100644
index 0000000..eb0d90f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
index 09ca253..74c02c2 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
index 0a7d3a1..345f4f5 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
index 54a1519..40e5db3 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
index 06ca0d4..0cbf6d2 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
index 9015299..bc56916 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
index b355cb3..84adf68 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
new file mode 100644
index 0000000..4a98e57
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
new file mode 100644
index 0000000..5cf6bf3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png
new file mode 100644
index 0000000..4aad237
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_holo.png b/core/res/res/drawable-mdpi/scrubber_control_holo.png
index 135b2aa..8457833 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_holo.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
new file mode 100644
index 0000000..8582b13
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
new file mode 100644
index 0000000..6ad876e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
index 7b48cf9..baf70cd 100644
--- a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
index 7c84ac9..6f31497 100644
--- a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png
new file mode 100644
index 0000000..8bb4048
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png
new file mode 100644
index 0000000..fdd3ee7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
index c98c951..ab6abdc 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
index 7691f81..dbdfc79 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
index fab86ac..06190a1 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
index 876eb794..8c16566 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png
new file mode 100644
index 0000000..0ce5d13
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png
new file mode 100644
index 0000000..945516e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png b/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png
new file mode 100644
index 0000000..2993b44
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png
new file mode 100644
index 0000000..33e6dc8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png
new file mode 100644
index 0000000..eb0d90f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png
index 2646899..44a5d82 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png
index 374d457..6613683 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
index 65c87ba..74c02c2 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
index 724b3fd..345f4f5 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
index 2cc7f62..bc56916 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
index a2d9d8a..84adf68 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png
new file mode 100644
index 0000000..4a98e57
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png
new file mode 100644
index 0000000..5cf6bf3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png
new file mode 100644
index 0000000..4aad237
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable/edit_text_holo_dark.xml b/core/res/res/drawable/edit_text_holo_dark.xml
index 63ccd1d..29a5150 100644
--- a/core/res/res/drawable/edit_text_holo_dark.xml
+++ b/core/res/res/drawable/edit_text_holo_dark.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" />
<item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_dark" />
- <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_dark" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_activated_holo_dark" />
+ <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_dark" />
<item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_dark" />
<item android:drawable="@drawable/textfield_disabled_holo_dark" />
diff --git a/core/res/res/drawable/edit_text_holo_light.xml b/core/res/res/drawable/edit_text_holo_light.xml
index 324acda..5426916 100644
--- a/core/res/res/drawable/edit_text_holo_light.xml
+++ b/core/res/res/drawable/edit_text_holo_light.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
<item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_light" />
- <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_light" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_activated_holo_light" />
+ <iten android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_light" />
<item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
<item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_light" />
<item android:drawable="@drawable/textfield_disabled_holo_light" />
diff --git a/core/res/res/drawable/edit_text_multiline_holo_dark.xml b/core/res/res/drawable/edit_text_multiline_holo_dark.xml
index 67d2748..d20ea19 100644
--- a/core/res/res/drawable/edit_text_multiline_holo_dark.xml
+++ b/core/res/res/drawable/edit_text_multiline_holo_dark.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" />
<item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_dark" />
- <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_dark" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_activated_holo_dark" />
+ <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_multiline_focused_holo_dark" />
<item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_dark" />
<item android:drawable="@drawable/textfield_multiline_disabled_holo_dark" />
diff --git a/core/res/res/drawable/edit_text_multiline_holo_light.xml b/core/res/res/drawable/edit_text_multiline_holo_light.xml
index 08b3ec6..41a4eab 100644
--- a/core/res/res/drawable/edit_text_multiline_holo_light.xml
+++ b/core/res/res/drawable/edit_text_multiline_holo_light.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" />
<item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_light" />
- <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_light" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_activated_holo_light" />
+ <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_multiline_focused_holo_light" />
<item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" />
<item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_light" />
<item android:drawable="@drawable/textfield_multiline_disabled_holo_light" />
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
index 90172a5..b117bb8 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
@@ -15,11 +15,14 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
<item android:id="@android:id/background"
- android:drawable="@android:drawable/scrubber_track_holo_dark" />
- <item android:id="@android:id/secondaryProgress"
- android:drawable="@android:drawable/scrubber_track_holo_dark" />
- <item android:id="@android:id/progress"
- android:drawable="@android:drawable/scrubber_track_holo_dark" />
+ android:drawable="@android:drawable/scrubber_track_holo_dark" />
+ <item android:id="@android:id/secondaryProgress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@android:drawable/scrubber_secondary_holo" />
+ </item>
+ <item android:id="@android:id/progress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@android:drawable/scrubber_primary_holo" />
+ </item>
</layer-list>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
index 5fc9697..6cd08ea 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
@@ -15,11 +15,14 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
<item android:id="@android:id/background"
- android:drawable="@android:drawable/scrubber_track_holo_light" />
- <item android:id="@android:id/secondaryProgress"
- android:drawable="@android:drawable/scrubber_track_holo_light" />
- <item android:id="@android:id/progress"
- android:drawable="@android:drawable/scrubber_track_holo_light" />
+ android:drawable="@android:drawable/scrubber_track_holo_light" />
+ <item android:id="@android:id/secondaryProgress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@android:drawable/scrubber_secondary_holo" />
+ </item>
+ <item android:id="@android:id/progress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@android:drawable/scrubber_primary_holo" />
+ </item>
</layer-list>
diff --git a/core/res/res/layout-xlarge/alert_dialog_holo.xml b/core/res/res/layout-xlarge/alert_dialog_holo.xml
index 72b1e31..6790a81 100644
--- a/core/res/res/layout-xlarge/alert_dialog_holo.xml
+++ b/core/res/res/layout-xlarge/alert_dialog_holo.xml
@@ -74,14 +74,17 @@
<ScrollView android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
android:paddingTop="32dip"
android:paddingBottom="32dip"
- android:paddingLeft="32dip"
- android:paddingRight="32dip">
+ android:clipToPadding="false">
<TextView android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip" />
</ScrollView>
</LinearLayout>
@@ -93,19 +96,24 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dip"
- android:paddingBottom="8dip" />
+ android:paddingBottom="8dip"
+ android:paddingLeft="32dip"
+ android:paddingRight="32dip" />
</FrameLayout>
<LinearLayout android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="16dip">
<LinearLayout
+ style="?android:attr/buttonGroupStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:paddingTop="4dip"
android:paddingLeft="2dip"
android:paddingRight="2dip"
android:measureWithLargestChild="true">
@@ -120,18 +128,21 @@
android:layout_gravity="left"
android:layout_weight="1"
android:maxLines="2"
+ style="?android:attr/borderlessButtonStyle"
android:layout_height="wrap_content" />
<Button android:id="@+id/button3"
android:layout_width="0dip"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:maxLines="2"
+ style="?android:attr/borderlessButtonStyle"
android:layout_height="wrap_content" />
<Button android:id="@+id/button2"
android:layout_width="0dip"
android:layout_gravity="right"
android:layout_weight="1"
android:maxLines="2"
+ style="?android:attr/borderlessButtonStyle"
android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/rightSpacer"
android:layout_width="0dip"
diff --git a/core/res/res/layout-xlarge/select_dialog_holo.xml b/core/res/res/layout-xlarge/select_dialog_holo.xml
new file mode 100644
index 0000000..7c95693
--- /dev/null
+++ b/core/res/res/layout-xlarge/select_dialog_holo.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the ListView to display the items.
+ Assign an ID so its state will be saved/restored.
+-->
+<view class="com.android.internal.app.AlertController$RecycleListView"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/select_dialog_listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:cacheColorHint="@null"
+ android:divider="?android:attr/listDividerAlertDialog"
+ android:scrollbars="vertical"
+ android:overScrollMode="ifContentScrolls" />
diff --git a/core/res/res/layout-xlarge/select_dialog_item_holo.xml b/core/res/res/layout-xlarge/select_dialog_item_holo.xml
new file mode 100644
index 0000000..396092e
--- /dev/null
+++ b/core/res/res/layout-xlarge/select_dialog_item_holo.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the TextView to display individual
+ items.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:ellipsize="marquee"
+/>
diff --git a/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml b/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml
new file mode 100644
index 0000000..8027035
--- /dev/null
+++ b/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ android:ellipsize="marquee"
+/>
diff --git a/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml b/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml
new file mode 100644
index 0000000..cab519f
--- /dev/null
+++ b/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:checkMark="?android:attr/listChoiceIndicatorSingle"
+ android:ellipsize="marquee"
+/>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 0f84418..fd488bd 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -64,8 +64,6 @@
android:layout_marginRight="@dimen/preference_screen_side_margin"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
- android:paddingLeft="32dip"
- android:paddingRight="32dip"
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 dbe0df0..69fb73a 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -18,11 +18,11 @@
-->
<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"
- android:background="@android:color/transparent">
+ android:background="@android:color/transparent"
+ android:layout_removeBorders="true">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
@@ -30,6 +30,8 @@
android:layout_weight="1"
android:paddingTop="48dip"
android:paddingBottom="48dip"
+ android:paddingLeft="32dip"
+ android:paddingRight="32dip"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
android:cacheColorHint="@android:color/transparent"
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index 7935e2a..4bc2d1a 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/res/layout/SearchBar.xml
+/*
**
** Copyright 2007, The Android Open Source Project
**
@@ -23,87 +23,36 @@
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
+ android:orientation="horizontal"
android:focusable="true"
+ android:background="?attr/actionModeBackground"
android:descendantFocusability="afterDescendants">
- <!-- Outer layout defines the entire search bar at the top of the screen -->
- <LinearLayout
- android:id="@+id/search_plate"
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="12dip"
- android:paddingRight="6dip"
- android:paddingTop="7dip"
- android:paddingBottom="16dip"
- android:background="@drawable/search_plate_global" >
+ android:layout_gravity="center_vertical"
+ >
- <!-- This is actually used for the badge icon *or* the badge label (or neither) -->
- <TextView
- android:id="@+id/search_badge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="2dip"
- android:drawablePadding="0dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimaryInverse" />
-
- <!-- Inner layout contains the app icon, button(s) and EditText -->
- <LinearLayout
- android:id="@+id/search_edit_frame"
+ <ImageView
+ android:id="@+id/search_app_icon"
+ android:layout_height="48dip"
+ android:layout_width="48dip"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_gravity="center_vertical"
+ android:layout_alignParentLeft="true"
+ />
+ <SearchView
+ android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/search_app_icon"
- android:layout_height="36dip"
- android:layout_width="36dip"
- android:layout_marginRight="7dip"
- android:layout_gravity="center_vertical"
- />
-
- <view class="android.app.SearchDialog$SearchAutoComplete"
- android:id="@+id/search_src_text"
- android:background="@drawable/textfield_search"
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1.0"
- android:paddingLeft="8dip"
- android:paddingRight="6dip"
- android:drawablePadding="2dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:inputType="text|textAutoComplete"
- android:dropDownWidth="match_parent"
- android:dropDownHeight="match_parent"
- android:dropDownAnchor="@id/search_plate"
- android:dropDownVerticalOffset="-9dip"
- android:popupBackground="@android:drawable/search_dropdown_background"
- />
-
- <!-- This button can switch between text and icon "modes" -->
- <Button
- android:id="@+id/search_go_btn"
- android:background="@drawable/btn_search_dialog"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:maxWidth="600dip"
+ android:iconifiedByDefault="false"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="center_vertical|right"
/>
- <ImageButton
- android:id="@+id/search_voice_btn"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginLeft="0dip"
- android:layout_marginTop="-6.5dip"
- android:layout_marginBottom="-7dip"
- android:layout_marginRight="-5dip"
- android:background="@drawable/btn_search_dialog_voice"
- android:src="@android:drawable/ic_btn_speak_now"
- />
- </LinearLayout>
-
- </LinearLayout>
+ </RelativeLayout>
</view>
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index eb0bb11..0fb824f 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -74,7 +74,7 @@
android:src="?android:attr/searchViewSearchIcon"
/>
- <AutoCompleteTextView
+ <view class="android.widget.SearchView$SearchAutoComplete"
android:id="@+id/search_src_text"
android:layout_height="36dip"
android:layout_width="0dp"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fd4b32c..4abcc9f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"تم إرسال عدد كبير من الرسائل القصيرة SMS. حدّد \"موافق\" للمتابعة، أو \"إلغاء\" لإيقاف الإرسال."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"موافق"</string>
<string name="sms_control_no" msgid="1715320703137199869">"إلغاء"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"تعيين"</string>
<string name="default_permission_group" msgid="2690160991405646128">"افتراضي"</string>
<string name="no_permissions" msgid="7283357728219338112">"لا أذونات مطلوبة"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e01bb9b..03206b7 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Изпращат се голям брой SMS съобщения. Изберете „OK“, за да продължите, или „Отказ“, за да спрете изпращането."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Отказ"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Задаване"</string>
<string name="default_permission_group" msgid="2690160991405646128">"По подразбиране"</string>
<string name="no_permissions" msgid="7283357728219338112">"Не се изискват разрешения"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index c32c95a..fd788e3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"S\'estan enviant molts missatges SMS. Seleccioneu \"D\'acord\" per continuar o \"Cancel·la\" per aturar l\'enviament."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"D\'acord"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancel·la"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Defineix"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predeterminat"</string>
<string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f65f585..1c233f0 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Bez upozornění smazat všechna data telefonu obnovením továrních dat"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Nastavit globální proxy server zařízení"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Vyberte globální proxy server, který se bude používat, když jsou zásady aktivní. Aktuální globální proxy server nastavuje pouze první správce zařízení."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Nastavit konec platnosti hesla"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ovládání doby, po jejímž uplynutí je nutné změnit heslo pro odemknutí obrazovky"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domů"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"čtení historie a záložek Prohlížeče"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Je odesílán velký počet zpráv SMS. Vyberte OK, chcete-li pokračovat, nebo Zrušit, chcete-li odesílání ukončit."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Zrušit"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Nastavit"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Výchozí"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Zobrazit vše"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Úložiště USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB připojeno"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Připojili jste telefon k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB zařízení Android či obráceně, vyberte následující tlačítko."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Připojili jste telefon k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB zařízení Android či obráceně, vyberte následující tlačítko."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Připojili jste se k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB v zařízení Android či obráceně, stiskněte tlačítko níže."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Připojili jste se k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače na kartu SD v zařízení Android či obráceně, stiskněte tlačítko níže."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Zapnout úložiště USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Problém s použitím úložiště USB jako velkokapacitního úložiště."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Problém s použitím karty SD jako velkokapacitního úložiště USB."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8bb762b..5f96369 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Slet telefonens data uden varsel ved at gendanne fabriksindstillinger"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angiv enhedens globale proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angiv enhedens globale proxy, der skal bruges, mens politikken er aktiveret. Kun den første enhedsadministrator angiver den effektive globale proxy."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Indstil udløb for adgangskode"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontroller, hvor lang tid der skal gå, før adgangskoden til skærmlåsen skal ændres."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hjem"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Der sendes et stort antal sms-beskeder. Vælg \"OK\" for at fortsætte eller \"Annuller\" for at stoppe afsendelsen."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Annuller"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Angiv"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
<string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masselagring"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB er tilsluttet"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har forbundet din telefon til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har forbundet din telefon til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har fået forbindelse til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har fået forbindelse til din computer via USB. Vælg knappen nedenfor, hvis du ønsker at kopiere filer mellem din computer og din Androids SD-kort."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Slå USB-lagringen til"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Der opstod et problem med at bruge USB-lager til USB-masselager."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Der opstod et problem med at bruge dit SD-kort til USB-masselager."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 87e57ce..8aa24d9 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Auf Werkseinstellungen zurücksetzen und Daten auf dem Telefon ohne Warnung löschen"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Den globalen Proxy des Geräts festlegen"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Den globalen Proxy des Geräts zur Verwendung während der Aktivierung der Richtlinie festlegen. Nur der erste Geräteadministrator kann den gültigen globalen Proxy festlegen."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Ablauf des Passworts festlegen"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Zeitraum bis zur Änderung des Passworts für die Bildschirmsperre festlegen"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Privat"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Browserverlauf und Lesezeichen lesen"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Es werden eine große Anzahl an Kurznachrichten versendet. Wählen Sie \"OK\", um fortzufahren, oder drücken Sie auf \"Abbrechen\", um den Sendevorgang zu beenden."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Abbrechen"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Einstellen"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
<string name="no_permissions" msgid="7283357728219338112">"Keine Berechtigungen erforderlich"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Alle anzeigen"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-Massenspeicher"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB-Verbindung"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Wählen Sie die Schaltfläche unten aus, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Wählen Sie die Schaltfläche unten aus, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Berühren Sie die Schaltfläche unten, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Berühren Sie die Schaltfläche unten, wenn Sie Dateien von Ihrem Computer auf die SD-Karte Ihres Android-Geräts und umgekehrt kopieren möchten."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-Speicher aktivieren"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Bei der Verwendung Ihres USB-Speichers als USB-Massenspeicher ist ein Problem aufgetreten."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Bei der Verwendung Ihrer SD-Karte als USB-Massenspeicher ist ein Problem aufgetreten."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index bbd02f2..c74839f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Διαγραφή των δεδομένων του τηλεφώνου χωρίς προειδοποίηση με επαναφορά των εργοστασιακών δεδομένων"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ρύθμιση του γενικού διακομιστή μεσολάβησης της συσκευής"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ορίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής όταν είναι ενεργοποιημένη η πολιτική. Μόνο ο διαχειριστής της πρώτης συσκευής ορίζει τον ισχύοντα γενικό διακομιστή μεσολάβησης."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Ορισμός λήξης κωδ. πρόσβασης"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ελέγξτε πόσος χρόνος απομένει προτού πρέπει να αλλάξετε τον κωδικό πρόσβασης κλειδώματος της οθόνης"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Οικία"</item>
<item msgid="869923650527136615">"Κινητό"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Αποστέλλεται μεγάλος αριθμός μηνυμάτων SMS. Επιλέξτε \"OK\" για συνέχεια, ή \"Ακύρωση\" για διακοπή αποστολής."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Ακύρωση"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Ορισμός"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Προεπιλεγμένο"</string>
<string name="no_permissions" msgid="7283357728219338112">"Δεν απαιτούνται άδειες"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Εμφάνιση όλων"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Μαζική αποθήκευση USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Το USB είναι συνδεδεμένο"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Συνδέσατε το τηλέφωνό σας στον υπολογιστή μέσω USB. Επιλέξτε το παρακάτω κουμπί αν θέλετε να αντιγράψετε αρχεία μεταξύ του υπολογιστή και του χώρου αποθήκευσης USB του Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Συνδέσατε το τηλέφωνό σας στον υπολογιστή μέσω USB. Επιλέξτε το παρακάτω κουμπί αν θέλετε να αντιγράψετε αρχεία μεταξύ του υπολογιστή και του χώρου αποθήκευσης USB του Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Συνδεθήκατε στον υπολογιστή σας μέσω USB. Αγγίξτε το παρακάτω κουμπί, αν θέλετε να αντιγράψετε αρχεία ανάμεσα στον υπολογιστή σας και τον χώρο αποθήκευσης USB του Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Συνδεθήκατε στον υπολογιστή σας μέσω USB. Αγγίξτε το παρακάτω κουμπί, αν θέλετε να αντιγράψετε αρχεία ανάμεσα στον υπολογιστή σας και την κάρτα SD του Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Ενεργοποίηση αποθηκευτικού χώρου USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Παρουσιάστηκε ένα πρόβλημα στη χρήση του αποθηκευτικού χώρου USB ως χώρο USB μαζικής αποθήκευσης."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Παρουσιάστηκε ένα πρόβλημα στη χρήση της κάρτας SD ως χώρο USB μαζικής αποθήκευσης."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index eab891f..11ae9f4 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -905,6 +905,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"A large number of SMS messages are being sent. Select \"OK\" to continue or \"Cancel\" to stop sending."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancel"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Set"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 7fc79b1..abce7dd 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -481,10 +481,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Borrar los datos del teléfono sin advertencias al restablecer la configuración original"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Configura el proxy global de dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configuración del proxy global de dispositivo que se utilizará mientras se habilita la política. Sólo la primera administración de dispositivo configura el proxy global efectivo."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Establecer la caducidad de la contraseña"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Verifica cuánto tiempo antes debes cambiar la contraseña de la pantalla de bloqueo"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Celular"</item>
@@ -867,6 +865,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Se envía una gran cantidad de mensajes SMS. Selecciona \"Aceptar\" para continuar o \"Cancelar\" para detener el envío."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Establecer"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string>
<string name="no_permissions" msgid="7283357728219338112">"No se requieren permisos"</string>
@@ -874,8 +874,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Almacenamiento masivo USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"conectado al USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y la tarjeta SD de Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar el almacenamiento USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Hay un problema para utilizar el almacenamiento USB en el almacenamiento masivo USB."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Hay un problema para utilizar tu tarjeta SD en el almacenamiento masivo USB."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1b28dca..8c3543c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Borrado de los datos del teléfono sin avisar restableciendo datos de fábrica"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir el servidor proxy global"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el primer administrador de dispositivos define el servidor proxy global efectivo."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir caducidad contraseña"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Permite controlar cuándo se debe cambiar la contraseña de bloqueo de la pantalla."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Móvil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer información de marcadores y del historial del navegador"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Se ha enviado un número elevado de mensajes SMS. Selecciona \"Aceptar\" para continuar o \"Cancelar\" para interrumpir el envío."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Establecer"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string>
<string name="no_permissions" msgid="7283357728219338112">"No es necesario ningún permiso"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Almacenamiento USB masivo"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Conectado por USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado el teléfono al equipo mediante USB. Selecciona el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono con Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado el teléfono al equipo mediante USB. Selecciona el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono con Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado el teléfono al equipo mediante USB. Toca el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado el teléfono al equipo mediante USB. Toca el botón situado debajo si deseas copiar archivos entre el equipo y la tarjeta SD del teléfono Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar almacenamiento USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Se ha producido un problema al utilizar el almacenamiento USB para el almacenamiento masivo USB."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Se ha producido un problema al utilizar la tarjeta SD para el almacenamiento USB masivo."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cbb5fe3..068bfe0 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"تعداد زیادی پیامک ارسال شده است. برای ادامه، \"تأیید\" را کلیک کرده و برای توقف ارسال، \"لغو\" را کلیک کنید."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"تأیید"</string>
<string name="sms_control_no" msgid="1715320703137199869">"لغو"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string>
<string name="default_permission_group" msgid="2690160991405646128">"پیش فرض"</string>
<string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3390669..bddb852 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Olet lähettämässä suurta määrää tekstiviestejä. Jatka valitsemalla OK tai peruuta lähetys valitsemalla Peruuta."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Peruuta"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Aseta"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Oletus"</string>
<string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f618070..700674c 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Effacer les données du téléphone sans avertissement, en restaurant les valeurs d\'usine"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Définir le proxy global du mobile"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Indiquez le proxy global à utiliser pour ce mobile lorsque les règles sont activées. Seul l\'administrateur principal du mobile peut définir le proxy global utilisé."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Définir date exp. mot de passe"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Définir la fréquence de changement du mot de passe de verrouillage d\'écran"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domicile"</item>
<item msgid="869923650527136615">"Mobile"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lire l\'historique et les favoris du navigateur"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Vous êtes sur le point d\'envoyer un grand nombre de messages SMS. Sélectionnez OK pour continuer ou Annuler pour interrompre l\'envoi."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Annuler"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Définir"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Par défaut"</string>
<string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Tout afficher"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Stockage de masse USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Connecté à l\'aide d\'un câble USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Vous avez connecté votre téléphone à votre ordinateur à l\'aide d\'un câble USB. Sélectionnez le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Vous avez connecté votre téléphone à votre ordinateur à l\'aide d\'un câble USB. Sélectionnez le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Vous êtes connecté à votre ordinateur via un câble USB. Appuyez sur le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Vous êtes connecté à votre ordinateur via un câble USB. Appuyez sur le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la carte SD de votre Android, ou inversement."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Activer la mémoire de stockage USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Un problème est survenu lors de l\'utilisation de votre mémoire de stockage USB comme mémoire de stockage de masse."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Un problème est survenu lors de l\'utilisation de votre carte SD comme mémoire de stockage de masse USB."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index bc6d451..a075566 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Šalje se velika količina SMS poruka. Odaberite \"U redu\" za nastavak, ili za prekid slanja odaberite \"Odustani\"."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"U redu"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Odustani"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Postavi"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Zadano"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nije potrebno dopuštenje"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 36aea65..3a4bf8d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Nagyszámú SMS-t kíván elküldeni. A folytatáshoz válassza az \"OK\", a küldés leállításához a \"Mégse\" lehetőséget."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Mégse"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Beállítás"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Alapértelmezett"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nincs szükség engedélyre"</string>
diff --git a/core/res/res/values-id-rID/arrays.xml b/core/res/res/values-in-rID/arrays.xml
index 512cace..512cace 100644
--- a/core/res/res/values-id-rID/arrays.xml
+++ b/core/res/res/values-in-rID/arrays.xml
diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml
index 4d4ebb2..4d4ebb2 100644
--- a/core/res/res/values-id-rID/donottranslate-cldr.xml
+++ b/core/res/res/values-in-rID/donottranslate-cldr.xml
diff --git a/core/res/res/values-id/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml
index 7a58a19..7a58a19 100644
--- a/core/res/res/values-id/donottranslate-cldr.xml
+++ b/core/res/res/values-in/donottranslate-cldr.xml
diff --git a/core/res/res/values-id/strings.xml b/core/res/res/values-in/strings.xml
index 2d07b33..aa243e0 100644
--- a/core/res/res/values-id/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Sejumlah besar pesan SMS sedang dikirimkan. Pilih \"OK\" untuk melanjutkan, atau \"Batal\" untuk menghentikan pengiriman."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Batal"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Setel"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Bawaan"</string>
<string name="no_permissions" msgid="7283357728219338112">"Tidak perlu izin"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 238a748..0b74664 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Cancella i dati del telefono senza preavviso eseguendo un ripristino dati di fabbrica"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Imposta il proxy globale del dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale effettivo è impostabile solo dal primo amministratore del dispositivo."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Imposta scadenza password"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Stabilisci la scadenza della password di blocco dello schermo"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Cellulare"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lettura cronologia e segnalibri del browser"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"È in corso l\'invio di numerosi SMS. Seleziona \"OK\" per continuare, oppure \"Annulla\" per interrompere l\'invio."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Annulla"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Imposta"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predefinito"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nessuna autorizzazione richiesta"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Mostra tutto"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Archivio di massa USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB collegata"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Il telefono è stato collegato al computer tramite USB. Seleziona il pulsante sottostante se desideri copiare file tra il computer e l\'archivio USB di Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Il telefono è stato collegato al computer tramite USB. Seleziona il pulsante sottostante se desideri copiare file tra il computer e l\'archivio USB di Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ti sei collegato al computer tramite USB. Tocca il pulsante sotto se desideri copiare file tra il computer e l\'archivio USB di Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ti sei collegato al computer tramite USB. Tocca il pulsante sotto se desideri copiare file tra il computer e la scheda SD di Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Attiva archivio USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Problema di utilizzo dell\'archivio USB come archivio di massa USB."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Problema di utilizzo della scheda SD come archivio di massa USB."</string>
diff --git a/core/res/res/values-he-rIL/arrays.xml b/core/res/res/values-iw-rIL/arrays.xml
index 87c1231..87c1231 100644
--- a/core/res/res/values-he-rIL/arrays.xml
+++ b/core/res/res/values-iw-rIL/arrays.xml
diff --git a/core/res/res/values-he/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml
index d373a34..d373a34 100644
--- a/core/res/res/values-he/donottranslate-cldr.xml
+++ b/core/res/res/values-iw/donottranslate-cldr.xml
diff --git a/core/res/res/values-he/strings.xml b/core/res/res/values-iw/strings.xml
index c5d945c..25074ea 100644
--- a/core/res/res/values-he/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"מספר גדול של הודעות SMS נשלח. בחר \'אישור\' כדי להמשיך או \'ביטול\' כדי לעצור את השליחה."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"אישור"</string>
<string name="sms_control_no" msgid="1715320703137199869">"ביטול"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"הגדר"</string>
<string name="default_permission_group" msgid="2690160991405646128">"ברירת מחדל"</string>
<string name="no_permissions" msgid="7283357728219338112">"לא דרושים אישורים"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 75d3722..e8bb57a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"警告せずにデータの初期化を実行して端末内のデータを消去します。"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"端末のグローバルプロキシを設定"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ポリシーが有効になっている場合は端末のグローバルプロキシが使用されるように設定します。有効なグローバルプロキシを設定できるのは最初のデバイス管理者だけです。"</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"パスワードの有効期限の設定"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"画面ロックパスワードの変更が必要になるまでの期間を指定します"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"自宅"</item>
<item msgid="869923650527136615">"携帯"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">"、 "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ブラウザの履歴とブックマークを読み取る"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"大量のSMSメッセージを送信しようとしています。[OK]で送信、[キャンセル]で中止します。"</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"キャンセル"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"設定"</string>
<string name="default_permission_group" msgid="2690160991405646128">"端末既定"</string>
<string name="no_permissions" msgid="7283357728219338112">"権限の許可は必要ありません"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"すべて表示"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USBマスストレージ"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB接続"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"携帯端末をUSBでパソコンに接続しています。パソコンとAndroidのUSBストレージの間でファイルをコピーするには、下のボタンを選択します。"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"携帯端末をUSBでパソコンに接続しています。パソコンとAndroidのUSBストレージの間でファイルをコピーするには、下のボタンを選択します。"</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USBでパソコンに接続しています。パソコンとAndroidのUSBストレージ間でファイルをコピーするには下のボタンをタップします。"</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USBでパソコンに接続しています。パソコンとAndroidのSDカード間でファイルをコピーするには下のボタンをタップします。"</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"USBストレージをONにする"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USBストレージをUSBマスストレージとして使用する際に問題が発生しました。"</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"USBをUSBマスストレージとして使用する際に問題が発生しました。"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 1ed8492..ab873be 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"초기화를 수행하여 경고 없이 휴대전화 데이터를 지웁니다."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"기기 전체 프록시 설정"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 첫 번째 기기 관리자가 설정한 전체 프록시만 유효합니다."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"비밀번호 만료 설정"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"화면 잠금 비밀번호를 변경해야 하는 기간 변경"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"집"</item>
<item msgid="869923650527136615">"모바일"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"여러 개의 SMS 메시지를 보내는 중입니다. 계속하려면 \'확인\'을 선택하고 전송을 중지하려면 \'취소\'를 선택하세요."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"확인"</string>
<string name="sms_control_no" msgid="1715320703137199869">"취소"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"설정"</string>
<string name="default_permission_group" msgid="2690160991405646128">"기본값"</string>
<string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"모두 표시"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 대용량 저장소"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB 연결됨"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USB를 통해 휴대전화를 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 선택하세요."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USB를 통해 휴대전화를 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 선택하세요."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USB를 통해 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 터치하세요."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USB를 통해 컴퓨터에 연결했습니다. 컴퓨터와 Android의 SD 카드 간에 파일을 복사하려면 아래의 버튼을 터치하세요."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"USB 저장소 사용"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USB 대용량 저장소로 공유 저장용량을 사용하는 동안 문제가 발생했습니다."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"USB 대용량 저장소로 SD 카드를 사용하는 동안 문제가 발생했습니다."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 41d9fbd..c9059cb 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Siunčiama daug SMS pranešimų. Pasirinkite „Gerai“, jei norite tęsti, arba „Atšaukti“, jei norite sustabdyti siuntimą."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Gerai"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Atšaukti"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Nustatyti"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Numatytasis"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nereikia leidimų"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d706ed5..ee3def8 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Tiek sūtīts liels īsziņu skaits. Atlasiet Labi, lai turpinātu, vai Atcelt, lai apturētu sūtīšanu."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Labi"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Atcelt"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Iestatīt"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Noklusējums"</string>
<string name="no_permissions" msgid="7283357728219338112">"Atļaujas nav nepieciešamas."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 96d9c32..a7e1879 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Tilbakestill telefonens data uten advarsel ved å utføre tilbakestilling til fabrikkstandard"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angi enhetens globale mellomtjener"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angir den globale mellomtjeneren på enheten som skal brukes når regelen er aktivert. Kun den opprinnelige administratoren av enheten kan angi den globale mellomtjeneren."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Angi utløpsdato for passordet"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Velg hvor lenge det skal gå før passordet til låseskjermen må byttes"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hjemmenummer"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Et stort antall SMS-meldinger blir sendt. Velg «OK» for å fortsette, eller «Avbryt» for å avbryte sendingen."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Lagre"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
<string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masselagring"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB koblet til"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og USB-lagring for Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og USB-lagring for Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har koblet telefonen til datamaskinen via USB. Trykk på knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og Android-telefonens USB-lagring."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har koblet telefonen til datamaskinen via USB. Trykk på knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og SD-kortet i Android-telefonen."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Slå på USB-lagring"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Det oppstod et problem under USB-lagring for USB-enheten."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Det oppstod et problem under SD-kortet for USB-enheten."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 8998fba..3f4c7d4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"De gegevens van de telefoon zonder waarschuwing wissen door de fabrieksinstellingen te herstellen"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Algemene proxy voor het apparaat instellen"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Stel de algemene proxy voor het apparaat in die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eerste apparaatbeheerder stelt de algemene proxy in."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Verval wachtwoord instellen"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Beheren hoe lang het duurt voordat het wachtwoord voor schermvergrendeling moet worden gewijzigd"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Thuis"</item>
<item msgid="869923650527136615">"Mobiel"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"browsergeschiedenis en bladwijzers lezen"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Er wordt een groot aantal SMS-berichten verzonden. Selecteer \'OK\' om door te gaan of \'Annuleren\' om de verzending te stoppen."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Annuleren"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Instellen"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standaard"</string>
<string name="no_permissions" msgid="7283357728219338112">"Geen machtigingen vereist"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Alles weergeven"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-massaopslag"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB-verbinding"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"U heeft uw telefoon via USB op uw computer aangesloten. Selecteer de onderstaande knop als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"U heeft uw telefoon via USB op uw computer aangesloten. Selecteer de onderstaande knop als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"U heeft uw telefoon via USB op uw computer aangesloten. Raak de onderstaande knop aan als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"U heeft uw telefoon via USB op uw computer aangesloten. Raak de onderstaande knop aan als u bestanden tussen uw computer en de SD-kaart van uw Android wilt kopiëren."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-opslag inschakelen"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Er is een probleem bij het gebruik van uw USB-opslag voor USB-massaopslag."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Er is een probleem bij het gebruik van uw SD-kaart voor USB-massaopslag."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ec80f83..fcd7275 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Wymazywanie danych z telefonu bez ostrzeżenia, przez przywrócenie danych fabrycznych"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ustaw globalny serwer proxy urządzenia"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ustaw globalny serwer proxy urządzenia do wykorzystywania przy włączonych zasadach. Tylko pierwszy administrator urządzenia ustawia obowiązujący globalny serwer proxy."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Ustaw wygasanie hasła"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrola czasu, po którym należy zmienić hasło blokowania ekranu"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Dom"</item>
<item msgid="869923650527136615">"Komórka"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"odczyt historii i zakładek przeglądarki"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Wysyłana jest duża liczba wiadomości SMS. Wybierz „OK”, aby kontynuować, lub „Anuluj”, aby zatrzymać wysyłanie."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Anuluj"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Ustaw"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Domyślne"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nie są wymagane żadne uprawnienia"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaż wszystko"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Pamięć masowa USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Połączenie przez USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Telefon został połączony z komputerem za pośrednictwem USB. Wybierz poniższy przycisk, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Telefon został połączony z komputerem za pośrednictwem USB. Wybierz poniższy przycisk, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Nawiązano połączenie z komputerem za pośrednictwem USB. Dotknij poniższego przycisku, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Nawiązano połączenie z komputerem za pośrednictwem USB. Dotknij poniższego przycisku, aby skopiować pliki między komputerem a kartą SD systemu Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Włącz nośnik USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Wystąpił problem z użyciem nośnika USB jako pamięci masowej USB."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Wystąpił problem z użyciem karty SD jako nośnika pamięci masowej USB."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5e9b0a4..47a9ed7 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Apagar os dados do telefone sem avisar, ao efectuar uma reposição de dados de fábrica"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do aparelho"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Definir o proxy global do aparelho a ser utilizado quando a política estiver activada. Só o primeiro administrador do aparelho define o proxy global efectivo."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Def. valid. da palavra-passe"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle com que antecedência é necessário alterar a palavra-passe de bloqueio do ecrã"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Residência"</item>
<item msgid="869923650527136615">"Móvel"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Está a ser enviado um grande número de mensagens SMS. Seleccione \"OK\" para continuar ou \"Cancelar\" para parar o envio."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predefinido"</string>
<string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar tudo"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Ligado através de USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ligou o telemóvel ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ligou o telemóvel ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ligou ao computador através de USB. Toque no botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ligou ao computador através de USB. Toque no botão abaixo se pretender copiar ficheiros entre o computador e o cartão SD do Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar armazenamento USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Existe um problema ao utilizar o armazenamento USB para o armazenamento em massa USB."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Existe um problema ao utilizar o cartão SD para armazenamento em massa USB."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ad8f64a..5aeb79f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Apaga os dados do telefone sem aviso, executando uma redefinição da configuração original"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configura o proxy global do dispositivo para ser usado enquanto a política estiver ativada. Somente o primeiro administrador do dispositivo pode configurar um verdadeiro proxy global."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir validade da senha"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle quanto tempo uma senha de bloqueio de tela deve ficar ativa antes de ser alterada"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Residencial"</item>
<item msgid="869923650527136615">"Celular"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Muitas mensagens SMS estão sendo enviadas. Selecione \"OK\" para continuar ou \"Cancelar\" para interromper o envio."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Padrão"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todas"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento USB em massa"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"Conectado por USB"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Você conectou o telefone ao computador via USB. Selecione o botão abaixo se quiser copiar arquivos entre seu computador e o armazenamento USB do Android."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Você conectou o telefone ao computador via USB. Selecione o botão abaixo se quiser copiar arquivos entre seu computador e o armazenamento USB do Android."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Você conectou ao computador via USB. Toque no botão abaixo se quiser copiar arquivos entre o computador e o armazenamento USB do seu Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Você conectou ao computador via USB. Toque no botão abaixo se quiser copiar arquivos entre o computador e o cartão SD do seu Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Ativar o armazenamento USB"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Há um problema com o uso do seu armazenamento USB para armazenamento USB em massa."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Há um problema com o uso do seu cartão SD para armazenamento USB em massa."</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 527e4e1..a1cbc4a 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -904,6 +904,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Vulais Vus trametter in grond dumber da messadis SMS? Tschernì OK per cuntinuar u Interrumper per annullar la spediziun."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Interrumper"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
<string name="no_permissions" msgid="7283357728219338112">"Naginas permissiuns obligatoricas"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 486591c..f235957 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"În acest moment se trimit multe mesaje SMS. Selectaţi „OK” pentru a continua sau „Anulaţi” pentru a opri trimiterea."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Anulaţi"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Setaţi"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Prestabilit"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nu se solicită nicio permisiune"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 976a006..4e4bee8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Уничтожить все данные на телефоне без предупреждения путем сброса настроек"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Глобальный прокси-сервер"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Настройте глобальный прокси-сервер устройства, который будет использоваться при активной политике. Глобальный прокси-сервер должен настроить первый администратор устройства."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Задать время действия пароля"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Задать время действия пароля перед появлением экрана блокировки"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Домашний"</item>
<item msgid="869923650527136615">"Мобильный"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"считывать историю и закладки браузера"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Отправляется большое количество SMS-сообщений. Нажмите \"ОК\" для продолжения или \"Отмена\" для прекращения отправки."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"ОК"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Отмена"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Установить"</string>
<string name="default_permission_group" msgid="2690160991405646128">"По умолчанию"</string>
<string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Показать все"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"Запоминающее устройство USB"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB-подключение установлено"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Телефон подключен к компьютеру через порт USB. Для копирования файлов между компьютером и USB-накопителем Android нажмите кнопку ниже."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Телефон подключен к компьютеру через порт USB. Для копирования файлов между компьютером и USB-накопителем Android нажмите кнопку ниже."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Телефон подключен к компьютеру через порт USB. Нажмите приведенную ниже кнопку, чтобы скопировать файлы с компьютера на USB-накопитель устройства Android."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Телефон подключен к компьютеру через порт USB. Нажмите приведенную ниже кнопку, чтобы скопировать файлы с компьютера на SD-карту устройства Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Включить USB-накопитель"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"При использовании USB-накопителя в качестве запоминающего устройства USB возникла неполадка."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"При использовании SD-карты в качестве запоминающего устройства USB возникла неполадка."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7e21ae5..d0413cf 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Odosiela sa veľký počet správ SMS. Ak chcete pokračovať, vyberte OK. Ak chcete odosielanie ukončiť, vyberte Zrušiť."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Zrušiť"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Nastaviť"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Predvolené"</string>
<string name="no_permissions" msgid="7283357728219338112">"Nevyžadujú sa žiadne oprávnenia."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 68eb899..ddcc6e5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"V pošiljanju je veliko sporočil SMS. Če želite nadaljevati, izberite »V redu«. Če želite pošiljanje ustaviti, izberite »Prekliči«."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"V redu"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Prekliči"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Nastavi"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Privzeto"</string>
<string name="no_permissions" msgid="7283357728219338112">"Ni zahtevanih dovoljenj"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 5cf7223..7287793 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Шаље се велики број SMS порука. Кликните на „Потврди“ да бисте наставили или на „Откажи“ да бисте зауставили слање."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Потврди"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Откажи"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Подеси"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Подразумевано"</string>
<string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index e5de6ad..3ace2e3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Ta bort data från telefonen utan förvarning genom att återställa standardinställningarna"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Ange lösenordets utgångsdatum"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Se hur långt det är kvar till du måste ändra lösenordet till låsningsskärmen"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hem"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Flera SMS-meddelanden skickas. Tryck på OK om du vill fortsätta eller på Avbryt om du vill avsluta sändningen."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Ställ in"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Standardinställning"</string>
<string name="no_permissions" msgid="7283357728219338112">"Inga behörigheter krävs"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Visa alla"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masslagring"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB-ansluten"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har anslutit telefonen till datorn via USB. Välj knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har anslutit telefonen till datorn via USB. Välj knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har anslutit telefonen till datorn via USB. Tryck på knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har anslutit telefonen till datorn via USB. Tryck på knappen nedan om du vill kopiera filer mellan datorn och SD-kortet i din Android."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"Aktivera USB-lagring"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Det gick inte att använda din USB-lagringsenhet för USB-masslagring."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Det gick inte att använda ditt SD-kort för USB-masslagring."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 0113275..d1adc6a 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"กำลังส่งข้อความ SMS จำนวนมาก เลือก \"ตกลง\" เพื่อทำงานต่อหรือ \"ยกเลิก\" เพื่อหยุดส่ง"</string>
<string name="sms_control_yes" msgid="2532062172402615953">"ตกลง"</string>
<string name="sms_control_no" msgid="1715320703137199869">"ยกเลิก"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"ตั้งค่า"</string>
<string name="default_permission_group" msgid="2690160991405646128">"เริ่มต้น"</string>
<string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 0e28a1a..209b4b2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Pinapadala ang malaking bilang ng mga SMS na mensahe. Piliin ang \"OK\" upang magpatuloy, o \"Kanselahin\" upang itigil ang pagpapadala."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Kanselahin"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Itakda"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
<string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a1abbeb..d24df0c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan silin"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Cihaz genelinde geçerli proxy\'i ayarla"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlayın. Etkin genel proxy\'yi yalnızca ilk cihaz yöneticisi ayarlar."</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"Şifre süre sonu tarihi ayarla"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ekran kilitleme şifresinin ne kadar süre sonra değiştirilmesi gerekeceğini denetleyin."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Ev"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Çok sayıda SMS mesajı gönderiliyor. Devam etmek için \"Tamam\"ı, göndermeyi durdurmak için \"İptal\"i seçin."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"Tamam"</string>
<string name="sms_control_no" msgid="1715320703137199869">"İptal"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Ayarla"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Varsayılan"</string>
<string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"Tümünü göster"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB Yığın Depolama"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB bağlandı"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Telefonunuzu USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeyi seçin."</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Telefonunuzu USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeyi seçin."</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Cihazınızı USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeye dokunun."</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Cihazınızı USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin SD kartı arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeye dokunun."</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"USB depolama birimini aç"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USB depolama biriminizi USB yığın depolama amaçlı kullanmayla ilgili bir sorun var."</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"SD kartınızı USB yığın dep br amaçlı kullanmada sorun var."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f9eacb9..a029694 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Надсил-ся завелика к-сть SMS повідомл. Натисн. \"OK\", щоб продовж, або \"Скасувати\", щоб припин. надсил."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Скасувати"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Устан."</string>
<string name="default_permission_group" msgid="2690160991405646128">"За умовч."</string>
<string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e33b7be..9c61058 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -925,6 +925,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"Một số lượng lớn các tin nhắn SMS đang được gửi. Chọn \"OK\" để tiếp tục hoặc \"Huỷ\" để dừng gửi."</string>
<string name="sms_control_yes" msgid="2532062172402615953">"OK"</string>
<string name="sms_control_no" msgid="1715320703137199869">"Huỷ"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"Đặt"</string>
<string name="default_permission_group" msgid="2690160991405646128">"Mặc định"</string>
<string name="no_permissions" msgid="7283357728219338112">"Không yêu cầu quyền"</string>
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index fef3c13..c67bbb7 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -37,4 +37,7 @@
<dimen name="thumbnail_width">160dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
<dimen name="thumbnail_height">100dp</dimen>
+ <!-- The standard size (both width and height) of an application icon that
+ will be displayed in the app launcher and elsewhere. -->
+ <dimen name="app_icon_size">64dip</dimen>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 031d488..e67e102 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"恢复出厂设置时,将擦除手机上的数据而不发送警告"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"设置设备全局代理"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"请设置在启用政策的情况下要使用的设备全局代理。只有第一设备管理员才可设置有效的全局代理。"</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"设置密码有效期"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制屏幕锁定密码的使用期限"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"住宅"</item>
<item msgid="869923650527136615">"手机"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"正在发送大量短信。选择“确定”继续,或选择“取消”停止发送。"</string>
<string name="sms_control_yes" msgid="2532062172402615953">"确定"</string>
<string name="sms_control_no" msgid="1715320703137199869">"取消"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"设置"</string>
<string name="default_permission_group" msgid="2690160991405646128">"默认"</string>
<string name="no_permissions" msgid="7283357728219338112">"不需要任何权限"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"全部显示"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 大容量存储设备"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB 已连接"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已通过 USB 将手机连接至计算机。如果您要在计算机与 Android 手机的 USB 存储设备之间复制文件,请点击下面的按钮。"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已通过 USB 将手机连接至计算机。如果您要在计算机与 Android 手机的 USB 存储设备之间复制文件,请点击下面的按钮。"</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已通过 USB 连接至计算机。如果您要在计算机与 Android 设备的 USB 存储设备之间复制文件,请触摸下面的按钮。"</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已通过 USB 连接至计算机。如果您要在计算机和 Android 设备的 SD 卡之间复制文件,请触摸下面的按钮。"</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"打开 USB 存储设备"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"使用 USB 存储设备作为 USB 大容量存储设备时出现问题。"</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"使用 SD 卡作为 USB 大容量存储设备时出现问题。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index c7cfe8e..bab4dfb 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -482,10 +482,8 @@
<string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"執行重設為原廠設定時,系統會直接清除手機資料而不提出警告"</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"設定裝置全域 Proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"設定政策啟用時所要使用的裝置全域 Proxy,只有第一個裝置管理員所設定的全域 Proxy 具有效力。"</string>
- <!-- no translation found for policylab_expirePassword (2314569545488269564) -->
- <skip />
- <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
- <skip />
+ <string name="policylab_expirePassword" msgid="2314569545488269564">"設定密碼到期日"</string>
+ <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制螢幕鎖定密碼的使用期限"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"住家電話"</item>
<item msgid="869923650527136615">"行動電話"</item>
@@ -668,8 +666,7 @@
<string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string>
<!-- no translation found for autofill_address_summary_name_format (3268041054899214945) -->
<skip />
- <!-- no translation found for autofill_address_summary_separator (7483307893170324129) -->
- <skip />
+ <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<!-- no translation found for autofill_address_summary_format (4874459455786827344) -->
<skip />
<string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"讀取瀏覽器的記錄與書籤"</string>
@@ -869,6 +866,8 @@
<string name="sms_control_message" msgid="1289331457999236205">"即將傳送大量 SMS 簡訊。選取 [確定] 繼續或 [取消] 停止傳送。"</string>
<string name="sms_control_yes" msgid="2532062172402615953">"確定"</string>
<string name="sms_control_no" msgid="1715320703137199869">"取消"</string>
+ <!-- no translation found for time_picker_dialog_title (8349362623068819295) -->
+ <skip />
<string name="date_time_set" msgid="5777075614321087758">"設定"</string>
<string name="default_permission_group" msgid="2690160991405646128">"預設值"</string>
<string name="no_permissions" msgid="7283357728219338112">"無須許可"</string>
@@ -876,8 +875,8 @@
<string name="perms_show_all" msgid="2671791163933091180"><b>"顯示全部"</b></string>
<string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 大量儲存裝置"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB 已連接"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"已透過 USB 連接手機與電腦。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請選取下方按鈕。"</string>
- <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"已透過 USB 連接手機與電腦。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請選取下方按鈕。"</string>
+ <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已透過 USB 與電腦建立連線。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請輕觸下方按鈕。"</string>
+ <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已透過 USB 與電腦建立連線。如要在電腦和 Android 的 SD 卡之間複製檔案,請輕觸下方按鈕。"</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"開啟 USB 儲存裝置"</string>
<string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"使用您的 USB 儲存裝置作為 USB 大量儲存裝置時發生問題。"</string>
<string name="usb_storage_error_message" product="default" msgid="120810397713773275">"使用您的 SD 卡作為 USB 大量儲存裝置時發生問題。"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5daa944..78465cf 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -602,9 +602,12 @@
<!-- Theme to use for alert dialogs spawned from this theme. -->
<attr name="alertDialogTheme" format="reference" />
- <!-- Drawable to use for vertical dividers. -->
+ <!-- Drawable to use for generic vertical dividers. -->
<attr name="dividerVertical" format="reference" />
+ <!-- Drawable to use for generic horizontal dividers. -->
+ <attr name="dividerHorizontal" format="reference" />
+
<!-- Style for button groups -->
<attr name="buttonGroupStyle" format="reference" />
@@ -614,6 +617,9 @@
<!-- Background drawable for standalone items that need focus/pressed states. -->
<attr name="selectableItemBackground" format="reference" />
+ <!-- Style for buttons without an explicit border, often used in groups. -->
+ <attr name="borderlessButtonStyle" format="reference" />
+
<!-- ============================ -->
<!-- SearchView styles and assets -->
<!-- ============================ -->
@@ -1293,6 +1299,11 @@
<attr name="bottomBright" format="reference|color" />
<attr name="bottomMedium" format="reference|color" />
<attr name="centerMedium" format="reference|color" />
+ <attr name="layout" />
+ <attr name="listLayout" format="reference" />
+ <attr name="multiChoiceItemLayout" format="reference" />
+ <attr name="singleChoiceItemLayout" format="reference" />
+ <attr name="listItemLayout" format="reference" />
</declare-styleable>
<!-- Fragment animation class attributes. -->
@@ -2178,6 +2189,17 @@
the minimum size of the largest child. If false, all children are
measured normally. -->
<attr name="measureWithLargestChild" format="boolean" />
+ <!-- Drawable to use as a vertical divider between buttons. -->
+ <attr name="divider" />
+ <!-- Setting for which dividers to show. -->
+ <attr name="showDividers">
+ <flag name="none" value="0" />
+ <flag name="beginning" value="1" />
+ <flag name="middle" value="2" />
+ <flag name="end" value="4" />
+ </attr>
+ <!-- Size of padding on either end of a divider. -->
+ <attr name="dividerPadding" format="dimension" />
</declare-styleable>
<declare-styleable name="ListView">
<!-- Reference to an array resource that will populate the ListView. For static content,
@@ -2201,9 +2223,17 @@
</declare-styleable>
<declare-styleable name="PreferenceFrameLayout">
<!-- Padding to use at the top of the prefs content. -->
- <attr name="topPadding" format="dimension" />
+ <attr name="borderTop" format="dimension" />
<!-- Padding to use at the bottom of the prefs content. -->
- <attr name="bottomPadding" format="dimension" />
+ <attr name="borderBottom" format="dimension" />
+ <!-- Padding to use at the left of the prefs content. -->
+ <attr name="borderLeft" format="dimension" />
+ <!-- Padding to use at the right of the prefs content. -->
+ <attr name="borderRight" format="dimension" />
+ </declare-styleable>
+ <declare-styleable name="PreferenceFrameLayout_Layout">
+ <!-- Padding to use at the top of the prefs content. -->
+ <attr name="layout_removeBorders" format="boolean" />
</declare-styleable>
<declare-styleable name="MenuView">
<!-- Default appearance of menu item text. -->
@@ -3410,21 +3440,18 @@
greater than 0 or infinite. The default value is restart. -->
<attr name="repeatMode"/>
<!-- Value the animation starts from. -->
- <attr name="valueFrom" format="float|integer"/>
+ <attr name="valueFrom" format="float|integer|color|dimension"/>
<!-- Value the animation animates to. -->
- <attr name="valueTo" format="float|integer"/>
+ <attr name="valueTo" format="float|integer|color|dimension"/>
<!-- The type of valueFrom and valueTo. -->
<attr name="valueType">
- <!-- valueFrom and valueTo are floats. -->
+ <!-- valueFrom and valueTo are floats. This is the default value is valueType is
+ unspecified. Note that if either valueFrom or valueTo represent colors
+ (beginning with "#"), then this attribute is ignored and the color values are
+ interpreted as integers. -->
<enum name="floatType" value="0" />
<!-- valueFrom and valueTo are integers. -->
<enum name="intType" value="1" />
- <!-- valueFrom and valueTo are doubles. -->
- <enum name="doubleType" value="2" />
- <!-- valueFrom and valueTo are colors. -->
- <enum name="colorType" value="3" />
- <!-- valueFrom and valueTo are a custom type. -->
- <enum name="customType" value="4" />
</attr>
</declare-styleable>
@@ -4542,11 +4569,7 @@
<!-- Drawable to use as a background for buttons added to this group. -->
<attr name="buttonBackground" format="reference" />
<!-- Setting for which dividers to show. -->
- <attr name="showDividers">
- <flag name="beginning" value="1" />
- <flag name="middle" value="2" />
- <flag name="end" value="4" />
- </attr>
+ <attr name="showDividers" />
</declare-styleable>
<declare-styleable name="ActionBar_LayoutParams">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1460e1..3fac653 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1392,6 +1392,10 @@
<public type="attr" name="rotation"/>
<public type="attr" name="rotationX"/>
<public type="attr" name="rotationY"/>
+ <public type="attr" name="showDividers" />
+ <public type="attr" name="dividerPadding" />
+ <public type="attr" name="borderlessButtonStyle" />
+ <public type="attr" name="dividerHorizontal" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
@@ -1555,6 +1559,7 @@
<public type="style" name="Widget.Holo.Light.ActionMode" />
<public type="style" name="Widget.Holo.Light.ActionButton.CloseMode" />
<public type="style" name="Widget.Holo.Light.ActionBar" />
+ <public type="style" name="Widget.Holo.Button.Borderless" />
<public type="string" name="selectTextMode" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eafa9b6..0f5ff05 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1806,7 +1806,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.12 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.12</string>
+ AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.13</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b3c3e0d..b7c11a8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -50,8 +50,10 @@
</style>
<style name="Widget.PreferenceFrameLayout">
- <item name="android:topPadding">0dip</item>
- <item name="android:bottomPadding">0dip</item>
+ <item name="android:borderTop">0dip</item>
+ <item name="android:borderBottom">0dip</item>
+ <item name="android:borderLeft">0dip</item>
+ <item name="android:borderRight">0dip</item>
</style>
<!-- Base style for animations. This style specifies no animations. -->
@@ -1058,7 +1060,6 @@
</style>
<style name="TextAppearance.Holo.Medium" parent="TextAppearance.Medium">
- <item name="android:textLineHeight">24dip</item>
</style>
<style name="TextAppearance.Holo.Small" parent="TextAppearance.Small">
@@ -1302,6 +1303,10 @@
<item name="android:paddingBottom">4dip</item>
</style>
+ <style name="Widget.Holo.Button.Borderless">
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
+
<style name="Widget.Holo.Button.Small">
<item name="android:background">@android:drawable/btn_default_holo_dark</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
@@ -1331,7 +1336,7 @@
<style name="Widget.Holo.ButtonGroup" parent="Widget.ButtonGroup">
<item name="divider">?android:attr/dividerVertical</item>
<item name="showDividers">middle</item>
- <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+ <item name="dividerPadding">8dip</item>
</style>
<style name="Widget.Holo.ButtonGroup.AlertDialog">
@@ -1699,7 +1704,7 @@
<style name="Widget.Holo.Light.ListView.DropDown">
</style>
- <style name="Widget.Holo.Light.EditText" parent="Widget.EditText">
+ <style name="Widget.Holo.Light.EditText" parent="Widget.Holo.EditText">
</style>
<style name="Widget.Holo.Light.ExpandableListView" parent="Widget.ExpandableListView">
@@ -1922,6 +1927,11 @@
<item name="bottomBright">@android:drawable/dialog_bottom_holo_dark</item>
<item name="bottomMedium">@android:drawable/dialog_bottom_holo_dark</item>
<item name="centerMedium">@android:drawable/dialog_middle_holo_dark</item>
+ <item name="layout">@android:layout/alert_dialog_holo</item>
+ <item name="listLayout">@android:layout/select_dialog_holo</item>
+ <item name="listItemLayout">@android:layout/select_dialog_item_holo</item>
+ <item name="multiChoiceItemLayout">@android:layout/select_dialog_multichoice_holo</item>
+ <item name="singleChoiceItemLayout">@android:layout/select_dialog_singlechoice_holo</item>
</style>
<style name="AlertDialog.Holo.Light">
@@ -1962,7 +1972,9 @@
</style>
<style name="Widget.Holo.PreferenceFrameLayout">
- <item name="android:topPadding">48dip</item>
- <item name="android:bottomPadding">48dip</item>
+ <item name="android:borderTop">48dip</item>
+ <item name="android:borderBottom">48dip</item>
+ <item name="android:borderLeft">32dip</item>
+ <item name="android:borderRight">32dip</item>
</style>
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index e1040d9..380d63b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -92,6 +92,7 @@
<item name="groupButtonBackground">@null</item>
<item name="selectableItemBackground">@android:drawable/item_background</item>
+ <item name="borderlessButtonStyle">?android:attr/buttonStyle</item>
<item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
<!-- List attributes -->
@@ -258,6 +259,7 @@
<item name="actionBarSize">56dip</item>
<item name="dividerVertical">@drawable/divider_vertical_dark</item>
+ <item name="dividerHorizontal">@drawable/divider_vertical_dark</item>
<item name="buttonGroupStyle">@android:style/Widget.ButtonGroup</item>
<!-- SearchView attributes -->
@@ -561,6 +563,21 @@
<item name="android:windowNoTitle">true</item>
</style>
+ <!-- Default holo light theme for panel windows. This removes all extraneous
+ window decorations, so you basically have an empty rectangle in which
+ to place your content. It makes the window floating, with a transparent
+ background, and turns off dimming behind the window. -->
+ <style name="Theme.Holo.Light.Panel">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
<!-- Default theme for input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.
this inherits from Theme.NoTitleBar, but makes the background
@@ -575,7 +592,7 @@
</style>
<!-- Theme for the search input bar. -->
- <style name="Theme.SearchBar" parent="Theme.Panel">
+ <style name="Theme.SearchBar" parent="Theme.Holo.Light.Panel">
<item name="windowContentOverlay">@null</item>
</style>
@@ -723,6 +740,7 @@
<item name="groupButtonBackground">@android:drawable/group_button_background_holo_dark</item>
<item name="selectableItemBackground">@android:drawable/item_background_holo_dark</item>
+ <item name="borderlessButtonStyle">@android:style/Widget.Holo.Button.Borderless</item>
<item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
<!-- List attributes -->
@@ -883,6 +901,7 @@
<item name="actionBarSize">56dip</item>
<item name="dividerVertical">@drawable/divider_vertical_holo_dark</item>
+ <item name="dividerHorizontal">@drawable/divider_vertical_holo_dark</item>
<item name="buttonGroupStyle">@android:style/Widget.Holo.ButtonGroup</item>
<!-- SearchView attributes -->
@@ -1046,20 +1065,20 @@
<item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
<!-- Widget styles -->
- <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
+ <item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item>
<item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.Light.AutoCompleteTextView</item>
- <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>
+ <item name="checkboxStyle">@android:style/Widget.Holo.Light.CompoundButton.CheckBox</item>
<item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item>
- <item name="editTextStyle">@android:style/Widget.Holo.EditText</item>
- <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item>
- <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item>
- <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item>
- <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item>
- <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item>
- <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item>
- <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item>
- <item name="listViewStyle">@android:style/Widget.Holo.ListView</item>
- <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item>
+ <item name="editTextStyle">@android:style/Widget.Holo.Light.EditText</item>
+ <item name="expandableListViewStyle">@android:style/Widget.Holo.Light.ExpandableListView</item>
+ <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.Light.ExpandableListView.White</item>
+ <item name="galleryStyle">@android:style/Widget.Holo.Light.Gallery</item>
+ <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.Light.GestureOverlayView</item>
+ <item name="gridViewStyle">@android:style/Widget.Holo.Light.GridView</item>
+ <item name="imageButtonStyle">@android:style/Widget.Holo.Light.ImageButton</item>
+ <item name="imageWellStyle">@android:style/Widget.Holo.Light.ImageWell</item>
+ <item name="listViewStyle">@android:style/Widget.Holo.Light.ListView</item>
+ <item name="listViewWhiteStyle">@android:style/Widget.Holo.Light.ListView.White</item>
<item name="popupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow</item>
<item name="progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar</item>
<item name="progressBarStyleHorizontal">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item>
@@ -1069,22 +1088,22 @@
<item name="progressBarStyleInverse">@android:style/Widget.Holo.Light.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.Light.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.Light.ProgressBar.Large.Inverse</item>
- <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item>
- <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item>
- <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item>
- <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item>
- <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item>
- <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item>
- <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item>
+ <item name="seekBarStyle">@android:style/Widget.Holo.Light.SeekBar</item>
+ <item name="ratingBarStyle">@android:style/Widget.Holo.Light.RatingBar</item>
+ <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.Light.RatingBar.Indicator</item>
+ <item name="ratingBarStyleSmall">@android:style/Widget.Holo.Light.RatingBar.Small</item>
+ <item name="radioButtonStyle">@android:style/Widget.Holo.Light.CompoundButton.RadioButton</item>
+ <item name="scrollViewStyle">@android:style/Widget.Holo.Light.ScrollView</item>
+ <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.Light.HorizontalScrollView</item>
<item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
<item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Light.Spinner.DropDown</item>
- <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item>
- <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item>
- <item name="textViewStyle">@android:style/Widget.Holo.TextView</item>
- <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item>
- <item name="webViewStyle">@android:style/Widget.Holo.WebView</item>
- <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item>
- <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item>
+ <item name="starStyle">@android:style/Widget.Holo.Light.CompoundButton.Star</item>
+ <item name="tabWidgetStyle">@android:style/Widget.Holo.Light.TabWidget</item>
+ <item name="textViewStyle">@android:style/Widget.Holo.Light.TextView</item>
+ <item name="webTextViewStyle">@android:style/Widget.Holo.Light.WebTextView</item>
+ <item name="webViewStyle">@android:style/Widget.Holo.Light.WebView</item>
+ <item name="dropDownItemStyle">@android:style/Widget.Holo.Light.DropDownItem</item>
+ <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.Light.DropDownItem.Spinner</item>
<item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item>
<item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item>
<item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item>
@@ -1128,6 +1147,7 @@
<item name="actionBarSize">56dip</item>
<item name="dividerVertical">@drawable/divider_vertical_holo_light</item>
+ <item name="dividerHorizontal">@drawable/divider_vertical_holo_light</item>
<item name="buttonGroupStyle">@android:style/Widget.Holo.Light.ButtonGroup</item>
<!-- SearchView attributes -->
diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
index 2b0e4af..5e3252c 100755
--- a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
+++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
@@ -4,7 +4,7 @@
<ssid>opennet</ssid>
<security>NONE</security>
</accesspoint>
- <accesspoint>
+ <accesspoint>
<ssid>GoogleGuest</ssid>
<security>NONE</security>
</accesspoint>
@@ -14,6 +14,16 @@
<password>androidwifi</password>
</accesspoint>
<accesspoint>
+ <ssid>securenetstatic</ssid>
+ <security>PSK</security>
+ <password>androidwifi</password>
+ <ip>192.168.14.2</ip>
+ <gateway>192.168.14.1</gateway>
+ <networkprefixlength>24</networkprefixlength>
+ <dns1>192.168.14.1</dns1>
+ <dns2>192.168.1.9</dns2>
+ </accesspoint>
+ <accesspoint>
<ssid>botnet</ssid>
<security>EAP</security>
<eap>PEAP</eap>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 863fbe6..21f1bfc 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -25,11 +25,18 @@ import org.xml.sax.helpers.DefaultHandler;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.KeyMgmt;
-
+import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.util.Log;
+
import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@@ -38,7 +45,8 @@ import java.util.List;
* The configurations of an access point is included in tag
* <accesspoint></accesspoint>. The supported configuration includes: ssid,
* security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
- * in which each is included in the corresponding tags. All access points have to be
+ * in which each is included in the corresponding tags. Static IP setting is also supported.
+ * Tags that can be used include: ip, gateway, netmask, dns1, dns2. All access points have to be
* enclosed in tags of <resources></resources>.
*
* The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
@@ -52,6 +60,9 @@ import java.util.List;
* <password>abcdefgh</password>
* </accesspoint>
* </resources>
+ *
+ * Note:ssid and security have to be the first two tags
+ * for static ip setting, tag "ip" should be listed before other fields: dns, gateway, netmask.
*/
public class AccessPointParserHelper {
private static final String KEYSTORE_SPACE = "keystore://";
@@ -93,9 +104,11 @@ public class AccessPointParserHelper {
boolean security = false;
boolean password = false;
boolean ip = false;
- boolean subnetmask = false;
boolean gateway = false;
- boolean dns = false;
+ boolean networkprefix = false;
+ boolean netmask = false;
+ boolean dns1 = false;
+ boolean dns2 = false;
boolean eap = false;
boolean phase2 = false;
boolean identity = false;
@@ -104,6 +117,8 @@ public class AccessPointParserHelper {
boolean usercert = false;
WifiConfiguration config = null;
int securityType = NONE;
+ LinkProperties mLinkProperties = null;
+ InetAddress mInetAddr = null;
@Override
public void startElement(String uri, String localName, String tagName,
@@ -138,12 +153,37 @@ public class AccessPointParserHelper {
if (tagName.equalsIgnoreCase("usercert")) {
usercert = true;
}
+ if (tagName.equalsIgnoreCase("ip")) {
+ mLinkProperties = new LinkProperties();
+ ip = true;
+ }
+ if (tagName.equalsIgnoreCase("gateway")) {
+ gateway = true;
+ }
+ if (tagName.equalsIgnoreCase("networkprefixlength")) {
+ networkprefix = true;
+ }
+ if (tagName.equalsIgnoreCase("netmask")) {
+ netmask = true;
+ }
+ if (tagName.equalsIgnoreCase("dns1")) {
+ dns1 = true;
+ }
+ if (tagName.equalsIgnoreCase("dns2")) {
+ dns2 = true;
+ }
}
@Override
public void endElement(String uri, String localName, String tagName) throws SAXException {
- Log.v(TAG, "endElement: " + tagName);
if (tagName.equalsIgnoreCase("accesspoint")) {
+ if (mLinkProperties != null) {
+ config.ipAssignment = IpAssignment.STATIC;
+ config.linkProperties = mLinkProperties;
+ } else {
+ config.ipAssignment = IpAssignment.DHCP;
+ }
+ config.proxySettings = ProxySettings.NONE;
networks.add(config);
}
}
@@ -152,14 +192,11 @@ public class AccessPointParserHelper {
public void characters(char ch[], int start, int length) throws SAXException {
if (ssid) {
config.SSID = new String(ch, start, length);
- Log.v(TAG, "ssid: " + config.SSID);
ssid = false;
}
if (security) {
String securityStr = (new String(ch, start, length)).toUpperCase();
- Log.v(TAG, "security: " + securityStr);
securityType = getSecurityType(securityStr);
- Log.v(TAG, "securityType = " + securityType);
switch (securityType) {
case NONE:
config.allowedKeyManagement.set(KeyMgmt.NONE);
@@ -175,6 +212,13 @@ public class AccessPointParserHelper {
case EAP:
config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+ // Initialize other fields.
+ config.phase2.setValue("");
+ config.ca_cert.setValue("");
+ config.client_cert.setValue("");
+ config.private_key.setValue("");
+ config.identity.setValue("");
+ config.anonymous_identity.setValue("");
break;
default:
throw new SAXException();
@@ -187,7 +231,6 @@ public class AccessPointParserHelper {
if (len == 0) {
throw new SAXException();
}
- Log.v(TAG, "passwordStr:" + passwordStr);
if (securityType == WEP) {
if ((len == 10 || len == 26 || len == 58) &&
passwordStr.matches("[0-9A-Fa-f]*")) {
@@ -242,21 +285,94 @@ public class AccessPointParserHelper {
config.client_cert.setValue(KEYSTORE_SPACE);
usercert = false;
}
+ if (ip) {
+ try {
+ String ipAddr = new String(ch, start, length);
+ if (!InetAddress.isNumeric(ipAddr)) {
+ throw new SAXException();
+ }
+ mInetAddr = InetAddress.getByName(ipAddr);
+ } catch (UnknownHostException e) {
+ throw new SAXException();
+ }
+ ip = false;
+ }
+ if (gateway) {
+ try {
+ String gwAddr = new String(ch, start, length);
+ if (!InetAddress.isNumeric(gwAddr)) {
+ throw new SAXException();
+ }
+ mLinkProperties.setGateway(InetAddress.getByName(gwAddr));
+ } catch (UnknownHostException e) {
+ throw new SAXException();
+ }
+ gateway = false;
+ }
+ if (networkprefix) {
+ try {
+ int nwPrefixLength = Integer.parseInt(new String(ch, start, length));
+ if ((nwPrefixLength < 0) || (nwPrefixLength > 32)) {
+ throw new SAXException();
+ }
+ mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, nwPrefixLength));
+ } catch (NumberFormatException e) {
+ throw new SAXException();
+ }
+ networkprefix = false;
+ }
+ if (netmask) {
+ try {
+ String netMaskStr = new String(ch, start, length);
+ if (!InetAddress.isNumeric(netMaskStr)) {
+ throw new SAXException();
+ }
+ InetAddress netMaskAddr = InetAddress.getByName(netMaskStr);
+ mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, netMaskAddr));
+ } catch (UnknownHostException e) {
+ throw new SAXException();
+ }
+ netmask = false;
+ }
+ if (dns1) {
+ try {
+ String dnsAddr = new String(ch, start, length);
+ if (!InetAddress.isNumeric(dnsAddr)) {
+ throw new SAXException();
+ }
+ mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+ } catch (UnknownHostException e) {
+ throw new SAXException();
+ }
+ dns1 = false;
+ }
+ if (dns2) {
+ try {
+ String dnsAddr = new String(ch, start, length);
+ if (!InetAddress.isNumeric(dnsAddr)) {
+ throw new SAXException();
+ }
+ mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+ } catch (UnknownHostException e) {
+ throw new SAXException();
+ }
+ dns2 = false;
+ }
}
};
- public AccessPointParserHelper() {
- }
-
/**
- * Process the accesspoint.xml file
- * @return List of WifiConfiguration
- * @throws Exception when parsing the XML file
+ * Process the InputStream in
+ * @param in is the InputStream that can be used for XML parsing
+ * @throws Exception
*/
- public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception {
+ public AccessPointParserHelper(InputStream in) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(in, mHandler);
+ }
+
+ public List<WifiConfiguration> getNetworkConfigurations() throws Exception {
return networks;
}
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 37b9f52..2888696 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -16,10 +16,8 @@
package com.android.connectivitymanagertest;
-import com.android.connectivitymanagertest.R;
import android.app.Activity;
import android.content.Context;
-import android.content.res.Resources;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -36,7 +34,6 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
-import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiInfo;
@@ -52,7 +49,7 @@ public class ConnectivityManagerTestActivity extends Activity {
public static final String LOG_TAG = "ConnectivityManagerTestActivity";
public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
- public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+ public static final int WIFI_SCAN_TIMEOUT = 50 * 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
@@ -61,6 +58,7 @@ public class ConnectivityManagerTestActivity extends Activity {
private static final String ACCESS_POINT_FILE = "accesspoints.xml";
public ConnectivityReceiver mConnectivityReceiver = null;
public WifiReceiver mWifiReceiver = null;
+ private AccessPointParserHelper mParseHelper = null;
/*
* Track network connectivity information
*/
@@ -101,7 +99,7 @@ public class ConnectivityManagerTestActivity extends Activity {
private class ConnectivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent);
+ log("ConnectivityReceiver: onReceive() is called with " + intent);
String action = intent.getAction();
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
@@ -126,9 +124,9 @@ public class ConnectivityManagerTestActivity extends Activity {
mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
- Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString());
+ log("mNetworkInfo: " + mNetworkInfo.toString());
if (mOtherNetworkInfo != null) {
- Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
+ log("mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
}
recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
if (mOtherNetworkInfo != null) {
@@ -148,7 +146,7 @@ public class ConnectivityManagerTestActivity extends Activity {
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mWifiNetworkInfo =
(NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
+ log("mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
if (mWifiNetworkInfo.getState() == State.CONNECTED) {
mBssid = intent.getStringExtra(WifiManager.EXTRA_BSSID);
}
@@ -181,7 +179,7 @@ public class ConnectivityManagerTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+ log("onCreate, inst=" + Integer.toHexString(hashCode()));
// Create a simple layout
LinearLayout contentView = new LinearLayout(this);
@@ -212,7 +210,7 @@ public class ConnectivityManagerTestActivity extends Activity {
initializeNetworkStates();
if (mWifiManager.isWifiEnabled()) {
- Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+ log("Clear Wifi before we start the test.");
removeConfiguredNetworksAndDisableWifi();
}
mWifiRegexs = mCM.getTetherableWifiRegexs();
@@ -220,32 +218,22 @@ public class ConnectivityManagerTestActivity extends Activity {
public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
InputStream in = getAssets().open(ACCESS_POINT_FILE);
- AccessPointParserHelper parseHelper = new AccessPointParserHelper();
- return parseHelper.processAccessPoint(in);
- }
-
- private void printNetConfig(String[] configuration) {
- for (int i = 0; i < configuration.length; i++) {
- if (i == 0) {
- Log.v(LOG_TAG, "SSID: " + configuration[0]);
- } else {
- Log.v(LOG_TAG, " " + configuration[i]);
- }
- }
+ mParseHelper = new AccessPointParserHelper(in);
+ return mParseHelper.getNetworkConfigurations();
}
// for each network type, initialize network states to UNKNOWN, and no verification flag is set
public void initializeNetworkStates() {
for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
connectivityState[networkType] = new NetworkState();
- Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+ log("Initialize network state for " + networkType + ": " +
connectivityState[networkType].toString());
}
}
// deposit a network state
public void recordNetworkState(int networkType, State networkState) {
- Log.v(LOG_TAG, "record network state for network " + networkType +
+ log("record network state for network " + networkType +
", state is " + networkState);
connectivityState[networkType].recordState(networkState);
}
@@ -259,40 +247,40 @@ public class ConnectivityManagerTestActivity extends Activity {
// Validate the states recorded
public boolean validateNetworkStates(int networkType) {
- Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+ log("validate network state for " + networkType + ": ");
return connectivityState[networkType].validateStateTransition();
}
// return result from network state validation
public String getTransitionFailureReason(int networkType) {
- Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+ log("get network state transition failure reason for " + networkType + ": " +
connectivityState[networkType].toString());
return connectivityState[networkType].getReason();
}
private void notifyNetworkConnectivityChange() {
synchronized(connectivityObject) {
- Log.v(LOG_TAG, "notify network connectivity changed");
+ log("notify network connectivity changed");
connectivityObject.notifyAll();
}
}
private void notifyScanResult() {
synchronized (this) {
- Log.v(LOG_TAG, "notify that scan results are available");
+ log("notify that scan results are available");
this.notify();
}
}
private void notifyWifiState() {
synchronized (wifiObject) {
- Log.v(LOG_TAG, "notify wifi state changed");
+ log("notify wifi state changed");
wifiObject.notify();
}
}
private void notifyWifiAPState() {
synchronized (this) {
- Log.v(LOG_TAG, "notify wifi AP state changed");
+ log("notify wifi AP state changed");
this.notify();
}
}
@@ -306,7 +294,7 @@ public class ConnectivityManagerTestActivity extends Activity {
for (Object obj: tethered) {
String str = (String)obj;
for (String tethRex: mWifiRegexs) {
- Log.v(LOG_TAG, "str: " + str +"tethRex: " + tethRex);
+ log("str: " + str +"tethRex: " + tethRex);
if (str.matches(tethRex)) {
wifiTethered = true;
}
@@ -316,7 +304,7 @@ public class ConnectivityManagerTestActivity extends Activity {
for (Object obj: errored) {
String str = (String)obj;
for (String tethRex: mWifiRegexs) {
- Log.v(LOG_TAG, "error: str: " + str +"tethRex: " + tethRex);
+ log("error: str: " + str +"tethRex: " + tethRex);
if (str.matches(tethRex)) {
wifiErrored = true;
}
@@ -328,7 +316,7 @@ public class ConnectivityManagerTestActivity extends Activity {
} else if (wifiErrored) {
mWifiTetherResult = FAILURE; // wifi tethering failed
}
- Log.v(LOG_TAG, "mWifiTetherResult: " + mWifiTetherResult);
+ log("mWifiTetherResult: " + mWifiTetherResult);
this.notify();
}
}
@@ -344,12 +332,12 @@ public class ConnectivityManagerTestActivity extends Activity {
return false;
} else {
// the broadcast has been sent out. the state has been changed.
- Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
+ log("networktype: " + networkType + " state: " +
mCM.getNetworkInfo(networkType));
return true;
}
}
- Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+ log("Wait for the connectivity state for network: " + networkType +
" to be " + expectedState.toString());
synchronized (connectivityObject) {
try {
@@ -359,7 +347,7 @@ public class ConnectivityManagerTestActivity extends Activity {
}
if ((mNetworkInfo.getType() != networkType) ||
(mNetworkInfo.getState() != expectedState)) {
- Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
+ log("network state for " + mNetworkInfo.getType() +
"is: " + mNetworkInfo.getState());
continue;
}
@@ -380,7 +368,7 @@ public class ConnectivityManagerTestActivity extends Activity {
return true;
}
}
- Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+ log("Wait for wifi state to be: " + expectedState);
synchronized (wifiObject) {
try {
wifiObject.wait(SHORT_TIMEOUT);
@@ -388,7 +376,7 @@ public class ConnectivityManagerTestActivity extends Activity {
e.printStackTrace();
}
if (mWifiState != expectedState) {
- Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState());
+ log("Wifi state is: " + mWifiState);
continue;
}
return true;
@@ -408,7 +396,7 @@ public class ConnectivityManagerTestActivity extends Activity {
return true;
}
}
- Log.v(LOG_TAG, "Wait for wifi AP state to be: " + expectedState);
+ log("Wait for wifi AP state to be: " + expectedState);
synchronized (wifiObject) {
try {
wifiObject.wait(SHORT_TIMEOUT);
@@ -416,7 +404,7 @@ public class ConnectivityManagerTestActivity extends Activity {
e.printStackTrace();
}
if (mWifiManager.getWifiApState() != expectedState) {
- Log.v(LOG_TAG, "Wifi state is: " + mWifiManager.getWifiApState());
+ log("Wifi state is: " + mWifiManager.getWifiApState());
continue;
}
return true;
@@ -436,7 +424,7 @@ public class ConnectivityManagerTestActivity extends Activity {
if ((System.currentTimeMillis() - startTime) > timeout) {
return mWifiTetherResult;
}
- Log.v(LOG_TAG, "Wait for wifi tethering result.");
+ log("Wait for wifi tethering result.");
synchronized (this) {
try {
this.wait(SHORT_TIMEOUT);
@@ -490,64 +478,61 @@ public class ConnectivityManagerTestActivity extends Activity {
//If Wifi is not enabled, enable it
if (!mWifiManager.isWifiEnabled()) {
- Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+ log("Wifi is not enabled, enable it");
mWifiManager.setWifiEnabled(true);
+ // wait for the wifi state change before start scanning.
+ if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2*SHORT_TIMEOUT)) {
+ log("wait for WIFI_STATE_ENABLED failed");
+ return false;
+ }
}
- List<ScanResult> netList = mWifiManager.getScanResults();
- if (netList == null) {
- Log.v(LOG_TAG, "scan results are null");
- // if no scan results are available, start active scan
- mWifiManager.startScanActive();
- mScanResultIsAvailable = false;
- long startTime = System.currentTimeMillis();
- while (!mScanResultIsAvailable) {
- if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
- return false;
+ boolean foundApInScanResults = false;
+ for (int retry = 0; retry < 5; retry++) {
+ List<ScanResult> netList = mWifiManager.getScanResults();
+ if (netList != null) {
+ log("size of scan result list: " + netList.size());
+ for (int i = 0; i < netList.size(); i++) {
+ ScanResult sr= netList.get(i);
+ if (sr.SSID.equals(ssid)) {
+ log("found " + ssid + " in the scan result list");
+ log("retry: " + retry);
+ foundApInScanResults = true;
+ mWifiManager.connectNetwork(config);
+ break;
+ }
}
- // wait for the scan results to be available
- synchronized (this) {
- // wait for the scan result to be available
- try {
- this.wait(WAIT_FOR_SCAN_RESULT);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ }
+ if (foundApInScanResults) {
+ return true;
+ } else {
+ // Start an active scan
+ mWifiManager.startScanActive();
+ mScanResultIsAvailable = false;
+ long startTime = System.currentTimeMillis();
+ while (!mScanResultIsAvailable) {
+ if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+ log("wait for scan results timeout");
+ return false;
}
- if ((mWifiManager.getScanResults() == null) ||
- (mWifiManager.getScanResults().size() <= 0)) {
- continue;
+ // wait for the scan results to be available
+ synchronized (this) {
+ // wait for the scan result to be available
+ try {
+ this.wait(WAIT_FOR_SCAN_RESULT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mWifiManager.getScanResults() == null) ||
+ (mWifiManager.getScanResults().size() <= 0)) {
+ continue;
+ }
+ mScanResultIsAvailable = true;
}
- mScanResultIsAvailable = true;
}
}
}
-
- netList = mWifiManager.getScanResults();
-
- for (int i = 0; i < netList.size(); i++) {
- ScanResult sr= netList.get(i);
- if (sr.SSID.equals(ssid)) {
- Log.v(LOG_TAG, "found " + ssid + " in the scan result list");
- int networkId = mWifiManager.addNetwork(config);
- // Connect to network by disabling others.
- mWifiManager.enableNetwork(networkId, true);
- mWifiManager.saveConfiguration();
- List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks();
- for (WifiConfiguration netConfig : wifiNetworks) {
- Log.v(LOG_TAG, netConfig.toString());
- }
-
- mWifiManager.reconnect();
- break;
- }
- }
-
- List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
- if (netConfList.size() <= 0) {
- Log.v(LOG_TAG, ssid + " is not available");
- return false;
- }
- return true;
+ return false;
}
/*
@@ -555,27 +540,13 @@ public class ConnectivityManagerTestActivity extends Activity {
*/
public boolean disconnectAP() {
if (mWifiManager.isWifiEnabled()) {
- //remove the current network Id
- WifiInfo curWifi = mWifiManager.getConnectionInfo();
- if (curWifi == null) {
- return false;
- }
- int curNetworkId = curWifi.getNetworkId();
- mWifiManager.removeNetwork(curNetworkId);
- mWifiManager.saveConfiguration();
-
- // remove other saved networks
- List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
- if (netConfList != null) {
- Log.v(LOG_TAG, "remove configured network ids");
- for (int i = 0; i < netConfList.size(); i++) {
- WifiConfiguration conf = new WifiConfiguration();
- conf = netConfList.get(i);
- mWifiManager.removeNetwork(conf.networkId);
- }
+ // remove saved networks
+ List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
+ for (WifiConfiguration wifiConfig: wifiConfigList) {
+ log("remove wifi configuration: " + wifiConfig.toString());
+ mWifiManager.forgetNetwork(wifiConfig.networkId);
}
}
- mWifiManager.saveConfiguration();
return true;
}
/**
@@ -599,7 +570,7 @@ public class ConnectivityManagerTestActivity extends Activity {
}
// Wait for the actions to be completed
try {
- Thread.sleep(5*1000);
+ Thread.sleep(SHORT_TIMEOUT);
} catch (InterruptedException e) {}
return true;
}
@@ -632,7 +603,7 @@ public class ConnectivityManagerTestActivity extends Activity {
if (mWifiReceiver != null) {
unregisterReceiver(mWifiReceiver);
}
- Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+ log("onDestroy, inst=" + Integer.toHexString(hashCode()));
}
@Override
@@ -674,4 +645,8 @@ public class ConnectivityManagerTestActivity extends Activity {
}
return super.onKeyDown(keyCode, event);
}
+
+ private void log(String message) {
+ Log.v(LOG_TAG, message);
+ }
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index 69eb5db..9c72102 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -17,26 +17,33 @@
package com.android.connectivitymanagertest.functional;
import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
-import com.android.connectivitymanagertest.NetworkState;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
import android.R;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Intent;
import android.content.Context;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.Status;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.ConnectivityManager;
+import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.provider.Settings;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Test Wi-Fi connection with different configuration
@@ -48,18 +55,25 @@ import java.util.List;
public class WifiConnectionTest
extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
private static final String TAG = "WifiConnectionTest";
- private static final boolean DEBUG = true;
- private static final String PKG_NAME = "com.android.connectivitymanagertests";
+ private static final boolean DEBUG = false;
private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
private ConnectivityManagerTestActivity mAct;
+ private ConnectivityManagerTestRunner mRunner;
+ private WifiManager mWifiManager = null;
+ private Set<WifiConfiguration> enabledNetworks = null;
public WifiConnectionTest() {
- super(PKG_NAME, ConnectivityManagerTestActivity.class);
+ super(ConnectivityManagerTestActivity.class);
}
@Override
public void setUp() throws Exception {
super.setUp();
+ log("before we launch the test activity, we preserve all the configured networks.");
+ mRunner = ((ConnectivityManagerTestRunner)getInstrumentation());
+ mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE);
+ enabledNetworks = getEnabledNetworks(mWifiManager.getConfiguredNetworks());
+
mAct = getActivity();
networks = mAct.loadNetworkConfigurations();
if (DEBUG) {
@@ -68,30 +82,61 @@ public class WifiConnectionTest
// enable Wifi and verify wpa_supplicant is started
assertTrue("enable Wifi failed", mAct.enableWifi());
- try {
- Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
- } catch (Exception e) {
- fail("interrupted while waiting for WPA_SUPPLICANT to start");
- }
+ sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+ "interrupted while waiting for WPA_SUPPLICANT to start");
WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo();
assertNotNull(mConnection);
assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant());
}
private void printNetworkConfigurations() {
- Log.v(TAG, "==== print network configurations parsed from XML file ====");
- Log.v(TAG, "number of access points: " + networks.size());
+ log("==== print network configurations parsed from XML file ====");
+ log("number of access points: " + networks.size());
for (WifiConfiguration config : networks) {
- Log.v(TAG, config.toString());
+ log(config.toString());
}
}
@Override
public void tearDown() throws Exception {
+ log("tearDown()");
mAct.removeConfiguredNetworksAndDisableWifi();
+ reEnableNetworks(enabledNetworks);
super.tearDown();
}
+ private Set<WifiConfiguration> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
+ Set<WifiConfiguration> networks = new HashSet<WifiConfiguration>();
+ for (WifiConfiguration wifiConfig : configuredNetworks) {
+ if (wifiConfig.status == Status.ENABLED || wifiConfig.status == Status.CURRENT) {
+ networks.add(wifiConfig);
+ log("remembering enabled network " + wifiConfig.SSID +
+ " status is " + wifiConfig.status);
+ }
+ }
+ return networks;
+ }
+
+ private void reEnableNetworks(Set<WifiConfiguration> enabledNetworks) {
+ if (!mWifiManager.isWifiEnabled()) {
+ log("reEnableNetworks: enable Wifi");
+ mWifiManager.setWifiEnabled(true);
+ sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+ "interruped while waiting for wifi to be enabled");
+ }
+
+ for (WifiConfiguration config : enabledNetworks) {
+ if (DEBUG) {
+ log("recover wifi configuration: " + config.toString());
+ }
+ config.SSID = "\"" + config.SSID + "\"";
+ config.networkId = -1;
+ mWifiManager.connectNetwork(config);
+ sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+ "interruped while connecting to " + config.SSID);
+ }
+ }
+
/**
* Connect to the provided Wi-Fi network
* @param config is the network configuration
@@ -103,32 +148,40 @@ public class WifiConnectionTest
mAct.connectToWifiWithConfiguration(config));
// step 2: verify Wifi state and network state;
- assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
- ConnectivityManagerTestActivity.SHORT_TIMEOUT));
assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
- State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ State.CONNECTED, 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
// step 3: verify the current connected network is the given SSID
+ assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo());
if (DEBUG) {
- Log.v(TAG, "config.SSID = " + config.SSID);
- Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" +
+ log("config.SSID = " + config.SSID);
+ log("mAct.mWifiManager.getConnectionInfo.getSSID()" +
mAct.mWifiManager.getConnectionInfo().getSSID());
}
assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID()));
+ }
- // Maintain the connection for 50 seconds before switching
+ private void sleep(long sometime, String errorMsg) {
try {
- Thread.sleep(50*1000);
- } catch (Exception e) {
- fail("interrupted while waiting for WPA_SUPPLICANT to start");
+ Thread.sleep(sometime);
+ } catch (InterruptedException e) {
+ fail(errorMsg);
}
}
+ private void log(String message) {
+ Log.v(TAG, message);
+ }
+
@LargeTest
public void testWifiConnections() {
for (int i = 0; i < networks.size(); i++) {
+ String ssid = networks.get(i).SSID;
+ log("-- START Wi-Fi connection test to : " + ssid + " --");
connectToWifi(networks.get(i));
- mAct.removeConfiguredNetworksAndDisableWifi();
+ sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+ "interruped while waiting for wifi disabled.");
+ log("-- END Wi-Fi connection test to " + ssid + " -- ");
}
}
}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index c8ad60d..b878aa5 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -732,4 +732,20 @@ public class UriTest extends TestCase {
assertEquals(1, names.size());
assertEquals("foo", names.iterator().next());
}
+
+ /**
+ * Query parameters may omit the '='. http://b/3124097
+ */
+ public void testGetQueryParametersEmptyValue() {
+ assertEquals(Arrays.asList(""),
+ Uri.parse("http://foo/path?abc").getQueryParameters("abc"));
+ assertEquals(Arrays.asList(""),
+ Uri.parse("http://foo/path?foo=bar&abc").getQueryParameters("abc"));
+ assertEquals(Arrays.asList(""),
+ Uri.parse("http://foo/path?abcd=abc&abc").getQueryParameters("abc"));
+ assertEquals(Arrays.asList("a", "", ""),
+ Uri.parse("http://foo/path?abc=a&abc=&abc").getQueryParameters("abc"));
+ assertEquals(Arrays.asList("a", "", ""),
+ Uri.parse("http://foo/path?abc=a&abc=&abc=").getQueryParameters("abc"));
+ }
}
diff --git a/data/keyboards/Logitech_USB_Receiver.kl b/data/keyboards/Vendor_046d_Product_c532.kl
index 23a8f54..741c2e1 100644
--- a/data/keyboards/Logitech_USB_Receiver.kl
+++ b/data/keyboards/Vendor_046d_Product_c532.kl
@@ -13,7 +13,7 @@
# limitations under the License.
#
-# Logitech Revue keyboard
+# Logitech Revue Wireless keyboard
#
# Notes:
# - The GRAVE key is emulated by the keyboard.
@@ -105,7 +105,7 @@ key 108 DPAD_DOWN
key 109 PAGE_DOWN
key 110 NUMPAD_ENTER
key 111 FORWARD_DEL
-key 113 VALUME_MUTE
+key 113 VOLUME_MUTE
key 114 VOLUME_DOWN
key 115 VOLUME_UP
key 119 MEDIA_PAUSE
diff --git a/data/keyboards/Apple_Wireless_Keyboard.kl b/data/keyboards/Vendor_05ac_Product_0239.kl
index 9262a03..5234d58 100644
--- a/data/keyboards/Apple_Wireless_Keyboard.kl
+++ b/data/keyboards/Vendor_05ac_Product_0239.kl
@@ -101,7 +101,7 @@ key 108 DPAD_DOWN
key 109 PAGE_DOWN
key 110 NUMPAD_ENTER
key 111 FORWARD_DEL
-key 113 VALUME_MUTE
+key 113 VOLUME_MUTE
key 114 VOLUME_DOWN
key 115 VOLUME_UP
# key 120 switch applications
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Vendor_22b8_Product_093d.kl
index 87b3c32..87b3c32 100644
--- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
+++ b/data/keyboards/Vendor_22b8_Product_093d.kl
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
index 5c2a75d..56c287a 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/common.mk
@@ -16,16 +16,20 @@
# Used by Android.mk and keyboards.mk.
keylayouts := \
- Apple_Wireless_Keyboard.kl \
- AVRCP.kl \
Generic.kl \
- Logitech_USB_Receiver.kl \
- Motorola_Bluetooth_Wireless_Keyboard.kl \
+ AVRCP.kl \
qwerty.kl \
- qwerty2.kl
+ Vendor_046d_Product_c532.kl \
+ Vendor_05ac_Product_0239.kl \
+ Vendor_22b8_Product_093d.kl
keycharmaps := \
Generic.kcm \
+ Virtual.kcm \
qwerty.kcm \
- qwerty2.kcm \
- Virtual.kcm
+ qwerty2.kcm
+
+keyconfigs := \
+ qwerty.idc \
+ qwerty2.idc
+
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index b32e436..564f41c 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -22,4 +22,8 @@ PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\
frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file))
-PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps)
+PRODUCT_COPY_FILES += $(foreach file,$(keyconfigs),\
+ frameworks/base/data/keyboards/$(file):system/usr/idc/$(file))
+
+PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) $(keyconfigs)
+
diff --git a/data/keyboards/qwerty.idc b/data/keyboards/qwerty.idc
new file mode 100644
index 0000000..129b0bc
--- /dev/null
+++ b/data/keyboards/qwerty.idc
@@ -0,0 +1,23 @@
+# 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 configuration file #1.
+#
+
+keyboard.layout = qwerty
+keyboard.characterMap = qwerty
+keyboard.orientationAware = 1
+keyboard.builtIn = 1
+
diff --git a/data/keyboards/qwerty.kcm b/data/keyboards/qwerty.kcm
index f31333e..f3e1524 100644
--- a/data/keyboards/qwerty.kcm
+++ b/data/keyboards/qwerty.kcm
@@ -259,9 +259,9 @@ key COMMA {
label: ','
number: ','
base: ','
- shift, capslock: ';'
+ shift: ';'
alt: ';'
- shift+alt, capslock+alt: '|'
+ shift+alt: '|'
}
key PERIOD {
diff --git a/data/keyboards/qwerty2.idc b/data/keyboards/qwerty2.idc
new file mode 100644
index 0000000..1a795c6
--- /dev/null
+++ b/data/keyboards/qwerty2.idc
@@ -0,0 +1,23 @@
+# 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 configuration file #2.
+#
+
+keyboard.layout = qwerty
+keyboard.characterMap = qwerty2
+keyboard.orientationAware = 1
+keyboard.builtIn = 1
+
diff --git a/data/keyboards/qwerty2.kl b/data/keyboards/qwerty2.kl
deleted file mode 100644
index 863a258..0000000
--- a/data/keyboards/qwerty2.kl
+++ /dev/null
@@ -1,109 +0,0 @@
-# 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/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 6c02031..623ee22 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -7,7 +7,9 @@ page.title=Data Backup
<h2>Quickview</h2>
<ul>
- <li>Back up your data to the cloud in case the user looses it</li>
+ <li>Back up the user's data to the cloud in case the user loses it</li>
+ <li>If the user upgrades to a new Android-powered device, your app can restore the user's
+data onto the new device</li>
<li>Easily back up SharedPreferences and private files with BackupAgentHelper</li>
<li>Requires API Level 8</li>
</ul>
@@ -389,7 +391,7 @@ conceptually a set of key-value pairs.</p>
<p>To add an entity to your backup data set, you must:</p>
<ol>
<li>Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int)
-writeEntityheader()}, passing a unique string key for the data you're about to write and the data
+writeEntityHeader()}, passing a unique string key for the data you're about to write and the data
size.</li>
<li>Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int)
writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write
@@ -403,8 +405,8 @@ single entity:</p>
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
DataOutputStream outWriter = new DataOutputStream(bufStream);
// Write structured data
-outWriter.writeString(playerName);
-outWriter.writeInt(playerScore);
+outWriter.writeUTF(mPlayerName);
+outWriter.writeInt(mPlayerScore);
// Send the data to the Backup Manager via the BackupDataOutput
byte[] buffer = bufStream.toByteArray();
int len = buffer.length;
@@ -422,10 +424,10 @@ android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,Pa
onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you
do not write the current data state to this file, then
{@code oldState} will be empty during the next callback.
- <p>Again, the following example saves a representation of the data using the file's
-last-modified timestamp:</p>
+ <p>The following example saves a representation of the current data into {@code newState} using
+the file's last-modified timestamp:</p>
<pre>
-FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
+FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
long modified = mDataFile.lastModified();
@@ -493,7 +495,8 @@ onBackup()} is called after the device is restored.</dd>
<p>In your implementation of {@link
android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} to iterate
+onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} on the
+{@code data} to iterate
through all entities in the data set. For each entity found, do the following:</p>
<ol>
@@ -517,6 +520,54 @@ android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,Pa
onBackup()}.
</ol>
+<p>For example, here's how you can restore the data backed up by the example in the previous
+section:</p>
+
+<pre>
+&#64;Override
+public void onRestore(BackupDataInput data, int appVersionCode,
+ ParcelFileDescriptor newState) throws IOException {
+ // There should be only one entity, but the safest
+ // way to consume it is using a while loop
+ while (data.readNextHeader()) {
+ String key = data.getKey();
+ int dataSize = data.getDataSize();
+
+ // If the key is ours (for saving top score). Note this key was used when
+ // we wrote the backup entity header
+ if (TOPSCORE_BACKUP_KEY.equals(key)) {
+ // Create an input stream for the BackupDataInput
+ byte[] dataBuf = new byte[dataSize];
+ data.readEntityData(dataBuf, 0, dataSize);
+ ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
+ DataInputStream in = new DataInputStream(baStream);
+
+ // Read the player name and score from the backup data
+ mPlayerName = in.readUTF();
+ mPlayerScore = in.readInt();
+
+ // Record the score on the device (to a file or something)
+ recordScore(mPlayerName, mPlayerScore);
+ } else {
+ // We don't know this entity key. Skip it. (Shouldn't happen.)
+ data.skipEntityData();
+ }
+ }
+
+ // Finally, write to the state blob (newState) that describes the restored data
+ FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
+ DataOutputStream out = new DataOutputStream(outstream);
+ out.writeUTF(mPlayerName);
+ out.writeInt(mPlayerScore);
+}
+</pre>
+
+<p>In this example, the {@code appVersionCode} parameter passed to {@link
+android.app.backup.BackupAgent#onRestore onRestore()} is not used. However, you might want to use
+it if you've chosen to perform backup when the user's version of the application has actually moved
+backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see
+the section about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</p>
+
<div class="special">
<p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a
href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code
@@ -592,7 +643,8 @@ public class MyPrefsBackupAgent extends BackupAgentHelper {
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
- void onCreate() {
+ &#64;Override
+ public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index c3d3482..44d75c1 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -455,7 +455,7 @@ Action Bar.</p>
<ol>
<li>Create a {@link android.widget.SpinnerAdapter} that provides the
list of selectable items for the list and the layout to use when drawing each item in the list.</li>
- <li>Implement {@link android.app.ActionBar.NavigationCallback} to define the behavior when the
+ <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the
user selects an item from the list.</li>
<li>Turn on navigation mode for the Action Bar with {@link
android.app.ActionBar#setNavigationMode setNavigationMode()}. For example:
@@ -472,17 +472,17 @@ android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}.
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
</pre>
<p>This method takes your {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback}. More about these next.</p>
+android.app.ActionBar.OnNavigationListener}. More about these next.</p>
</li>
</ol>
<p>That's the basic setup. The {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback} is where most of the work is done. There are many ways
+android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many ways
you can implement these to define the functionality for your drop-down navigation. Implementing
various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this
document&mdash;you should refer to the class refrence for more information about implementing it or
extending an existing implementation. However, below is a simple example for a {@link
-android.widget.SpinnerAdapter} and {@link android.app.ActionBar.NavigationCallback} to get you
+android.widget.SpinnerAdapter} and {@link android.app.ActionBar.OnNavigationListener} to get you
started.</p>
@@ -520,24 +520,24 @@ defined as a resource looks like this:</p>
</pre>
-<h3 id="NavigationCallback">Example: simple NavigationCallback</h3>
+<h3 id="OnNavigationListener">Example: simple OnNavigationListener</h3>
-<p>Your implementation of {@link android.app.ActionBar.NavigationCallback} is where you handle
+<p>Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle
fragment changes or other modifications to your activity when the user selects an item from the
drop-down list. There's only one callback method to implement: {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}.</p>
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.</p>
<p>The {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}
method receives the position of the item in the list and an item ID provided by the {@link
android.widget.SpinnerAdapter}.</p>
<p>Here's an example that instantiates an anonymous implementation of {@link
-android.app.ActionBar.NavigationCallback}, which inserts a {@link android.app.Fragment} into the
+android.app.ActionBar.OnNavigationListener}, which inserts a {@link android.app.Fragment} into the
layout container identified by {@code R.id.fragment_container}:</p>
<pre>
-mNavigationCallback = new NavigationCallback() {
+mOnNavigationListener = new OnNavigationListener() {
// Get the same strings provided for the drop-down's ArrayAdapter
String[] strings = getResources().getStringArray(R.array.action_list);
@@ -556,7 +556,7 @@ mNavigationCallback = new NavigationCallback() {
};
</pre>
-<p>This instance of {@link android.app.ActionBar.NavigationCallback} can be given to {@link
+<p>This instance of {@link android.app.ActionBar.OnNavigationListener} can be given to {@link
android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from
above.</p>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index cef057e..7416764 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,9 +52,8 @@ Android Market within a 14-day period ending on the data collection date noted b
<div class="dashboard-panel">
<img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:7.9,15.0,0.1,40.8,36.2&chl=
-Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
-6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:6.3,10.6,0.1,39.6,43.4&chl
+=Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -62,13 +61,13 @@ Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td>Android 1.5</td><td>3</td><td>7.9%</td></tr>
-<tr><td>Android 1.6</td><td>4</td><td>15.0%</td></tr>
-<tr><td>Android 2.1</td><td>7</td><td>40.8%</td></tr>
-<tr><td>Android 2.2</td><td>8</td><td>36.2%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>6.3%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>10.6%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>39.6%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>43.4%</td></tr>
</table>
-<p><em>Data collected during two weeks ending on November 1, 2010</em></p>
+<p><em>Data collected during two weeks ending on December 1, 2010</em></p>
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
</div><!-- end dashboard-panel -->
@@ -97,17 +96,16 @@ Android Market within a 14-day period ending on the date indicated on the x-axis
<img alt="" height="250" width="660" style="padding:5px;background:#fff"
src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
-chxl=0%3A%7C2010/05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/
-01%7C10/15%7C2010/11/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25
-%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.9,99.3,100.3,100.8,99.7,99.
-8,99.8,99.7,99.8,99.9,99.9,99.9,99.9|61.6,63.1,72.7,76.1,78.4,80.9,84.3,86.5,87.9,89.2,90.2,91.1,92.
-0|32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77.0|0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3,
-27.8,32.1,33.4,34.5,36.2&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,
-5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202.
-2,131d02,3,7,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1|
-Android%202.2&chco=add274,94d134,73ad18,507d08" />
-
-<p><em>Last historical dataset collected during two weeks ending on November 1, 2010</em></p>
+chxl=0:|2010/06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|10/01|10/15|11/01|11/15|2010/12/01|1:|0
+%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&
+chxtc=0,5&chd=t:100.3,100.8,99.7,99.8,99.8,99.7,99.8,99.9,99.9,99.9,99.9,99.9,99.9|72.7,76.1,78.4,80
+.9,84.3,86.5,87.9,89.2,90.2,91.1,92.0,92.7,93.6|45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77
+.0,79.0,83.0|0.8,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4,34.5,36.2,38.3,43.4&chm=tAndroid%201.5,7caa36,0
+,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b
+,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202.2,131d02,3,5,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=
+Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=add274,94d134,73ad18,507d08" />
+
+<p><em>Last historical dataset collected during two weeks ending on December 1, 2010</em></p>
</div><!-- end dashboard-panel -->
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 0f36345..2c8e4c2 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -67,73 +67,71 @@ padding: .25em 1em;
width="9px" /> Android NDK, Revision 5</a> <em>(November 2010)</em>
<div class="toggleme">
- <dl>
- <dt>NDK r5 notes:</dt>
-
- <dd>
- <p>The r5 release of the NDK includes many new APIs, many of which are introduced to
- support native game development and applications that require similar requirements. Most
- notably, native activities are now supported, which allow you to write an application
- entirely with native code. For detailed information describing the changes in this
- release, read the CHANGES.HTML document included in the downloaded NDK package.</p>
- </dd>
- </dl>
-
+ <p>This release of the NDK includes many new APIs, most of which are introduced to
+ support the development of games and similar applications that make extensive use
+ of native code. Using the APIs, developers have direct native access to events, audio,
+ graphics and window management, assets, and storage. Developers can also implement the
+ Android application lifecycle in native code with help from the new
+ {@link android.app.NativeActivity} class. For detailed information describing the changes in this
+ release, read the CHANGES.HTML document included in the downloaded NDK package.
+ </p>
<dl>
<dt>General notes:</dt>
-
<dd>
<ul>
-
- <li>A new toolchain (based on GCC 4.4.3), which generates better code, and can also now
-be used as a standalone cross-compiler, for people who want to build their stuff with
-<code>./configure &amp;&amp; make</code>. See
-docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided,
-but the 4.2.1 binaries were removed.</li>
-
- <li>Support for prebuilt static and shared libraries (docs/PREBUILTS.html), module
-exports and imports to make sharing and reuse of third-party modules much easier
-(docs/IMPORT-MODULE.html explains why).</li>
-
- <li>A C++ STL implementation (based on STLport) is now provided as a helper module. It
-can be used either as a static or shared library (details and usage exemple under
-sources/android/stlport/README). <strong>Note:</strong> For now, C++ Exceptions and RTTI are still
-not supported.</li>
-
- <li>Improvements to the <code>cpufeatures</code> helper library to deal with buggy
-kernel that incorrectly report they run on an ARMv7 CPU (while the device really is an ARMv6). We
-recommend developers that use it to simply rebuild their applications to benefit from it, then
-upload to Market.</li>
-
- <li>Adds support for native activities, which allows you to write completely native
- applications.</li>
-
- <li>Adds an EGL library that lets you create and manage OpenGL ES textures and
- services.</li>
+ <li>Adds support for native activities, which allows you to implement the
+ Android application lifecycle in native code.</li>
<li>Adds native support for the following:
<ul>
+
<li>Input subsystem (such as the keyboard and touch screen)</li>
+ <li>Access to sensor data (accelerometer, compass, gyroscope, etc).</li>
+
+ <li>Event loop APIs to wait for things such as input and sensor events.</li>
+
<li>Window and surface subsystem</li>
<li>Audio APIs based on the OpenSL ES standard that support playback and recording
as well as control over platform audio effects</li>
- <li>Event loop APIs to wait for things such as input and sensor events</li>
-
- <li>Access to assets packaged in the <code>.apk</code></li>
-
- <li>Access to sensor data (accelerometer, compass, gyroscope, etc.)</li>
+ <li>Access to assets packaged in an <code>.apk</code> file.</li>
+
</ul>
</li>
- <li>New sample applications, <code>native-plasma</code> and
- <code>native-activity</code>, to demonstrate how to write a native activity.</li>
-
- <li>Plus many bugfixes and other small improvements; see docs/CHANGES.html for a more
-detailed list of changes.</li>
+ <li>Includes a new toolchain (based on GCC 4.4.3), which generates better code, and can also now
+ be used as a standalone cross-compiler, for people who want to build their stuff with
+ <code>./configure &amp;&amp; make</code>. See
+ docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided,
+ but the 4.2.1 binaries were removed.</li>
+
+ <li>Adds support for prebuilt static and shared libraries (docs/PREBUILTS.html) and module
+ exports and imports to make sharing and reuse of third-party modules much easier
+ (docs/IMPORT-MODULE.html explains why).</li>
+
+ <li>Provides a default C++ STL implementation (based on STLport) as a helper module. It can be used either
+ as a static or shared library (details and usage examples are in sources/android/stlport/README). Prebuilt
+ binaries for STLport (static or shared) and GNU libstdc++ (static only) are also provided if you choose to
+ compile against those libraries instead of the default C++ STL implementation.
+ C++ Exceptions and RTTI are not supported in the default STL implementation. For more information, see
+ docs/CPLUSPLUS-SUPPORT.HTML.</li>
+
+ <li>Includes improvements to the <code>cpufeatures</code> helper library that improves reporting
+ of the CPU type (some devices previously reported ARMv7 CPU when the device really was an ARMv6). We
+ recommend developers that use this library to rebuild their applications then
+ upload to Market to benefit from the improvements.</li>
+
+ <li>Adds an EGL library that lets you create and manage OpenGL ES textures and
+ services.</li>
+
+ <li>Adds new sample applications, <code>native-plasma</code> and <code>native-activity</code>,
+ to demonstrate how to write a native activity.</li>
+
+ <li>Includes many bugfixes and other small improvements; see docs/CHANGES.html for a more
+ detailed list of changes.</li>
</ul>
</dd>
</dl>
@@ -296,14 +294,13 @@ detailed list of changes.</li>
<h2 id="installing">Installing the NDK</h2>
<p>Installing the NDK on your development computer is straightforward and involves extracting the
- NDK from its download package. Unlike previous releases, there is no need to run a host-setup
- script.</p>
+ NDK from its download package.</p>
<p>Before you get started make sure that you have downloaded the latest <a href=
"{@docRoot}sdk/index.html">Android SDK</a> and upgraded your applications and environment as
- needed. The NDK will not work with older versions of the Android SDK. Also, take a moment to
- review the <a href="{@docRoot}sdk/ndk/reqs.html">System and Software Requirements</a> for the
- NDK, if you haven't already.</p>
+ needed. The NDK is compatible with older platform versions but not older versions of the SDK tools.
+ Also, take a moment to review the <a href="{@docRoot}sdk/ndk/reqs.html">System and Software Requirements</a>
+ for the NDK, if you haven't already.</p>
<p>To install the NDK, follow these steps:</p>
@@ -318,7 +315,7 @@ detailed list of changes.</li>
<code>&lt;ndk&gt;</code>.</li>
</ol>
- <p>You are now ready start working with the NDK.</p>
+ <p>You are now ready to start working with the NDK.</p>
<h2 id="gettingstarted">Getting Started with the NDK</h2>
@@ -342,8 +339,7 @@ detailed list of changes.</li>
<li>Build your native code by running the 'ndk-build' script from your project's directory. It
is located in the top-level NDK directory:
- <pre class="no-pretty-print">
-cd &lt;project&gt;
+ <pre class="no-pretty-print">cd &lt;project&gt;
&lt;ndk&gt;/ndk-build
</pre>
@@ -360,220 +356,10 @@ cd &lt;project&gt;
<h2 id="samples">Sample Applications</h2>
- <p>The NDK includes sample applications that illustrate how to use native code in your Android
- applications:</p>
-
- <ul>
- <li><code>hello-jni</code> &mdash; a simple application that loads a string from a native
- method implemented in a shared library and then displays it in the application UI.</li>
-
- <li><code>two-libs</code> &mdash; a simple application that loads a shared library dynamically
- and calls a native method provided by the library. In this case, the method is implemented in a
- static library imported by the shared library.</li>
-
- <li><code>san-angeles</code> &mdash; a simple application that renders 3D graphics through the
- native OpenGL ES APIs, while managing activity lifecycle with a {@link
- android.opengl.GLSurfaceView} object.</li>
-
- <li><code>hello-gl2</code> &mdash; a simple application that renders a triangle using OpenGL ES
- 2.0 vertex and fragment shaders.</li>
-
- <li><code>hello-neon</code> &mdash; a simple application that shows how to use the
- <code>cpufeatures</code> library to check CPU capabilities at runtime, then use NEON intrinsics
- if supported by the CPU. Specifically, the application implements two versions of a tiny
- benchmark for a FIR filter loop, a C version and a NEON-optimized version for devices that
- support it.</li>
-
- <li><code>bitmap-plasma</code> &mdash; a simple application that demonstrates how to access the
- pixel buffers of Android {@link android.graphics.Bitmap} objects from native code, and uses
- this to generate an old-school "plasma" effect.</li>
-
- <li><code>native-activity</code> &mdash; a simple application that demonstrates how to use the
- native-app-glue static library to create a native activity</li>
-
- <li><code>native-plasma</code> &mdash; a version of bitmap-plasma implemented with a native
- activity.</li>
- </ul>
-
- <p>For each sample, the NDK includes the corresponding C source code and the necessary Android.mk
- and Application.mk files. There are located under <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code>
- and their source code can be found under <code>&lt;ndk&gt;/samples/&lt;name&gt;/jni/</code>.</p>
-
- <p>You can build the shared libraries for the sample apps by going into
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code> then calling the <code>ndk-build</code> command.
- The generated shared libraries will be located under
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi/</code> for (ARMv5TE machine code) and/or
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi-v7a/</code> for (ARMv7 machine code).</p>
-
- <p>Next, build the sample Android applications that use the shared libraries:</p>
-
- <ul>
- <li>If you are developing in Eclipse with ADT, use the New Project Wizard to create a new
- Android project for each sample, using the "Import from Existing Source" option and importing
- the source from <code>&lt;ndk&gt;/apps/&lt;app_name&gt;/project/</code>. Then, set up an AVD,
- if necessary, and build/run the application in the emulator. For more information about
- creating a new Android project in Eclipse, see <a href=
- "{@docRoot}guide/developing/eclipse-adt.html">Developing in Eclipse</a>.</li>
-
- <li>If you are developing with Ant, use the <code>android</code> tool to create the build file
- for each of the sample projects at <code>&lt;ndk&gt;/apps/&lt;app_name&gt;/project/</code>.
- Then set up an AVD, if necessary, build your project in the usual way, and run it in the
- emulator. For more information, see <a href=
- "{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</li>
- </ul>
-
- <h3 id="hello-jni">Exploring the hello-jni Sample</h3>
-
- <p>The hello-jni sample is a simple demonstration on how to use JNI from an Android application.
- The HelloJni activity receives a string from a simple C function and displays it in a
- TextView.</p>
-
- <p>The main components of the sample include:</p>
-
- <ul>
- <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
- file, a <code>src/</code> and <code>res</code> directories, and a main activity)</li>
-
- <li>A <code>jni/</code> directory that includes the implemented source file for the native code
- as well as the Android.mk file</li>
-
- <li>A <code>tests/</code> directory that contains unit test code.</li>
- </ul>
-
- <ol>
- <li>Create a new project in Eclipse from the existing sample source or use the
- <code>android</code> tool to update the project so it generates a build.xml file that you can
- use to build the sample.
-
- <ul>
- <li>In Eclipse:
-
- <ol type="a">
- <li>Click <strong>File &gt; New Android Project...</strong></li>
+ <p>The NDK includes sample Android applications that illustrate how to use native code in your
+ Android applications. For more information, see <a href=
+ "{@docRoot}sdk/ndk/overview.html#samples">Sample Applications</a>.</p>
- <li>Select the <strong>Create project from existing source</strong> radio button.</li>
-
- <li>Select any API level above Android 1.5.</li>
-
- <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
- the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
-
- <li>Click <strong>Finish</strong>.</li>
- </ol>
- </li>
-
- <li>On the command line:
-
- <ol type="a">
- <li>Change to the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
-
- <li>Run the following command to generate a build.xml file:
- <pre class="no-pretty-print">
-android update project -p . -s
-</pre>
- </li>
- </ol>
- </li>
- </ul>
- </li>
-
- <li>Compile the native code using the <code>ndk-build</code> command.
- <pre class="no-pretty-print">
-cd &lt;ndk-root&gt;/samples/hello-jni
-&lt;ndk_root&gt;/ndk-build
-</pre>
- </li>
-
- <li>Build and install the application as you would a normal Android application. If you are
- using Eclipse, run the application to build and install it on a device. If you are using Ant,
- run the following commands from the project directory:
- <pre class="no-pretty-print">
-ant debug
-adb install bin/HelloJni-debug.apk
-</pre>
- </li>
- </ol>
-
- <p>When you run the application on the device, the string <code>Hello JNI</code> should appear on
- your device. You can explore the rest of the samples that are located in the
- <code>&lt;ndk-root&gt;/samples</code> directory for more examples on how to use the JNI.</p>
-
- <h3 id="native-activity">Exploring the native-activity Sample Application</h3>
-
- <p>The native-activity sample provided with the Android NDK demonstrates how to use the
- android_native_app_glue static library. This static library makes creating a native activity
- easier by providing you with an implementation that handles your callbacks in another thread, so
- you do not have to worry about them blocking your main UI thread. The main parts of the sample
- are described below:</p>
-
- <ul>
- <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
- file, a <code>src/</code> and <code>res</code> directories). The AndroidManifest.xml declares
- that the application is native and specifies the .so file of the native activity. See {@link
- android.app.NativeActivity} for the source or see the
- <code>&lt;ndk_root&gt;/platforms/samples/native-activity/AndroidManifest.xml</code> file.</li>
-
- <li>A <code>jni/</code> directory contains the native activity, main.c, which uses the
- <code>android_native_app_glue.h</code> interface to implement the activity. The Android.mk that
- describes the native module to the build system also exists here.</li>
- </ul>
-
- <p>To build this sample application:</p>
-
- <ol>
- <li>Create a new project in Eclipse from the existing sample source or use the
- <code>android</code> tool to update the project so it generates a build.xml file that you can
- use to build the sample.
-
- <ul>
- <li>In Eclipse:
-
- <ol type="a">
- <li>Click <strong>File &gt; New Android Project...</strong></li>
-
- <li>Select the <strong>Create project from existing source</strong> radio button.</li>
-
- <li>Select any API level above Android 2.3.</li>
-
- <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
- the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
-
- <li>Click <strong>Finish</strong>.</li>
- </ol>
- </li>
-
- <li>On the command line:
-
- <ol type="a">
- <li>Change to the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
-
- <li>Run the following command to generate a build.xml file:
- <pre class="no-pretty-print">
-android update project -p . -s
-</pre>
- </li>
- </ol>
- </li>
- </ul>
- </li>
-
- <li>Compile the native code using the <code>ndk-build</code> command.
- <pre class="no-pretty-print">
-cd &lt;ndk-root&gt;/platforms/samples/android-9/samples/native-activity
-&lt;ndk_root&gt;/ndk-build
-</pre>
- </li>
-
- <li>Build and install the application as you would a normal Android application. If you are
- using Eclipse, run the application to build and install it on a device. If you are using Ant,
- run the following commands in the project directory, then run the application on the device:
- <pre class="no-pretty-print">
-ant debug
-adb install bin/NativeActivity-debug.apk
-</pre>
- </li>
- </ol>
-
<h2 id="forum">Discussion Forum and Mailing List</h2>
<p>If you have questions about the NDK or would like to read or contribute to discussions about
diff --git a/docs/html/sdk/ndk/overview.jd b/docs/html/sdk/ndk/overview.jd
index a7ec5d4..f6d148a 100644
--- a/docs/html/sdk/ndk/overview.jd
+++ b/docs/html/sdk/ndk/overview.jd
@@ -7,10 +7,8 @@ page.title=What is the NDK?
<ol>
<li><a href="#choosing">When to Develop in Native Code</a></li>
-
<li>
<a href="#contents">Contents of the NDK</a>
-
<ol>
<li><a href="#tools">Development tools</a></li>
@@ -19,9 +17,7 @@ page.title=What is the NDK?
<li><a href="#samples">Sample applications</a></li>
</ol>
</li>
-
<li><a href="#reqs">System and Software Requirements</a></li>
-
</ol>
</div>
</div>
@@ -102,9 +98,8 @@ page.title=What is the NDK?
later.</li>
<li>
- <p>Write a native activity, which allows you to potentially create an application completely in native
- code, because you can implement the lifecycle callbacks natively. The Android SDK provides
- the {@link android.app.NativeActivity} class, which is a convenience class that notifies your
+ <p>Write a native activity, which allows you to implement the lifecycle callbacks in native
+ code. The Android SDK provides the {@link android.app.NativeActivity} class, which is a convenience class that notifies your
native code of any activity lifecycle callbacks (<code>onCreate()</code>, <code>onPause()</code>,
<code>onResume()</code>, etc). You can implement the callbacks in your native code to handle
these events when they occur. Applications that use native activities must be run on Android
@@ -142,6 +137,10 @@ page.title=What is the NDK?
<li>libjnigraphics (Pixel buffer access) header (for Android 2.2 and above).</li>
<li>A Minimal set of headers for C++ support</li>
+
+ <li>OpenSL ES native audio libraries</li>
+
+ <li>Android native application APIS</li>
</ul>
<p>The NDK also provides a build system that lets you work efficiently with your sources, without
@@ -163,25 +162,18 @@ page.title=What is the NDK?
the <code>&lt;ndk&gt;/docs/</code> directory. Included are these files:</p>
<ul>
- <li>INSTALL.HTML &mdash; describes how to install the NDK and configure it for your host
+ <li>
+ INSTALL.HTML &mdash; describes how to install the NDK and configure it for your host
system</li>
<li>OVERVIEW.HTML &mdash; provides an overview of the NDK capabilities and usage</li>
-
+
<li>ANDROID-MK.HTML &mdash; describes the use of the Android.mk file, which defines the native
sources you want to compile</li>
-
+
<li>APPLICATION-MK.HTML &mdash; describes the use of the Application.mk file, which describes
- the native sources required by your Android application</li>
-
- <li>HOWTO.HTML &mdash; information about common tasks associated with NDK development.</li>
-
- <li>SYSTEM-ISSUES.HTML &mdash; known issues in the Android system images that you should be
- aware of, if you are developing using the NDK.</li>
-
- <li>STABLE-APIS.HTML &mdash; a complete list of the stable APIs exposed by headers in the
- NDK.</li>
-
+ the native sources required by your Android application</li>
+ <li>CPLUSPLUS-SUPPORT.HTML &mdash; describes the C++ support provided in the Android NDK</li>
<li>CPU-ARCH-ABIS.HTML &mdash; a description of supported CPU architectures and how to target
them.</li>
@@ -193,6 +185,32 @@ page.title=What is the NDK?
instructions.</li>
<li>CHANGES.HTML &mdash; a complete list of changes to the NDK across all releases.</li>
+
+ <li>DEVELOPMENT.HTML &mdash; describes how to modify the NDK and generate release packages for it</li>
+
+ <li>HOWTO.HTML &mdash; information about common tasks associated with NDK development</li>
+
+ <li>IMPORT-MODULE.HTML &mdash; describes how to share and reuse modules</li>
+
+ <li>LICENSES.HTML &mdash; information about the various open source licenses that govern the Android NDK</li>
+
+ <li>NATIVE-ACTIVITY.HTML &mdash; describes how to implement native activities</li>
+
+ <li>NDK-BUILD.HTML &mdash; describes the usage of the ndk-build script</li>
+
+ <li>NDK-GDB.HTML &mdash; describes how to use the native code debugger</li>
+
+ <li>PREBUILTS.HTML &mdash; information about how shared and static prebuilt libraries work </li>
+
+ <li>STANDALONE-TOOLCHAIN.HTML &mdash; describes how to use Android NDK toolchain as a standalone
+ compiler (still in beta).</li>
+
+ <li>SYSTEM-ISSUES.HTML &mdash; known issues in the Android system images that you should be
+ aware of, if you are developing using the NDK.</li>
+
+ <li>STABLE-APIS.HTML &mdash; a complete list of the stable APIs exposed by headers in the
+ NDK.</li>
+
</ul>
<p>Additionally, the package includes detailed information about the "bionic" C library provided
@@ -206,9 +224,218 @@ page.title=What is the NDK?
<h3 id="samples">Sample applications</h3>
- <p>The NDK includes sample Android applications that illustrate how to use native code in your
- Android applications. For more information, see <a href=
- "{@docRoot}sdk/ndk/installing.html#samples">Sample Applications</a>.</p>
+<p>The NDK includes sample applications that illustrate how to use native code in your Android
+ applications:</p>
+
+ <ul>
+ <li><code>hello-jni</code> &mdash; a simple application that loads a string from a native
+ method implemented in a shared library and then displays it in the application UI.</li>
+
+ <li><code>two-libs</code> &mdash; a simple application that loads a shared library dynamically
+ and calls a native method provided by the library. In this case, the method is implemented in a
+ static library imported by the shared library.</li>
+
+ <li><code>san-angeles</code> &mdash; a simple application that renders 3D graphics through the
+ native OpenGL ES APIs, while managing activity lifecycle with a {@link
+ android.opengl.GLSurfaceView} object.</li>
+
+ <li><code>hello-gl2</code> &mdash; a simple application that renders a triangle using OpenGL ES
+ 2.0 vertex and fragment shaders.</li>
+
+ <li><code>hello-neon</code> &mdash; a simple application that shows how to use the
+ <code>cpufeatures</code> library to check CPU capabilities at runtime, then use NEON intrinsics
+ if supported by the CPU. Specifically, the application implements two versions of a tiny
+ benchmark for a FIR filter loop, a C version and a NEON-optimized version for devices that
+ support it.</li>
+
+ <li><code>bitmap-plasma</code> &mdash; a simple application that demonstrates how to access the
+ pixel buffers of Android {@link android.graphics.Bitmap} objects from native code, and uses
+ this to generate an old-school "plasma" effect.</li>
+
+ <li><code>native-activity</code> &mdash; a simple application that demonstrates how to use the
+ native-app-glue static library to create a native activity</li>
+
+ <li><code>native-plasma</code> &mdash; a version of bitmap-plasma implemented with a native
+ activity.</li>
+ </ul>
+
+ <p>For each sample, the NDK includes the corresponding C source code and the necessary Android.mk
+ and Application.mk files. There are located under <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code>
+ and their source code can be found under <code>&lt;ndk&gt;/samples/&lt;name&gt;/jni/</code>.</p>
+
+ <p>You can build the shared libraries for the sample apps by going into
+ <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code> then calling the <code>ndk-build</code> command.
+ The generated shared libraries will be located under
+ <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi/</code> for (ARMv5TE machine code) and/or
+ <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi-v7a/</code> for (ARMv7 machine code).</p>
+
+ <p>Next, build the sample Android applications that use the shared libraries:</p>
+
+ <ul>
+ <li>If you are developing in Eclipse with ADT, use the New Project Wizard to create a new
+ Android project for each sample, using the "Import from Existing Source" option and importing
+ the source from <code>&lt;ndk&gt;/apps/&lt;app_name&gt;/project/</code>. Then, set up an AVD,
+ if necessary, and build/run the application in the emulator. For more information about
+ creating a new Android project in Eclipse, see <a href=
+ "{@docRoot}guide/developing/eclipse-adt.html">Developing in Eclipse</a>.</li>
+
+ <li>If you are developing with Ant, use the <code>android</code> tool to create the build file
+ for each of the sample projects at <code>&lt;ndk&gt;/apps/&lt;app_name&gt;/project/</code>.
+ Then set up an AVD, if necessary, build your project in the usual way, and run it in the
+ emulator. For more information, see <a href=
+ "{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</li>
+ </ul>
+
+ <h4 id="hello-jni">Exploring the hello-jni Sample</h4>
+
+ <p>The hello-jni sample is a simple demonstration on how to use JNI from an Android application.
+ The HelloJni activity receives a string from a simple C function and displays it in a
+ TextView.</p>
+
+ <p>The main components of the sample include:</p>
+
+ <ul>
+ <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
+ file, a <code>src/</code> and <code>res</code> directories, and a main activity)</li>
+
+ <li>A <code>jni/</code> directory that includes the implemented source file for the native code
+ as well as the Android.mk file</li>
+
+ <li>A <code>tests/</code> directory that contains unit test code.</li>
+ </ul>
+
+ <ol>
+ <li>Create a new project in Eclipse from the existing sample source or use the
+ <code>android</code> tool to update the project so it generates a build.xml file that you can
+ use to build the sample.
+
+ <ul>
+ <li>In Eclipse:
+
+ <ol type="a">
+ <li>Click <strong>File &gt; New Android Project...</strong></li>
+
+ <li>Select the <strong>Create project from existing source</strong> radio button.</li>
+
+ <li>Select any API level above Android 1.5.</li>
+
+ <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
+ the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
+
+ <li>Click <strong>Finish</strong>.</li>
+ </ol>
+ </li>
+
+ <li>On the command line:
+
+ <ol type="a">
+ <li>Change to the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
+
+ <li>Run the following command to generate a build.xml file:
+ <pre class="no-pretty-print">android update project -p . -s</pre>
+ </li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+
+ <li>Compile the native code using the <code>ndk-build</code> command.
+ <pre class="no-pretty-print">
+cd &lt;ndk-root&gt;/samples/hello-jni
+&lt;ndk_root&gt;/ndk-build
+</pre>
+ </li>
+
+ <li>Build and install the application as you would a normal Android application. If you are
+ using Eclipse, run the application to build and install it on a device. If you are using Ant,
+ run the following commands from the project directory:
+ <pre class="no-pretty-print">
+ant debug
+adb install bin/HelloJni-debug.apk
+</pre>
+ </li>
+ </ol>
+
+ <p>When you run the application on the device, the string <code>Hello JNI</code> should appear on
+ your device. You can explore the rest of the samples that are located in the
+ <code>&lt;ndk-root&gt;/samples</code> directory for more examples on how to use the JNI.</p>
+
+ <h4 id="native-activity">Exploring the native-activity Sample Application</h4>
+
+ <p>The native-activity sample provided with the Android NDK demonstrates how to use the
+ android_native_app_glue static library. This static library makes creating a native activity
+ easier by providing you with an implementation that handles your callbacks in another thread, so
+ you do not have to worry about them blocking your main UI thread. The main parts of the sample
+ are described below:</p>
+
+ <ul>
+ <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
+ file, a <code>src/</code> and <code>res</code> directories). The AndroidManifest.xml declares
+ that the application is native and specifies the .so file of the native activity. See {@link
+ android.app.NativeActivity} for the source or see the
+ <code>&lt;ndk_root&gt;/platforms/samples/native-activity/AndroidManifest.xml</code> file.</li>
+
+ <li>A <code>jni/</code> directory contains the native activity, main.c, which uses the
+ <code>android_native_app_glue.h</code> interface to implement the activity. The Android.mk that
+ describes the native module to the build system also exists here.</li>
+ </ul>
+
+ <p>To build this sample application:</p>
+
+ <ol>
+ <li>Create a new project in Eclipse from the existing sample source or use the
+ <code>android</code> tool to update the project so it generates a build.xml file that you can
+ use to build the sample.
+
+ <ul>
+ <li>In Eclipse:
+
+ <ol type="a">
+ <li>Click <strong>File &gt; New Android Project...</strong></li>
+
+ <li>Select the <strong>Create project from existing source</strong> radio button.</li>
+
+ <li>Select any API level above Android 2.3.</li>
+
+ <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
+ the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
+
+ <li>Click <strong>Finish</strong>.</li>
+ </ol>
+ </li>
+
+ <li>On the command line:
+
+ <ol type="a">
+ <li>Change to the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
+
+ <li>Run the following command to generate a build.xml file:
+ <pre class="no-pretty-print">
+android update project -p . -s
+</pre>
+ </li>
+ </ol>
+ </li>
+ </ul>
+ </li>
+
+ <li>Compile the native code using the <code>ndk-build</code> command.
+ <pre class="no-pretty-print">
+cd &lt;ndk-root&gt;/platforms/samples/android-9/samples/native-activity
+&lt;ndk_root&gt;/ndk-build
+</pre>
+ </li>
+
+ <li>Build and install the application as you would a normal Android application. If you are
+ using Eclipse, run the application to build and install it on a device. If you are using Ant,
+ run the following commands in the project directory, then run the application on the device:
+ <pre class="no-pretty-print">
+ant debug
+adb install bin/NativeActivity-debug.apk
+</pre>
+ </li>
+ </ol>
+
<h2 id="reqs">System and Software Requirements</h2>
@@ -313,7 +540,7 @@ page.title=What is the NDK?
to users whose devices are capable of supporting your application. For example:
<pre style="margin:1em;">
&lt;manifest&gt;
- ...
+ ...
<!-- Declare that the application uses the OpenGL ES 2.0 API and is designed
to run only on devices that support OpenGL ES 2.0 or higher. -->
&lt;uses-feature android:glEsVersion="0x00020000" /&gt;
@@ -331,4 +558,4 @@ page.title=What is the NDK?
containing the library can be deployed only to devices running Android 2.2 (API level 8) or
higher. To ensure compatibility, make sure that your application declares <code>&lt;uses-sdk
android:minSdkVersion="8" /&gt;</code> attribute value in its manifest.</li>
- </ul> \ No newline at end of file
+ </ul>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 62fbfb4..e3bb6eb 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1818,6 +1818,7 @@ public class Paint {
nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds);
}
+ @Override
protected void finalize() throws Throwable {
finalizer(mNativePaint);
}
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6364d58..431aaa47 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -79,6 +79,14 @@ public:
// the camera only has a single output, and does not have
// separate output for video frames and preview frame.
void getSupportedVideoSizes(Vector<Size> &sizes) const;
+ // Retrieve the preferred preview size (width and height) in pixels
+ // for video recording. The given width and height must be one of
+ // supported preview sizes returned from getSupportedPreviewSizes().
+ // Must not be called if getSupportedVideoSizes() returns an empty
+ // Vector of Size. If getSupportedVideoSizes() returns an empty
+ // Vector of Size, the width and height returned from this method
+ // is invalid, and is "-1x-1".
+ void getPreferredPreviewSizeForVideo(int *width, int *height) const;
void setPreviewFrameRate(int fps);
int getPreviewFrameRate() const;
@@ -319,6 +327,21 @@ public:
// frameworks/base/include/camera/Camera.h.
// Example: "176x144,1280x720". Read only.
static const char KEY_SUPPORTED_VIDEO_SIZES[];
+
+ // Preferred preview frame size in pixels for video recording.
+ // The width and height must be one of the supported sizes retrieved
+ // via KEY_SUPPORTED_PREVIEW_SIZES. This key can be used only when
+ // getSupportedVideoSizes() does not return an empty Vector of Size.
+ // Camcorder applications are recommended to set the preview size
+ // to a value that is not larger than the preferred preview size.
+ // In other words, the product of the width and height of the
+ // preview size should not be larger than that of the preferred
+ // preview size. In addition, we recommend to choos a preview size
+ // that has the same aspect ratio as the resolution of video to be
+ // recorded.
+ // Example value: "800x600". Read only.
+ static const char KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[];
+
// The image format for video frames. See CAMERA_MSG_VIDEO_FRAME in
// frameworks/base/include/camera/Camera.h.
// Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read only.
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index f4dc536..6c6c297 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -18,14 +18,19 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
-#include <android/input.h>
+#include <ui/Input.h>
#include <ui/Keyboard.h>
+#include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
+#include <ui/VirtualKeyMap.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
+#include <utils/PropertyMap.h>
+#include <utils/Vector.h>
#include <linux/input.h>
@@ -58,8 +63,6 @@ struct pollfd;
namespace android {
-class KeyLayoutMap;
-
/*
* A raw event as retrieved from the EventHub.
*/
@@ -156,6 +159,8 @@ public:
virtual String8 getDeviceName(int32_t deviceId) const = 0;
+ virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
+
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const = 0;
@@ -191,6 +196,9 @@ public:
virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
+ virtual void getVirtualKeyDefinitions(int32_t deviceId,
+ Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
+
virtual void dump(String8& dump) = 0;
};
@@ -205,6 +213,8 @@ public:
virtual String8 getDeviceName(int32_t deviceId) const;
+ virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const;
@@ -225,6 +235,9 @@ public:
virtual bool hasLed(int32_t deviceId, int32_t led) const;
virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+ virtual void getVirtualKeyDefinitions(int32_t deviceId,
+ Vector<VirtualKeyDefinition>& outVirtualKeys) const;
+
virtual void dump(String8& dump);
protected:
@@ -233,75 +246,80 @@ protected:
private:
bool openPlatformInput(void);
- int openDevice(const char *device);
- int closeDevice(const char *device);
+ int openDevice(const char *devicePath);
+ int closeDevice(const char *devicePath);
int scanDir(const char *dirname);
int readNotify(int nfd);
status_t mError;
- struct device_t {
- const int32_t id;
- const String8 path;
- String8 name;
- uint32_t classes;
- uint8_t* keyBitmask;
- KeyLayoutMap* layoutMap;
- KeyMapInfo keyMapInfo;
- int fd;
- device_t* next;
-
- device_t(int32_t _id, const char* _path, const char* name);
- ~device_t();
+ struct Device {
+ Device* next;
+
+ int fd;
+ const int32_t id;
+ const String8 path;
+ const InputDeviceIdentifier identifier;
+
+ uint32_t classes;
+ uint8_t* keyBitmask;
+ String8 configurationFile;
+ PropertyMap* configuration;
+ VirtualKeyMap* virtualKeyMap;
+ KeyMap keyMap;
+
+ Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
+ ~Device();
+
+ void close();
};
- device_t* getDeviceLocked(int32_t deviceId) const;
- bool hasKeycodeLocked(device_t* device, int keycode) const;
+ Device* getDeviceLocked(int32_t deviceId) const;
+ bool hasKeycodeLocked(Device* device, int keycode) const;
- int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
- int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
- int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
- bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const;
+ int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const;
+ int32_t getSwitchStateLocked(Device* device, int32_t sw) const;
+ bool markSupportedKeyCodesLocked(Device* device, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const;
- void configureKeyMap(device_t* device);
- void setKeyboardProperties(device_t* device, bool firstKeyboard);
- void clearKeyboardProperties(device_t* device, bool firstKeyboard);
+ void loadConfiguration(Device* device);
+ status_t loadVirtualKeyMap(Device* device);
+ status_t loadKeyMap(Device* device);
+ void setKeyboardProperties(Device* device, bool builtInKeyboard);
+ void clearKeyboardProperties(Device* device, bool builtInKeyboard);
// Protect all internal state.
- mutable Mutex mLock;
-
- bool mHaveFirstKeyboard;
- int32_t mFirstKeyboardId; // the API is that the built-in keyboard is id 0, so map it
-
- struct device_ent {
- device_t* device;
- uint32_t seq;
- };
- device_ent *mDevicesById;
- int mNumDevicesById;
-
- device_t *mOpeningDevices;
- device_t *mClosingDevices;
-
- device_t **mDevices;
- struct pollfd *mFDs;
- int mFDCount;
+ mutable Mutex mLock;
+
+ // The actual id of the built-in keyboard, or -1 if none.
+ // EventHub remaps the built-in keyboard to id 0 externally as required by the API.
+ int32_t mBuiltInKeyboardId;
+
+ int32_t mNextDeviceId;
+
+ // Parallel arrays of fds and devices.
+ // First index is reserved for inotify.
+ Vector<struct pollfd> mFds;
+ Vector<Device*> mDevices;
+
+ Device *mOpeningDevices;
+ Device *mClosingDevices;
- bool mOpened;
- bool mNeedToSendFinishedDeviceScan;
- List<String8> mExcludedDevices;
+ bool mOpened;
+ bool mNeedToSendFinishedDeviceScan;
+ List<String8> mExcludedDevices;
// device ids that report particular switches.
#ifdef EV_SW
- int32_t mSwitches[SW_MAX + 1];
+ int32_t mSwitches[SW_MAX + 1];
#endif
static const int INPUT_BUFFER_SIZE = 64;
struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
- int32_t mInputBufferIndex;
- int32_t mInputBufferCount;
- int32_t mInputDeviceIndex;
+ size_t mInputBufferIndex;
+ size_t mInputBufferCount;
+ size_t mInputFdIndex;
};
}; // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 1355bab..27f65bc 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -497,6 +497,54 @@ private:
KeyedVector<int32_t, MotionRange> mMotionRanges;
};
+/*
+ * Identifies a device.
+ */
+struct InputDeviceIdentifier {
+ inline InputDeviceIdentifier() :
+ bus(0), vendor(0), product(0), version(0) {
+ }
+
+ String8 name;
+ String8 location;
+ String8 uniqueId;
+ uint16_t bus;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+};
+
+/* Types of input device configuration files. */
+enum InputDeviceConfigurationFileType {
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+};
+
+/*
+ * Gets the path of an input device configuration file, if one is available.
+ * Considers both system provided and user installed configuration files.
+ *
+ * The device identifier is used to construct several default configuration file
+ * names to try based on the device name, vendor, product, and version.
+ *
+ * Returns an empty string if not found.
+ */
+extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+ const InputDeviceIdentifier& deviceIdentifier,
+ InputDeviceConfigurationFileType type);
+
+/*
+ * Gets the path of an input device configuration file, if one is available.
+ * Considers both system provided and user installed configuration files.
+ *
+ * The name is case-sensitive and is used to construct the filename to resolve.
+ * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
+ *
+ * Returns an empty string if not found.
+ */
+extern String8 getInputDeviceConfigurationFilePathByName(
+ const String8& name, InputDeviceConfigurationFileType type);
} // namespace android
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d0812de..b621680 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -291,9 +291,7 @@ public:
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
- uint32_t& policyFlags) = 0;
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
/* Intercepts a generic touch, trackball or other event before queueing it.
* The policy can use this method as an opportunity to perform power management functions
@@ -894,9 +892,6 @@ private:
// Input channels that will receive a copy of all input events.
Vector<sp<InputChannel> > mMonitoringChannels;
- // Preallocated key event object used for policy inquiries.
- KeyEvent mReusableKeyEvent;
-
// Event injection and synchronization.
Condition mInjectionResultAvailableCondition;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f3a2dd2..8ec5421 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -35,34 +35,6 @@ namespace android {
class InputDevice;
class InputMapper;
-/* Describes a virtual key. */
-struct VirtualKeyDefinition {
- int32_t scanCode;
-
- // configured position data, specified in display coords
- int32_t centerX;
- int32_t centerY;
- int32_t width;
- int32_t height;
-};
-
-
-/* Specifies input device calibration settings. */
-class InputDeviceCalibration {
-public:
- InputDeviceCalibration();
-
- void clear();
- void addProperty(const String8& key, const String8& value);
-
- bool tryGetProperty(const String8& key, String8& outValue) const;
- bool tryGetProperty(const String8& key, int32_t& outValue) const;
- bool tryGetProperty(const String8& key, float& outValue) const;
-
-private:
- KeyedVector<String8, String8> mProperties;
-};
-
/*
* Input reader policy interface.
@@ -103,14 +75,6 @@ public:
*/
virtual bool filterJumpyTouchEvents() = 0;
- /* Gets the configured virtual key definitions for an input device. */
- virtual void getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
-
- /* Gets the calibration for an input device. */
- virtual void getInputDeviceCalibration(const String8& deviceName,
- InputDeviceCalibration& outCalibration) = 0;
-
/* Gets the excluded device names for the platform. */
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
};
@@ -314,8 +278,8 @@ public:
int32_t getMetaState();
- inline const InputDeviceCalibration& getCalibration() {
- return mCalibration;
+ inline const PropertyMap& getConfiguration() {
+ return mConfiguration;
}
private:
@@ -330,7 +294,7 @@ private:
typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
- InputDeviceCalibration mCalibration;
+ PropertyMap mConfiguration;
};
@@ -389,13 +353,13 @@ private:
class KeyboardInputMapper : public InputMapper {
public:
- KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources,
- int32_t keyboardType);
+ KeyboardInputMapper(InputDevice* device, uint32_t sources, int32_t keyboardType);
virtual ~KeyboardInputMapper();
virtual uint32_t getSources();
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
virtual void dump(String8& dump);
+ virtual void configure();
virtual void reset();
virtual void process(const RawEvent* rawEvent);
@@ -414,10 +378,15 @@ private:
int32_t scanCode;
};
- int32_t mAssociatedDisplayId;
uint32_t mSources;
int32_t mKeyboardType;
+ // Immutable configuration parameters.
+ struct Parameters {
+ int32_t associatedDisplayId;
+ bool orientationAware;
+ } mParameters;
+
struct LockedState {
Vector<KeyDown> keyDowns; // keys that are down
int32_t metaState;
@@ -435,6 +404,9 @@ private:
void initializeLocked();
void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
+ void configureParameters();
+ void dumpParameters(String8& dump);
+
bool isKeyboardOrGamepadKey(int32_t scanCode);
void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
@@ -450,12 +422,13 @@ private:
class TrackballInputMapper : public InputMapper {
public:
- TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ TrackballInputMapper(InputDevice* device);
virtual ~TrackballInputMapper();
virtual uint32_t getSources();
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
virtual void dump(String8& dump);
+ virtual void configure();
virtual void reset();
virtual void process(const RawEvent* rawEvent);
@@ -467,7 +440,11 @@ private:
Mutex mLock;
- int32_t mAssociatedDisplayId;
+ // Immutable configuration parameters.
+ struct Parameters {
+ int32_t associatedDisplayId;
+ bool orientationAware;
+ } mParameters;
struct Accumulator {
enum {
@@ -499,13 +476,16 @@ private:
void initializeLocked();
+ void configureParameters();
+ void dumpParameters(String8& dump);
+
void sync(nsecs_t when);
};
class TouchInputMapper : public InputMapper {
public:
- TouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ TouchInputMapper(InputDevice* device);
virtual ~TouchInputMapper();
virtual uint32_t getSources();
@@ -591,10 +571,17 @@ protected:
}
};
- int32_t mAssociatedDisplayId;
-
// Immutable configuration parameters.
struct Parameters {
+ enum DeviceType {
+ DEVICE_TYPE_TOUCH_SCREEN,
+ DEVICE_TYPE_TOUCH_PAD,
+ };
+
+ DeviceType deviceType;
+ int32_t associatedDisplayId;
+ bool orientationAware;
+
bool useBadTouchFilter;
bool useJumpyTouchFilter;
bool useAveragingTouchFilter;
@@ -641,7 +628,7 @@ protected:
bool haveToolSizeAreaBias;
float toolSizeAreaBias;
bool haveToolSizeIsSummed;
- int32_t toolSizeIsSummed;
+ bool toolSizeIsSummed;
// Pressure
enum PressureCalibration {
@@ -846,7 +833,7 @@ private:
class SingleTouchInputMapper : public TouchInputMapper {
public:
- SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ SingleTouchInputMapper(InputDevice* device);
virtual ~SingleTouchInputMapper();
virtual void reset();
@@ -892,7 +879,7 @@ private:
class MultiTouchInputMapper : public TouchInputMapper {
public:
- MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ MultiTouchInputMapper(InputDevice* device);
virtual ~MultiTouchInputMapper();
virtual void reset();
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 3b477c7..50296e2 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -20,6 +20,7 @@
#include <ui/Input.h>
#include <utils/Errors.h>
#include <utils/String8.h>
+#include <utils/PropertyMap.h>
namespace android {
@@ -32,26 +33,58 @@ enum {
DEVICE_ID_VIRTUAL_KEYBOARD = -1,
};
-struct KeyMapInfo {
- String8 keyMapName;
+class KeyLayoutMap;
+class KeyCharacterMap;
+
+/**
+ * Loads the key layout map and key character map for a keyboard device.
+ */
+class KeyMap {
+public:
String8 keyLayoutFile;
+ KeyLayoutMap* keyLayoutMap;
+
String8 keyCharacterMapFile;
- bool isDefaultKeyMap;
+ KeyCharacterMap* keyCharacterMap;
+
+ KeyMap();
+ ~KeyMap();
+
+ status_t load(const InputDeviceIdentifier& deviceIdenfier,
+ const PropertyMap* deviceConfiguration);
- KeyMapInfo() : isDefaultKeyMap(false) {
+ inline bool haveKeyLayout() const {
+ return !keyLayoutFile.isEmpty();
}
+
+ inline bool haveKeyCharacterMap() const {
+ return !keyCharacterMapFile.isEmpty();
+ }
+
+ inline bool isComplete() const {
+ return haveKeyLayout() && haveKeyCharacterMap();
+ }
+
+private:
+ bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+ status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+ status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& name);
+ String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& name, InputDeviceConfigurationFileType type);
};
/**
- * Resolves the key map to use for a particular keyboard device.
+ * Returns true if the keyboard is eligible for use as a built-in keyboard.
*/
-extern status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo);
+extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
+ const PropertyMap* deviceConfiguration, const KeyMap* keyMap);
/**
* Sets keyboard system properties.
*/
-extern void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
- const KeyMapInfo& keyMapInfo);
+extern void setKeyboardProperties(int32_t deviceId, const InputDeviceIdentifier& deviceIdentifier,
+ const String8& keyLayoutFile, const String8& keyCharacterMapFile);
/**
* Clears keyboard system properties.
diff --git a/include/ui/VirtualKeyMap.h b/include/ui/VirtualKeyMap.h
new file mode 100644
index 0000000..7813d9d
--- /dev/null
+++ b/include/ui/VirtualKeyMap.h
@@ -0,0 +1,79 @@
+/*
+ * 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 _UI_VIRTUAL_KEY_MAP_H
+#define _UI_VIRTUAL_KEY_MAP_H
+
+#include <stdint.h>
+
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Tokenizer.h>
+#include <utils/String8.h>
+#include <utils/Unicode.h>
+
+namespace android {
+
+/* Describes a virtual key. */
+struct VirtualKeyDefinition {
+ int32_t scanCode;
+
+ // configured position data, specified in display coords
+ int32_t centerX;
+ int32_t centerY;
+ int32_t width;
+ int32_t height;
+};
+
+
+/**
+ * Describes a collection of virtual keys on a touch screen in terms of
+ * virtual scan codes and hit rectangles.
+ */
+class VirtualKeyMap {
+public:
+ ~VirtualKeyMap();
+
+ static status_t load(const String8& filename, VirtualKeyMap** outMap);
+
+ inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
+ return mVirtualKeys;
+ }
+
+private:
+ class Parser {
+ VirtualKeyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(VirtualKeyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ bool consumeFieldDelimiterAndSkipWhitespace();
+ bool parseNextIntField(int32_t* outValue);
+ };
+
+ Vector<VirtualKeyDefinition> mVirtualKeys;
+
+ VirtualKeyMap();
+};
+
+} // namespace android
+
+#endif // _UI_KEY_CHARACTER_MAP_H
diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h
new file mode 100644
index 0000000..a54f819
--- /dev/null
+++ b/include/utils/PropertyMap.h
@@ -0,0 +1,100 @@
+/*
+ * 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 _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+ /* Creates an empty property map. */
+ PropertyMap();
+ ~PropertyMap();
+
+ /* Clears the property map. */
+ void clear();
+
+ /* Adds a property.
+ * Replaces the property with the same key if it is already present.
+ */
+ void addProperty(const String8& key, const String8& value);
+
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const String8& key) const;
+
+ /* Gets the value of a property and parses it.
+ * Returns true and sets outValue if the key was found and its value was parsed successfully.
+ * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ */
+ bool tryGetProperty(const String8& key, String8& outValue) const;
+ bool tryGetProperty(const String8& key, bool& outValue) const;
+ bool tryGetProperty(const String8& key, int32_t& outValue) const;
+ bool tryGetProperty(const String8& key, float& outValue) const;
+
+ /* Loads a property map from a file. */
+ static status_t load(const String8& filename, PropertyMap** outMap);
+
+private:
+ class Parser {
+ PropertyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(PropertyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 6abfb06..6b49ff5 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -47,7 +47,12 @@ public:
explicit String8(const char32_t* o);
explicit String8(const char32_t* o, size_t numChars);
~String8();
-
+
+ static inline const String8 empty();
+
+ static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
+ static String8 formatV(const char* fmt, va_list args);
+
inline const char* string() const;
inline size_t size() const;
inline size_t length() const;
@@ -229,6 +234,10 @@ inline int strictly_order_type(const String8& lhs, const String8& rhs)
return compare_type(lhs, rhs) < 0;
}
+inline const String8 String8::empty() {
+ return String8();
+}
+
inline const char* String8::string() const
{
return mString;
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
index 21e58e6..c7db5fb 100644
--- a/include/utils/Tokenizer.h
+++ b/include/utils/Tokenizer.h
@@ -28,7 +28,7 @@ namespace android {
* A simple tokenizer for loading and parsing ASCII text files line by line.
*/
class Tokenizer {
- Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length);
+ Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length);
public:
~Tokenizer();
@@ -110,7 +110,7 @@ private:
String8 mFilename;
FileMap* mFileMap;
- const char* mBuffer;
+ char* mBuffer;
size_t mLength;
const char* mCurrent;
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 45b1b9a..e9a5f8c 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -75,6 +75,7 @@ const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
+const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
const char CameraParameters::TRUE[] = "true";
const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
@@ -333,6 +334,14 @@ void CameraParameters::getPreviewSize(int *width, int *height) const
parse_pair(p, width, height, 'x');
}
+void CameraParameters::getPreferredPreviewSizeForVideo(int *width, int *height) const
+{
+ *width = *height = -1;
+ const char *p = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO);
+ if (p == 0) return;
+ parse_pair(p, width, height, 'x');
+}
+
void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
{
const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 7ca289d..ebffd34 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -42,6 +42,9 @@ Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQua
mXDivs = new int32_t[mXCount];
mYDivs = new int32_t[mYCount];
+ PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, vertices = %d",
+ xCount, yCount, emptyQuads, verticesCount);
+
glGenBuffers(1, &meshBuffer);
}
@@ -208,7 +211,15 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
float u1, float v1, float u2, float v2, uint32_t& quadCount) {
- if (((mColorKey >> quadCount++) & 0x1) == 1) {
+ uint32_t oldQuadCount = quadCount;
+
+ // Degenerate quads are an artifact of our implementation and should not
+ // be taken into account when checking for transparent quads
+ if (x2 - x1 > 0.999f && y2 - y1 > 0.999f) {
+ quadCount++;
+ }
+
+ if (((mColorKey >> oldQuadCount) & 0x1) == 1) {
return;
}
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
index 91b1c32..b86bbc5 100644
--- a/libs/hwui/SkiaColorFilter.cpp
+++ b/libs/hwui/SkiaColorFilter.cpp
@@ -36,6 +36,11 @@ SkiaColorFilter::~SkiaColorFilter() {
SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector):
SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) {
+ // Skia uses the range [0..255] for the addition vector, but we need
+ // the [0..1] range to apply the vector in GLSL
+ for (int i = 0; i < 4; i++) {
+ mVector[i] /= 255.0f;
+ }
}
SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 5948e04..d0e041a 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -21,6 +21,7 @@ commonSources:= \
Keyboard.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
+ VirtualKeyMap.cpp
# For the host
# =====================================================
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index f468217..8f4bac6 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -33,6 +33,8 @@
#include <assert.h>
#include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
+#include <ui/VirtualKeyMap.h>
#include <string.h>
#include <stdint.h>
@@ -56,10 +58,6 @@
/* this macro computes the number of bytes needed to represent a bit array of the specified size */
#define sizeof_bit_array(bits) ((bits + 7) / 8)
-#define ID_MASK 0x0000ffff
-#define SEQ_MASK 0x7fff0000
-#define SEQ_SHIFT 16
-
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#endif
@@ -72,6 +70,9 @@
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#endif
+// Fd at index 0 is always reserved for inotify
+#define FIRST_ACTUAL_DEVICE_INDEX 1
+
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
@@ -79,7 +80,7 @@
namespace android {
static const char *WAKE_LOCK_ID = "KeyEvents";
-static const char *device_path = "/dev/input";
+static const char *DEVICE_PATH = "/dev/input";
/* return the larger integer */
static inline int max(int v1, int v2)
@@ -91,72 +92,89 @@ static inline const char* toString(bool value) {
return value ? "true" : "false";
}
-EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
- : id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(NULL), fd(-1), next(NULL) {
+// --- EventHub::Device ---
+
+EventHub::Device::Device(int fd, int32_t id, const String8& path,
+ const InputDeviceIdentifier& identifier) :
+ next(NULL),
+ fd(fd), id(id), path(path), identifier(identifier),
+ classes(0), keyBitmask(NULL), configuration(NULL), virtualKeyMap(NULL) {
}
-EventHub::device_t::~device_t() {
- delete [] keyBitmask;
- delete layoutMap;
+EventHub::Device::~Device() {
+ close();
+ delete[] keyBitmask;
+ delete configuration;
+ delete virtualKeyMap;
}
-EventHub::EventHub(void)
- : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(-1)
- , mDevicesById(0), mNumDevicesById(0)
- , mOpeningDevices(0), mClosingDevices(0)
- , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
- , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
-{
+void EventHub::Device::close() {
+ if (fd >= 0) {
+ ::close(fd);
+ fd = -1;
+ }
+}
+
+
+// --- EventHub ---
+
+EventHub::EventHub(void) :
+ mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1),
+ mOpeningDevices(0), mClosingDevices(0),
+ mOpened(false), mNeedToSendFinishedDeviceScan(false),
+ mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
memset(mSwitches, 0, sizeof(mSwitches));
#endif
}
-/*
- * Clean up.
- */
-EventHub::~EventHub(void)
-{
+EventHub::~EventHub(void) {
release_wake_lock(WAKE_LOCK_ID);
// we should free stuff here...
}
-status_t EventHub::errorCheck() const
-{
+status_t EventHub::errorCheck() const {
return mError;
}
-String8 EventHub::getDeviceName(int32_t deviceId) const
-{
+String8 EventHub::getDeviceName(int32_t deviceId) const {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device == NULL) return String8();
- return device->name;
+ return device->identifier.name;
}
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
-{
+uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device == NULL) return 0;
return device->classes;
}
+void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && device->configuration) {
+ *outConfiguration = *device->configuration;
+ } else {
+ outConfiguration->clear();
+ }
+}
+
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const {
outAxisInfo->clear();
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device == NULL) return -1;
struct input_absinfo info;
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
LOGW("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), device->fd);
+ axis, device->identifier.name.string(), device->fd);
return -errno;
}
@@ -174,7 +192,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
if (scanCode >= 0 && scanCode <= KEY_MAX) {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getScanCodeStateLocked(device, scanCode);
}
@@ -182,7 +200,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
return AKEY_STATE_UNKNOWN;
}
-int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
+int32_t EventHub::getScanCodeStateLocked(Device* device, int32_t scanCode) const {
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(device->fd,
@@ -195,20 +213,20 @@ int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) con
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getKeyCodeStateLocked(device, keyCode);
}
return AKEY_STATE_UNKNOWN;
}
-int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
- if (!device->layoutMap) {
+int32_t EventHub::getKeyCodeStateLocked(Device* device, int32_t keyCode) const {
+ if (!device->keyMap.haveKeyLayout()) {
return AKEY_STATE_UNKNOWN;
}
Vector<int32_t> scanCodes;
- device->layoutMap->findScanCodes(keyCode, &scanCodes);
+ device->keyMap.keyLayoutMap->findScanCodes(keyCode, &scanCodes);
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
@@ -236,7 +254,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
if (sw >= 0 && sw <= SW_MAX) {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getSwitchStateLocked(device, sw);
}
@@ -245,7 +263,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
return AKEY_STATE_UNKNOWN;
}
-int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
+int32_t EventHub::getSwitchStateLocked(Device* device, int32_t sw) const {
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(device->fd,
@@ -259,16 +277,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device != NULL) {
return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
}
return false;
}
-bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const {
- if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+ if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
return false;
}
@@ -276,7 +294,7 @@ bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
- status_t err = device->layoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
+ status_t err = device->keyMap.keyLayoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
if (! err) {
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
@@ -295,20 +313,20 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const
{
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
- if (device != NULL && device->layoutMap != NULL) {
- status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (device && device->keyMap.haveKeyLayout()) {
+ status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
if (err == NO_ERROR) {
return NO_ERROR;
}
}
- if (mHaveFirstKeyboard) {
- device = getDeviceLocked(mFirstKeyboardId);
+ if (mBuiltInKeyboardId != -1) {
+ device = getDeviceLocked(mBuiltInKeyboardId);
- if (device != NULL && device->layoutMap != NULL) {
- status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (device && device->keyMap.haveKeyLayout()) {
+ status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
if (err == NO_ERROR) {
return NO_ERROR;
}
@@ -330,7 +348,7 @@ void EventHub::addExcludedDevice(const char* deviceName)
bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device) {
uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)];
memset(bitmask, 0, sizeof(bitmask));
@@ -345,7 +363,7 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
AutoMutex _l(mLock);
- device_t* device = getDeviceLocked(deviceId);
+ Device* device = getDeviceLocked(deviceId);
if (device) {
struct input_event ev;
ev.time.tv_sec = 0;
@@ -361,21 +379,33 @@ void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
}
}
-EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
-{
- if (deviceId == 0) deviceId = mFirstKeyboardId;
- int32_t id = deviceId & ID_MASK;
- if (id >= mNumDevicesById || id < 0) return NULL;
- device_t* dev = mDevicesById[id].device;
- if (dev == NULL) return NULL;
- if (dev->id == deviceId) {
- return dev;
+void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
+ Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ outVirtualKeys.clear();
+
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && device->virtualKeyMap) {
+ outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys());
+ }
+}
+
+EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
+ if (deviceId == 0) {
+ deviceId = mBuiltInKeyboardId;
+ }
+
+ size_t numDevices = mDevices.size();
+ for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < numDevices; i++) {
+ Device* device = mDevices[i];
+ if (device->id == deviceId) {
+ return device;
+ }
}
return NULL;
}
-bool EventHub::getEvent(RawEvent* outEvent)
-{
+bool EventHub::getEvent(RawEvent* outEvent) {
outEvent->deviceId = 0;
outEvent->type = 0;
outEvent->scanCode = 0;
@@ -396,11 +426,11 @@ bool EventHub::getEvent(RawEvent* outEvent)
for (;;) {
// Report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
- device_t* device = mClosingDevices;
- LOGV("Reporting device closed: id=0x%x, name=%s\n",
+ Device* device = mClosingDevices;
+ LOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
- if (device->id == mFirstKeyboardId) {
+ if (device->id == mBuiltInKeyboardId) {
outEvent->deviceId = 0;
} else {
outEvent->deviceId = device->id;
@@ -413,11 +443,11 @@ bool EventHub::getEvent(RawEvent* outEvent)
}
if (mOpeningDevices != NULL) {
- device_t* device = mOpeningDevices;
- LOGV("Reporting device opened: id=0x%x, name=%s\n",
+ Device* device = mOpeningDevices;
+ LOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
- if (device->id == mFirstKeyboardId) {
+ if (device->id == mBuiltInKeyboardId) {
outEvent->deviceId = 0;
} else {
outEvent->deviceId = device->id;
@@ -440,11 +470,11 @@ bool EventHub::getEvent(RawEvent* outEvent)
// Consume buffered input events, if any.
if (mInputBufferIndex < mInputBufferCount) {
const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
- const device_t* device = mDevices[mInputDeviceIndex];
+ const Device* device = mDevices[mInputFdIndex];
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
- if (device->id == mFirstKeyboardId) {
+ if (device->id == mBuiltInKeyboardId) {
outEvent->deviceId = 0;
} else {
outEvent->deviceId = device->id;
@@ -454,8 +484,8 @@ bool EventHub::getEvent(RawEvent* outEvent)
outEvent->flags = 0;
if (iev.type == EV_KEY) {
outEvent->keyCode = AKEYCODE_UNKNOWN;
- if (device->layoutMap) {
- status_t err = device->layoutMap->map(iev.code,
+ if (device->keyMap.haveKeyLayout()) {
+ status_t err = device->keyMap.keyLayoutMap->map(iev.code,
&outEvent->keyCode, &outEvent->flags);
LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
iev.code, outEvent->keyCode, outEvent->flags, err);
@@ -475,13 +505,13 @@ bool EventHub::getEvent(RawEvent* outEvent)
// Finish reading all events from devices identified in previous poll().
// This code assumes that mInputDeviceIndex is initially 0 and that the
// revents member of pollfd is initialized to 0 when the device is first added.
- // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
- mInputDeviceIndex += 1;
- if (mInputDeviceIndex >= mFDCount) {
+ // Since mFds[0] is used for inotify, we process regular events starting at index 1.
+ mInputFdIndex += 1;
+ if (mInputFdIndex >= mFds.size()) {
break;
}
- const struct pollfd& pfd = mFDs[mInputDeviceIndex];
+ const struct pollfd& pfd = mFds[mInputFdIndex];
if (pfd.revents & POLLIN) {
int32_t readSize = read(pfd.fd, mInputBufferData,
sizeof(struct input_event) * INPUT_BUFFER_SIZE);
@@ -492,7 +522,7 @@ bool EventHub::getEvent(RawEvent* outEvent)
} else if ((readSize % sizeof(struct input_event)) != 0) {
LOGE("could not get event (wrong size: %d)", readSize);
} else {
- mInputBufferCount = readSize / sizeof(struct input_event);
+ mInputBufferCount = size_t(readSize) / sizeof(struct input_event);
mInputBufferIndex = 0;
}
}
@@ -501,14 +531,14 @@ bool EventHub::getEvent(RawEvent* outEvent)
#if HAVE_INOTIFY
// readNotify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
- if(mFDs[0].revents & POLLIN) {
- readNotify(mFDs[0].fd);
- mFDs[0].revents = 0;
+ if(mFds[0].revents & POLLIN) {
+ readNotify(mFds[0].fd);
+ mFds.editItemAt(0).revents = 0;
continue; // report added or removed devices immediately
}
#endif
- mInputDeviceIndex = 0;
+ mInputFdIndex = 0;
// Poll for events. Mind the wake lock dance!
// We hold a wake lock at all times except during poll(). This works due to some
@@ -520,7 +550,7 @@ bool EventHub::getEvent(RawEvent* outEvent)
// pending or currently being processed.
release_wake_lock(WAKE_LOCK_ID);
- int pollResult = poll(mFDs, mFDCount, -1);
+ int pollResult = poll(mFds.editArray(), mFds.size(), -1);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
@@ -536,36 +566,37 @@ bool EventHub::getEvent(RawEvent* outEvent)
/*
* Open the platform-specific input device.
*/
-bool EventHub::openPlatformInput(void)
-{
+bool EventHub::openPlatformInput(void) {
/*
* Open platform-specific input device(s).
*/
- int res;
+ int res, fd;
- mFDCount = 1;
- mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
- mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
- mFDs[0].events = POLLIN;
- mFDs[0].revents = 0;
- mDevices[0] = NULL;
#ifdef HAVE_INOTIFY
- mFDs[0].fd = inotify_init();
- res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE);
+ fd = inotify_init();
+ res = inotify_add_watch(fd, DEVICE_PATH, IN_DELETE | IN_CREATE);
if(res < 0) {
- LOGE("could not add watch for %s, %s\n", device_path, strerror(errno));
+ LOGE("could not add watch for %s, %s\n", DEVICE_PATH, strerror(errno));
}
#else
/*
* The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd.
* We allocate space for it and set it to something invalid.
*/
- mFDs[0].fd = -1;
+ fd = -1;
#endif
- res = scanDir(device_path);
+ // Reserve fd index 0 for inotify.
+ struct pollfd pollfd;
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+ mFds.push(pollfd);
+ mDevices.push(NULL);
+
+ res = scanDir(DEVICE_PATH);
if(res < 0) {
- LOGE("scan dir failed for %s\n", device_path);
+ LOGE("scan dir failed for %s\n", DEVICE_PATH);
}
return true;
@@ -593,128 +624,104 @@ static const int32_t GAMEPAD_KEYCODES[] = {
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
};
-int EventHub::openDevice(const char *deviceName) {
- int version;
- int fd;
- struct pollfd *new_mFDs;
- device_t **new_devices;
- char **new_device_names;
- char name[80];
- char location[80];
- char idstr[80];
- struct input_id id;
+int EventHub::openDevice(const char *devicePath) {
+ char buffer[80];
- LOGV("Opening device: %s", deviceName);
+ LOGV("Opening device: %s", devicePath);
AutoMutex _l(mLock);
- fd = open(deviceName, O_RDWR);
+ int fd = open(devicePath, O_RDWR);
if(fd < 0) {
- LOGE("could not open %s, %s\n", deviceName, strerror(errno));
+ LOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
- if(ioctl(fd, EVIOCGVERSION, &version)) {
- LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno));
- return -1;
- }
- if(ioctl(fd, EVIOCGID, &id)) {
- LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno));
- return -1;
- }
- name[sizeof(name) - 1] = '\0';
- location[sizeof(location) - 1] = '\0';
- idstr[sizeof(idstr) - 1] = '\0';
- if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
- //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
- name[0] = '\0';
+ InputDeviceIdentifier identifier;
+
+ // Get device name.
+ if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
+ //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ identifier.name.setTo(buffer);
}
- // check to see if the device is on our excluded list
+ // Check to see if the device is on our excluded list
List<String8>::iterator iter = mExcludedDevices.begin();
List<String8>::iterator end = mExcludedDevices.end();
for ( ; iter != end; iter++) {
const char* test = *iter;
- if (strcmp(name, test) == 0) {
- LOGI("ignoring event id %s driver %s\n", deviceName, test);
+ if (identifier.name == test) {
+ LOGI("ignoring event id %s driver %s\n", devicePath, test);
close(fd);
return -1;
}
}
- if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
- //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno));
- location[0] = '\0';
- }
- if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
- //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno));
- idstr[0] = '\0';
- }
-
- if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
- LOGE("Error %d making device file descriptor non-blocking.", errno);
+ // Get device driver version.
+ int driverVersion;
+ if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
+ LOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
- int devid = 0;
- while (devid < mNumDevicesById) {
- if (mDevicesById[devid].device == NULL) {
- break;
- }
- devid++;
+ // Get device identifier.
+ struct input_id inputId;
+ if(ioctl(fd, EVIOCGID, &inputId)) {
+ LOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+ close(fd);
+ return -1;
}
- if (devid >= mNumDevicesById) {
- device_ent* new_devids = (device_ent*)realloc(mDevicesById,
- sizeof(mDevicesById[0]) * (devid + 1));
- if (new_devids == NULL) {
- LOGE("out of memory");
- return -1;
- }
- mDevicesById = new_devids;
- mNumDevicesById = devid+1;
- mDevicesById[devid].device = NULL;
- mDevicesById[devid].seq = 0;
+ identifier.bus = inputId.bustype;
+ identifier.product = inputId.product;
+ identifier.vendor = inputId.vendor;
+ identifier.version = inputId.version;
+
+ // Get device physical location.
+ if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
+ //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ identifier.location.setTo(buffer);
}
- mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if (mDevicesById[devid].seq == 0) {
- mDevicesById[devid].seq = 1<<SEQ_SHIFT;
+ // Get device unique id.
+ if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
+ //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ identifier.uniqueId.setTo(buffer);
}
- new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
- new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
- if (new_mFDs == NULL || new_devices == NULL) {
- LOGE("out of memory");
+ // Make file descriptor non-blocking for use with poll().
+ if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+ LOGE("Error %d making device file descriptor non-blocking.", errno);
+ close(fd);
return -1;
}
- mFDs = new_mFDs;
- mDevices = new_devices;
+
+ // Allocate device. (The device object takes ownership of the fd at this point.)
+ int32_t deviceId = mNextDeviceId++;
+ Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
#if 0
- LOGI("add device %d: %s\n", mFDCount, deviceName);
- LOGI(" bus: %04x\n"
- " vendor %04x\n"
- " product %04x\n"
- " version %04x\n",
- id.bustype, id.vendor, id.product, id.version);
- LOGI(" name: \"%s\"\n", name);
- LOGI(" location: \"%s\"\n"
- " id: \"%s\"\n", location, idstr);
- LOGI(" version: %d.%d.%d\n",
- version >> 16, (version >> 8) & 0xff, version & 0xff);
+ LOGI("add device %d: %s\n", deviceId, devicePath);
+ LOGI(" bus: %04x\n"
+ " vendor %04x\n"
+ " product %04x\n"
+ " version %04x\n",
+ identifier.bus, identifier.vendor, identifier.product, identifier.version);
+ LOGI(" name: \"%s\"\n", identifier.name.string());
+ LOGI(" location: \"%s\"\n", identifier.location.string());
+ LOGI(" unique id: \"%s\"\n", identifier.uniqueId.string());
+ LOGI(" driver: v%d.%d.%d\n",
+ driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
#endif
- device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
- if (device == NULL) {
- LOGE("out of memory");
- return -1;
- }
-
- device->fd = fd;
- mFDs[mFDCount].fd = fd;
- mFDs[mFDCount].events = POLLIN;
- mFDs[mFDCount].revents = 0;
+ // Load the configuration file for the device.
+ loadConfiguration(device);
// Figure out the kinds of events the device reports.
@@ -784,7 +791,7 @@ int EventHub::openDevice(const char *deviceName) {
bool hasSwitches = false;
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
- //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
+ //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
if (test_bit(i, sw_bitmask)) {
hasSwitches = true;
if (mSwitches[i] == 0) {
@@ -798,38 +805,29 @@ int EventHub::openDevice(const char *deviceName) {
}
#endif
- if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
- // a more descriptive name
- device->name = name;
-
- // Configure the keymap for the device.
+ if ((device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) {
+ // Load the virtual keys for the touch screen, if any.
+ // We do this now so that we can make sure to load the keymap if necessary.
+ status_t status = loadVirtualKeyMap(device);
+ if (!status) {
+ device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+ }
+ }
- configureKeyMap(device);
+ if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
+ // Load the keymap for the device.
+ status_t status = loadKeyMap(device);
- // Tell the world about the devname (the descriptive name)
- if (!mHaveFirstKeyboard && !device->keyMapInfo.isDefaultKeyMap && strstr(name, "-keypad")) {
- // the built-in keyboard has a well-known device ID of 0,
- // this device better not go away.
- mHaveFirstKeyboard = true;
- mFirstKeyboardId = device->id;
- setKeyboardProperties(device, true);
- } else {
- // ensure mFirstKeyboardId is set to -something-.
- if (mFirstKeyboardId == -1) {
- mFirstKeyboardId = device->id;
- setKeyboardProperties(device, true);
- }
- }
+ // Set system properties for the keyboard.
setKeyboardProperties(device, false);
- // Load the keylayout.
- if (!device->keyMapInfo.keyLayoutFile.isEmpty()) {
- status_t status = KeyLayoutMap::load(device->keyMapInfo.keyLayoutFile,
- &device->layoutMap);
- if (status) {
- LOGE("Error %d loading key layout file '%s'.", status,
- device->keyMapInfo.keyLayoutFile.string());
- }
+ // Register the keyboard as a built-in keyboard if it is eligible.
+ if (!status
+ && mBuiltInKeyboardId == -1
+ && isEligibleBuiltInKeyboard(device->identifier,
+ device->configuration, &device->keyMap)) {
+ mBuiltInKeyboardId = device->id;
+ setKeyboardProperties(device, true);
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
@@ -853,58 +851,87 @@ int EventHub::openDevice(const char *deviceName) {
break;
}
}
-
- LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
- device->id, name,
- device->keyMapInfo.keyLayoutFile.string(),
- device->keyMapInfo.keyCharacterMapFile.string());
}
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
- LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
- close(fd);
+ LOGV("Dropping device: id=%d, path='%s', name='%s'",
+ deviceId, devicePath, device->identifier.name.string());
delete device;
return -1;
}
- LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
- deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
-
- LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n",
- deviceName, device, mFDCount, devid, device->classes);
+ LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
+ "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
+ deviceId, fd, devicePath, device->identifier.name.string(),
+ device->classes,
+ device->configurationFile.string(),
+ device->keyMap.keyLayoutFile.string(),
+ device->keyMap.keyCharacterMapFile.string(),
+ toString(mBuiltInKeyboardId == deviceId));
+
+ struct pollfd pollfd;
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+ mFds.push(pollfd);
+ mDevices.push(device);
- mDevicesById[devid].device = device;
device->next = mOpeningDevices;
mOpeningDevices = device;
- mDevices[mFDCount] = device;
-
- mFDCount++;
return 0;
}
-void EventHub::configureKeyMap(device_t* device) {
- android::resolveKeyMap(device->name, device->keyMapInfo);
+void EventHub::loadConfiguration(Device* device) {
+ device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
+ device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+ if (device->configurationFile.isEmpty()) {
+ LOGD("No input device configuration file found for device '%s'.",
+ device->identifier.name.string());
+ } else {
+ status_t status = PropertyMap::load(device->configurationFile,
+ &device->configuration);
+ if (status) {
+ LOGE("Error loading input device configuration file for device '%s'. "
+ "Using default configuration.",
+ device->identifier.name.string());
+ }
+ }
+}
+
+status_t EventHub::loadVirtualKeyMap(Device* device) {
+ // The virtual key map is supplied by the kernel as a system board property file.
+ String8 path;
+ path.append("/sys/board_properties/virtualkeys.");
+ path.append(device->identifier.name);
+ if (access(path.string(), R_OK)) {
+ return NAME_NOT_FOUND;
+ }
+ return VirtualKeyMap::load(path, &device->virtualKeyMap);
+}
+
+status_t EventHub::loadKeyMap(Device* device) {
+ return device->keyMap.load(device->identifier, device->configuration);
}
-void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
- int32_t id = firstKeyboard ? 0 : device->id;
- android::setKeyboardProperties(id, device->name, device->keyMapInfo);
+void EventHub::setKeyboardProperties(Device* device, bool builtInKeyboard) {
+ int32_t id = builtInKeyboard ? 0 : device->id;
+ android::setKeyboardProperties(id, device->identifier,
+ device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile);
}
-void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
- int32_t id = firstKeyboard ? 0 : device->id;
+void EventHub::clearKeyboardProperties(Device* device, bool builtInKeyboard) {
+ int32_t id = builtInKeyboard ? 0 : device->id;
android::clearKeyboardProperties(id);
}
-bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
-{
- if (device->keyBitmask == NULL || device->layoutMap == NULL) {
+bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
+ if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
return false;
}
Vector<int32_t> scanCodes;
- device->layoutMap->findScanCodes(keycode, &scanCodes);
+ device->keyMap.keyLayoutMap->findScanCodes(keycode, &scanCodes);
const size_t N = scanCodes.size();
for (size_t i=0; i<N && i<=KEY_MAX; i++) {
int32_t sc = scanCodes.itemAt(i);
@@ -916,29 +943,15 @@ bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
return false;
}
-int EventHub::closeDevice(const char *deviceName) {
+int EventHub::closeDevice(const char *devicePath) {
AutoMutex _l(mLock);
- int i;
- for(i = 1; i < mFDCount; i++) {
- if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
- //LOGD("remove device %d: %s\n", i, deviceName);
- device_t* device = mDevices[i];
-
- LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
- device->path.string(), device->name.string(), device->id,
- mNumDevicesById, mFDCount, mFDs[i].fd, device->classes);
-
- // Clear this device's entry.
- int index = (device->id&ID_MASK);
- mDevicesById[index].device = NULL;
-
- // Close the file descriptor and compact the fd array.
- close(mFDs[i].fd);
- int count = mFDCount - i - 1;
- memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count);
- memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count);
- mFDCount--;
+ for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
+ Device* device = mDevices[i];
+ if (device->path == devicePath) {
+ LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
+ device->path.string(), device->identifier.name.string(), device->id,
+ device->fd, device->classes);
#ifdef EV_SW
for (int j=0; j<EV_SW; j++) {
@@ -947,21 +960,25 @@ int EventHub::closeDevice(const char *deviceName) {
}
}
#endif
-
- device->next = mClosingDevices;
- mClosingDevices = device;
- if (device->id == mFirstKeyboardId) {
+ if (device->id == mBuiltInKeyboardId) {
LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.string(), mFirstKeyboardId);
- mFirstKeyboardId = -1;
+ device->path.string(), mBuiltInKeyboardId);
+ mBuiltInKeyboardId = -1;
clearKeyboardProperties(device, true);
}
clearKeyboardProperties(device, false);
+
+ mFds.removeAt(i);
+ mDevices.removeAt(i);
+ device->close();
+
+ device->next = mClosingDevices;
+ mClosingDevices = device;
return 0;
}
}
- LOGE("remove device: %s not found\n", deviceName);
+ LOGE("remove device: %s not found\n", devicePath);
return -1;
}
@@ -985,7 +1002,7 @@ int EventHub::readNotify(int nfd) {
}
//printf("got %d bytes of event information\n", res);
- strcpy(devname, device_path);
+ strcpy(devname, DEVICE_PATH);
filename = devname + strlen(devname);
*filename++ = '/';
@@ -1009,7 +1026,6 @@ int EventHub::readNotify(int nfd) {
return 0;
}
-
int EventHub::scanDir(const char *dirname)
{
char devname[PATH_MAX];
@@ -1040,30 +1056,34 @@ void EventHub::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
- dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard));
- dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId);
+ dump.appendFormat(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
dump.append(INDENT "Devices:\n");
- for (int i = 0; i < mNumDevicesById; i++) {
- const device_t* device = mDevicesById[i].device;
+ for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
+ const Device* device = mDevices[i];
if (device) {
- if (mFirstKeyboardId == device->id) {
- dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n",
- device->id, device->name.string());
+ if (mBuiltInKeyboardId == device->id) {
+ dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
+ device->id, device->identifier.name.string());
} else {
- dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string());
+ dump.appendFormat(INDENT2 "%d: %s\n", device->id,
+ device->identifier.name.string());
}
dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
- dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n",
- toString(device->keyMapInfo.isDefaultKeyMap));
- dump.appendFormat(INDENT3 "KeyMapName: %s\n",
- device->keyMapInfo.keyMapName.string());
+ dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
+ dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
+ dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
+ "product=0x%04x, version=0x%04x\n",
+ device->identifier.bus, device->identifier.vendor,
+ device->identifier.product, device->identifier.version);
dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
- device->keyMapInfo.keyLayoutFile.string());
+ device->keyMap.keyLayoutFile.string());
dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
- device->keyMapInfo.keyCharacterMapFile.string());
+ device->keyMap.keyCharacterMapFile.string());
+ dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
+ device->configurationFile.string());
}
}
} // release lock
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 944a79b..b8d59e6 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -7,11 +7,117 @@
//#define LOG_NDEBUG 0
+#define DEBUG_PROBE 0
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
#include <ui/Input.h>
namespace android {
-// class InputEvent
+static const char* CONFIGURATION_FILE_DIR[] = {
+ "idc/",
+ "keylayout/",
+ "keychars/",
+};
+
+static const char* CONFIGURATION_FILE_EXTENSION[] = {
+ ".idc",
+ ".kl",
+ ".kcm",
+};
+
+static bool isValidNameChar(char ch) {
+ return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
+}
+
+static void appendInputDeviceConfigurationFileRelativePath(String8& path,
+ const String8& name, InputDeviceConfigurationFileType type) {
+ path.append(CONFIGURATION_FILE_DIR[type]);
+ for (size_t i = 0; i < name.length(); i++) {
+ char ch = name[i];
+ if (!isValidNameChar(ch)) {
+ ch = '_';
+ }
+ path.append(&ch, 1);
+ }
+ path.append(CONFIGURATION_FILE_EXTENSION[type]);
+}
+
+String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+ const InputDeviceIdentifier& deviceIdentifier,
+ InputDeviceConfigurationFileType type) {
+ if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
+ if (deviceIdentifier.version != 0) {
+ // Try vendor product version.
+ String8 versionPath(getInputDeviceConfigurationFilePathByName(
+ String8::format("Vendor_%04x_Product_%04x_Version_%04x",
+ deviceIdentifier.vendor, deviceIdentifier.product,
+ deviceIdentifier.version),
+ type));
+ if (!versionPath.isEmpty()) {
+ return versionPath;
+ }
+ }
+
+ // Try vendor product.
+ String8 productPath(getInputDeviceConfigurationFilePathByName(
+ String8::format("Vendor_%04x_Product_%04x",
+ deviceIdentifier.vendor, deviceIdentifier.product),
+ type));
+ if (!productPath.isEmpty()) {
+ return productPath;
+ }
+ }
+
+ // Try device name.
+ return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
+}
+
+String8 getInputDeviceConfigurationFilePathByName(
+ const String8& name, InputDeviceConfigurationFileType type) {
+ // Search system repository.
+ String8 path;
+ path.setTo(getenv("ANDROID_ROOT"));
+ path.append("/usr/");
+ appendInputDeviceConfigurationFileRelativePath(path, name, type);
+#if DEBUG_PROBE
+ LOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+#endif
+ if (!access(path.string(), R_OK)) {
+#if DEBUG_PROBE
+ LOGD("Found");
+#endif
+ return path;
+ }
+
+ // Search user repository.
+ // TODO Should only look here if not in safe mode.
+ path.setTo(getenv("ANDROID_DATA"));
+ path.append("/system/devices/");
+ appendInputDeviceConfigurationFileRelativePath(path, name, type);
+#if DEBUG_PROBE
+ LOGD("Probing for system user input device configuration file: path='%s'", path.string());
+#endif
+ if (!access(path.string(), R_OK)) {
+#if DEBUG_PROBE
+ LOGD("Found");
+#endif
+ return path;
+ }
+
+ // Not found.
+#if DEBUG_PROBE
+ LOGD("Probe failed to find input device configuration file: name='%s', type=%d",
+ name.string(), type);
+#endif
+ return String8();
+}
+
+
+// --- InputEvent ---
void InputEvent::initialize(int32_t deviceId, int32_t source) {
mDeviceId = deviceId;
@@ -23,7 +129,7 @@ void InputEvent::initialize(const InputEvent& from) {
mSource = from.mSource;
}
-// class KeyEvent
+// --- KeyEvent ---
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
switch (keyCode) {
@@ -131,7 +237,7 @@ void KeyEvent::initialize(const KeyEvent& from) {
mEventTime = from.mEventTime;
}
-// class MotionEvent
+// --- MotionEvent ---
void MotionEvent::initialize(
int32_t deviceId,
@@ -178,7 +284,7 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) {
mYOffset += yOffset;
}
-// class InputDeviceInfo
+// --- InputDeviceInfo ---
InputDeviceInfo::InputDeviceInfo() {
initialize(-1, String8("uninitialized device info"));
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index db7d448..f1223f1 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -315,7 +315,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
// Throttle it!
#if DEBUG_THROTTLING
LOGD("Throttling - Delaying motion event for "
- "device 0x%x, source 0x%08x by up to %0.3fms.",
+ "device %d, source 0x%08x by up to %0.3fms.",
deviceId, source, (nextTime - currentTime) * 0.000001);
#endif
if (nextTime < *nextWakeupTime) {
@@ -704,7 +704,7 @@ bool InputDispatcher::dispatchKeyLocked(
void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
"repeatCount=%d, downTime=%lld",
prefix,
@@ -767,7 +767,7 @@ bool InputDispatcher::dispatchMotionLocked(
void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, "
"metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
prefix,
@@ -2072,7 +2072,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou
uint32_t policyFlags, int32_t action, int32_t flags,
int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
#if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ LOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
"flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
eventTime, deviceId, source, policyFlags, action, flags,
keyCode, scanCode, metaState, downTime);
@@ -2081,9 +2081,22 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou
return;
}
+ if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
+ policyFlags |= POLICY_FLAG_VIRTUAL;
+ flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ }
+
policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
- keyCode, scanCode, /*byref*/ policyFlags);
+
+ KeyEvent event;
+ event.initialize(deviceId, source, action, flags, keyCode, scanCode,
+ metaState, 0, downTime, eventTime);
+
+ mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
+
+ if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+ flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+ }
bool needWake;
{ // acquire lock
@@ -2107,7 +2120,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime) {
#if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ LOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
"xPrecision=%f, yPrecision=%f, downTime=%lld",
eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
@@ -2289,17 +2302,22 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
return INPUT_EVENT_INJECTION_FAILED;
}
- nsecs_t eventTime = keyEvent->getEventTime();
- int32_t deviceId = keyEvent->getDeviceId();
int32_t flags = keyEvent->getFlags();
- int32_t keyCode = keyEvent->getKeyCode();
- int32_t scanCode = keyEvent->getScanCode();
- mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
- keyCode, scanCode, /*byref*/ policyFlags);
+ if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
+ policyFlags |= POLICY_FLAG_VIRTUAL;
+ }
+
+ mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+
+ if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+ flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+ }
mLock.lock();
- injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(),
- policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(),
+ injectedEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+ keyEvent->getDeviceId(), keyEvent->getSource(),
+ policyFlags, action, flags,
+ keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
keyEvent->getRepeatCount(), keyEvent->getDownTime());
break;
}
@@ -2999,12 +3017,14 @@ void InputDispatcher::doNotifyANRLockedInterruptible(
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
- initializeKeyEvent(&mReusableKeyEvent, entry);
+
+ KeyEvent event;
+ initializeKeyEvent(&event, entry);
mLock.unlock();
bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
- & mReusableKeyEvent, entry->policyFlags);
+ &event, entry->policyFlags);
mLock.lock();
@@ -3025,12 +3045,13 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
&& dispatchEntry->hasForegroundTarget()
&& dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
- initializeKeyEvent(&mReusableKeyEvent, keyEntry);
+ KeyEvent event;
+ initializeKeyEvent(&event, keyEntry);
mLock.unlock();
mPolicy->dispatchUnhandledKey(connection->inputChannel,
- & mReusableKeyEvent, keyEntry->policyFlags);
+ &event, keyEntry->policyFlags);
mLock.lock();
}
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index daff2d0..9cc96ad 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -25,6 +25,7 @@
#include <cutils/log.h>
#include <ui/InputReader.h>
#include <ui/Keyboard.h>
+#include <ui/VirtualKeyMap.h>
#include <stddef.h>
#include <stdlib.h>
@@ -98,64 +99,6 @@ static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
}
-// --- InputDeviceCalibration ---
-
-InputDeviceCalibration::InputDeviceCalibration() {
-}
-
-void InputDeviceCalibration::clear() {
- mProperties.clear();
-}
-
-void InputDeviceCalibration::addProperty(const String8& key, const String8& value) {
- mProperties.add(key, value);
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const {
- ssize_t index = mProperties.indexOfKey(key);
- if (index < 0) {
- return false;
- }
-
- outValue = mProperties.valueAt(index);
- return true;
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const {
- String8 stringValue;
- if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
- }
-
- char* end;
- int value = strtol(stringValue.string(), & end, 10);
- if (*end != '\0') {
- LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.",
- key.string(), stringValue.string());
- return false;
- }
- outValue = value;
- return true;
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const {
- String8 stringValue;
- if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
- }
-
- char* end;
- float value = strtof(stringValue.string(), & end);
- if (*end != '\0') {
- LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.",
- key.string(), stringValue.string());
- return false;
- }
- outValue = value;
- return true;
-}
-
-
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
@@ -179,7 +122,7 @@ void InputReader::loopOnce() {
mEventHub->getEvent(& rawEvent);
#if DEBUG_RAW_EVENTS
- LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
+ LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d",
rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
rawEvent.value);
#endif
@@ -215,9 +158,9 @@ void InputReader::addDevice(int32_t deviceId) {
device->configure();
if (device->isIgnored()) {
- LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
+ LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string());
} else {
- LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+ LOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(),
device->getSources());
}
@@ -259,10 +202,10 @@ void InputReader::removeDevice(int32_t deviceId) {
}
if (device->isIgnored()) {
- LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+ LOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
device->getId(), device->getName().string());
} else {
- LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+ LOGI("Device removed: id=%d, name='%s', sources=0x%08x",
device->getId(), device->getName().string(), device->getSources());
}
@@ -274,8 +217,6 @@ void InputReader::removeDevice(int32_t deviceId) {
InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
InputDevice* device = new InputDevice(this, deviceId, name);
- const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
-
// Switch-like devices.
if (classes & INPUT_DEVICE_CLASS_SWITCH) {
device->addMapper(new SwitchInputMapper(device));
@@ -295,20 +236,19 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui
}
if (keyboardSources != 0) {
- device->addMapper(new KeyboardInputMapper(device,
- associatedDisplayId, keyboardSources, keyboardType));
+ device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
}
// Trackball-like devices.
if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
- device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+ device->addMapper(new TrackballInputMapper(device));
}
// Touchscreen-like devices.
if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
- device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+ device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
- device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+ device->addMapper(new SingleTouchInputMapper(device));
}
return device;
@@ -596,7 +536,7 @@ void InputDevice::dump(String8& dump) {
InputDeviceInfo deviceInfo;
getDeviceInfo(& deviceInfo);
- dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+ dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getName().string());
dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -626,7 +566,7 @@ void InputDevice::addMapper(InputMapper* mapper) {
void InputDevice::configure() {
if (! isIgnored()) {
- mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration);
+ mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
}
mSources = 0;
@@ -792,9 +732,9 @@ int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCod
// --- KeyboardInputMapper ---
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
uint32_t sources, int32_t keyboardType) :
- InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+ InputMapper(device), mSources(sources),
mKeyboardType(keyboardType) {
initializeLocked();
}
@@ -832,7 +772,7 @@ void KeyboardInputMapper::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Keyboard Input Mapper:\n");
- dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dumpParameters(dump);
dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
@@ -840,6 +780,30 @@ void KeyboardInputMapper::dump(String8& dump) {
} // release lock
}
+
+void KeyboardInputMapper::configure() {
+ InputMapper::configure();
+
+ // Configure basic parameters.
+ configureParameters();
+}
+
+void KeyboardInputMapper::configureParameters() {
+ mParameters.orientationAware = false;
+ getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"),
+ mParameters.orientationAware);
+
+ mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
+}
+
+void KeyboardInputMapper::dumpParameters(String8& dump) {
+ dump.append(INDENT3 "Parameters:\n");
+ dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+ mParameters.associatedDisplayId);
+ dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+ toString(mParameters.orientationAware));
+}
+
void KeyboardInputMapper::reset() {
for (;;) {
int32_t keyCode, scanCode;
@@ -896,9 +860,10 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
if (down) {
// Rotate key codes according to orientation if needed.
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
- if (mAssociatedDisplayId >= 0) {
+ if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) {
int32_t orientation;
- if (!getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+ NULL, NULL, & orientation)) {
orientation = InputReaderPolicyInterface::ROTATION_0;
}
@@ -1011,8 +976,8 @@ void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState&
// --- TrackballInputMapper ---
-TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
- InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+TrackballInputMapper::TrackballInputMapper(InputDevice* device) :
+ InputMapper(device) {
mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
@@ -1039,7 +1004,7 @@ void TrackballInputMapper::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Trackball Input Mapper:\n");
- dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dumpParameters(dump);
dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
@@ -1047,6 +1012,29 @@ void TrackballInputMapper::dump(String8& dump) {
} // release lock
}
+void TrackballInputMapper::configure() {
+ InputMapper::configure();
+
+ // Configure basic parameters.
+ configureParameters();
+}
+
+void TrackballInputMapper::configureParameters() {
+ mParameters.orientationAware = false;
+ getDevice()->getConfiguration().tryGetProperty(String8("trackball.orientationAware"),
+ mParameters.orientationAware);
+
+ mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
+}
+
+void TrackballInputMapper::dumpParameters(String8& dump) {
+ dump.append(INDENT3 "Parameters:\n");
+ dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+ mParameters.associatedDisplayId);
+ dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+ toString(mParameters.orientationAware));
+}
+
void TrackballInputMapper::initializeLocked() {
mAccumulator.clear();
@@ -1155,11 +1143,13 @@ void TrackballInputMapper::sync(nsecs_t when) {
pointerCoords.toolMinor = 0;
pointerCoords.orientation = 0;
- if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) {
+ if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
+ && (x != 0.0f || y != 0.0f)) {
// Rotate motion based on display orientation if needed.
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
int32_t orientation;
- if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+ NULL, NULL, & orientation)) {
orientation = InputReaderPolicyInterface::ROTATION_0;
}
@@ -1205,8 +1195,8 @@ int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scan
// --- TouchInputMapper ---
-TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
- InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+TouchInputMapper::TouchInputMapper(InputDevice* device) :
+ InputMapper(device) {
mLocked.surfaceOrientation = -1;
mLocked.surfaceWidth = -1;
mLocked.surfaceHeight = -1;
@@ -1218,7 +1208,15 @@ TouchInputMapper::~TouchInputMapper() {
}
uint32_t TouchInputMapper::getSources() {
- return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+ switch (mParameters.deviceType) {
+ case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+ return AINPUT_SOURCE_TOUCHSCREEN;
+ case Parameters::DEVICE_TYPE_TOUCH_PAD:
+ return AINPUT_SOURCE_TOUCHPAD;
+ default:
+ assert(false);
+ return AINPUT_SOURCE_UNKNOWN;
+ }
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1269,7 +1267,6 @@ void TouchInputMapper::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Touch Input Mapper:\n");
- dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
dumpParameters(dump);
dumpVirtualKeysLocked(dump);
dumpRawAxes(dump);
@@ -1339,14 +1336,50 @@ void TouchInputMapper::configureParameters() {
mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+
+ String8 deviceTypeString;
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
+ deviceTypeString)) {
+ if (deviceTypeString == "touchPad") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ } else if (deviceTypeString != "touchScreen") {
+ LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+ }
+ }
+ bool isTouchScreen = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+
+ mParameters.orientationAware = isTouchScreen;
+ getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+ mParameters.orientationAware);
+
+ mParameters.associatedDisplayId = mParameters.orientationAware || isTouchScreen ? 0 : -1;
}
void TouchInputMapper::dumpParameters(String8& dump) {
- dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+ dump.append(INDENT3 "Parameters:\n");
+
+ switch (mParameters.deviceType) {
+ case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+ dump.append(INDENT4 "DeviceType: touchScreen\n");
+ break;
+ case Parameters::DEVICE_TYPE_TOUCH_PAD:
+ dump.append(INDENT4 "DeviceType: touchPad\n");
+ break;
+ default:
+ assert(false);
+ }
+
+ dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+ mParameters.associatedDisplayId);
+ dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+ toString(mParameters.orientationAware));
+
+ dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n",
toString(mParameters.useBadTouchFilter));
- dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+ dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n",
toString(mParameters.useAveragingTouchFilter));
- dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+ dump.appendFormat(INDENT4 "UseJumpyTouchFilter: %s\n",
toString(mParameters.useJumpyTouchFilter));
}
@@ -1384,17 +1417,20 @@ void TouchInputMapper::dumpRawAxes(String8& dump) {
bool TouchInputMapper::configureSurfaceLocked() {
// Update orientation and dimensions if needed.
- int32_t orientation;
- int32_t width, height;
- if (mAssociatedDisplayId >= 0) {
+ int32_t orientation = InputReaderPolicyInterface::ROTATION_0;
+ int32_t width = mRawAxes.x.getRange();
+ int32_t height = mRawAxes.y.getRange();
+
+ if (mParameters.associatedDisplayId >= 0) {
+ bool wantSize = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ bool wantOrientation = mParameters.orientationAware;
+
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
- if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+ if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+ wantSize ? &width : NULL, wantSize ? &height : NULL,
+ wantOrientation ? &orientation : NULL)) {
return false;
}
- } else {
- orientation = InputReaderPolicyInterface::ROTATION_0;
- width = mRawAxes.x.getRange();
- height = mRawAxes.y.getRange();
}
bool orientationChanged = mLocked.surfaceOrientation != orientation;
@@ -1404,7 +1440,7 @@ bool TouchInputMapper::configureSurfaceLocked() {
bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
if (sizeChanged) {
- LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+ LOGI("Device reconfigured: id=%d, name='%s', display size is now %dx%d",
getDeviceId(), getDeviceName().string(), width, height);
mLocked.surfaceWidth = width;
@@ -1616,9 +1652,8 @@ void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
void TouchInputMapper::configureVirtualKeysLocked() {
assert(mRawAxes.x.valid && mRawAxes.y.valid);
- // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
Vector<VirtualKeyDefinition> virtualKeyDefinitions;
- getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+ getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
mLocked.virtualKeys.clear();
@@ -1686,7 +1721,7 @@ void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
}
void TouchInputMapper::parseCalibration() {
- const InputDeviceCalibration& in = getDevice()->getCalibration();
+ const PropertyMap& in = getDevice()->getConfiguration();
Calibration& out = mCalibration;
// Position
@@ -1972,8 +2007,8 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
}
if (mCalibration.haveToolSizeIsSummed) {
- dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n",
- mCalibration.toolSizeIsSummed);
+ dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %s\n",
+ toString(mCalibration.toolSizeIsSummed));
}
// Pressure
@@ -3157,8 +3192,8 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode
// --- SingleTouchInputMapper ---
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
- TouchInputMapper(device, associatedDisplayId) {
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
+ TouchInputMapper(device) {
initialize();
}
@@ -3286,8 +3321,8 @@ void SingleTouchInputMapper::configureRawAxes() {
// --- MultiTouchInputMapper ---
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
- TouchInputMapper(device, associatedDisplayId) {
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
+ TouchInputMapper(device) {
initialize();
}
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index de76e25..6faa600 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -22,102 +22,173 @@
#include <ui/Keyboard.h>
#include <ui/KeycodeLabels.h>
+#include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <cutils/properties.h>
namespace android {
-static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
- if (keyMapInfo.keyMapName.isEmpty()) {
- keyMapInfo.keyMapName.setTo(keyMapName);
- keyMapInfo.isDefaultKeyMap = defaultKeyMap;
- }
+// --- KeyMap ---
+
+KeyMap::KeyMap() :
+ keyLayoutMap(NULL), keyCharacterMap(NULL) {
+}
+
+KeyMap::~KeyMap() {
+ delete keyLayoutMap;
+ delete keyCharacterMap;
}
-static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
- const char* root = getenv("ANDROID_ROOT");
+status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
+ const PropertyMap* deviceConfiguration) {
+ // Use the configured key layout if available.
+ if (deviceConfiguration) {
+ String8 keyLayoutName;
+ if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
+ keyLayoutName)) {
+ status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
+ if (status == NAME_NOT_FOUND) {
+ LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
+ "it was not found.",
+ deviceIdenfifier.name.string(), keyLayoutName.string());
+ }
+ }
- // TODO Consider also looking somewhere in a writeable partition like /data for a
- // custom keymap supplied by the user for this device.
- bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty();
- if (!haveKeyLayout) {
- keyMapInfo.keyLayoutFile.setTo(root);
- keyMapInfo.keyLayoutFile.append("/usr/keylayout/");
- keyMapInfo.keyLayoutFile.append(keyMapName);
- keyMapInfo.keyLayoutFile.append(".kl");
- if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) {
- keyMapInfo.keyLayoutFile.clear();
- } else {
- haveKeyLayout = true;
+ String8 keyCharacterMapName;
+ if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
+ keyCharacterMapName)) {
+ status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
+ if (status == NAME_NOT_FOUND) {
+ LOGE("Configuration for keyboard device '%s' requested keyboard character "
+ "map '%s' but it was not found.",
+ deviceIdenfifier.name.string(), keyLayoutName.string());
+ }
}
- }
- bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty();
- if (!haveKeyCharacterMap) {
- keyMapInfo.keyCharacterMapFile.setTo(root);
- keyMapInfo.keyCharacterMapFile.append("/usr/keychars/");
- keyMapInfo.keyCharacterMapFile.append(keyMapName);
- keyMapInfo.keyCharacterMapFile.append(".kcm");
- if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) {
- keyMapInfo.keyCharacterMapFile.clear();
- } else {
- haveKeyCharacterMap = true;
+ if (isComplete()) {
+ return OK;
}
}
- if (haveKeyLayout || haveKeyCharacterMap) {
- selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap);
+ // Try searching by device identifier.
+ if (probeKeyMap(deviceIdenfifier, String8::empty())) {
+ return OK;
+ }
+
+ // Fall back on the Generic key map.
+ // TODO Apply some additional heuristics here to figure out what kind of
+ // generic key map to use (US English, etc.) for typical external keyboards.
+ if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
+ return OK;
+ }
+
+ // Try the Virtual key map as a last resort.
+ if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
+ return OK;
}
- return haveKeyLayout && haveKeyCharacterMap;
+
+ // Give up!
+ LOGE("Could not determine key map for device '%s' and no default key maps were found!",
+ deviceIdenfifier.name.string());
+ return NAME_NOT_FOUND;
}
-status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) {
- // As an initial key map name, try using the device name.
- String8 keyMapName(deviceName);
- char* p = keyMapName.lockBuffer(keyMapName.size());
- while (*p) {
- if (*p == ' ') *p = '_';
- p++;
+bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& keyMapName) {
+ if (!haveKeyLayout()) {
+ loadKeyLayout(deviceIdentifier, keyMapName);
}
- keyMapName.unlockBuffer();
+ if (!haveKeyCharacterMap()) {
+ loadKeyCharacterMap(deviceIdentifier, keyMapName);
+ }
+ return isComplete();
+}
- if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK;
+status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& name) {
+ String8 path(getPath(deviceIdentifier, name,
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+ if (path.isEmpty()) {
+ return NAME_NOT_FOUND;
+ }
- // TODO Consider allowing the user to configure a specific key map somehow.
+ KeyLayoutMap* map;
+ status_t status = KeyLayoutMap::load(path, &map);
+ if (status) {
+ return status;
+ }
- // Try the Generic key map.
- // TODO Apply some additional heuristics here to figure out what kind of
- // generic key map to use (US English, etc.).
- keyMapName.setTo("Generic");
- if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK;
+ keyLayoutFile.setTo(path);
+ keyLayoutMap = map;
+ return OK;
+}
- // Give up!
- keyMapName.setTo("unknown");
- selectKeyMap(outKeyMapInfo, keyMapName, true);
- LOGE("Could not determine key map for device '%s'.", deviceName.string());
- return NAME_NOT_FOUND;
+status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& name) {
+ String8 path(getPath(deviceIdentifier, name,
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+ if (path.isEmpty()) {
+ return NAME_NOT_FOUND;
+ }
+
+ KeyCharacterMap* map;
+ status_t status = KeyCharacterMap::load(path, &map);
+ if (status) {
+ return status;
+ }
+
+ keyCharacterMapFile.setTo(path);
+ keyCharacterMap = map;
+ return OK;
}
-void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
- const KeyMapInfo& keyMapInfo) {
+String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
+ const String8& name, InputDeviceConfigurationFileType type) {
+ return name.isEmpty()
+ ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
+ : getInputDeviceConfigurationFilePathByName(name, type);
+}
+
+
+// --- Global functions ---
+
+bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
+ const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
+ if (!keyMap->haveKeyCharacterMap()
+ || keyMap->keyCharacterMap->getKeyboardType()
+ == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+ return false;
+ }
+
+ if (deviceConfiguration) {
+ bool builtIn = false;
+ if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
+ && builtIn) {
+ return true;
+ }
+ }
+
+ return strstr(deviceIdentifier.name.string(), "-keypad");
+}
+
+void setKeyboardProperties(int32_t deviceId,
+ const InputDeviceIdentifier& deviceIdentifier,
+ const String8& keyLayoutFile, const String8& keyCharacterMapFile) {
char propName[PROPERTY_KEY_MAX];
snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
- property_set(propName, deviceName.string());
- snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
- property_set(propName, keyMapInfo.keyMapName.string());
+ property_set(propName, deviceIdentifier.name.string());
snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
- property_set(propName, keyMapInfo.keyLayoutFile.string());
+ property_set(propName, keyLayoutFile.string());
snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
- property_set(propName, keyMapInfo.keyCharacterMapFile.string());
+ property_set(propName, keyCharacterMapFile.string());
}
void clearKeyboardProperties(int32_t deviceId) {
char propName[PROPERTY_KEY_MAX];
snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
property_set(propName, "");
- snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
- property_set(propName, "");
snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
property_set(propName, "");
snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
@@ -125,31 +196,24 @@ void clearKeyboardProperties(int32_t deviceId) {
}
status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
- char propName[PROPERTY_KEY_MAX];
- char fn[PROPERTY_VALUE_MAX];
- snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
- if (property_get(propName, fn, "") > 0) {
- outKeyCharacterMapFile.setTo(fn);
- return OK;
- }
-
- const char* root = getenv("ANDROID_ROOT");
- char path[PATH_MAX];
- if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
- snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root);
- if (!access(path, R_OK)) {
- outKeyCharacterMapFile.setTo(path);
+ if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) {
+ char propName[PROPERTY_KEY_MAX];
+ char fn[PROPERTY_VALUE_MAX];
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
+ if (property_get(propName, fn, "") > 0) {
+ outKeyCharacterMapFile.setTo(fn);
return OK;
}
}
- snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root);
- if (!access(path, R_OK)) {
- outKeyCharacterMapFile.setTo(path);
+ // Default to Virtual since the keyboard does not appear to be installed.
+ outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"),
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+ if (!outKeyCharacterMapFile.isEmpty()) {
return OK;
}
- LOGE("Can't find any key character map files (also tried %s)", path);
+ LOGE("Can't find any key character map files including the Virtual key map!");
return NAME_NOT_FOUND;
}
diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/ui/VirtualKeyMap.cpp
new file mode 100644
index 0000000..e756cdd
--- /dev/null
+++ b/libs/ui/VirtualKeyMap.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VirtualKeyMap"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ui/VirtualKeyMap.h>
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+#include <utils/Timers.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:";
+
+
+// --- VirtualKeyMap ---
+
+VirtualKeyMap::VirtualKeyMap() {
+}
+
+VirtualKeyMap::~VirtualKeyMap() {
+}
+
+status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
+ *outMap = NULL;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ LOGE("Error %d opening virtual key map file %s.", status, filename.string());
+ } else {
+ VirtualKeyMap* map = new VirtualKeyMap();
+ if (!map) {
+ LOGE("Error allocating virtual key map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+
+// --- VirtualKeyMap::Parser ---
+
+VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer) {
+}
+
+VirtualKeyMap::Parser::~Parser() {
+}
+
+status_t VirtualKeyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ // Multiple keys can appear on one line or they can be broken up across multiple lines.
+ do {
+ String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
+ if (token != "0x01") {
+ LOGE("%s: Unknown virtual key type, expected 0x01.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ VirtualKeyDefinition defn;
+ bool success = parseNextIntField(&defn.scanCode)
+ && parseNextIntField(&defn.centerX)
+ && parseNextIntField(&defn.centerY)
+ && parseNextIntField(&defn.width)
+ && parseNextIntField(&defn.height);
+ if (!success) {
+ LOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+#if DEBUG_PARSER
+ LOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "
+ "width=%d, height=%d",
+ defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
+#endif
+ mMap->mVirtualKeys.push(defn);
+ } while (consumeFieldDelimiterAndSkipWhitespace());
+
+ if (!mTokenizer->isEol()) {
+ LOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+ }
+
+ mTokenizer->nextLine();
+ }
+
+ return NO_ERROR;
+}
+
+bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (mTokenizer->peekChar() == ':') {
+ mTokenizer->nextChar();
+ mTokenizer->skipDelimiters(WHITESPACE);
+ return true;
+ }
+ return false;
+}
+
+bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
+ if (!consumeFieldDelimiterAndSkipWhitespace()) {
+ return false;
+ }
+
+ String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
+ char* end;
+ *outValue = strtol(token.string(), &end, 0);
+ if (token.isEmpty() || *end != '\0') {
+ LOGE("Expected an integer, got '%s'.", token.string());
+ return false;
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
index f352dbf..68f9037 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -54,9 +54,7 @@ private:
return 60;
}
- virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
- uint32_t& policyFlags) {
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
}
virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index ded0225..d6c2cbd 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -42,8 +42,6 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface {
KeyedVector<int32_t, DisplayInfo> mDisplayInfos;
bool mFilterTouchEvents;
bool mFilterJumpyTouchEvents;
- KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions;
- KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations;
Vector<String8> mExcludedDeviceNames;
protected:
@@ -76,29 +74,6 @@ public:
mFilterJumpyTouchEvents = enabled;
}
- void addInputDeviceCalibration(const String8& deviceName,
- const InputDeviceCalibration& calibration) {
- mInputDeviceCalibrations.add(deviceName, calibration);
- }
-
- void addInputDeviceCalibrationProperty(const String8& deviceName,
- const String8& key, const String8& value) {
- ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
- if (index < 0) {
- index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration());
- }
- mInputDeviceCalibrations.editValueAt(index).addProperty(key, value);
- }
-
- void addVirtualKeyDefinition(const String8& deviceName,
- const VirtualKeyDefinition& definition) {
- if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) {
- mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>());
- }
-
- mVirtualKeyDefinitions.editValueFor(deviceName).push(definition);
- }
-
void addExcludedDeviceName(const String8& deviceName) {
mExcludedDeviceNames.push(deviceName);
}
@@ -131,22 +106,6 @@ private:
return mFilterJumpyTouchEvents;
}
- virtual void getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
- ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName);
- if (index >= 0) {
- outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index));
- }
- }
-
- virtual void getInputDeviceCalibration(const String8& deviceName,
- InputDeviceCalibration& outCalibration) {
- ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
- if (index >= 0) {
- outCalibration = mInputDeviceCalibrations.valueAt(index);
- }
- }
-
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
}
@@ -371,12 +330,14 @@ class FakeEventHub : public EventHubInterface {
struct Device {
String8 name;
uint32_t classes;
+ PropertyMap configuration;
KeyedVector<int, RawAbsoluteAxisInfo> axes;
KeyedVector<int32_t, int32_t> keyCodeStates;
KeyedVector<int32_t, int32_t> scanCodeStates;
KeyedVector<int32_t, int32_t> switchStates;
KeyedVector<int32_t, KeyInfo> keys;
KeyedVector<int32_t, bool> leds;
+ Vector<VirtualKeyDefinition> virtualKeys;
Device(const String8& name, uint32_t classes) :
name(name), classes(classes) {
@@ -415,6 +376,11 @@ public:
enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0);
}
+ void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+ Device* device = getDevice(deviceId);
+ device->configuration.addProperty(key, value);
+ }
+
void addAxis(int32_t deviceId, int axis,
int32_t minValue, int32_t maxValue, int flat, int fuzz) {
Device* device = getDevice(deviceId);
@@ -465,6 +431,11 @@ public:
return mExcludedDevices;
}
+ void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
+ Device* device = getDevice(deviceId);
+ device->virtualKeys.push(definition);
+ }
+
void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) {
RawEvent event;
@@ -499,6 +470,13 @@ private:
return device ? device->name : String8("unknown");
}
+ virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ *outConfiguration = device->configuration;
+ }
+ }
+
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const {
Device* device = getDevice(deviceId);
@@ -613,6 +591,16 @@ private:
}
}
+ virtual void getVirtualKeyDefinitions(int32_t deviceId,
+ Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ outVirtualKeys.clear();
+
+ Device* device = getDevice(deviceId);
+ if (device) {
+ outVirtualKeys.appendVector(device->virtualKeys);
+ }
+ }
+
virtual void dump(String8& dump) {
}
};
@@ -1208,9 +1196,7 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
// Configuration.
- InputDeviceCalibration calibration;
- calibration.addProperty(String8("key"), String8("value"));
- mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration);
+ mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
@@ -1231,8 +1217,8 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe
mDevice->configure();
String8 propertyValue;
- ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue))
- << "Device should have read calibration during configuration phase.";
+ ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
+ << "Device should have read configuration during configuration phase.";
ASSERT_STREQ("value", propertyValue.string());
ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
@@ -1329,9 +1315,8 @@ protected:
mFakeEventHub.clear();
}
- void prepareCalibration(const char* key, const char* value) {
- mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME),
- String8(key), String8(value));
+ void addConfigurationProperty(const char* key, const char* value) {
+ mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
}
void addMapperAndConfigure(InputMapper* mapper) {
@@ -1448,7 +1433,7 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
TEST_F(KeyboardInputMapperTest, GetSources) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1456,7 +1441,7 @@ TEST_F(KeyboardInputMapperTest, GetSources) {
}
TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1493,7 +1478,7 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
}
TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1513,7 +1498,7 @@ TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp)
}
TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1558,7 +1543,7 @@ TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) {
}
TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1597,11 +1582,14 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
}
-TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1612,9 +1600,10 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotate
KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
}
-TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID,
+TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addConfigurationProperty("keyboard.orientationAware", "1");
addMapperAndConfigure(mapper);
mFakePolicy->setDisplayInfo(DISPLAY_ID,
@@ -1689,7 +1678,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad)
}
TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1701,7 +1690,7 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
}
TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1713,7 +1702,7 @@ TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
}
TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1731,7 +1720,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds)
mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
@@ -1830,14 +1819,14 @@ void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper,
}
TEST_F(TrackballInputMapperTest, GetSources) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
}
TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
InputDeviceInfo info;
@@ -1850,7 +1839,7 @@ TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) {
}
TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
@@ -1898,7 +1887,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaS
}
TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
FakeInputDispatcher::NotifyMotionArgs args;
@@ -1922,7 +1911,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
}
TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
FakeInputDispatcher::NotifyMotionArgs args;
@@ -1943,7 +1932,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
}
TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
FakeInputDispatcher::NotifyMotionArgs args;
@@ -1978,7 +1967,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates)
}
TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
FakeInputDispatcher::NotifyMotionArgs args;
@@ -1998,7 +1987,7 @@ TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeBu
}
TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
FakeInputDispatcher::NotifyMotionArgs args;
@@ -2016,10 +2005,13 @@ TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
}
-TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+TEST_F(TrackballInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
addMapperAndConfigure(mapper);
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -2030,8 +2022,9 @@ TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotat
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
}
-TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) {
- TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID);
+TEST_F(TrackballInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+ addConfigurationProperty("trackball.orientationAware", "1");
addMapperAndConfigure(mapper);
mFakePolicy->setDisplayInfo(DISPLAY_ID,
@@ -2152,8 +2145,8 @@ void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
}
void TouchInputMapperTest::prepareVirtualKeys() {
- mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]);
- mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]);
+ mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
+ mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE);
}
@@ -2232,24 +2225,26 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
}
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1);
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchPad_ReturnsTouchPad) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareAxes(POSITION);
+ addConfigurationProperty("touch.displayType", "touchPad");
addMapperAndConfigure(mapper);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
}
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchScreen_ReturnsTouchScreen) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareAxes(POSITION);
+ addConfigurationProperty("touch.displayType", "touchScreen");
addMapperAndConfigure(mapper);
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
}
TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2276,7 +2271,7 @@ TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
}
TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2303,7 +2298,7 @@ TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
}
TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2319,7 +2314,7 @@ TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) {
// Note: Ideally we should send cancels but the implementation is more straightforward
// with up and this will only happen if a device is forcibly removed.
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2352,7 +2347,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) {
}
TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2378,7 +2373,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens
}
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2427,7 +2422,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor
}
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2541,7 +2536,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB
}
TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2609,7 +2604,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMoves
}
TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -2691,8 +2686,30 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
}
-TEST_F(SingleTouchInputMapperTest, Process_Rotation) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.orientationAware", "0");
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Rotation 90.
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_90);
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(50, args.pointerCoords[0].x, 1);
+ ASSERT_NEAR(75, args.pointerCoords[0].y, 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareAxes(POSITION);
addMapperAndConfigure(mapper);
@@ -2752,7 +2769,7 @@ TEST_F(SingleTouchInputMapperTest, Process_Rotation) {
}
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | PRESSURE | TOOL);
addMapperAndConfigure(mapper);
@@ -2884,7 +2901,7 @@ void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -3135,7 +3152,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackin
}
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | ID);
prepareVirtualKeys();
@@ -3295,7 +3312,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
}
TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR);
addMapperAndConfigure(mapper);
@@ -3340,11 +3357,11 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
}
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | MINOR);
- prepareCalibration("touch.touchSize.calibration", "geometric");
- prepareCalibration("touch.toolSize.calibration", "geometric");
+ addConfigurationProperty("touch.touchSize.calibration", "geometric");
+ addConfigurationProperty("touch.toolSize.calibration", "geometric");
addMapperAndConfigure(mapper);
// These calculations are based on the input device calibration documentation.
@@ -3381,17 +3398,17 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration)
}
TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
- prepareCalibration("touch.touchSize.calibration", "pressure");
- prepareCalibration("touch.toolSize.calibration", "linear");
- prepareCalibration("touch.toolSize.linearScale", "10");
- prepareCalibration("touch.toolSize.linearBias", "160");
- prepareCalibration("touch.toolSize.isSummed", "1");
- prepareCalibration("touch.pressure.calibration", "amplitude");
- prepareCalibration("touch.pressure.source", "touch");
- prepareCalibration("touch.pressure.scale", "0.01");
+ addConfigurationProperty("touch.touchSize.calibration", "pressure");
+ addConfigurationProperty("touch.toolSize.calibration", "linear");
+ addConfigurationProperty("touch.toolSize.linearScale", "10");
+ addConfigurationProperty("touch.toolSize.linearBias", "160");
+ addConfigurationProperty("touch.toolSize.isSummed", "1");
+ addConfigurationProperty("touch.pressure.calibration", "amplitude");
+ addConfigurationProperty("touch.pressure.source", "touch");
+ addConfigurationProperty("touch.pressure.scale", "0.01");
addMapperAndConfigure(mapper);
// These calculations are based on the input device calibration documentation.
@@ -3437,18 +3454,18 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinear
}
TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
- prepareCalibration("touch.touchSize.calibration", "pressure");
- prepareCalibration("touch.toolSize.calibration", "area");
- prepareCalibration("touch.toolSize.areaScale", "22");
- prepareCalibration("touch.toolSize.areaBias", "1");
- prepareCalibration("touch.toolSize.linearScale", "9.2");
- prepareCalibration("touch.toolSize.linearBias", "3");
- prepareCalibration("touch.pressure.calibration", "amplitude");
- prepareCalibration("touch.pressure.source", "touch");
- prepareCalibration("touch.pressure.scale", "0.01");
+ addConfigurationProperty("touch.touchSize.calibration", "pressure");
+ addConfigurationProperty("touch.toolSize.calibration", "area");
+ addConfigurationProperty("touch.toolSize.areaScale", "22");
+ addConfigurationProperty("touch.toolSize.areaBias", "1");
+ addConfigurationProperty("touch.toolSize.linearScale", "9.2");
+ addConfigurationProperty("touch.toolSize.linearBias", "3");
+ addConfigurationProperty("touch.pressure.calibration", "amplitude");
+ addConfigurationProperty("touch.pressure.source", "touch");
+ addConfigurationProperty("touch.pressure.scale", "0.01");
addMapperAndConfigure(mapper);
// These calculations are based on the input device calibration documentation.
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 9c01aea..8bd833b 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -28,6 +28,7 @@ commonSources:= \
Flattenable.cpp \
ObbFile.cpp \
Pool.cpp \
+ PropertyMap.cpp \
RefBase.cpp \
ResourceTypes.cpp \
SharedBuffer.cpp \
diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp
index e1ba9b2..f1f8bda 100644
--- a/libs/utils/FileMap.cpp
+++ b/libs/utils/FileMap.cpp
@@ -63,16 +63,18 @@ FileMap::~FileMap(void)
free(mFileName);
}
#ifdef HAVE_POSIX_FILEMAP
- if (munmap(mBasePtr, mBaseLength) != 0) {
+ if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
}
#endif
#ifdef HAVE_WIN32_FILEMAP
- if ( UnmapViewOfFile(mBasePtr) == 0) {
+ if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr,
GetLastError() );
}
- CloseHandle(mFileMapping);
+ if (mFileMapping != INVALID_HANDLE_VALUE) {
+ CloseHandle(mFileMapping);
+ }
CloseHandle(mFileHandle);
#endif
}
diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp
new file mode 100644
index 0000000..fd7edec
--- /dev/null
+++ b/libs/utils/PropertyMap.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PropertyMap"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/PropertyMap.h>
+#include <utils/Log.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {
+}
+
+PropertyMap::~PropertyMap() {
+}
+
+void PropertyMap::clear() {
+ mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+ return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+ int32_t intValue;
+ if (!tryGetProperty(key, intValue)) {
+ return false;
+ }
+
+ outValue = intValue;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), & end, 10);
+ if (*end != '\0') {
+ LOGW("Property key '%s' has invalid value '%s'. Expected an integer.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), & end);
+ if (*end != '\0') {
+ LOGW("Property key '%s' has invalid value '%s'. Expected a float.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+ *outMap = NULL;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ LOGE("Error %d opening property file %s.", status, filename.string());
+ } else {
+ PropertyMap* map = new PropertyMap();
+ if (!map) {
+ LOGE("Error allocating property map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ LOGD("Parsed property file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer) {
+}
+
+PropertyMap::Parser::~Parser() {
+}
+
+status_t PropertyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (keyToken.isEmpty()) {
+ LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (mTokenizer->nextChar() != '=') {
+ LOGE("%s: Expected '=' between property key and value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+ if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+ LOGE("%s: Found reserved character '\\' or '\"' in property value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ LOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+
+ if (mMap->hasProperty(keyToken)) {
+ LOGE("%s: Duplicate property value for key '%s'.",
+ mTokenizer->getLocation().string(), keyToken.string());
+ return BAD_VALUE;
+ }
+
+ mMap->addProperty(keyToken, valueToken);
+ }
+
+ mTokenizer->nextLine();
+ }
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index e531a2a..0bc5aff 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -195,6 +195,24 @@ String8::~String8()
SharedBuffer::bufferFromData(mString)->release();
}
+String8 String8::format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ String8 result(formatV(fmt, args));
+
+ va_end(args);
+ return result;
+}
+
+String8 String8::formatV(const char* fmt, va_list args)
+{
+ String8 result;
+ result.appendFormatV(fmt, args);
+ return result;
+}
+
void String8::clear() {
SharedBuffer::bufferFromData(mString)->release();
mString = getEmptyString();
diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp
index 9251973..b3445b7 100644
--- a/libs/utils/Tokenizer.cpp
+++ b/libs/utils/Tokenizer.cpp
@@ -35,16 +35,16 @@ static inline bool isDelimiter(char ch, const char* delimiters) {
return strchr(delimiters, ch) != NULL;
}
-
-Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap,
- const char* buffer, size_t length) :
- mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length),
- mCurrent(buffer), mLineNumber(1) {
+Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) :
+ mFilename(filename), mFileMap(fileMap),
+ mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) {
}
Tokenizer::~Tokenizer() {
if (mFileMap) {
mFileMap->release();
+ } else {
+ delete[] mBuffer;
}
}
@@ -63,22 +63,33 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
} else {
size_t length = size_t(stat.st_size);
+
FileMap* fileMap = new FileMap();
- if (!fileMap->create(NULL, fd, 0, length, true)) {
- result = NO_MEMORY;
- LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno));
- } else {
+ char* buffer;
+ if (fileMap->create(NULL, fd, 0, length, true)) {
fileMap->advise(FileMap::SEQUENTIAL);
-
- *outTokenizer = new Tokenizer(filename, fileMap,
- static_cast<const char*>(fileMap->getDataPtr()), length);
- if (!*outTokenizer) {
- result = NO_MEMORY;
- LOGE("Error allocating tokenizer for file=%s.", filename.string());
+ buffer = static_cast<char*>(fileMap->getDataPtr());
+ } else {
+ fileMap->release();
+ fileMap = NULL;
+
+ // Fall back to reading into a buffer since we can't mmap files in sysfs.
+ // The length we obtained from stat is wrong too (it will always be 4096)
+ // so we must trust that read will read the entire file.
+ buffer = new char[length];
+ ssize_t nrd = read(fd, buffer, length);
+ if (nrd < 0) {
+ result = -errno;
+ LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
+ delete[] buffer;
+ buffer = NULL;
+ } else {
+ length = size_t(nrd);
}
}
- if (result) {
- fileMap->release();
+
+ if (!result) {
+ *outTokenizer = new Tokenizer(filename, fileMap, buffer, length);
}
}
close(fd);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 0e689e4..beac04c 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -194,6 +194,7 @@ public class MediaFile {
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
+ addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST);
addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST);
addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl");
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 69b872b..e17a640 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -992,7 +992,12 @@ public class MediaScanner
private boolean inScanDirectory(String path, String[] directories) {
for (int i = 0; i < directories.length; i++) {
- if (path.startsWith(directories[i])) {
+ String directory = directories[i];
+ if (mExternalStoragePath != null && directory.equals(mMediaStoragePath)) {
+ // database paths use external storage prefix
+ directory = mExternalStoragePath;
+ }
+ if (path.startsWith(directory)) {
return true;
}
}
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 0387989..139a6ea 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -33,6 +33,7 @@ import android.provider.MediaStore.MediaColumns;
import android.util.Log;
import java.io.File;
+import java.util.HashMap;
/**
* {@hide}
@@ -48,6 +49,14 @@ public class MtpDatabase {
private final String mMediaStoragePath;
private final String mExternalStoragePath;
+ // cached property groups for single properties
+ private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
+ = new HashMap<Integer, MtpPropertyGroup>();
+
+ // cached property groups for all properties for a given format
+ private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat
+ = new HashMap<Integer, MtpPropertyGroup>();
+
// true if the database has been modified in the current MTP session
private boolean mDatabaseModified;
@@ -386,6 +395,43 @@ public class MtpDatabase {
MtpConstants.PROPERTY_DESCRIPTION,
};
+ static final int[] ALL_PROPERTIES = {
+ // NOTE must match FILE_PROPERTIES above
+ MtpConstants.PROPERTY_STORAGE_ID,
+ MtpConstants.PROPERTY_OBJECT_FORMAT,
+ MtpConstants.PROPERTY_PROTECTION_STATUS,
+ MtpConstants.PROPERTY_OBJECT_SIZE,
+ MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+ MtpConstants.PROPERTY_DATE_MODIFIED,
+ MtpConstants.PROPERTY_PARENT_OBJECT,
+ MtpConstants.PROPERTY_PERSISTENT_UID,
+ MtpConstants.PROPERTY_NAME,
+ MtpConstants.PROPERTY_DISPLAY_NAME,
+ MtpConstants.PROPERTY_DATE_ADDED,
+
+ // image specific properties
+ MtpConstants.PROPERTY_DESCRIPTION,
+
+ // audio specific properties
+ MtpConstants.PROPERTY_ARTIST,
+ MtpConstants.PROPERTY_ALBUM_NAME,
+ MtpConstants.PROPERTY_ALBUM_ARTIST,
+ MtpConstants.PROPERTY_TRACK,
+ MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
+ MtpConstants.PROPERTY_DURATION,
+ MtpConstants.PROPERTY_GENRE,
+ MtpConstants.PROPERTY_COMPOSER,
+
+ // video specific properties
+ MtpConstants.PROPERTY_ARTIST,
+ MtpConstants.PROPERTY_ALBUM_NAME,
+ MtpConstants.PROPERTY_DURATION,
+ MtpConstants.PROPERTY_DESCRIPTION,
+
+ // image specific properties
+ MtpConstants.PROPERTY_DESCRIPTION,
+ };
+
private int[] getSupportedObjectProperties(int format) {
switch (format) {
case MtpConstants.FORMAT_MP3:
@@ -403,6 +449,8 @@ public class MtpDatabase {
case MtpConstants.FORMAT_PNG:
case MtpConstants.FORMAT_BMP:
return IMAGE_PROPERTIES;
+ case 0:
+ return ALL_PROPERTIES;
default:
return FILE_PROPERTIES;
}
@@ -415,324 +463,32 @@ public class MtpDatabase {
};
}
- private String queryString(int id, String column) {
- Cursor c = null;
- try {
- // for now we are only reading properties from the "objects" table
- c = mMediaProvider.query(mObjectsUri,
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
- if (c != null && c.moveToNext()) {
- return c.getString(1);
- } else {
- return "";
- }
- } catch (Exception e) {
- return null;
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
-
- private String queryAudio(int id, String column) {
- Cursor c = null;
- try {
- c = mMediaProvider.query(Audio.Media.getContentUri(mVolumeName),
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
- if (c != null && c.moveToNext()) {
- return c.getString(1);
- } else {
- return "";
- }
- } catch (Exception e) {
- return null;
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
- private String queryGenre(int id) {
- Cursor c = null;
- try {
- Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
- c = mMediaProvider.query(uri,
- new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
- null, null, null);
- if (c != null && c.moveToNext()) {
- return c.getString(1);
- } else {
- return "";
- }
- } catch (Exception e) {
- Log.e(TAG, "queryGenre exception", e);
- return null;
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
-
- private Long queryLong(int id, String column) {
- Cursor c = null;
- try {
- // for now we are only reading properties from the "objects" table
- c = mMediaProvider.query(mObjectsUri,
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
- if (c != null && c.moveToNext()) {
- return new Long(c.getLong(1));
- }
- } catch (Exception e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
- }
-
- private String nameFromPath(String path) {
- // extract name from full path
- int start = 0;
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0) {
- start = lastSlash + 1;
- }
- int end = path.length();
- if (end - start > 255) {
- end = start + 255;
- }
- return path.substring(start, end);
- }
-
- private MtpPropertyList getObjectPropertyList(int handle, int format, int property,
+ private MtpPropertyList getObjectPropertyList(long handle, int format, long property,
int groupCode, int depth) {
// FIXME - implement group support
- // For now we only support a single property at a time
if (groupCode != 0) {
return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED);
}
- if (depth > 1) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED);
- }
- String column = null;
- int type = MtpConstants.TYPE_UNDEFINED;
-
- switch (property) {
- case MtpConstants.PROPERTY_STORAGE_ID:
- // no query needed until we support multiple storage units
- // for now it is always mStorageID
- type = MtpConstants.TYPE_UINT32;
- break;
- case MtpConstants.PROPERTY_OBJECT_FORMAT:
- column = Files.FileColumns.FORMAT;
- type = MtpConstants.TYPE_UINT16;
- break;
- case MtpConstants.PROPERTY_PROTECTION_STATUS:
- // protection status is always 0
- type = MtpConstants.TYPE_UINT16;
- break;
- case MtpConstants.PROPERTY_OBJECT_SIZE:
- column = Files.FileColumns.SIZE;
- type = MtpConstants.TYPE_UINT64;
- break;
- case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
- column = Files.FileColumns.DATA;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_NAME:
- column = MediaColumns.TITLE;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_DATE_MODIFIED:
- column = Files.FileColumns.DATE_MODIFIED;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_DATE_ADDED:
- column = Files.FileColumns.DATE_ADDED;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
- column = Audio.AudioColumns.YEAR;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_PARENT_OBJECT:
- column = Files.FileColumns.PARENT;
- type = MtpConstants.TYPE_UINT32;
- break;
- case MtpConstants.PROPERTY_PERSISTENT_UID:
- // PUID is concatenation of storageID and object handle
- type = MtpConstants.TYPE_UINT128;
- break;
- case MtpConstants.PROPERTY_DURATION:
- column = Audio.AudioColumns.DURATION;
- type = MtpConstants.TYPE_UINT32;
- break;
- case MtpConstants.PROPERTY_TRACK:
- column = Audio.AudioColumns.TRACK;
- type = MtpConstants.TYPE_UINT16;
- break;
- case MtpConstants.PROPERTY_DISPLAY_NAME:
- column = MediaColumns.DISPLAY_NAME;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_ARTIST:
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_ALBUM_NAME:
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_ALBUM_ARTIST:
- column = Audio.AudioColumns.ALBUM_ARTIST;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_GENRE:
- // genre requires a special query
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_COMPOSER:
- column = Audio.AudioColumns.COMPOSER;
- type = MtpConstants.TYPE_STR;
- break;
- case MtpConstants.PROPERTY_DESCRIPTION:
- column = Images.ImageColumns.DESCRIPTION;
- type = MtpConstants.TYPE_STR;
- break;
- default:
- return new MtpPropertyList(0, MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED);
- }
-
- Cursor c = null;
- try {
- if (column != null) {
- c = mMediaProvider.query(mObjectsUri,
- new String [] { Files.FileColumns._ID, column },
- // depth 0: single record, depth 1: immediate children
- (depth == 0 ? ID_WHERE : PARENT_WHERE),
- new String[] { Integer.toString(handle) }, null);
- if (c == null) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- } else if (depth == 1) {
- c = mMediaProvider.query(mObjectsUri,
- new String [] { Files.FileColumns._ID },
- PARENT_WHERE, new String[] { Integer.toString(handle) }, null);
- if (c == null) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- }
-
- int count = (c == null ? 1 : c.getCount());
- MtpPropertyList result = new MtpPropertyList(count, MtpConstants.RESPONSE_OK);
-
- for (int index = 0; index < count; index++) {
- if (c != null) {
- c.moveToNext();
- }
- if (depth == 1) {
- handle = (int)c.getLong(0);
- }
-
- switch (property) {
- // handle special cases here
- case MtpConstants.PROPERTY_STORAGE_ID:
- result.setProperty(index, handle, property, MtpConstants.TYPE_UINT32,
- mStorageID);
- break;
- case MtpConstants.PROPERTY_PROTECTION_STATUS:
- // protection status is always 0
- result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16, 0);
- break;
- case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
- // special case - need to extract file name from full path
- String value = c.getString(1);
- if (value != null) {
- result.setProperty(index, handle, property, nameFromPath(value));
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- case MtpConstants.PROPERTY_NAME:
- // first try title
- String name = c.getString(1);
- // then try name
- if (name == null) {
- name = queryString(handle, Audio.PlaylistsColumns.NAME);
- }
- // if title and name fail, extract name from full path
- if (name == null) {
- name = queryString(handle, Files.FileColumns.DATA);
- if (name != null) {
- name = nameFromPath(name);
- }
- }
- if (name != null) {
- result.setProperty(index, handle, property, name);
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- case MtpConstants.PROPERTY_DATE_MODIFIED:
- case MtpConstants.PROPERTY_DATE_ADDED:
- // convert from seconds to DateTime
- result.setProperty(index, handle, property, format_date_time(c.getInt(1)));
- break;
- case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
- // release date is stored internally as just the year
- int year = c.getInt(1);
- String dateTime = Integer.toString(year) + "0101T000000";
- result.setProperty(index, handle, property, dateTime);
- break;
- case MtpConstants.PROPERTY_PERSISTENT_UID:
- // PUID is concatenation of storageID and object handle
- long puid = mStorageID;
- puid <<= 32;
- puid += handle;
- result.setProperty(index, handle, property, MtpConstants.TYPE_UINT128, puid);
- break;
- case MtpConstants.PROPERTY_TRACK:
- result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16,
- c.getInt(1) % 1000);
- break;
- case MtpConstants.PROPERTY_ARTIST:
- result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ARTIST));
- break;
- case MtpConstants.PROPERTY_ALBUM_NAME:
- result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ALBUM));
- break;
- case MtpConstants.PROPERTY_GENRE:
- String genre = queryGenre(handle);
- if (genre != null) {
- result.setProperty(index, handle, property, genre);
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- default:
- if (type == MtpConstants.TYPE_STR) {
- result.setProperty(index, handle, property, c.getString(1));
- } else {
- result.setProperty(index, handle, property, type, c.getLong(1));
- }
- }
+ MtpPropertyGroup propertyGroup;
+ if (property == 0xFFFFFFFFL) {
+ propertyGroup = mPropertyGroupsByFormat.get(format);
+ if (propertyGroup == null) {
+ int[] propertyList = getSupportedObjectProperties(format);
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
+ mPropertyGroupsByFormat.put(new Integer(format), propertyGroup);
}
-
- return result;
- } catch (RemoteException e) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR);
- } finally {
- if (c != null) {
- c.close();
+ } else {
+ propertyGroup = mPropertyGroupsByProperty.get(property);
+ if (propertyGroup == null) {
+ int[] propertyList = new int[] { (int)property };
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
+ mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup);
}
}
- // impossible to get here, so no return statement
+
+ return propertyGroup.getPropertyList((int)handle, format, depth, mStorageID);
}
private int renameFile(int handle, String newName) {
@@ -1028,5 +784,4 @@ public class MtpDatabase {
private native final void native_setup();
private native final void native_finalize();
- private native String format_date_time(long seconds);
}
diff --git a/media/java/android/media/MtpPropertyGroup.java b/media/java/android/media/MtpPropertyGroup.java
new file mode 100644
index 0000000..bb733e2
--- /dev/null
+++ b/media/java/android/media/MtpPropertyGroup.java
@@ -0,0 +1,442 @@
+/*
+ * 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.media;
+
+import android.content.IContentProvider;
+import android.database.Cursor;
+import android.net.Uri;
+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.util.Log;
+
+import java.util.ArrayList;
+
+class MtpPropertyGroup {
+
+ private static final String TAG = "MtpPropertyGroup";
+
+ private class Property {
+ // MTP property code
+ int code;
+ // MTP data type
+ int type;
+ // column index for our query
+ int column;
+
+ Property(int code, int type, int column) {
+ this.code = code;
+ this.type = type;
+ this.column = column;
+ }
+ }
+
+ private final MtpDatabase mDatabase;
+ private final IContentProvider mProvider;
+ private final String mVolumeName;
+ private final Uri mUri;
+
+ // list of all properties in this group
+ private final Property[] mProperties;
+
+ // list of columns for database query
+ private String[] mColumns;
+
+ private static final String ID_WHERE = Files.FileColumns._ID + "=?";
+ private static final String ID_FORMAT_WHERE = ID_WHERE + " AND "
+ + Files.FileColumns.FORMAT + "=?";
+ private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
+ private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+ + Files.FileColumns.FORMAT + "=?";
+ // constructs a property group for a list of properties
+ public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String volume,
+ int[] properties) {
+ mDatabase = database;
+ mProvider = provider;
+ mVolumeName = volume;
+ mUri = Files.getMtpObjectsUri(volume);
+
+ int count = properties.length;
+ ArrayList<String> columns = new ArrayList<String>(count);
+ columns.add(Files.FileColumns._ID);
+
+ mProperties = new Property[count];
+ for (int i = 0; i < count; i++) {
+ mProperties[i] = createProperty(properties[i], columns);
+ }
+ count = columns.size();
+ mColumns = new String[count];
+ for (int i = 0; i < count; i++) {
+ mColumns[i] = columns.get(i);
+ }
+ }
+
+ private Property createProperty(int code, ArrayList<String> columns) {
+ String column = null;
+ int type;
+
+ switch (code) {
+ case MtpConstants.PROPERTY_STORAGE_ID:
+ // no query needed until we support multiple storage units
+ type = MtpConstants.TYPE_UINT32;
+ break;
+ case MtpConstants.PROPERTY_OBJECT_FORMAT:
+ column = Files.FileColumns.FORMAT;
+ type = MtpConstants.TYPE_UINT16;
+ break;
+ case MtpConstants.PROPERTY_PROTECTION_STATUS:
+ // protection status is always 0
+ type = MtpConstants.TYPE_UINT16;
+ break;
+ case MtpConstants.PROPERTY_OBJECT_SIZE:
+ column = Files.FileColumns.SIZE;
+ type = MtpConstants.TYPE_UINT64;
+ break;
+ case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
+ column = Files.FileColumns.DATA;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_NAME:
+ column = MediaColumns.TITLE;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_DATE_MODIFIED:
+ column = Files.FileColumns.DATE_MODIFIED;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_DATE_ADDED:
+ column = Files.FileColumns.DATE_ADDED;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
+ column = Audio.AudioColumns.YEAR;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_PARENT_OBJECT:
+ column = Files.FileColumns.PARENT;
+ type = MtpConstants.TYPE_UINT32;
+ break;
+ case MtpConstants.PROPERTY_PERSISTENT_UID:
+ // PUID is concatenation of storageID and object handle
+ type = MtpConstants.TYPE_UINT128;
+ break;
+ case MtpConstants.PROPERTY_DURATION:
+ column = Audio.AudioColumns.DURATION;
+ type = MtpConstants.TYPE_UINT32;
+ break;
+ case MtpConstants.PROPERTY_TRACK:
+ column = Audio.AudioColumns.TRACK;
+ type = MtpConstants.TYPE_UINT16;
+ break;
+ case MtpConstants.PROPERTY_DISPLAY_NAME:
+ column = MediaColumns.DISPLAY_NAME;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_ARTIST:
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_ALBUM_NAME:
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_ALBUM_ARTIST:
+ column = Audio.AudioColumns.ALBUM_ARTIST;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_GENRE:
+ // genre requires a special query
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_COMPOSER:
+ column = Audio.AudioColumns.COMPOSER;
+ type = MtpConstants.TYPE_STR;
+ break;
+ case MtpConstants.PROPERTY_DESCRIPTION:
+ column = Images.ImageColumns.DESCRIPTION;
+ type = MtpConstants.TYPE_STR;
+ break;
+ default:
+ type = MtpConstants.TYPE_UNDEFINED;
+ Log.e(TAG, "unsupported property " + code);
+ break;
+ }
+
+ if (column != null) {
+ columns.add(column);
+ return new Property(code, type, columns.size() - 1);
+ } else {
+ return new Property(code, type, -1);
+ }
+ }
+
+ private String queryString(int id, String column) {
+ Cursor c = null;
+ try {
+ // for now we are only reading properties from the "objects" table
+ c = mProvider.query(mUri,
+ new String [] { Files.FileColumns._ID, column },
+ ID_WHERE, new String[] { Integer.toString(id) }, null);
+ if (c != null && c.moveToNext()) {
+ return c.getString(1);
+ } else {
+ return "";
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ private String queryAudio(int id, String column) {
+ Cursor c = null;
+ try {
+ c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
+ new String [] { Files.FileColumns._ID, column },
+ ID_WHERE, new String[] { Integer.toString(id) }, null);
+ if (c != null && c.moveToNext()) {
+ return c.getString(1);
+ } else {
+ return "";
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ private String queryGenre(int id) {
+ Cursor c = null;
+ try {
+ Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
+ c = mProvider.query(uri,
+ new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
+ null, null, null);
+ if (c != null && c.moveToNext()) {
+ return c.getString(1);
+ } else {
+ return "";
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "queryGenre exception", e);
+ return null;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ private Long queryLong(int id, String column) {
+ Cursor c = null;
+ try {
+ // for now we are only reading properties from the "objects" table
+ c = mProvider.query(mUri,
+ new String [] { Files.FileColumns._ID, column },
+ ID_WHERE, new String[] { Integer.toString(id) }, null);
+ if (c != null && c.moveToNext()) {
+ return new Long(c.getLong(1));
+ }
+ } catch (Exception e) {
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return null;
+ }
+
+ private static String nameFromPath(String path) {
+ // extract name from full path
+ int start = 0;
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ start = lastSlash + 1;
+ }
+ int end = path.length();
+ if (end - start > 255) {
+ end = start + 255;
+ }
+ return path.substring(start, end);
+ }
+
+ MtpPropertyList getPropertyList(int handle, int format, int depth, int storageID) {
+ Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth);
+ if (depth > 1) {
+ // we only support depth 0 and 1
+ // depth 0: single object, depth 1: immediate children
+ return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED);
+ }
+
+ String where;
+ String[] whereArgs;
+ if (format == 0) {
+ whereArgs = new String[] { Integer.toString(handle) };
+ if (depth == 1) {
+ where = PARENT_WHERE;
+ } else {
+ where = ID_WHERE;
+ }
+ } else {
+ whereArgs = new String[] { Integer.toString(handle), Integer.toString(format) };
+ if (depth == 1) {
+ where = PARENT_FORMAT_WHERE;
+ } else {
+ where = ID_FORMAT_WHERE;
+ }
+ }
+
+ Cursor c = null;
+ try {
+ // don't query if not necessary
+ if (depth > 0 || mColumns.length > 1) {
+ c = mProvider.query(mUri, mColumns, where, whereArgs, null);
+ if (c == null) {
+ return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ }
+
+ int count = (c == null ? 1 : c.getCount());
+ MtpPropertyList result = new MtpPropertyList(count * mProperties.length,
+ MtpConstants.RESPONSE_OK);
+
+ // iterate over all objects in the query
+ for (int objectIndex = 0; objectIndex < count; objectIndex++) {
+ if (c != null) {
+ c.moveToNext();
+ }
+ if (depth == 1) {
+ handle = (int)c.getLong(0);
+ }
+
+ // iterate over all properties in the query for the given object
+ for (int propertyIndex = 0; propertyIndex < mProperties.length; propertyIndex++) {
+ Property property = mProperties[propertyIndex];
+ int propertyCode = property.code;
+ int column = property.column;
+
+ // handle some special cases
+ switch (propertyCode) {
+ case MtpConstants.PROPERTY_STORAGE_ID:
+ result.append(handle, propertyCode, MtpConstants.TYPE_UINT32,
+ storageID);
+ break;
+ case MtpConstants.PROPERTY_PROTECTION_STATUS:
+ // protection status is always 0
+ result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
+ break;
+ case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
+ // special case - need to extract file name from full path
+ String value = c.getString(column);
+ if (value != null) {
+ result.append(handle, propertyCode, nameFromPath(value));
+ } else {
+ result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ break;
+ case MtpConstants.PROPERTY_NAME:
+ // first try title
+ String name = c.getString(column);
+ // then try name
+ if (name == null) {
+ name = queryString(handle, Audio.PlaylistsColumns.NAME);
+ }
+ // if title and name fail, extract name from full path
+ if (name == null) {
+ name = queryString(handle, Files.FileColumns.DATA);
+ if (name != null) {
+ name = nameFromPath(name);
+ }
+ }
+ if (name != null) {
+ result.append(handle, propertyCode, name);
+ } else {
+ result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ break;
+ case MtpConstants.PROPERTY_DATE_MODIFIED:
+ case MtpConstants.PROPERTY_DATE_ADDED:
+ // convert from seconds to DateTime
+ result.append(handle, propertyCode, format_date_time(c.getInt(column)));
+ break;
+ case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
+ // release date is stored internally as just the year
+ int year = c.getInt(column);
+ String dateTime = Integer.toString(year) + "0101T000000";
+ result.append(handle, propertyCode, dateTime);
+ break;
+ case MtpConstants.PROPERTY_PERSISTENT_UID:
+ // PUID is concatenation of storageID and object handle
+ long puid = storageID;
+ puid <<= 32;
+ puid += handle;
+ result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid);
+ break;
+ case MtpConstants.PROPERTY_TRACK:
+ result.append(handle, propertyCode, MtpConstants.TYPE_UINT16,
+ c.getInt(column) % 1000);
+ break;
+ case MtpConstants.PROPERTY_ARTIST:
+ result.append(handle, propertyCode,
+ queryAudio(handle, Audio.AudioColumns.ARTIST));
+ break;
+ case MtpConstants.PROPERTY_ALBUM_NAME:
+ result.append(handle, propertyCode,
+ queryAudio(handle, Audio.AudioColumns.ALBUM));
+ break;
+ case MtpConstants.PROPERTY_GENRE:
+ String genre = queryGenre(handle);
+ if (genre != null) {
+ result.append(handle, propertyCode, genre);
+ } else {
+ result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ break;
+ default:
+ if (property.type == MtpConstants.TYPE_STR) {
+ result.append(handle, propertyCode, c.getString(column));
+ } else if (property.type == MtpConstants.TYPE_UNDEFINED) {
+ result.append(handle, propertyCode, property.type, 0);
+ } else {
+ result.append(handle, propertyCode, property.type,
+ c.getLong(column));
+ }
+ break;
+ }
+ }
+ }
+
+ return result;
+ } catch (RemoteException e) {
+ return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ // impossible to get here, so no return statement
+ }
+
+ private native String format_date_time(long seconds);
+}
diff --git a/media/java/android/media/MtpPropertyList.java b/media/java/android/media/MtpPropertyList.java
index f598981..d3f0b34 100644
--- a/media/java/android/media/MtpPropertyList.java
+++ b/media/java/android/media/MtpPropertyList.java
@@ -19,14 +19,14 @@ package android.media;
/**
* Encapsulates the ObjectPropList dataset used by the GetObjectPropList command.
* The fields of this class are read by JNI code in android_media_MtpDatabase.cpp
- *
- * {@hide}
*/
-public class MtpPropertyList {
+class MtpPropertyList {
// number of results returned
- public final int mCount;
+ private int mCount;
+ // maximum number of results
+ private final int mMaxCount;
// result code for GetObjectPropList
public int mResult;
// list of object handles (first field in quadruplet)
@@ -41,18 +41,19 @@ public class MtpPropertyList {
public String[] mStringValues;
// constructor only called from MtpDatabase
- public MtpPropertyList(int count, int result) {
- mCount = count;
+ public MtpPropertyList(int maxCount, int result) {
+ mMaxCount = maxCount;
mResult = result;
- mObjectHandles = new int[count];
- mPropertyCodes = new int[count];
- mDataTypes = new int[count];
+ mObjectHandles = new int[maxCount];
+ mPropertyCodes = new int[maxCount];
+ mDataTypes = new int[maxCount];
// mLongValues and mStringValues are created lazily since both might not be necessary
}
- public void setProperty(int index, int handle, int property, int type, long value) {
+ public void append(int handle, int property, int type, long value) {
+ int index = mCount++;
if (mLongValues == null) {
- mLongValues = new long[mCount];
+ mLongValues = new long[mMaxCount];
}
mObjectHandles[index] = handle;
mPropertyCodes[index] = property;
@@ -60,9 +61,10 @@ public class MtpPropertyList {
mLongValues[index] = value;
}
- public void setProperty(int index, int handle, int property, String value) {
+ public void append(int handle, int property, String value) {
+ int index = mCount++;
if (mStringValues == null) {
- mStringValues = new String[mCount];
+ mStringValues = new String[mMaxCount];
}
mObjectHandles[index] = handle;
mPropertyCodes[index] = property;
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 0c48556..41309dc 100755
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -43,10 +43,8 @@ import android.os.Message;
* <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li>
* </ul>
* <p>The length of the capture can be retrieved or specified by calling respectively
- * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. Note that the size of the FFT
- * is half of the specified capture size but both sides of the spectrum are returned yielding in a
- * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
- * returned by {@link #getCaptureSizeRange()}.
+ * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. The capture size must be a
+ * power of 2 in the range returned by {@link #getCaptureSizeRange()}.
* <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and
* {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by
* use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
@@ -333,11 +331,43 @@ public class Visualizer {
}
}
/**
- * Returns a frequency capture of currently playing audio content. The capture is a 8-bit
- * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both
- * sides of the spectrum are returned yielding in a number of bytes equal to the capture size.
- * {@see #getCaptureSize()}.
+ * Returns a frequency capture of currently playing audio content.
* <p>This method must be called when the Visualizer is enabled.
+ * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of
+ * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and
+ * imaginary parts of a number of frequency points equal to half of the capture size plus one.
+ * <p>Note: only the real part is returned for the first point (DC) and the last point
+ * (sampling frequency / 2).
+ * <p>The layout in the returned byte array is as follows:
+ * <ul>
+ * <li> n is the capture size returned by getCaptureSize()</li>
+ * <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency
+ * component</li>
+ * <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
+ * (k*Fs)/(n/2) </li>
+ * </ul>
+ * <table border="0" cellspacing="0" cellpadding="0">
+ * <tr><td>Index </p></td>
+ * <td>0 </p></td>
+ * <td>1 </p></td>
+ * <td>2 </p></td>
+ * <td>3 </p></td>
+ * <td>4 </p></td>
+ * <td>5 </p></td>
+ * <td>... </p></td>
+ * <td>n - 2 </p></td>
+ * <td>n - 1 </p></td></tr>
+ * <tr><td>Data </p></td>
+ * <td>Rf0 </p></td>
+ * <td>Rf(n/2) </p></td>
+ * <td>Rf1 </p></td>
+ * <td>If1 </p></td>
+ * <td>Rf2 </p></td>
+ * <td>If2 </p></td>
+ * <td>... </p></td>
+ * <td>Rf(n-1)/2 </p></td>
+ * <td>If(n-1)/2 </p></td></tr>
+ * </table>
* @param fft array of bytes where the FFT should be returned
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java
index 64d3229..37bb661 100755
--- a/media/java/android/media/videoeditor/VideoEditor.java
+++ b/media/java/android/media/videoeditor/VideoEditor.java
@@ -98,6 +98,31 @@ public interface VideoEditor {
}
/**
+ * This listener interface is used by the VideoEditor to emit export status
+ * notifications.
+ * {@link #generatePreview(MediaProcessingProgressListener listener)}
+ */
+ public interface MediaProcessingProgressListener {
+ // Values used for the action parameter
+ public static final int ACTION_ENCODE = 1;
+ public static final int ACTION_DECODE = 2;
+
+ /**
+ * This method notifies the listener of the progress status of
+ * processing a media object such as a Transition, AudioTrack or a
+ * media image item (when Ken Burns effect is applied).
+ * This method may be called maximum 100 times for one operation.
+ *
+ * @param object The object that is being processed such as a
+ * Transition or AudioTrack
+ * @param action The type of processing being performed
+ * @param progress The progress in %. At the beginning of the operation,
+ * this value is set to 0; at the end, the value is set to 100.
+ */
+ public void onProgress(Object item, int action, int progress);
+ }
+
+ /**
* @return The path where the VideoEditor stores all files related to the
* project
*/
@@ -496,8 +521,11 @@ public interface VideoEditor {
* This method must be called after the aspect ratio of the project changes
* and before startPreview is called. Note that this method may block for
* an extensive period of time.
+ *
+ * @param listener The listener interface which will be used to notify
+ * the caller of the progress of each storyboard item being processed.
*/
- public void generatePreview();
+ public void generatePreview(MediaProcessingProgressListener listener);
/**
* Start the preview of all the storyboard items applied on all MediaItems
diff --git a/media/java/android/media/videoeditor/VideoEditorFactory.java b/media/java/android/media/videoeditor/VideoEditorFactory.java
index 41eed16..85b2666 100755
--- a/media/java/android/media/videoeditor/VideoEditorFactory.java
+++ b/media/java/android/media/videoeditor/VideoEditorFactory.java
@@ -19,8 +19,8 @@ package android.media.videoeditor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
/**
@@ -30,34 +30,19 @@ import java.lang.reflect.InvocationTargetException;
* {@hide}
*/
public class VideoEditorFactory {
- // VideoEditor implementation classes
- public static final String TEST_CLASS_IMPLEMENTATION
- = "android.media.videoeditor.VideoEditorTestImpl";
- public static final String DEFAULT_CLASS_IMPLEMENTATION
- = "android.media.videoeditor.VideoEditorImpl";
-
/**
* This is the factory method for creating a new VideoEditor instance.
*
* @param projectPath The path where all VideoEditor internal
* files are stored. When a project is deleted the application is
* responsible for deleting the path and its contents.
- * @param className The implementation class name
*
* @return The VideoEditor instance
*
* @throws IOException if path does not exist or if the path can
* not be accessed in read/write mode
- * @throws IllegalStateException if a previous VideoEditor instance has not
- * been released
- * @throws ClassNotFoundException, NoSuchMethodException,
- * InvocationTargetException, IllegalAccessException,
- * InstantiationException if the implementation class cannot
- * be instantiated.
*/
- public static VideoEditor create(String projectPath, String className) throws IOException,
- ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
- IllegalAccessException, InstantiationException {
+ public static VideoEditor create(String projectPath) throws IOException {
// If the project path does not exist create it
final File dir = new File(projectPath);
if (!dir.exists()) {
@@ -72,14 +57,7 @@ public class VideoEditorFactory {
}
}
- final Class<?> cls = Class.forName(className);
- final Class<?> partypes[] = new Class[1];
- partypes[0] = String.class;
- final Constructor<?> ct = cls.getConstructor(partypes);
- final Object arglist[] = new Object[1];
- arglist[0] = projectPath;
-
- return (VideoEditor)ct.newInstance(arglist);
+ return new VideoEditorImpl(projectPath);
}
/**
@@ -90,32 +68,21 @@ public class VideoEditorFactory {
* @param projectPath The path where all VideoEditor internal files
* are stored. When a project is deleted the application is
* responsible for deleting the path and its contents.
- * @param className The implementation class name
* @param generatePreview if set to true the
- * {@link MediaEditor#generatePreview()} will be called internally to
- * generate any needed transitions.
+ * {@link MediaEditor#generatePreview(MediaProcessingProgressListener listener)}
+ * will be called internally to generate any needed transitions.
*
* @return The VideoEditor instance
*
* @throws IOException if path does not exist or if the path can
* not be accessed in read/write mode or if one of the resource
* media files cannot be retrieved
- * @throws IllegalStateException if a previous VideoEditor instance has not
- * been released
*/
- public static VideoEditor load(String projectPath, String className, boolean generatePreview)
- throws IOException, ClassNotFoundException, NoSuchMethodException,
- InvocationTargetException, IllegalAccessException, InstantiationException {
- final Class<?> cls = Class.forName(className);
- final Class<?> partypes[] = new Class[1];
- partypes[0] = String.class;
- final Constructor<?> ct = cls.getConstructor(partypes);
- final Object arglist[] = new Object[1];
- arglist[0] = projectPath;
-
- final VideoEditor videoEditor = (VideoEditor)ct.newInstance(arglist);
+ public static VideoEditor load(String projectPath, boolean generatePreview)
+ throws IOException {
+ final VideoEditor videoEditor = new VideoEditorImpl(projectPath);
if (generatePreview) {
- videoEditor.generatePreview();
+ videoEditor.generatePreview(null);
}
return videoEditor;
}
diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java
index ca896c3..1a145e6 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorImpl.java
@@ -39,7 +39,7 @@ import android.view.SurfaceHolder;
/**
* The VideoEditor implementation {@hide}
*/
-public class VideoEditorTestImpl implements VideoEditor {
+public class VideoEditorImpl implements VideoEditor {
// Logging
private static final String TAG = "VideoEditorImpl";
@@ -165,7 +165,7 @@ public class VideoEditorTestImpl implements VideoEditor {
if (mPositionMs >= mToMs) {
if (!mLoop) {
if (mListener != null) {
- mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, true);
+ mListener.onProgress(VideoEditorImpl.this, mPositionMs, true);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "PreviewThread.run playback complete");
@@ -174,13 +174,13 @@ public class VideoEditorTestImpl implements VideoEditor {
} else {
// Fire a notification for the end of the clip
if (mListener != null) {
- mListener.onProgress(VideoEditorTestImpl.this, mToMs, false);
+ mListener.onProgress(VideoEditorImpl.this, mToMs, false);
}
// Rewind
mPositionMs = mFromMs;
if (mListener != null) {
- mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+ mListener.onProgress(VideoEditorImpl.this, mPositionMs, false);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "PreviewThread.run playback complete");
@@ -190,7 +190,7 @@ public class VideoEditorTestImpl implements VideoEditor {
} else {
if (frameCount == mCallbackAfterFrameCount) {
if (mListener != null) {
- mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+ mListener.onProgress(VideoEditorImpl.this, mPositionMs, false);
}
frameCount = 0;
}
@@ -222,7 +222,7 @@ public class VideoEditorTestImpl implements VideoEditor {
*
* @param projectPath
*/
- public VideoEditorTestImpl(String projectPath) throws IOException {
+ public VideoEditorImpl(String projectPath) throws IOException {
mProjectPath = projectPath;
final File projectXml = new File(projectPath, PROJECT_FILENAME);
if (projectXml.exists()) {
@@ -1055,7 +1055,7 @@ public class VideoEditorTestImpl implements VideoEditor {
/*
* {@inheritDoc}
*/
- public void generatePreview() {
+ public void generatePreview(MediaProcessingProgressListener listener) {
// Generate all the needed transitions
for (Transition transition : mTransitions) {
if (!transition.isGenerated()) {
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index f04a2ae..1909e6a 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -134,8 +134,7 @@ public:
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property);
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle,
- MtpObjectFormat format,
- MtpObjectProperty property,
+ uint32_t format, uint32_t property,
int groupCode, int depth,
MtpDataPacket& packet);
@@ -355,7 +354,7 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
MtpDataPacket& packet) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
- (jint)handle, 0, (jint)property, 0, 0);
+ (jlong)handle, 0, (jlong)property, 0, 0);
MtpResponseCode result = env->GetIntField(list, field_mResult);
int count = env->GetIntField(list, field_mCount);
if (result == MTP_RESPONSE_OK && count != 1)
@@ -646,18 +645,19 @@ MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
}
MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
- MtpObjectFormat format,
- MtpObjectProperty property,
+ uint32_t format, uint32_t property,
int groupCode, int depth,
MtpDataPacket& packet) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
- (jint)handle, (jint)format, (jint)property, (jint)groupCode, (jint)depth);
+ (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ if (!list)
+ return MTP_RESPONSE_GENERAL_ERROR;
int count = env->GetIntField(list, field_mCount);
MtpResponseCode result = env->GetIntField(list, field_mResult);
packet.putUInt32(count);
-
if (count > 0) {
jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
@@ -1042,7 +1042,7 @@ android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
}
static jstring
-android_media_MtpDatabase_format_date_time(JNIEnv *env, jobject thiz, jlong seconds)
+android_media_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds)
{
#ifdef HAVE_ANDROID_OS
char date[20];
@@ -1055,11 +1055,14 @@ android_media_MtpDatabase_format_date_time(JNIEnv *env, jobject thiz, jlong seco
// ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static JNINativeMethod gMtpDatabaseMethods[] = {
{"native_setup", "()V", (void *)android_media_MtpDatabase_setup},
{"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize},
+};
+
+static JNINativeMethod gMtpPropertyGroupMethods[] = {
{"format_date_time", "(J)Ljava/lang/String;",
- (void *)android_media_MtpDatabase_format_date_time},
+ (void *)android_media_MtpPropertyGroup_format_date_time},
};
static const char* const kClassPathName = "android/media/MtpDatabase";
@@ -1131,7 +1134,7 @@ int register_android_media_MtpDatabase(JNIEnv *env)
return -1;
}
method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
- "(IIIII)Landroid/media/MtpPropertyList;");
+ "(JIJII)Landroid/media/MtpPropertyList;");
if (method_getObjectPropertyList == NULL) {
LOGE("Can't find getObjectPropertyList");
return -1;
@@ -1220,6 +1223,10 @@ int register_android_media_MtpDatabase(JNIEnv *env)
return -1;
}
+ if (AndroidRuntime::registerNativeMethods(env,
+ "android/media/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
+ return -1;
+
return AndroidRuntime::registerNativeMethods(env,
- "android/media/MtpDatabase", gMethods, NELEM(gMethods));
+ "android/media/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
}
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 68f2e9b..a660429 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -209,8 +209,8 @@ status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
int32_t nonzero = 0;
for (uint32_t i = 0; i < mCaptureSize; i += 2) {
- workspace[i >> 1] = (waveform[i] ^ 0x80) << 23;
- workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7;
+ workspace[i >> 1] =
+ ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
nonzero |= workspace[i >> 1];
}
@@ -219,8 +219,8 @@ status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
}
for (uint32_t i = 0; i < mCaptureSize; i += 2) {
- fft[i] = workspace[i >> 1] >> 23;
- fft[i + 1] = workspace[i >> 1] >> 7;
+ fft[i] = workspace[i >> 1] >> 24;
+ fft[i + 1] = workspace[i >> 1] >> 8;
}
return NO_ERROR;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ec3b5a2..cee92d2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -366,6 +366,9 @@ status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
return BAD_VALUE;
}
+ if (timeUs <= 15 * 1000000LL) {
+ LOGW("Target duration (%lld us) too short to be respected", timeUs);
+ }
mMaxFileDurationUs = timeUs;
return OK;
}
@@ -376,6 +379,11 @@ status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) {
LOGE("Max file size is too small: %lld bytes", bytes);
return BAD_VALUE;
}
+
+ if (bytes <= 100 * 1024) {
+ LOGW("Target file size (%lld bytes) is too small to be respected", bytes);
+ }
+
mMaxFileSizeBytes = bytes;
return OK;
}
@@ -1156,8 +1164,11 @@ status_t StagefrightRecorder::setupVideoEncoder(
CHECK_EQ(client.connect(), OK);
// Use software codec for time lapse
- uint32_t encoder_flags = (mCaptureTimeLapse) ? OMXCodec::kPreferSoftwareCodecs : 0;
- if (mIsMetaDataStoredInVideoBuffers) {
+ uint32_t encoder_flags = 0;
+ if (mCaptureTimeLapse) {
+ encoder_flags |= OMXCodec::kPreferSoftwareCodecs;
+ } else if (mIsMetaDataStoredInVideoBuffers) {
+ encoder_flags |= OMXCodec::kHardwareCodecsOnly;
encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
}
sp<MediaSource> encoder = OMXCodec::Create(
@@ -1165,6 +1176,11 @@ status_t StagefrightRecorder::setupVideoEncoder(
true /* createEncoder */, cameraSource,
NULL, encoder_flags);
if (encoder == NULL) {
+ LOGW("Failed to create the encoder");
+ // When the encoder fails to be created, we need
+ // release the camera source due to the camera's lock
+ // and unlock mechanism.
+ cameraSource->stop();
return UNKNOWN_ERROR;
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index a804866..8ebbe6c 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -426,7 +426,8 @@ status_t AwesomePlayer::setDataSource_l(
// Hack to support http live.
size_t len = strlen(uri);
- if (!strcasecmp(&uri[len - 5], ".m3u8")) {
+ if (!strcasecmp(&uri[len - 5], ".m3u8")
+ || strstr(&uri[7], "m3u8") != NULL) {
mUri = "httplive://";
mUri.append(&uri[7]);
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6760707..602aa9f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -878,7 +878,10 @@ bool MPEG4Writer::exceedsFileSizeLimit() {
nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
}
- return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
+ // Be conservative in the estimate: do not exceed 95% of
+ // the target file limit. For small target file size limit, though,
+ // this will not help.
+ return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
}
bool MPEG4Writer::exceedsFileDurationLimit() {
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index e6a0976..e3292e6 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -178,7 +178,7 @@ AVCEncoder::AVCEncoder(
mInputFrameData(NULL),
mGroup(NULL) {
- LOGV("Construct software AVCEncoder");
+ LOGI("Construct software AVCEncoder");
mHandle = new tagAVCHandle;
memset(mHandle, 0, sizeof(tagAVCHandle));
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index c7a475b..15ed219 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -178,7 +178,7 @@ M4vH263Encoder::M4vH263Encoder(
mInputFrameData(NULL),
mGroup(NULL) {
- LOGV("Construct software M4vH263Encoder");
+ LOGI("Construct software M4vH263Encoder");
mHandle = new tagvideoEncControls;
memset(mHandle, 0, sizeof(tagvideoEncControls));
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 4b4c3d2..4451bd5 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -359,14 +359,6 @@ bool LiveSource::setupCipher() {
return false;
}
- if (keyURI.size() >= 2
- && keyURI.c_str()[0] == '"'
- && keyURI.c_str()[keyURI.size() - 1] == '"') {
- // Remove surrounding quotes.
- AString tmp(keyURI, 1, keyURI.size() - 2);
- keyURI = tmp;
- }
-
ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
sp<ABuffer> key;
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index b166cc3..d4a29c0 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -162,7 +162,7 @@ status_t M3UParser::parse(const void *_data, size_t size) {
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
}
- err = parseCipherInfo(line, &itemMeta);
+ err = parseCipherInfo(line, &itemMeta, mBaseURI);
} else if (line.startsWith("#EXT-X-ENDLIST")) {
mIsComplete = true;
} else if (line.startsWith("#EXTINF")) {
@@ -298,7 +298,7 @@ status_t M3UParser::parseStreamInf(
// static
status_t M3UParser::parseCipherInfo(
- const AString &line, sp<AMessage> *meta) {
+ const AString &line, sp<AMessage> *meta, const AString &baseURI) {
ssize_t colonPos = line.find(":");
if (colonPos < 0) {
@@ -338,6 +338,24 @@ status_t M3UParser::parseCipherInfo(
*meta = new AMessage;
}
+ if (key == "uri") {
+ if (val.size() >= 2
+ && val.c_str()[0] == '"'
+ && val.c_str()[val.size() - 1] == '"') {
+ // Remove surrounding quotes.
+ AString tmp(val, 1, val.size() - 2);
+ val = tmp;
+ }
+
+ AString absURI;
+ if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) {
+ val = absURI;
+ } else {
+ LOGE("failed to make absolute url for '%s'.",
+ val.c_str());
+ }
+ }
+
key.insert(AString("cipher-"), 0);
(*meta)->setString(key.c_str(), val.c_str(), val.size());
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 531d184..63895b4 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -67,7 +67,7 @@ private:
const AString &line, sp<AMessage> *meta);
static status_t parseCipherInfo(
- const AString &line, sp<AMessage> *meta);
+ const AString &line, sp<AMessage> *meta, const AString &baseURI);
static status_t ParseInt32(const char *s, int32_t *x);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index f06a1bb..a559b21 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -238,10 +238,15 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
}
sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
+ size_t index = (type == MPEG2ADTS_AUDIO) ? 0 : 0;
+
for (size_t i = 0; i < mStreams.size(); ++i) {
sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
if (source != NULL) {
- return source;
+ if (index == 0) {
+ return source;
+ }
+ --index;
}
}
@@ -508,7 +513,10 @@ void ATSParser::Stream::onPayloadData(
int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
status_t err = mQueue.appendData(data, size, timeUs);
- CHECK_EQ(err, (status_t)OK);
+
+ if (err != OK) {
+ return;
+ }
sp<ABuffer> accessUnit;
while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) {
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f11b3c3..37bcb23 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -55,9 +55,34 @@ status_t ElementaryStreamQueue::appendData(
switch (mMode) {
case H264:
{
+#if 0
if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
return ERROR_MALFORMED;
}
+#else
+ uint8_t *ptr = (uint8_t *)data;
+
+ ssize_t startOffset = -1;
+ for (size_t i = 0; i + 3 < size; ++i) {
+ if (!memcmp("\x00\x00\x00\x01", &ptr[i], 4)) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ LOGI("found something resembling an H.264 syncword at "
+ "offset %ld",
+ startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+#endif
break;
}
@@ -65,9 +90,31 @@ status_t ElementaryStreamQueue::appendData(
{
uint8_t *ptr = (uint8_t *)data;
+#if 0
if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
return ERROR_MALFORMED;
}
+#else
+ ssize_t startOffset = -1;
+ for (size_t i = 0; i + 1 < size; ++i) {
+ if (ptr[i] == 0xff && (ptr[i + 1] >> 4) == 0x0f) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ LOGI("found something resembling an AAC syncword at offset %ld",
+ startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+#endif
break;
}
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 900b517..9929805 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -77,8 +77,7 @@ public:
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0;
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle,
- MtpObjectFormat format,
- MtpObjectProperty property,
+ uint32_t format, uint32_t property,
int groupCode, int depth,
MtpDataPacket& packet) = 0;
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index 42945f5..4356a6f 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -53,7 +53,7 @@ MtpProperty::MtpProperty(MtpPropertyCode propCode,
mDefaultArrayValues(NULL),
mCurrentArrayLength(0),
mCurrentArrayValues(NULL),
- mGroupCode(-1), // disable multiple properties in GetObjectPropList for now
+ mGroupCode(0),
mFormFlag(kFormNone),
mEnumLength(0),
mEnumValues(NULL)
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index c3755f3..de6cbac 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -536,8 +536,9 @@ MtpResponseCode MtpServer::doResetDevicePropValue() {
MtpResponseCode MtpServer::doGetObjectPropList() {
MtpObjectHandle handle = mRequest.getParameter(1);
- MtpObjectFormat format = mRequest.getParameter(2);
- MtpDeviceProperty property = mRequest.getParameter(3);
+ // use uint32_t so we can support 0xFFFFFFFF
+ uint32_t format = mRequest.getParameter(2);
+ uint32_t property = mRequest.getParameter(3);
int groupCode = mRequest.getParameter(4);
int depth = mRequest.getParameter(5);
LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 078daa7..b0597c4 100755
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.defcontainer">
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
+ <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS"/>
<uses-permission android:name="android.permission.ASEC_ACCESS"/>
<uses-permission android:name="android.permission.ASEC_CREATE"/>
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
diff --git a/packages/DefaultContainerService/res/values-id/strings.xml b/packages/DefaultContainerService/res/values-in/strings.xml
index 2f830b1..2f830b1 100644
--- a/packages/DefaultContainerService/res/values-id/strings.xml
+++ b/packages/DefaultContainerService/res/values-in/strings.xml
diff --git a/packages/DefaultContainerService/res/values-he/strings.xml b/packages/DefaultContainerService/res/values-iw/strings.xml
index ede8178..ede8178 100644
--- a/packages/DefaultContainerService/res/values-he/strings.xml
+++ b/packages/DefaultContainerService/res/values-iw/strings.xml
diff --git a/packages/SettingsProvider/res/values-id/strings.xml b/packages/SettingsProvider/res/values-in/strings.xml
index bed20eb..bed20eb 100644
--- a/packages/SettingsProvider/res/values-id/strings.xml
+++ b/packages/SettingsProvider/res/values-in/strings.xml
diff --git a/packages/SettingsProvider/res/values-he/strings.xml b/packages/SettingsProvider/res/values-iw/strings.xml
index ad2eaf4..ad2eaf4 100644
--- a/packages/SettingsProvider/res/values-he/strings.xml
+++ b/packages/SettingsProvider/res/values-iw/strings.xml
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
index 615c8b6..25fffd6 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
index 0122025..b6e4ebc 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
index 0786916..189a089 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
index 35f9240..ebade92 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
index 3eb22df..6ef71c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
index 1ce9bd1..f6b0a17 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
index fef2cf9..91bc4ee 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
index 05593bc..aa64de4 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
index 32c2c79..246c6fe 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
index 142c413..34515c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
new file mode 100644
index 0000000..b8adc97
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
new file mode 100644
index 0000000..621e980
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/panel_notification.png b/packages/SystemUI/res/drawable-nodpi/panel_notification.png
index eca47d7..b0d9c18 100644
--- a/packages/SystemUI/res/drawable-nodpi/panel_notification.png
+++ b/packages/SystemUI/res/drawable-nodpi/panel_notification.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_toggle_button.xml b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml
new file mode 100644
index 0000000..e17c62f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true"
+ android:drawable="@*android:drawable/scrubber_primary_holo" />
+ <item
+ android:drawable="@*android:drawable/scrubber_track_holo_dark" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 758377b..666bfdc 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -158,10 +158,8 @@
/>
<com.android.systemui.statusbar.tablet.ShirtPocket
android:id="@+id/pocket"
- android:layout_width="96dip"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingLeft="18dip"
- android:paddingRight="18dip"
android:animateLayoutChanges="true"
android:clickable="true"
android:descendantFocusability="blocksDescendants"
@@ -170,10 +168,12 @@
<ImageView
android:id="@+id/pocket_icon"
android:src="@drawable/ic_sysbar_pocket"
- android:visibility="invisible"
- android:layout_width="match_parent"
+ android:paddingLeft="18dip"
+ android:paddingRight="18dip"
+ android:layout_width="96dip"
android:layout_height="wrap_content"
android:gravity="center"
+ android:visibility="gone"
/>
</com.android.systemui.statusbar.tablet.ShirtPocket>
<com.android.systemui.statusbar.tablet.InputMethodButton
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
index 6dd97c3..5e867e5 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
@@ -17,11 +17,13 @@
<com.android.systemui.statusbar.tablet.SettingsView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/status_bar_item_background"
android:paddingLeft="16dp"
+ android:paddingRight="46dp"
>
<!-- Airplane mode -->
@@ -39,11 +41,12 @@
style="@style/StatusBarPanelSettingsContents"
android:text="@string/status_bar_settings_airplane"
/>
- <CheckBox
+ <Switch
android:id="@+id/airplane_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_marginRight="5dp"
/>
</LinearLayout>
<View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -67,7 +70,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginTop="16dp"
- android:layout_marginRight="8dp"
+ android:layout_marginRight="2dp"
android:src="@drawable/ic_notification_open"
/>
</LinearLayout>
@@ -88,11 +91,12 @@
style="@style/StatusBarPanelSettingsContents"
android:text="@string/status_bar_settings_rotation_lock"
/>
- <CheckBox
+ <Switch
android:id="@+id/rotate_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_marginRight="5dp"
/>
</LinearLayout>
<View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -104,6 +108,14 @@
style="@style/StatusBarPanelSettingsIcon"
android:src="@drawable/ic_sysbar_brightness"
/>
+ <com.android.systemui.statusbar.policy.ToggleSlider
+ android:id="@+id/brightness"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_marginRight="2dp"
+ systemui:text="@string/status_bar_settings_auto_brightness_label"
+ />
</LinearLayout>
<View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -114,6 +126,14 @@
style="@style/StatusBarPanelSettingsIcon"
android:src="@drawable/ic_sysbar_sound_on"
/>
+ <com.android.systemui.statusbar.policy.ToggleSlider
+ android:id="@+id/volume"
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_marginRight="2dp"
+ systemui:text="@string/status_bar_settings_mute_label"
+ />
</LinearLayout>
<View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -131,11 +151,12 @@
style="@style/StatusBarPanelSettingsContents"
android:text="@string/status_bar_settings_notifications"
/>
- <CheckBox
+ <Switch
android:id="@+id/do_not_disturb_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_marginRight="5dp"
/>
</LinearLayout>
<View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -160,7 +181,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginTop="16dp"
- android:layout_marginRight="8dp"
+ android:layout_marginRight="2dp"
android:src="@drawable/ic_notification_open"
/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
new file mode 100644
index 0000000..cdf56c5
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ >
+ <CheckBox
+ android:id="@+id/toggle"
+ android:layout_width="48dp"
+ android:layout_height="0dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:button="@drawable/status_bar_toggle_button"
+ />
+ <SeekBar
+ android:id="@+id/slider"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/toggle"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ />
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@id/toggle"
+ android:layout_alignRight="@id/toggle"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:paddingTop="26dp"
+ android:textColor="#666666"
+ android:textSize="12sp"
+ />
+</merge>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index cfdf0dd..21c0645 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"UI systému"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nerušit"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Zobrazit upozornění"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Žádná oznámení"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Prosím připojte dobíjecí zařízení"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Baterie je vybitá:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Baterie je vybitá."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Zbývá <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Nabíjení pomocí rozhraní USB není podporováno."\n"Používejte pouze nabíječku, která byla dodána se zařízením."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Využití baterie"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Nejnovější"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Žádné nedávno použité aplikace."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Obrazovka se automaticky otočí."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Otáčení obrazovky je uzamčeno."</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index e4fc728..a569aa4 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"System-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Forstyr ikke"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis meddelelser"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen meddelelser"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelelser"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Forbind oplader"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er ved at være fladt:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er ved at være fladt."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> tilbage"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Opladning via USB understøttes ikke."\n"Brug kun den medfølgende oplader."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Batteriforbrug"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Seneste"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Der er ingen nye programmer."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Skærmen roterer automatisk."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Skærmrotationen er nu låst."</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index edb1bef..fce8200 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"System-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Bitte nicht stören"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Benachrichtigungen zeigen"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Keine Benachrichtigungen"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Ladegerät anschließen"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Akku ist fast leer."</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Akku ist fast leer."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Noch <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt."\n"Verwenden Sie das mitgelieferte Aufladegerät."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Akkuverbrauch"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Keine neuen Anwendungen"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Bildschirm wird automatisch gedreht."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Bildschirmrotation ist jetzt gesperrt."</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 21ea803..01bf5a1 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"UI συστήματ."</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Εκκαθάριση"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Μην ενοχλείτε"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Εμφάνιση ειδοποιήσεων"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Δεν υπάρχουν ειδοποιήσεις"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Συνδέστε τον φορτιστή"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Η στάθμη της μπαταρίας είναι χαμηλή:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Η στάθμη της μπαταρίας είναι χαμηλή."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Απομένει <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Δεν υποστηρίζεται η φόρτιση USB."\n"Χρησιμοποιείτε μόνο τον φορτιστή που παρέχεται."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Χρήση μπαταρίας"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Πρόσφατα"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Δεν υπάρχουν πρόσφατες εφαρμογές."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Θα γίνεται αυτόματη περιστροφή της οθόνης."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Η περιστροφή οθόνης είναι κλειδωμένη."</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index b6d3618..e2172c0 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Sistema UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No hay notificaciones"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Hay poca batería:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Hay poca batería."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Quedan <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"No admite la carga USB."\n"Usa sólo el cargador provisto."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"No hay aplicaciones recientes."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"La pantalla rotará automáticamente."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotación de la pantalla se encuentra actualmente bloqueada."</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 77773af..b402e15 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"IU sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No tienes notificaciones"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Entrante"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Se está agotando la batería:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Se está agotando la batería."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"No se admite la carga por USB."\n"Utiliza solo el cargador proporcionado."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"No hay aplicaciones recientes."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"La pantalla girará automáticamente."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotación de la pantalla esta bloqueada."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 8877329..70d2a51 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"IU système"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne pas déranger"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Afficher les notifications"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Aucune notification"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Branchez le chargeur"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Le niveau de la batterie est bas :"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Le niveau de la batterie est faible."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restant(s)"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non disponible."\n"Vous devez utiliser le chargeur fourni."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Utilisation de la batterie"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Récentes"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Aucune application récente"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"L\'écran pivote automatiquement."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotation de l\'écran est verrouillée."</string>
</resources>
diff --git a/packages/SystemUI/res/values-id/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 974498a..974498a 100644
--- a/packages/SystemUI/res/values-id/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0507cf9..4b16033 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"UI sistema"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Non disturbare"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostra notifiche"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nessuna notifica"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Collegare il caricabatterie"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteria quasi scarica:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteria quasi scarica."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> rimanente"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Ricarica tramite USB non supportata."\n"Utilizza solo il caricatore in dotazione."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Utilizzo batteria"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Recenti"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Nessuna applicazione recente."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Lo schermo ruoterà automaticamente."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotazione dello schermo è bloccata."</string>
</resources>
diff --git a/packages/SystemUI/res/values-he/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ad1f755..ad1f755 100644
--- a/packages/SystemUI/res/values-he/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 782d03b..b2b6c54 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"システムUI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"通知を非表示"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"通知を表示"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"通知なし"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="7923774589611311406">"充電してください"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"電池が残り少なくなっています:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"電池が残り少なくなっています。"</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"残り<xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB充電には対応していません。"\n"付属の充電器をお使いください。"</string>
<string name="battery_low_why" msgid="7279169609518386372">"電池使用量"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"新着"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"新着のアプリケーションはありません。"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"画面は自動的に回転します。"</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"画面の回転をロックしました。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 5e9b9d5..d550253 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"시스템 UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"응답 거부"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"알림 표시"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 없음"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string>
<string name="battery_low_title" msgid="7923774589611311406">"충전기를 연결하세요."</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"배터리 전원이 부족합니다."</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"배터리 전원이 부족합니다."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g>개 남음"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB 충전이 지원되지 않습니다."\n"제공된 충전기만 사용하세요."</string>
<string name="battery_low_why" msgid="7279169609518386372">"배터리 사용량"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"최근 사용한 앱"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"최근에 사용한 애플리케이션이 없습니다."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"화면은 자동으로 회전합니다."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"현재 화면 회전이 잠겨 있습니다."</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 147242f..1eb7851 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Sys.gr.snitt"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ikke forstyrr"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis varslinger"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen varslinger"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varslinger"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Koble til en lader"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er nesten tomt:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Lavt batterinivå."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> gjenværende"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB-lading støttes ikke."\n"Bruk kun den medfølgende laderen."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Batteribruk"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Nylig"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Ingen nylig brukte programmer."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Skjermen vil rotere automatisk."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Skjermrotering er låst."</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6067cca..c642240 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Systeem-UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Niet storen"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Meldingen weergeven"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Geen meldingen"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Sluit de oplader aan"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"De accu raakt op:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"De accu raakt leeg."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> resterend"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Opladen via USB niet ondersteund."\n"Gebruik alleen de bijgeleverde oplader."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Accugebruik"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Recent"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Geen recente toepassingen."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Scherm wordt automatisch geroteerd."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Schermrotatie is nu vergrendeld."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6b19a34..4f5f328 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Interfejs"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nie przeszkadzać"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Pokaż powiadomienia"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Brak powiadomień"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Bieżące"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Powiadomienia"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Podłącz ładowarkę"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Bateria się rozładowuje:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Bateria wkrótce zostanie rozładowana."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Pozostało: <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Ładowanie przy użyciu złącza USB nie jest obsługiwane."\n"Należy używać tylko dołączonej ładowarki."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Użycie baterii"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Najnowsze"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Brak ostatnio używanych aplikacji."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Ekran zostanie obrócony automatycznie."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Obracanie ekranu zostało zablokowane."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 7bf6eb4..8ec603a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"IU do sist."</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não incomodar"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Ligue o carregador"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está a ficar fraca:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está a ficar fraca."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Carregamento USB não suportado. "\n"Utilize apenas o carregador fornecido."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Utilização da bateria"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Nenhuma aplicação recente."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"O ecrã será rodado automaticamente."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"A rotação do ecrã está agora bloqueada."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 14b4b1f..6f98ed6 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Interf sist"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não perturbe"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Conecte o carregador"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está ficando baixa:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está ficando baixa."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado."\n"Use apenas o carregador fornecido."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Uso da bateria"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Nenhum aplicativo recente."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"A tela girará automaticamente."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"A rotação da tela está bloqueada."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 0a15bcd..471525f 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Графический интерфейс системы"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не беспокоить"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Показать уведомления"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нет уведомлений"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Подключите зарядное устройство"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея разряжена:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея разряжена."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"Осталось: <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Зарядка через порт USB не поддерживается."\n"Используйте только зарядное устройство из комплекта поставки."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Расход заряда батареи"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Недавние"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Новых приложений нет"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Экран будет поворачиваться автоматически."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Поворот экрана заблокирован."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 06188ab..7b7f29f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Gränssnitt"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Stör ej"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Visa aviseringar"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Inga aviseringar"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelanden"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Anslut laddaren"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet håller på att ta slut:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet håller på att ta slut."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> återstår"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Det går inte att ladda via USB."\n"Använd endast den laddare som levererades med telefonen."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Batteriförbrukning"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"Senaste"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Inga nya program."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Skärmen roteras automatiskt."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Skärmrotationen är nu låst."</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 253fbe0..7c31960 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"Sist Arayüzü"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Rahatsız etmeyin"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Bildirimleri göster"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirim yok"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string>
<string name="battery_low_title" msgid="7923774589611311406">"Lütfen şarj cihazını takın"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Pil tükeniyor:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Pil azalıyor."</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> kaldı"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB üzerinden şarj desteklenmiyor."\n"Yalnızca ürünle birlikte verilen şarj cihazını kullanın."</string>
<string name="battery_low_why" msgid="7279169609518386372">"Pil kullanımı"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"En Son Görevler"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"Hiçbir yeni uygulama yok."</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"Ekran otomatik olarak dönecektir."</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"Ekran dönüşü şimdi kilitlendi."</string>
</resources>
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index e3e5148..279a135 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -26,44 +26,16 @@
<!-- Text to display underneath the graphical signal strength meter when
no connection is available. [CHAR LIMIT=20] -->
<string name="status_bar_settings_signal_meter_disconnected">
- no internet connection
- </string>
-
- <!-- Text to display underneath the graphical signal strength meter when
- it is displaying information about a connected, named Wi-Fi network.
- [CHAR LIMIT=20] -->
- <string name="status_bar_settings_signal_meter_wifi_ssid_format">
- <xliff:g id="ssid">%s</xliff:g>
+ No Internet connection
</string>
<!-- Text to display underneath the graphical signal strength meter when
it is displaying Wi-Fi status and Wi-Fi is connected to a network
whose SSID is not available.
[CHAR LIMIT=20] -->
- <string name="status_bar_settings_signal_meter_wifi_nossid">
- Wi-Fi: connected
- </string>
+ <string name="status_bar_settings_signal_meter_wifi_nossid">Wi-Fi connected</string>
- <!-- Text to display underneath the graphical signal strength meter when
- it is displaying Wi-Fi status and Wi-Fi is in the process of
- connecting to a network. [CHAR LIMIT=20] -->
- <string name="status_bar_settings_signal_meter_wifi_connecting">
- Wi-Fi: connecting…
- </string>
-
- <!-- Text to display underneath the graphical signal strength meter when
- it is displaying mobile data (3G) status and a network connection is
- available.
- [CHAR LIMIT=20] -->
- <string name="status_bar_settings_signal_meter_data_connected">
- Mobile data: connected
- </string>
+ <!-- Separator for PLMN and SPN in network name. -->
+ <string name="status_bar_network_name_separator" translatable="false">" – "</string>
- <!-- Text to display underneath the graphical signal strength meter when
- it is displaying mobile data (3G) status and a network connection is
- unavailable.
- [CHAR LIMIT=20] -->
- <string name="status_bar_settings_signal_meter_data_connecting">
- Mobile data: connecting…
- </string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 161a085..4705013 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"系统用户界面"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"请勿打扰"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"显示通知"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="7923774589611311406">"请连接充电器"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"电量所剩不多:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"电池电量低。"</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"还剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"不支持 USB 充电功能。"\n"只能使用随附的充电器充电。"</string>
<string name="battery_low_why" msgid="7279169609518386372">"电量使用情况"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"近期任务"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"没有最近使用的应用程序。"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"屏幕会自动旋转。"</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"屏幕旋转现已锁定。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index eb9108d..ec9c298 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -19,22 +19,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for app_label (7164937344850004466) -->
- <skip />
+ <string name="app_label" msgid="7164937344850004466">"系統 UI"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
- <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
- <skip />
- <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
- <skip />
+ <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"勿干擾"</string>
+ <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"顯示通知"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"沒有通知"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"進行中"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="7923774589611311406">"請連接充電器"</string>
- <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"電池電量即將不足:"</string>
- <!-- no translation found for battery_low_percent_format (1077244949318261761) -->
- <skip />
- <!-- no translation found for invalid_charger (4549105996740522523) -->
- <skip />
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"電池電量即將不足。"</string>
+ <string name="battery_low_percent_format" msgid="1077244949318261761">"還剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"不支援 USB 充電。"\n"僅能使用隨附的充電器。"</string>
<string name="battery_low_why" msgid="7279169609518386372">"電池使用狀況"</string>
<!-- no translation found for status_bar_settings_settings_button (3023889916699270224) -->
<skip />
@@ -45,12 +40,9 @@
<!-- no translation found for status_bar_settings_notifications (397146176280905137) -->
<skip />
<string name="recent_tasks_title" msgid="3691764623638127888">"最新的"</string>
- <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
- <skip />
+ <string name="recent_tasks_empty" msgid="1905484479067697884">"沒有最近用過的應用程式。"</string>
<!-- no translation found for recent_tasks_app_label (3796483981246752469) -->
<skip />
- <!-- no translation found for toast_rotation_free (2700542202836832631) -->
- <skip />
- <!-- no translation found for toast_rotation_locked (7484691306949652450) -->
- <skip />
+ <string name="toast_rotation_free" msgid="2700542202836832631">"螢幕會自動旋轉。"</string>
+ <string name="toast_rotation_locked" msgid="7484691306949652450">"螢幕旋轉功能現已鎖定。"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 23bcf20..87395c1 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -18,5 +18,8 @@
<declare-styleable name="KeyButtonView">
<attr name="keyCode" format="integer" />
</declare-styleable>
+ <declare-styleable name="ToggleSlider">
+ <attr name="text" format="string" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ed31a34..644cca0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -80,6 +80,12 @@
<!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] -->
<string name="status_bar_settings_rotation_lock">Lock screen orientation</string>
+ <!-- Abbreviation / label for mute brightness mode button. Should be all caps. [CHAR LIMIT=6] -->
+ <string name="status_bar_settings_mute_label">MUTE</string>
+
+ <!-- Abbreviation / label for automatic brightness mode button. Should be all caps. [CHAR LIMIT=6] -->
+ <string name="status_bar_settings_auto_brightness_label">AUTO</string>
+
<!-- Label in system panel saying the device will show notifications [CHAR LIMIT=30] -->
<string name="status_bar_settings_notifications">Notifications</string>
@@ -88,6 +94,9 @@
<xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>
</string>
+ <!-- Separator for PLMN and SPN in network name. -->
+ <string name="status_bar_network_name_separator" translatable="false">"\n"</string>
+
<!-- Recent Tasks dialog: title [CHAR LIMIT=30] -->
<string name="recent_tasks_title">Recent</string>
<!-- Recent Tasks dialog: message when there are no recent applications [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
new file mode 100644
index 0000000..c11d04e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
@@ -0,0 +1,97 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.IPowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Slog;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+
+public class BrightnessController implements ToggleSlider.Listener {
+ private static final String TAG = "StatusBar.BrightnessController";
+
+ // Backlight range is from 0 - 255. Need to make sure that user
+ // doesn't set the backlight to 0 and get stuck
+ private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
+ private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+
+ private Context mContext;
+ private ToggleSlider mControl;
+ private IPowerManager mPower;
+
+ public BrightnessController(Context context, ToggleSlider control) {
+ mContext = context;
+ mControl = control;
+
+ boolean automaticAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available);
+ mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
+
+ if (automaticAvailable) {
+ int automatic;
+ try {
+ automatic = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE);
+ } catch (SettingNotFoundException snfe) {
+ automatic = 0;
+ }
+ control.setChecked(automatic != 0);
+ } else {
+ control.setChecked(false);
+ //control.hideToggle();
+ }
+
+ int value;
+ try {
+ value = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS);
+ } catch (SettingNotFoundException ex) {
+ value = MAXIMUM_BACKLIGHT;
+ }
+
+ control.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
+ control.setValue(value - MINIMUM_BACKLIGHT);
+
+ control.setOnChangedListener(this);
+ }
+
+ public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
+ setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
+ : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ if (!automatic) {
+ setBrightness(value + MINIMUM_BACKLIGHT);
+ }
+ }
+
+ private void setMode(int mode) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE, mode);
+ }
+
+ private void setBrightness(int brightness) {
+ try {
+ mPower.setBacklightBrightness(brightness);
+ } catch (RemoteException ex) {
+ }
+ }
+}
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 ec23a3d..1090463 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -32,6 +32,7 @@ import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.RemoteException;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -65,6 +66,9 @@ public class NetworkController extends BroadcastReceiver {
ServiceState mServiceState;
SignalStrength mSignalStrength;
int[] mDataIconList = TelephonyIcons.DATA_G[0];
+ String mNetworkName;
+ String mNetworkNameDefault;
+ String mNetworkNameSeparator;
int mPhoneSignalIconId;
int mDataDirectionIconId;
int mDataSignalIconId;
@@ -116,7 +120,10 @@ public class NetworkController extends BroadcastReceiver {
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
mHspaDataDistinguishable = mContext.getResources().getBoolean(
R.bool.config_hspa_data_distinguishable);
-
+ mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
+ mNetworkNameDefault = mContext.getString(
+ com.android.internal.R.string.lockscreen_carrier_default);
+ mNetworkName = mNetworkNameDefault;
// wifi
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -127,6 +134,9 @@ public class NetworkController extends BroadcastReceiver {
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
context.registerReceiver(this, filter);
// yuck
@@ -168,6 +178,12 @@ public class NetworkController extends BroadcastReceiver {
updateSimState(intent);
updateDataIcon();
refreshViews();
+ } else if (action.equals(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ refreshViews();
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
updateConnectivity(intent);
@@ -516,6 +532,31 @@ public class NetworkController extends BroadcastReceiver {
mDataConnected = visible;
}
+ void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+ if (false) {
+ Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ + " showPlmn=" + showPlmn + " plmn=" + plmn);
+ }
+ StringBuilder str = new StringBuilder();
+ boolean something = false;
+ if (showPlmn && plmn != null) {
+ str.append(plmn);
+ something = true;
+ }
+ if (showSpn && spn != null) {
+ if (something) {
+ str.append(mNetworkNameSeparator);
+ }
+ str.append(spn);
+ something = true;
+ }
+ if (something) {
+ mNetworkName = str.toString();
+ } else {
+ mNetworkName = mNetworkNameDefault;
+ }
+ }
+
// ===== Wifi ===================================================================
private void updateWifiState(Intent intent) {
@@ -618,14 +659,13 @@ public class NetworkController extends BroadcastReceiver {
if (mWifiSsid == null) {
label = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
} else {
- label = context.getString(R.string.status_bar_settings_signal_meter_wifi_ssid_format,
- mWifiSsid);
+ label = mWifiSsid;
}
combinedSignalIconId = mWifiIconId;
dataTypeIconId = 0;
} else {
if (mDataConnected) {
- label = context.getString(R.string.status_bar_settings_signal_meter_data_connected);
+ label = mNetworkName;
} else {
label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
new file mode 100644
index 0000000..46207ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
@@ -0,0 +1,134 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.CompoundButton;
+
+import com.android.systemui.R;
+
+public class ToggleSlider extends RelativeLayout
+ implements CompoundButton.OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
+ private static final String TAG = "StatusBar.ToggleSlider";
+
+ public interface Listener {
+ public void onChanged(ToggleSlider v, boolean tracking, boolean checked, int value);
+ }
+
+ private Listener mListener;
+ private boolean mTracking;
+
+ private CompoundButton mToggle;
+ private SeekBar mSlider;
+ private TextView mLabel;
+
+ public ToggleSlider(Context context) {
+ this(context, null);
+ }
+
+ public ToggleSlider(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ToggleSlider(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ View.inflate(context, R.layout.status_bar_toggle_slider, this);
+
+ final Resources res = context.getResources();
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleSlider,
+ defStyle, 0);
+
+ mToggle = (CompoundButton)findViewById(R.id.toggle);
+ mToggle.setOnCheckedChangeListener(this);
+ mToggle.setBackgroundDrawable(res.getDrawable(R.drawable.status_bar_toggle_button));
+
+ mSlider = (SeekBar)findViewById(R.id.slider);
+ mSlider.setOnSeekBarChangeListener(this);
+
+ mLabel = (TextView)findViewById(R.id.label);
+ mLabel.setText(a.getString(R.styleable.ToggleSlider_text));
+
+ a.recycle();
+ }
+
+ public void onCheckedChanged(CompoundButton toggle, boolean checked) {
+ Drawable thumb;
+ final Resources res = getContext().getResources();
+ if (checked) {
+ thumb = res.getDrawable(R.drawable.scrubber_control_disabled_holo);
+ } else {
+ thumb = res.getDrawable(R.drawable.scrubber_control_holo);
+ }
+ mSlider.setThumb(thumb);
+
+ if (mListener != null) {
+ mListener.onChanged(this, mTracking, checked, mSlider.getProgress());
+ }
+ }
+
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mListener != null) {
+ mListener.onChanged(this, mTracking, mToggle.isChecked(), progress);
+ }
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mTracking = true;
+ if (mListener != null) {
+ mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress());
+ }
+ mToggle.setChecked(false);
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mTracking = false;
+ if (mListener != null) {
+ mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress());
+ }
+ }
+
+ public void setOnChangedListener(Listener l) {
+ mListener = l;
+ }
+
+ public void setChecked(boolean checked) {
+ mToggle.setChecked(checked);
+ }
+
+ public boolean isChecked() {
+ return mToggle.isChecked();
+ }
+
+ public void setMax(int max) {
+ mSlider.setMax(max);
+ }
+
+ public void setValue(int value) {
+ mSlider.setProgress(value);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
new file mode 100644
index 0000000..c9da01a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+
+public class VolumeController implements ToggleSlider.Listener {
+ private static final String TAG = "StatusBar.VolumeController";
+ private static final int STREAM = AudioManager.STREAM_NOTIFICATION;
+
+ private Context mContext;
+ private ToggleSlider mControl;
+ private AudioManager mAudioManager;
+
+ private boolean mMute;
+ private int mVolume;
+
+ public VolumeController(Context context, ToggleSlider control) {
+ mContext = context;
+ mControl = control;
+ mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+
+ mMute = mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ mVolume = mAudioManager.getStreamVolume(STREAM);
+ control.setMax(mAudioManager.getStreamMaxVolume(STREAM));
+ control.setValue(mVolume);
+ control.setChecked(mMute);
+
+ control.setOnChangedListener(this);
+ }
+
+ public void onChanged(ToggleSlider view, boolean tracking, boolean mute, int level) {
+ if (!tracking) {
+ if (mute) {
+ boolean vibeInSilent = (1 == Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.VIBRATE_IN_SILENT, 1));
+ mAudioManager.setRingerMode(
+ vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE
+ : AudioManager.RINGER_MODE_SILENT);
+ } else {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ mAudioManager.setStreamVolume(STREAM, level, AudioManager.FLAG_PLAY_SOUND);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index d1f8dd0..0491baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -31,13 +31,18 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.AirplaneModeController;
import com.android.systemui.statusbar.policy.AutoRotateController;
+import com.android.systemui.statusbar.policy.BrightnessController;
import com.android.systemui.statusbar.policy.DoNotDisturbController;
+import com.android.systemui.statusbar.policy.ToggleSlider;
+import com.android.systemui.statusbar.policy.VolumeController;
public class SettingsView extends LinearLayout implements View.OnClickListener {
static final String TAG = "SettingsView";
AirplaneModeController mAirplane;
AutoRotateController mRotate;
+ VolumeController mVolume;
+ BrightnessController mBrightness;
DoNotDisturbController mDoNotDisturb;
public SettingsView(Context context, AttributeSet attrs) {
@@ -59,6 +64,10 @@ public class SettingsView extends LinearLayout implements View.OnClickListener {
findViewById(R.id.network).setOnClickListener(this);
mRotate = new AutoRotateController(context,
(CompoundButton)findViewById(R.id.rotate_checkbox));
+ mVolume = new VolumeController(context,
+ (ToggleSlider)findViewById(R.id.volume));
+ mBrightness = new BrightnessController(context,
+ (ToggleSlider)findViewById(R.id.brightness));
mDoNotDisturb = new DoNotDisturbController(context,
(CompoundButton)findViewById(R.id.do_not_disturb_checkbox));
findViewById(R.id.settings).setOnClickListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
index d1e61a9..698f5af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
@@ -81,7 +81,7 @@ public class ShirtPocket extends FrameLayout {
mIcon.setImageResource(mClipping == null
? R.drawable.ic_sysbar_pocket_hidden
: R.drawable.ic_sysbar_pocket_holding);
- mIcon.setVisibility(mClipping == null ? View.INVISIBLE : View.VISIBLE);
+ mIcon.setVisibility(mClipping == null ? View.GONE : View.VISIBLE);
}
private void showWindow() {
@@ -169,7 +169,7 @@ public class ShirtPocket extends FrameLayout {
thumb = new DragThumbnailBuilder(mWindow.findViewById(R.id.preview));
}
- v.startDrag(clip, thumb, false);
+ v.startDrag(clip, thumb, false, null);
// TODO: only discard the clipping if it was accepted
stash(null);
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 31311a4..563b8ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -114,6 +114,7 @@ public class TabletStatusBar extends StatusBar {
NotificationPeekPanel mNotificationPeekWindow;
ViewGroup mNotificationPeekRow;
int mNotificationPeekIndex;
+ IBinder mNotificationPeekKey;
LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
int mNotificationPeekTapDuration;
@@ -344,9 +345,11 @@ public class TabletStatusBar extends StatusBar {
if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
if (m.arg1 >= 0) {
final int N = mNotns.size();
- if (mNotificationPeekIndex < N) {
+ if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
entry.icon.setBackgroundColor(0);
+ mNotificationPeekIndex = -1;
+ mNotificationPeekKey = null;
}
final int peekIndex = m.arg1;
@@ -373,6 +376,7 @@ public class TabletStatusBar extends StatusBar {
mNotificationPanel.setVisibility(View.GONE);
mNotificationPeekIndex = peekIndex;
+ mNotificationPeekKey = entry.key;
}
}
break;
@@ -381,10 +385,13 @@ public class TabletStatusBar extends StatusBar {
mNotificationPeekWindow.setVisibility(View.GONE);
mNotificationPeekRow.removeAllViews();
final int N = mNotns.size();
- if (mNotificationPeekIndex < N) {
+ if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
entry.icon.setBackgroundColor(0);
}
+
+ mNotificationPeekIndex = -1;
+ mNotificationPeekKey = null;
break;
case MSG_OPEN_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "opening notifications panel");
@@ -647,6 +654,12 @@ public class TabletStatusBar extends StatusBar {
// called by StatusBar
@Override
public void setLightsOn(boolean on) {
+ // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
+ // that can't handle lights-out mode.
+ if (mMenuButton.getVisibility() == View.VISIBLE
+ || mMenuShadow.getVisibility() == View.VISIBLE) {
+ on = true;
+ }
mHandler.removeMessages(MSG_SHOW_SHADOWS);
mHandler.removeMessages(MSG_HIDE_SHADOWS);
mHandler.sendEmptyMessage(on ? MSG_HIDE_SHADOWS : MSG_SHOW_SHADOWS);
@@ -657,6 +670,9 @@ public class TabletStatusBar extends StatusBar {
Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button");
}
mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+ // See above re: lights-out policy for legacy apps.
+ if (visible) setLightsOn(true);
}
public void setIMEButtonVisible(IBinder token, boolean visible) {
@@ -814,6 +830,11 @@ public class TabletStatusBar extends StatusBar {
// Remove the expanded view.
ViewGroup rowParent = (ViewGroup)entry.row.getParent();
if (rowParent != null) rowParent.removeView(entry.row);
+
+ if (key == mNotificationPeekKey) {
+ // must close the peek as well, since it's gone
+ mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
+ }
// Remove the icon.
// ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
// if (iconParent != null) iconParent.removeView(entry.icon);
diff --git a/packages/VpnServices/res/values-id/strings.xml b/packages/VpnServices/res/values-in/strings.xml
index 8b6b4c2..8b6b4c2 100644
--- a/packages/VpnServices/res/values-id/strings.xml
+++ b/packages/VpnServices/res/values-in/strings.xml
diff --git a/packages/VpnServices/res/values-he/strings.xml b/packages/VpnServices/res/values-iw/strings.xml
index 74971d6..74971d6 100644
--- a/packages/VpnServices/res/values-he/strings.xml
+++ b/packages/VpnServices/res/values-iw/strings.xml
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index a8dd76c..abed18f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -32,7 +32,8 @@ import android.view.FallbackEventHandler;
import android.view.KeyEvent;
public class PhoneFallbackEventHandler implements FallbackEventHandler {
- static String TAG = "PhoneFallbackEventHandler";
+ private static String TAG = "PhoneFallbackEventHandler";
+ private static final boolean DEBUG = false;
Context mContext;
View mView;
@@ -180,7 +181,9 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
}
boolean onKeyUp(int keyCode, KeyEvent event) {
- Slog.d(TAG, "up " + keyCode);
+ if (DEBUG) {
+ Slog.d(TAG, "up " + keyCode);
+ }
final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
if (dispatcher != null) {
dispatcher.handleUpEvent(event);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index cd8a065..b487d92 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -160,6 +160,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ContextMenuBuilder mContextMenu;
private MenuDialogHelper mContextMenuHelper;
+ private ActionButtonSubmenu mActionButtonPopup;
+ private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -542,6 +544,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (doCallback) {
callOnPanelClosed(st.featureId, st, null);
}
+ } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback &&
+ mActionBar != null) {
+ checkCloseActionMenu(st.menu);
}
st.isPrepared = false;
st.isHandled = false;
@@ -563,6 +568,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
+ private void checkCloseActionMenu(Menu menu) {
+ if (mClosingActionMenu) {
+ return;
+ }
+
+ boolean closed = false;
+ mClosingActionMenu = true;
+ if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) {
+ closed = true;
+ }
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ closed = true;
+ }
+ Callback cb = getCallback();
+ if (cb != null && closed) {
+ cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
+ }
+ mClosingActionMenu = false;
+ }
+
@Override
public final void togglePanel(int featureId, KeyEvent event) {
PanelFeatureState st = getPanelState(featureId, true);
@@ -841,8 +867,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (panel.isOpen) {
// The window manager will give us a valid window token
new MenuDialogHelper(subMenu).show(null);
- } else {
- new MenuPopupHelper(getContext(), subMenu).show();
+ } else if (hasFeature(FEATURE_ACTION_BAR)) {
+ mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu);
+ mActionButtonPopup.show();
+ Callback cb = getCallback();
+ if (cb != null) {
+ cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
+ }
}
return true;
@@ -854,16 +885,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private void reopenMenu(boolean toggleMenuMode) {
if (mActionBar != null) {
+ final Callback cb = getCallback();
if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
- final Callback cb = getCallback();
if (cb != null) {
final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
- mActionBar.showOverflowMenu();
+ cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
+ mActionBar.openOverflowMenu();
}
}
} else {
mActionBar.hideOverflowMenu();
+ if (cb != null) {
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
+ }
}
return;
}
@@ -2042,8 +2078,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (cb != null && mFeatureId < 0) {
cb.onDetachedFromWindow();
}
+
+ if (mActionButtonPopup != null) {
+ if (mActionButtonPopup.isShowing()) {
+ mActionButtonPopup.dismiss();
+ }
+ mActionButtonPopup = null;
+ }
}
-
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ post(mActionButtonPopup);
+ }
+ }
+
@Override
public void onCloseSystemDialogs(String reason) {
if (mFeatureId >= 0) {
@@ -2921,4 +2972,29 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
void sendCloseSystemWindows(String reason) {
PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
}
+
+ private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable {
+ private SubMenuBuilder mSubMenu;
+
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+ super(context, subMenu);
+ mSubMenu = subMenu;
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mSubMenu.getCallback().onCloseSubMenu(mSubMenu);
+ mActionButtonPopup = null;
+ }
+
+ @Override
+ public void run() {
+ show();
+ Callback cb = getCallback();
+ if (cb != null) {
+ cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu);
+ }
+ }
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1373627..c3112d8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1215,11 +1215,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
- int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
+ public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final boolean keyguardOn = keyguardOn();
- final boolean down = (action == KeyEvent.ACTION_DOWN);
- final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0);
+ final int keyCode = event.getKeyCode();
+ final int repeatCount = event.getRepeatCount();
+ final int metaState = event.getMetaState();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final boolean canceled = event.isCanceled();
if (false) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -1348,7 +1350,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Shortcuts are invoked through Search+key, so intercept those here
if (mSearchKeyPressed) {
if (down && repeatCount == 0 && !keyguardOn) {
- Intent shortcutIntent = mShortcutManager.getIntent(keyCode, metaState);
+ Intent shortcutIntent = mShortcutManager.getIntent(event);
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(shortcutIntent);
@@ -1368,13 +1370,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public boolean dispatchUnhandledKey(WindowState win, int action, int flags,
- int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
+ public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
if (false) {
- Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + action
- + ", flags=" + flags + ", keyCode=" + keyCode
- + ", scanCode=" + scanCode + ", metaState=" + metaState
- + ", repeatCount=" + repeatCount + ", policyFlags=" + policyFlags);
+ Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
+ + ", flags=" + event.getFlags()
+ + ", keyCode=" + event.getKeyCode()
+ + ", scanCode=" + event.getScanCode()
+ + ", metaState=" + event.getMetaState()
+ + ", repeatCount=" + event.getRepeatCount()
+ + ", policyFlags=" + policyFlags);
}
return false;
}
@@ -1816,12 +1820,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final IStatusBarService sbs = mStatusBarService;
if (mStatusBarService != null) {
try {
- if (changedFullscreen) {
- sbs.setActiveWindowIsFullscreen(topIsFullscreenF);
- }
if (changedMenu) {
sbs.setMenuKeyVisible(topNeedsMenuF);
}
+ if (changedFullscreen) {
+ sbs.setActiveWindowIsFullscreen(topIsFullscreenF);
+ }
} catch (RemoteException e) {
// This should be impossible because we're in the same process.
mStatusBarService = null;
@@ -1970,10 +1974,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
- final boolean down = action == KeyEvent.ACTION_DOWN;
- final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0;
+ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final boolean canceled = event.isCanceled();
+ final int keyCode = event.getKeyCode();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
@@ -2164,12 +2168,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Only do this if we would otherwise not pass it to the user. In that
// case, the PhoneWindow class will do the same thing, except it will
// only do it if the showing app doesn't process the key on its own.
- long when = whenNanos / 1000000;
- KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, scanCode, flags,
- InputDevice.SOURCE_KEYBOARD);
mBroadcastWakeLock.acquire();
- mHandler.post(new PassHeadsetKey(keyEvent));
+ mHandler.post(new PassHeadsetKey(new KeyEvent(event)));
}
break;
}
diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
index 51377d8..fc66a20 100644
--- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java
+++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
@@ -25,6 +25,7 @@ import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import java.net.URISyntaxException;
@@ -100,20 +101,17 @@ class ShortcutManager extends ContentObserver {
* This will first try an exact match (with modifiers), and then try a
* match without modifiers (primary character on a key).
*
- * @param keyCode The keycode of the key pushed.
- * @param modifiers The modifiers without any that are used for chording
- * to invoke a shortcut.
+ * @param event The key event of the key that was pressed.
* @return The intent that matches the shortcut, or null if not found.
*/
- public Intent getIntent(int keyCode, int modifiers) {
- KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ public Intent getIntent(KeyEvent event) {
// First try the exact keycode (with modifiers)
- int shortcut = kcm.get(keyCode, modifiers);
+ int shortcut = event.getUnicodeChar();
Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
if (intent != null) return intent;
-
+
// Next try the keycode without modifiers (the primary character on that key)
- shortcut = Character.toLowerCase(kcm.get(keyCode, 0));
+ shortcut = Character.toLowerCase(event.getUnicodeChar(0));
return shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
}
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index e3b5db1..175f613 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -81,12 +81,6 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev
LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
// keep track of SCO device address
mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 &&
- mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
-#endif
}
}
break;
@@ -115,12 +109,6 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev
{
if (AudioSystem::isBluetoothScoDevice(device)) {
mScoDeviceAddress = "";
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 &&
- mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
-#endif
}
}
} break;
@@ -138,6 +126,7 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev
if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
closeA2dpOutputs();
}
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -280,14 +269,7 @@ void AudioPolicyManagerBase::setPhoneState(int state)
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
- // suspend A2DP output if a SCO device is present.
- if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
- if (oldState == AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- } else if (state == AudioSystem::MODE_NORMAL) {
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
- }
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
@@ -397,6 +379,7 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -1025,8 +1008,10 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),
- mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
+ mMusicStopTime(0), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
+ mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
+ mA2dpSuspended(false)
{
mpClientInterface = clientInterface;
@@ -1322,17 +1307,6 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices
}
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
- if (mScoDeviceAddress != "") {
- // It is normal to suspend twice if we are both in call,
- // and have the hardware audio output routed to BT SCO
- if (mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- }
-
if (!a2dpUsedForSonification()) {
// mute music on A2DP output if a notification or ringtone is playing
uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
@@ -1340,6 +1314,9 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices
setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
}
}
+
+ mA2dpSuspended = false;
+
return NO_ERROR;
}
@@ -1369,6 +1346,7 @@ status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devi
}
}
mA2dpDeviceAddress = "";
+ mA2dpSuspended = false;
return NO_ERROR;
}
@@ -1467,6 +1445,48 @@ void AudioPolicyManagerBase::checkOutputForAllStrategies()
checkOutputForStrategy(STRATEGY_DTMF);
}
+void AudioPolicyManagerBase::checkA2dpSuspend()
+{
+ // suspend A2DP output if:
+ // (NOT already suspended) &&
+ // ((SCO device is connected &&
+ // (forced usage for communication || for record is SCO))) ||
+ // (phone state is ringing || in call)
+ //
+ // restore A2DP output if:
+ // (Already suspended) &&
+ // ((SCO device is NOT connected ||
+ // (forced usage NOT for communication && NOT for record is SCO))) &&
+ // (phone state is NOT ringing && NOT in call)
+ //
+ if (mA2dpOutput == 0) {
+ return;
+ }
+
+ if (mA2dpSuspended) {
+ if (((mScoDeviceAddress == "") ||
+ ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
+ (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
+ ((mPhoneState != AudioSystem::MODE_IN_CALL) &&
+ (mPhoneState != AudioSystem::MODE_RINGTONE))) {
+
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ mA2dpSuspended = false;
+ }
+ } else {
+ if (((mScoDeviceAddress != "") &&
+ ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+ (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
+ ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
+ (mPhoneState == AudioSystem::MODE_RINGTONE))) {
+
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ mA2dpSuspended = true;
+ }
+ }
+}
+
+
#endif
uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
@@ -1714,14 +1734,7 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t
// wait for the PCM output buffers to empty before proceeding with the rest of the command
usleep(outputDesc->mLatency*2*1000);
}
-#ifdef WITH_A2DP
- // suspend A2DP output if SCO device is selected
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
- if (mA2dpOutput != 0) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- }
-#endif
+
// do the routing
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)device);
@@ -1729,15 +1742,6 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t
// update stream volumes according to new device
applyStreamVolumes(output, device, delayMs);
-#ifdef WITH_A2DP
- // if disconnecting SCO device, restore A2DP output
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
- if (mA2dpOutput != 0) {
- LOGV("restore A2DP output");
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
- }
-#endif
// if changing from a combined headset + speaker route, unmute media streams
if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 5c67da7..b5e3888 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -430,6 +430,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
mSettingsObserver.observe(mContext);
+
+ loadGlobalProxy();
}
@@ -2089,7 +2091,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
ContentResolver res = mContext.getContentResolver();
Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host);
Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port);
- Settings.Secure.putString(res,Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclList);
}
@@ -2099,6 +2101,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
sendProxyBroadcast(proxyProperties);
}
+ private void loadGlobalProxy() {
+ ContentResolver res = mContext.getContentResolver();
+ String host = Settings.Secure.getString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST);
+ int port = Settings.Secure.getInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, 0);
+ String exclList = Settings.Secure.getString(res,
+ Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+ if (!TextUtils.isEmpty(host)) {
+ ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+ synchronized (mGlobalProxyLock) {
+ mGlobalProxy = proxyProperties;
+ }
+ }
+ }
+
public ProxyProperties getGlobalProxy() {
synchronized (mGlobalProxyLock) {
return mGlobalProxy;
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 4364c04..8634eec 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -30,6 +30,7 @@ import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.KeyEvent;
import android.view.Surface;
import java.io.BufferedReader;
@@ -41,7 +42,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Properties;
/*
* Wraps the C++ InputManager and provides its callbacks.
@@ -358,21 +358,6 @@ public class InputManager {
}
}
- private static final class VirtualKeyDefinition {
- public int scanCode;
-
- // configured position data, specified in display coords
- public int centerX;
- public int centerY;
- public int width;
- public int height;
- }
-
- private static final class InputDeviceCalibration {
- public String[] keys;
- public String[] values;
- }
-
/*
* Callbacks from native.
*/
@@ -404,26 +389,23 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
- whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
+ event, policyFlags, isScreenOn);
}
@SuppressWarnings("unused")
- public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
- int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
- return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
- action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ public boolean interceptKeyBeforeDispatching(InputChannel focus,
+ KeyEvent event, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(
+ focus, event, policyFlags);
}
@SuppressWarnings("unused")
- public boolean dispatchUnhandledKey(InputChannel focus, int action,
- int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
- return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(focus,
- action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ public boolean dispatchUnhandledKey(InputChannel focus,
+ KeyEvent event, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(
+ focus, event, policyFlags);
}
@SuppressWarnings("unused")
@@ -446,81 +428,6 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
- ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
-
- try {
- FileInputStream fis = new FileInputStream(
- "/sys/board_properties/virtualkeys." + deviceName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr, 2048);
- String str = br.readLine();
- if (str != null) {
- String[] it = str.split(":");
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
- final int N = it.length-6;
- for (int i=0; i<=N; i+=6) {
- if (!"0x01".equals(it[i])) {
- Slog.w(TAG, "Unknown virtual key type at elem #"
- + i + ": " + it[i] + " for device " + deviceName);
- continue;
- }
- try {
- VirtualKeyDefinition key = new VirtualKeyDefinition();
- key.scanCode = Integer.parseInt(it[i+1]);
- key.centerX = Integer.parseInt(it[i+2]);
- key.centerY = Integer.parseInt(it[i+3]);
- key.width = Integer.parseInt(it[i+4]);
- key.height = Integer.parseInt(it[i+5]);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
- + key.scanCode + ": center=" + key.centerX + ","
- + key.centerY + " size=" + key.width + "x"
- + key.height);
- keys.add(key);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Bad number in virtual key definition at region "
- + i + " in: " + str + " for device " + deviceName, e);
- }
- }
- }
- br.close();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
- } catch (IOException e) {
- Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
- }
-
- return keys.toArray(new VirtualKeyDefinition[keys.size()]);
- }
-
- @SuppressWarnings("unused")
- public InputDeviceCalibration getInputDeviceCalibration(String deviceName) {
- // Calibration is specified as a sequence of colon-delimited key value pairs.
- Properties properties = new Properties();
- File calibrationFile = new File(Environment.getRootDirectory(),
- CALIBRATION_DIR_PATH + deviceName + ".idc");
- if (calibrationFile.exists()) {
- try {
- FileInputStream fis = new FileInputStream(calibrationFile);
- properties.load(fis);
- fis.close();
- } catch (IOException ex) {
- Slog.w(TAG, "Error reading input device calibration properties for device "
- + deviceName + " from " + calibrationFile + ".", ex);
- }
- } else {
- Slog.i(TAG, "No input device calibration properties found for device "
- + deviceName + ".");
- return null;
- }
-
- InputDeviceCalibration calibration = new InputDeviceCalibration();
- calibration.keys = properties.keySet().toArray(new String[properties.size()]);
- calibration.values = properties.values().toArray(new String[properties.size()]);
- return calibration;
- }
-
- @SuppressWarnings("unused")
public String[] getExcludedDeviceNames() {
ArrayList<String> names = new ArrayList<String>();
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6de7e6a..7101bb0 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -41,7 +41,6 @@ import android.database.ContentObserver;
import android.hardware.Usb;
import android.media.AudioManager;
import android.net.Uri;
-import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Binder;
import android.os.Handler;
@@ -61,7 +60,6 @@ import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import android.widget.Toast;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -92,7 +90,6 @@ public class NotificationManagerService extends INotificationManager.Stub
private WorkerHandler mHandler;
private StatusBarManagerService mStatusBar;
- private LightsService mLightsService;
private LightsService.Light mNotificationLight;
private LightsService.Light mAttentionLight;
@@ -440,7 +437,7 @@ public class NotificationManagerService extends INotificationManager.Stub
mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
}
- // register for battery changed notifications
+ // register for various Intents
IntentFilter filter = new IntentFilter();
filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(Intent.ACTION_SCREEN_ON);
diff --git a/services/java/com/android/server/SamplingProfilerService.java b/services/java/com/android/server/SamplingProfilerService.java
index 26af7f7..61267d0 100644
--- a/services/java/com/android/server/SamplingProfilerService.java
+++ b/services/java/com/android/server/SamplingProfilerService.java
@@ -88,7 +88,7 @@ public class SamplingProfilerService extends Binder {
private void registerSettingObserver(Context context) {
ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_HZ),
+ Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_MS),
false, new SamplingProfilerSettingsObserver(contentResolver));
}
@@ -107,12 +107,11 @@ public class SamplingProfilerService extends Binder {
}
@Override
public void onChange(boolean selfChange) {
- Integer samplingProfilerHz = Settings.Secure.getInt(
- mContentResolver, Settings.Secure.SAMPLING_PROFILER_HZ, 0);
+ Integer samplingProfilerMs = Settings.Secure.getInt(
+ mContentResolver, Settings.Secure.SAMPLING_PROFILER_MS, 0);
// setting this secure property will start or stop sampling profiler,
- // as well as adjust the frequency of taking snapshots.
- SystemProperties.set("persist.sys.profiler_hz", samplingProfilerHz.toString());
+ // as well as adjust the the time between taking snapshots.
+ SystemProperties.set("persist.sys.profiler_ms", samplingProfilerMs.toString());
}
}
}
-
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java
index 1cc6a2a..a95a6c7 100644
--- a/services/java/com/android/server/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/ScreenRotationAnimation.java
@@ -122,7 +122,9 @@ class ScreenRotationAnimation {
mSurface.unlockCanvasAndPost(c);
Surface.closeTransaction();
- screenshot.recycle();
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
}
static int deltaRotation(int oldRotation, int newRotation) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index d523fa8..7f81a25 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -197,7 +197,7 @@ public class WifiService extends IWifiManager.Stub {
WifiServiceHandler(android.os.Looper looper, Context context) {
super(looper);
mWshChannel = new AsyncChannel();
- mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0);
+ mWshChannel.connect(context, this, mWifiStateMachine.getHandler());
}
@Override
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index cbb35c6..7504bb4 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -619,7 +619,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mDragInProgress && newWin.isPotentialDragTarget()) {
DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
- desc, null, false);
+ null, desc, null, false);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
@@ -659,7 +659,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.d(TAG, "broadcasting DRAG_ENDED");
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- 0, 0, null, null, mDragResult);
+ 0, 0, null, null, null, mDragResult);
for (WindowState ws: mNotifiedWindows) {
try {
ws.mClient.dispatchDragEvent(evt);
@@ -711,7 +711,7 @@ public class WindowManagerService extends IWindowManager.Stub
// force DRAG_EXITED_EVENT if appropriate
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
- null, null, false);
+ null, null, null, false);
mTargetWindow.mClient.dispatchDragEvent(evt);
if (myPid != mTargetWindow.mSession.mPid) {
evt.recycle();
@@ -723,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
- null, null, false);
+ null, null, null, false);
touchedWin.mClient.dispatchDragEvent(evt);
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
@@ -754,7 +754,7 @@ public class WindowManagerService extends IWindowManager.Stub
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
- null, mData, false);
+ null, null, mData, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -5829,30 +5829,25 @@ public class WindowManagerService extends IWindowManager.Stub
/* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device. */
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
- return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags,
- keyCode, scanCode, policyFlags, isScreenOn);
+ public int interceptKeyBeforeQueueing(
+ KeyEvent event, int policyFlags, boolean isScreenOn) {
+ return mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
}
/* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch. */
- public boolean interceptKeyBeforeDispatching(InputChannel focus,
- int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
+ public boolean interceptKeyBeforeDispatching(
+ InputChannel focus, KeyEvent event, int policyFlags) {
WindowState windowState = getWindowStateForInputChannel(focus);
- return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ return mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
/* Provides an opportunity for the window manager policy to process a key that
* the application did not handle. */
- public boolean dispatchUnhandledKey(InputChannel focus,
- int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
+ public boolean dispatchUnhandledKey(
+ InputChannel focus, KeyEvent event, int policyFlags) {
WindowState windowState = getWindowStateForInputChannel(focus);
- return mPolicy.dispatchUnhandledKey(windowState, action, flags,
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
}
/* Called when the current input focus changes.
@@ -6010,6 +6005,7 @@ public class WindowManagerService extends IWindowManager.Stub
int deviceId = ev.getDeviceId();
int scancode = ev.getScanCode();
int source = ev.getSource();
+ int flags = ev.getFlags();
if (source == InputDevice.SOURCE_UNKNOWN) {
source = InputDevice.SOURCE_KEYBOARD;
@@ -6019,7 +6015,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (downTime == 0) downTime = eventTime;
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
+ deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -6853,6 +6849,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (!mParentFrame.equals(pf)) {
+ //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame
+ // + " to " + pf);
mParentFrame.set(pf);
mContentChanged = true;
}
@@ -7739,12 +7737,10 @@ public class WindowManagerService extends IWindowManager.Stub
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
boolean shouldAnimateMove() {
- return mContentChanged && !mAnimating && !mLastHidden && !mDisplayFrozen
+ return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen
&& (mFrame.top != mLastFrame.top
|| mFrame.left != mLastFrame.left)
- && (mAttachedWindow == null
- || (mAttachedWindow.mAnimation == null
- && !mAttachedWindow.shouldAnimateMove()))
+ && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove())
&& mPolicy.isScreenOn();
}
@@ -9228,6 +9224,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (!gone || !win.mHaveFrame) {
if (!win.mLayoutAttached) {
if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
win.mContentChanged = false;
}
mPolicy.layoutWindowLw(win, win.mAttrs, null);
@@ -9262,6 +9259,7 @@ public class WindowManagerService extends IWindowManager.Stub
if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
|| !win.mHaveFrame) {
if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
win.mContentChanged = false;
}
mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
@@ -9460,7 +9458,6 @@ 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.
@@ -10247,6 +10244,11 @@ public class WindowManagerService extends IWindowManager.Stub
w.mOrientationChanging = false;
}
+ if (w.mContentChanged) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+ }
+
final boolean canBeSeen = w.isDisplayedLw();
if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 3fd6965..1996dd0 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,8 +56,6 @@ static struct {
jmethodID checkInjectEventsPermission;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
- jmethodID getVirtualKeyDefinitions;
- jmethodID getInputDeviceCalibration;
jmethodID getExcludedDeviceNames;
jmethodID getMaxEventsPerSecond;
} gCallbacksClassInfo;
@@ -65,23 +63,6 @@ static struct {
static struct {
jclass clazz;
- jfieldID scanCode;
- jfieldID centerX;
- jfieldID centerY;
- jfieldID width;
- jfieldID height;
-} gVirtualKeyDefinitionClassInfo;
-
-static struct {
- jclass clazz;
-
- jfieldID keys;
- jfieldID values;
-} gInputDeviceCalibrationClassInfo;
-
-static struct {
- jclass clazz;
-
jfieldID inputChannel;
jfieldID name;
jfieldID layoutParamsFlags;
@@ -184,10 +165,6 @@ public:
int32_t* width, int32_t* height, int32_t* orientation);
virtual bool filterTouchEvents();
virtual bool filterJumpyTouchEvents();
- virtual void getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
- virtual void getInputDeviceCalibration(const String8& deviceName,
- InputDeviceCalibration& outCalibration);
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -201,9 +178,7 @@ public:
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
virtual int32_t getMaxEventsPerSecond();
- virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
- uint32_t& policyFlags);
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags);
@@ -454,83 +429,6 @@ bool NativeInputManager::filterJumpyTouchEvents() {
return mFilterJumpyTouchEvents;
}
-void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
- outVirtualKeyDefinitions.clear();
-
- JNIEnv* env = jniEnv();
-
- jstring deviceNameStr = env->NewStringUTF(deviceName.string());
- if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
- jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
- gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
- if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
- jsize length = env->GetArrayLength(result);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(result, i);
-
- outVirtualKeyDefinitions.add();
- outVirtualKeyDefinitions.editTop().scanCode =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
- outVirtualKeyDefinitions.editTop().centerX =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
- outVirtualKeyDefinitions.editTop().centerY =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
- outVirtualKeyDefinitions.editTop().width =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
- outVirtualKeyDefinitions.editTop().height =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
-
- env->DeleteLocalRef(item);
- }
- env->DeleteLocalRef(result);
- }
- env->DeleteLocalRef(deviceNameStr);
- }
-}
-
-void NativeInputManager::getInputDeviceCalibration(const String8& deviceName,
- InputDeviceCalibration& outCalibration) {
- outCalibration.clear();
-
- JNIEnv* env = jniEnv();
-
- jstring deviceNameStr = env->NewStringUTF(deviceName.string());
- if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration")) {
- jobject result = env->CallObjectMethod(mCallbacksObj,
- gCallbacksClassInfo.getInputDeviceCalibration, deviceNameStr);
- if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration") && result) {
- jobjectArray keys = jobjectArray(env->GetObjectField(result,
- gInputDeviceCalibrationClassInfo.keys));
- jobjectArray values = jobjectArray(env->GetObjectField(result,
- gInputDeviceCalibrationClassInfo.values));
-
- jsize length = env->GetArrayLength(keys);
- for (jsize i = 0; i < length; i++) {
- jstring keyStr = jstring(env->GetObjectArrayElement(keys, i));
- jstring valueStr = jstring(env->GetObjectArrayElement(values, i));
-
- const char* keyChars = env->GetStringUTFChars(keyStr, NULL);
- String8 key(keyChars);
- env->ReleaseStringUTFChars(keyStr, keyChars);
-
- const char* valueChars = env->GetStringUTFChars(valueStr, NULL);
- String8 value(valueChars);
- env->ReleaseStringUTFChars(valueStr, valueChars);
-
- outCalibration.addProperty(key, value);
-
- env->DeleteLocalRef(keyStr);
- env->DeleteLocalRef(valueStr);
- }
- env->DeleteLocalRef(keys);
- env->DeleteLocalRef(values);
- env->DeleteLocalRef(result);
- }
- env->DeleteLocalRef(deviceNameStr);
- }
-}
-
void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
outExcludedDeviceNames.clear();
@@ -770,6 +668,7 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
outWindow.ownerUid = ownerUid;
env->ReleaseStringUTFChars(name, nameStr);
+ env->DeleteLocalRef(name);
valid = true;
} else {
LOGW("Dropping input target because its input channel is not initialized.");
@@ -831,20 +730,8 @@ bool NativeInputManager::isScreenBright() {
return android_server_PowerManagerService_isScreenBright();
}
-void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when,
- int32_t deviceId, int32_t action, int32_t &flags,
- int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, "
- "keyCode=%d, scanCode=%d, policyFlags=0x%x",
- when, deviceId, action, flags, keyCode, scanCode, policyFlags);
-#endif
-
- if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
- policyFlags |= POLICY_FLAG_VIRTUAL;
- flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
- }
-
+void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
+ uint32_t& policyFlags) {
// Policy:
// - Ignore untrusted events and pass them along.
// - Ask the window manager what to do with normal events and trusted injected events.
@@ -854,21 +741,30 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when,
const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
const int32_t WM_ACTION_GO_TO_SLEEP = 4;
+ nsecs_t when = keyEvent->getEventTime();
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
JNIEnv* env = jniEnv();
- jint wmActions = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeQueueing,
- when, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+ jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+ jint wmActions;
+ if (keyEventObj) {
+ wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
+ keyEventObj, policyFlags, isScreenOn);
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+ wmActions = 0;
+ }
+ android_view_KeyEvent_recycle(env, keyEventObj);
+ env->DeleteLocalRef(keyEventObj);
+ } else {
+ LOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
wmActions = 0;
}
- if (!(flags & POLICY_FLAG_INJECTED)) {
+ if (!(policyFlags & POLICY_FLAG_INJECTED)) {
if (!isScreenOn) {
policyFlags |= POLICY_FLAG_WOKE_HERE;
- flags |= AKEY_EVENT_FLAG_WOKE_HERE;
}
if (!isScreenBright) {
@@ -893,10 +789,6 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when,
}
void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags);
-#endif
-
// Policy:
// - Ignore untrusted events and pass them along.
// - No special filtering for injected events required at this time.
@@ -921,46 +813,62 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i
// - Ignore untrusted events and pass them along.
// - Filter normal events and trusted injected events through the window manager policy to
// handle the HOME key and the like.
+ bool result;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
// Note: inputChannel may be null.
jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
- jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeDispatching,
- inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
- keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), policyFlags);
- bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+ jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+ if (keyEventObj) {
+ jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeDispatching,
+ inputChannelObj, keyEventObj, policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+ android_view_KeyEvent_recycle(env, keyEventObj);
+ env->DeleteLocalRef(keyEventObj);
+ result = consumed && !error;
+ } else {
+ LOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
+ result = false;
+ }
env->DeleteLocalRef(inputChannelObj);
- return consumed && ! error;
} else {
- return false;
+ result = false;
}
+ return result;
}
bool NativeInputManager::dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
// Policy:
// - Ignore untrusted events and do not perform default handling.
+ bool result;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
// Note: inputChannel may be null.
jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
- jboolean handled = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.dispatchUnhandledKey,
- inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
- keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), policyFlags);
- bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+ jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+ if (keyEventObj) {
+ jboolean handled = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.dispatchUnhandledKey,
+ inputChannelObj, keyEventObj, policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+ android_view_KeyEvent_recycle(env, keyEventObj);
+ env->DeleteLocalRef(keyEventObj);
+ result = handled && !error;
+ } else {
+ LOGE("Failed to obtain key event object for dispatchUnhandledKey.");
+ result = false;
+ }
env->DeleteLocalRef(inputChannelObj);
- return handled && ! error;
} else {
- return false;
+ result = false;
}
+ return result;
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
@@ -1159,13 +1067,21 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
KeyEvent keyEvent;
- android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
+ status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
+ if (status) {
+ jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
& keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
MotionEvent motionEvent;
- android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
+ status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
+ if (status) {
+ jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
& motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
@@ -1384,13 +1300,14 @@ int register_android_server_InputManager(JNIEnv* env) {
"notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
- "interceptKeyBeforeQueueing", "(JIIIIIZ)I");
+ "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
- "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z");
+ "interceptKeyBeforeDispatching",
+ "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z");
GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz,
- "dispatchUnhandledKey", "(Landroid/view/InputChannel;IIIIIII)Z");
+ "dispatchUnhandledKey", "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z");
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");
@@ -1401,51 +1318,12 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
"filterJumpyTouchEvents", "()Z");
- GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz,
- "getVirtualKeyDefinitions",
- "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;");
-
- GET_METHOD_ID(gCallbacksClassInfo.getInputDeviceCalibration, gCallbacksClassInfo.clazz,
- "getInputDeviceCalibration",
- "(Ljava/lang/String;)Lcom/android/server/InputManager$InputDeviceCalibration;");
-
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
"getMaxEventsPerSecond", "()I");
- // VirtualKeyDefinition
-
- FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
- "com/android/server/InputManager$VirtualKeyDefinition");
-
- GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz,
- "scanCode", "I");
-
- GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz,
- "centerX", "I");
-
- GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz,
- "centerY", "I");
-
- GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz,
- "width", "I");
-
- GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
- "height", "I");
-
- // InputDeviceCalibration
-
- FIND_CLASS(gInputDeviceCalibrationClassInfo.clazz,
- "com/android/server/InputManager$InputDeviceCalibration");
-
- GET_FIELD_ID(gInputDeviceCalibrationClassInfo.keys, gInputDeviceCalibrationClassInfo.clazz,
- "keys", "[Ljava/lang/String;");
-
- GET_FIELD_ID(gInputDeviceCalibrationClassInfo.values, gInputDeviceCalibrationClassInfo.clazz,
- "values", "[Ljava/lang/String;");
-
// InputWindow
FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
diff --git a/services/sensorservice/GravitySensor.cpp b/services/sensorservice/GravitySensor.cpp
index 18bd359..5c6aa99 100644
--- a/services/sensorservice/GravitySensor.cpp
+++ b/services/sensorservice/GravitySensor.cpp
@@ -29,8 +29,8 @@ namespace android {
GravitySensor::GravitySensor(sensor_t const* list, size_t count)
: mSensorDevice(SensorDevice::getInstance()),
- mEnabled(false), mAccTime(0),
- mLowPass(M_SQRT1_2, 1),
+ mAccTime(0),
+ mLowPass(M_SQRT1_2, 1.5f),
mX(mLowPass), mY(mLowPass), mZ(mLowPass)
{
@@ -71,15 +71,9 @@ bool GravitySensor::process(sensors_event_t* outEvent,
}
return false;
}
-
-bool GravitySensor::isEnabled() const {
- return mEnabled;
-}
-
status_t GravitySensor::activate(void* ident, bool enabled) {
status_t err = mSensorDevice.activate(this, mAccelerometer.getHandle(), enabled);
if (err == NO_ERROR) {
- mEnabled = enabled;
if (enabled) {
mAccTime = 0;
}
diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h
index f9850b7..decfbb8 100644
--- a/services/sensorservice/GravitySensor.h
+++ b/services/sensorservice/GravitySensor.h
@@ -33,17 +33,15 @@ namespace android {
class GravitySensor : public SensorInterface {
SensorDevice& mSensorDevice;
Sensor mAccelerometer;
- bool mEnabled;
double mAccTime;
SecondOrderLowPassFilter mLowPass;
- BiquadFilter mX, mY, mZ;
+ CascadedBiquadFilter mX, mY, mZ;
public:
GravitySensor(sensor_t const* list, size_t count);
virtual bool process(sensors_event_t* outEvent,
const sensors_event_t& event);
- virtual bool isEnabled() const;
virtual status_t activate(void* ident, bool enabled);
virtual status_t setDelay(void* ident, int handle, int64_t ns);
virtual Sensor getSensor() const;
diff --git a/services/sensorservice/LinearAccelerationSensor.cpp b/services/sensorservice/LinearAccelerationSensor.cpp
index 2dc12dc..9425a92 100644
--- a/services/sensorservice/LinearAccelerationSensor.cpp
+++ b/services/sensorservice/LinearAccelerationSensor.cpp
@@ -53,10 +53,6 @@ bool LinearAccelerationSensor::process(sensors_event_t* outEvent,
return result;
}
-bool LinearAccelerationSensor::isEnabled() const {
- return mGravitySensor.isEnabled();
-}
-
status_t LinearAccelerationSensor::activate(void* ident, bool enabled) {
return mGravitySensor.activate(ident, enabled);
}
diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h
index ee918ce..c577086 100644
--- a/services/sensorservice/LinearAccelerationSensor.h
+++ b/services/sensorservice/LinearAccelerationSensor.h
@@ -40,7 +40,6 @@ class LinearAccelerationSensor : public SensorInterface {
const sensors_event_t& event);
public:
LinearAccelerationSensor(sensor_t const* list, size_t count);
- virtual bool isEnabled() const;
virtual status_t activate(void* ident, bool enabled);
virtual status_t setDelay(void* ident, int handle, int64_t ns);
virtual Sensor getSensor() const;
diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp
index 6f4b8be..418e7f8 100644
--- a/services/sensorservice/RotationVectorSensor.cpp
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -34,7 +34,6 @@ static inline T clamp(T v) {
RotationVectorSensor::RotationVectorSensor(sensor_t const* list, size_t count)
: mSensorDevice(SensorDevice::getInstance()),
- mEnabled(false),
mALowPass(M_SQRT1_2, 5.0f),
mAX(mALowPass), mAY(mALowPass), mAZ(mALowPass),
mMLowPass(M_SQRT1_2, 2.5f),
@@ -114,15 +113,18 @@ bool RotationVectorSensor::process(sensors_event_t* outEvent,
float qx = sqrtf( clamp( Hx - My - Az + 1) * 0.25f );
float qy = sqrtf( clamp(-Hx + My - Az + 1) * 0.25f );
float qz = sqrtf( clamp(-Hx - My + Az + 1) * 0.25f );
- const float n = 1.0f / (qw*qw + qx*qx + qy*qy + qz*qz);
- qx = copysignf(qx, Ay - Mz) * n;
- qy = copysignf(qy, Hz - Ax) * n;
- qz = copysignf(qz, Mx - Hy) * n;
+ qx = copysignf(qx, Ay - Mz);
+ qy = copysignf(qy, Hz - Ax);
+ qz = copysignf(qz, Mx - Hy);
+
+ // this quaternion is guaranteed to be normalized, by construction
+ // of the rotation matrix.
*outEvent = event;
outEvent->data[0] = qx;
outEvent->data[1] = qy;
outEvent->data[2] = qz;
+ outEvent->data[3] = qw;
outEvent->sensor = '_rov';
outEvent->type = SENSOR_TYPE_ROTATION_VECTOR;
return true;
@@ -130,19 +132,12 @@ bool RotationVectorSensor::process(sensors_event_t* outEvent,
return false;
}
-bool RotationVectorSensor::isEnabled() const {
- return mEnabled;
-}
-
status_t RotationVectorSensor::activate(void* ident, bool enabled) {
- if (mEnabled != enabled) {
- mSensorDevice.activate(this, mAcc.getHandle(), enabled);
- mSensorDevice.activate(this, mMag.getHandle(), enabled);
- mEnabled = enabled;
- if (enabled) {
- mMagTime = 0;
- mAccTime = 0;
- }
+ mSensorDevice.activate(this, mAcc.getHandle(), enabled);
+ mSensorDevice.activate(this, mMag.getHandle(), enabled);
+ if (enabled) {
+ mMagTime = 0;
+ mAccTime = 0;
}
return NO_ERROR;
}
diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
index e7f28c9..b7c9512 100644
--- a/services/sensorservice/RotationVectorSensor.h
+++ b/services/sensorservice/RotationVectorSensor.h
@@ -34,7 +34,6 @@ class RotationVectorSensor : public SensorInterface {
SensorDevice& mSensorDevice;
Sensor mAcc;
Sensor mMag;
- bool mEnabled;
float mMagData[3];
double mAccTime;
double mMagTime;
@@ -47,7 +46,6 @@ public:
RotationVectorSensor(sensor_t const* list, size_t count);
virtual bool process(sensors_event_t* outEvent,
const sensors_event_t& event);
- virtual bool isEnabled() const;
virtual status_t activate(void* ident, bool enabled);
virtual status_t setDelay(void* ident, int handle, int64_t ns);
virtual Sensor getSensor() const;
diff --git a/services/sensorservice/SecondOrderLowPassFilter.cpp b/services/sensorservice/SecondOrderLowPassFilter.cpp
index e13e136..eeb6d1e 100644
--- a/services/sensorservice/SecondOrderLowPassFilter.cpp
+++ b/services/sensorservice/SecondOrderLowPassFilter.cpp
@@ -67,4 +67,23 @@ float BiquadFilter::operator()(float x)
}
// ---------------------------------------------------------------------------
+
+CascadedBiquadFilter::CascadedBiquadFilter(const SecondOrderLowPassFilter& s)
+ : mA(s), mB(s)
+{
+}
+
+float CascadedBiquadFilter::init(float x)
+{
+ mA.init(x);
+ mB.init(x);
+ return x;
+}
+
+float CascadedBiquadFilter::operator()(float x)
+{
+ return mB(mA(x));
+}
+
+// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SecondOrderLowPassFilter.h b/services/sensorservice/SecondOrderLowPassFilter.h
index 998ca35..85698ca 100644
--- a/services/sensorservice/SecondOrderLowPassFilter.h
+++ b/services/sensorservice/SecondOrderLowPassFilter.h
@@ -54,6 +54,18 @@ public:
float operator()(float in);
};
+/*
+ * Two cascaded biquad IIR filters
+ * (4-poles IIR)
+ */
+class CascadedBiquadFilter {
+ BiquadFilter mA;
+ BiquadFilter mB;
+public:
+ CascadedBiquadFilter(const SecondOrderLowPassFilter& s);
+ float init(float in);
+ float operator()(float in);
+};
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 73f85ba..f192913 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -137,9 +137,8 @@ void SensorDevice::dump(String8& result, char* buffer, size_t SIZE)
Mutex::Autolock _l(mLock);
for (size_t i=0 ; i<size_t(count) ; i++) {
- snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d / %d\n",
+ snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d\n",
list[i].handle,
- mActivationCount.valueFor(list[i].handle).count,
mActivationCount.valueFor(list[i].handle).rates.size());
result.append(buffer);
}
@@ -167,22 +166,25 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled)
bool actuateHardware = false;
Info& info( mActivationCount.editValueFor(handle) );
- int32_t& count(info.count);
if (enabled) {
- if (android_atomic_inc(&count) == 0) {
- actuateHardware = true;
- }
Mutex::Autolock _l(mLock);
if (info.rates.indexOfKey(ident) < 0) {
info.rates.add(ident, DEFAULT_EVENTS_PERIOD);
- }
- } else {
- if (android_atomic_dec(&count) == 1) {
actuateHardware = true;
+ } else {
+ // sensor was already activated for this ident
}
+ } else {
Mutex::Autolock _l(mLock);
- info.rates.removeItem(ident);
+ if (info.rates.removeItem(ident) >= 0) {
+ if (info.rates.size() == 0) {
+ actuateHardware = true;
+ }
+ } else {
+ // sensor wasn't enabled for this ident
+ }
}
+
if (actuateHardware) {
err = mSensorDevice->activate(mSensorDevice, handle, enabled);
if (enabled) {
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 63ecbcd..c19b2ce 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -40,8 +40,7 @@ class SensorDevice : public Singleton<SensorDevice> {
Mutex mLock; // protect mActivationCount[].rates
// fixed-size array after construction
struct Info {
- Info() : count(0) { }
- int32_t count;
+ Info() { }
KeyedVector<void*, nsecs_t> rates;
};
DefaultKeyedVector<int, Info> mActivationCount;
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 93d23d9..be8eaff 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -32,7 +32,7 @@ SensorInterface::~SensorInterface()
HardwareSensor::HardwareSensor(const sensor_t& sensor)
: mSensorDevice(SensorDevice::getInstance()),
- mSensor(&sensor), mEnabled(false)
+ mSensor(&sensor)
{
LOGI("%s", sensor.name);
}
@@ -46,15 +46,8 @@ bool HardwareSensor::process(sensors_event_t* outEvent,
return true;
}
-bool HardwareSensor::isEnabled() const {
- return mEnabled;
-}
-
-status_t HardwareSensor::activate(void* ident,bool enabled) {
- status_t err = mSensorDevice.activate(ident, mSensor.getHandle(), enabled);
- if (err == NO_ERROR)
- mEnabled = enabled;
- return err;
+status_t HardwareSensor::activate(void* ident, bool enabled) {
+ return mSensorDevice.activate(ident, mSensor.getHandle(), enabled);
}
status_t HardwareSensor::setDelay(void* ident, int handle, int64_t ns) {
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index eebd563..084f2f5 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -38,7 +38,6 @@ public:
virtual bool process(sensors_event_t* outEvent,
const sensors_event_t& event) = 0;
- virtual bool isEnabled() const = 0;
virtual status_t activate(void* ident, bool enabled) = 0;
virtual status_t setDelay(void* ident, int handle, int64_t ns) = 0;
virtual Sensor getSensor() const = 0;
@@ -51,7 +50,6 @@ class HardwareSensor : public SensorInterface
{
SensorDevice& mSensorDevice;
Sensor mSensor;
- bool mEnabled;
public:
HardwareSensor(const sensor_t& sensor);
@@ -61,7 +59,6 @@ public:
virtual bool process(sensors_event_t* outEvent,
const sensors_event_t& event);
- virtual bool isEnabled() const;
virtual status_t activate(void* ident, bool enabled);
virtual status_t setDelay(void* ident, int handle, int64_t ns);
virtual Sensor getSensor() const;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7be58c6..145618e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -231,7 +231,7 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) {
hwcl->handle = NULL;
return;
}
- hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+ hwcl->handle = buffer->handle;
// TODO: set the crop value properly
hwcl->sourceCrop.left = 0;
hwcl->sourceCrop.top = 0;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 5b49305..719e5b4 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -700,6 +700,10 @@ public final class CallManager {
Log.d(LOG_TAG, this.toString());
}
+ if (!canDial(phone)) {
+ throw new CallStateException("cannot dial in current state");
+ }
+
if ( hasActiveFgCall() ) {
Phone activePhone = getActiveFgCall().getPhone();
boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
@@ -753,6 +757,32 @@ public final class CallManager {
}
/**
+ * Phone can make a call only if ALL of the following are true:
+ * - Phone is not powered off
+ * - There's no incoming or waiting call
+ * - There's available call slot in either foreground or background
+ * - The foreground call is ACTIVE or IDLE or DISCONNECTED.
+ * (We mainly need to make sure it *isn't* DIALING or ALERTING.)
+ * @param phone
+ * @return true if the phone can make a new call
+ */
+ private boolean canDial(Phone phone) {
+ int serviceState = phone.getServiceState().getState();
+ boolean hasRingingCall = hasActiveRingingCall();
+ boolean hasActiveCall = hasActiveFgCall();
+ boolean hasHoldingCall = hasActiveBgCall();
+ boolean allLinesTaken = hasActiveCall && hasHoldingCall;
+ Call.State fgCallState = getActiveFgCallState();
+
+ return (serviceState != ServiceState.STATE_POWER_OFF
+ && !hasRingingCall
+ && !allLinesTaken
+ && ((fgCallState == Call.State.ACTIVE)
+ || (fgCallState == Call.State.IDLE)
+ || (fgCallState == Call.State.DISCONNECTED)));
+ }
+
+ /**
* Whether or not the phone can do explicit call transfer in the current
* phone state--that is, one call holding and one call active.
* @return true if the phone can do explicit call transfer; false otherwise.
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 82fcb6a..014901d 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -20,12 +20,13 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-import static android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
-import android.telephony.TelephonyManager;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
@@ -171,33 +172,17 @@ public class CallerInfo {
}
}
- // Look for the person ID.
-
- // TODO: This is pretty ugly now, see bug 2269240 for
- // more details. The column to use depends upon the type of URL,
- // for content://com.android.contacts/data/phones the "contact_id"
- // column is used. For content/com.andriod.contacts/phone_lookup"
- // the "_ID" column is used. If it is neither we leave columnIndex
- // at -1 and no person ID will be available.
-
- columnIndex = -1;
- String url = contactRef.toString();
- if (url.startsWith("content://com.android.contacts/data/phones")) {
- if (VDBG) Log.v(TAG,
- "URL path starts with 'data/phones' using RawContacts.CONTACT_ID");
- columnIndex = cursor.getColumnIndex(RawContacts.CONTACT_ID);
- } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
- if (VDBG) Log.v(TAG,
- "URL path starts with 'phone_lookup' using PhoneLookup._ID");
- columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
- } else {
- Log.e(TAG, "Bad contact URL '" + url + "'");
- }
-
+ // Look for the person_id.
+ columnIndex = getColumnIndexForPersonId(contactRef, cursor);
if (columnIndex != -1) {
info.person_id = cursor.getLong(columnIndex);
+ if (VDBG) Log.v(TAG, "==> got info.person_id: " + info.person_id);
} else {
- Log.e(TAG, "person_id column missing for " + contactRef);
+ // No valid columnIndex, so we can't look up person_id.
+ Log.w(TAG, "Couldn't find person_id column for " + contactRef);
+ // Watch out: this means that anything that depends on
+ // person_id will be broken (like contact photo lookups in
+ // the in-call UI, for example.)
}
// look for the custom ringtone, create from the string stored
@@ -411,30 +396,120 @@ public class CallerInfo {
}
/**
+ * Returns the column index to use to find the "person_id" field in
+ * the specified cursor, based on the contact URI that was originally
+ * queried.
+ *
+ * This is a helper function for the getCallerInfo() method that takes
+ * a Cursor. Looking up the person_id is nontrivial (compared to all
+ * the other CallerInfo fields) since the column we need to use
+ * depends on what query we originally ran.
+ *
+ * Watch out: be sure to not do any database access in this method, since
+ * it's run from the UI thread (see comments below for more info.)
+ *
+ * @return the columnIndex to use (with cursor.getLong()) to get the
+ * person_id, or -1 if we couldn't figure out what colum to use.
+ *
+ * TODO: Add a unittest for this method. (This is a little tricky to
+ * test, since we'll need a live contacts database to test against,
+ * preloaded with at least some phone numbers and SIP addresses. And
+ * we'll probably have to hardcode the column indexes we expect, so
+ * the test might break whenever the contacts schema changes. But we
+ * can at least make sure we handle all the URI patterns we claim to,
+ * and that the mime types match what we expect...)
+ */
+ private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) {
+ // TODO: This is pretty ugly now, see bug 2269240 for
+ // more details. The column to use depends upon the type of URL:
+ // - content://com.android.contacts/data/phones ==> use the "contact_id" column
+ // - content://com.android.contacts/phone_lookup ==> use the "_ID" column
+ // - content://com.android.contacts/data ==> use the "contact_id" column
+ // If it's none of the above, we leave columnIndex=-1 which means
+ // that the person_id field will be left unset.
+ //
+ // The logic here *used* to be based on the mime type of contactRef
+ // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the
+ // RawContacts.CONTACT_ID column). But looking up the mime type requires
+ // a call to context.getContentResolver().getType(contactRef), which
+ // isn't safe to do from the UI thread since it can cause an ANR if
+ // the contacts provider is slow or blocked (like during a sync.)
+ //
+ // So instead, figure out the column to use for person_id by just
+ // looking at the URI itself.
+
+ if (VDBG) Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '"
+ + contactRef + "'...");
+ // Warning: Do not enable the following logging (due to ANR risk.)
+ // if (VDBG) Log.v(TAG, "- MIME type: "
+ // + context.getContentResolver().getType(contactRef));
+
+ String url = contactRef.toString();
+ String columnName = null;
+ if (url.startsWith("content://com.android.contacts/data/phones")) {
+ // Direct lookup in the Phone table.
+ // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2")
+ if (VDBG) Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID");
+ columnName = RawContacts.CONTACT_ID;
+ } else if (url.startsWith("content://com.android.contacts/data")) {
+ // Direct lookup in the Data table.
+ // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data")
+ if (VDBG) Log.v(TAG, "'data' URI; using Data.CONTACT_ID");
+ // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.)
+ columnName = Data.CONTACT_ID;
+ } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
+ // Lookup in the PhoneLookup table, which provides "fuzzy matching"
+ // for phone numbers.
+ // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup")
+ if (VDBG) Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID");
+ columnName = PhoneLookup._ID;
+ } else {
+ Log.w(TAG, "Unexpected prefix for contactRef '" + url + "'");
+ }
+ int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1;
+ if (VDBG) Log.v(TAG, "==> Using column '" + columnName
+ + "' (columnIndex = " + columnIndex + ") for person_id lookup...");
+ return columnIndex;
+ }
+
+ /**
* @return a string debug representation of this instance.
*/
public String toString() {
- return new StringBuilder(384)
- .append("\nname: " + /*name*/ "nnnnnn")
- .append("\nphoneNumber: " + /*phoneNumber*/ "xxxxxxx")
- .append("\ncnapName: " + cnapName)
- .append("\nnumberPresentation: " + numberPresentation)
- .append("\nnamePresentation: " + namePresentation)
- .append("\ncontactExits: " + contactExists)
- .append("\nphoneLabel: " + phoneLabel)
- .append("\nnumberType: " + numberType)
- .append("\nnumberLabel: " + numberLabel)
- .append("\nphotoResource: " + photoResource)
- .append("\nperson_id: " + person_id)
- .append("\nneedUpdate: " + needUpdate)
- .append("\ncontactRefUri: " + /*contactRefUri*/ "xxxxxxx")
- .append("\ncontactRingtoneUri: " + /*contactRefUri*/ "xxxxxxx")
- .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
- .append("\ncachedPhoto: " + cachedPhoto)
- .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
- .append("\nemergency: " + mIsEmergency)
- .append("\nvoicemail " + mIsVoiceMail)
- .append("\ncontactExists " + contactExists)
- .toString();
+ // Warning: never check in this file with VERBOSE_DEBUG = true
+ // because that will result in PII in the system log.
+ final boolean VERBOSE_DEBUG = false;
+
+ if (VERBOSE_DEBUG) {
+ return new StringBuilder(384)
+ .append("\nname: " + name)
+ .append("\nphoneNumber: " + phoneNumber)
+ .append("\ncnapName: " + cnapName)
+ .append("\nnumberPresentation: " + numberPresentation)
+ .append("\nnamePresentation: " + namePresentation)
+ .append("\ncontactExits: " + contactExists)
+ .append("\nphoneLabel: " + phoneLabel)
+ .append("\nnumberType: " + numberType)
+ .append("\nnumberLabel: " + numberLabel)
+ .append("\nphotoResource: " + photoResource)
+ .append("\nperson_id: " + person_id)
+ .append("\nneedUpdate: " + needUpdate)
+ .append("\ncontactRefUri: " + contactRefUri)
+ .append("\ncontactRingtoneUri: " + contactRefUri)
+ .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
+ .append("\ncachedPhoto: " + cachedPhoto)
+ .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
+ .append("\nemergency: " + mIsEmergency)
+ .append("\nvoicemail " + mIsVoiceMail)
+ .append("\ncontactExists " + contactExists)
+ .toString();
+ } else {
+ return new StringBuilder(128)
+ .append("CallerInfo { ")
+ .append("name " + ((name == null) ? "null" : "non-null"))
+ .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null"))
+ .append(" }")
+ .toString();
+ }
}
}
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 27a4dca..c7da3d4 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -951,22 +951,6 @@ public interface CommandsInterface {
void writeSmsToRuim(int status, String pdu, Message response);
- /**
- * @param apn
- * @param user
- * @param password
- * @param response
- */
- @Deprecated
- void setupDefaultPDP(String apn, String user, String password, Message response);
-
- /**
- * @param cid
- * @param response
- */
- @Deprecated
- void deactivateDefaultPDP(int cid, Message response);
-
void setRadioPower(boolean on, Message response);
void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response);
@@ -1354,10 +1338,12 @@ public interface CommandsInterface {
*
* @param cid
* The connection ID
+ * @param reason
+ * Data disconnect reason.
* @param result
* Callback message is empty on completion
*/
- public void deactivateDataCall(int cid, Message result);
+ public void deactivateDataCall(int cid, int reason, Message result);
/**
* Activate or deactivate cell broadcast SMS for CDMA.
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 2536745..58fb13b 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -26,6 +26,7 @@ import android.net.LinkProperties;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.EventLog;
import java.net.InetAddress;
@@ -312,9 +313,19 @@ public abstract class DataConnection extends HierarchicalStateMachine {
* and is either a DisconnectParams or ConnectionParams.
*/
private void tearDownData(Object o) {
+ int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+ if ((o != null) && (o instanceof DisconnectParams)) {
+ DisconnectParams dp = (DisconnectParams)o;
+ Message m = dp.onCompletedMsg;
+ if ((m != null) && (m.obj != null) && (m.obj instanceof String)) {
+ String reason = (String)m.obj;
+ if (TextUtils.equals(reason, Phone.REASON_RADIO_TURNED_OFF))
+ discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+ }
+ }
if (phone.mCM.getRadioState().isOn()) {
if (DBG) log("tearDownData radio is on, call deactivateDataCall");
- phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, o));
+ phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o));
} else {
if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
AsyncResult ar = new AsyncResult(o, null, null);
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index a77e73e..916602f 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1265,33 +1265,6 @@ public final class RIL extends BaseCommands implements CommandsInterface {
}
/**
- * @deprecated
- */
- public void
- setupDefaultPDP(String apn, String user, String password, Message result) {
- int radioTechnology;
- int authType;
- String profile = ""; //profile number, NULL for GSM/UMTS
-
- radioTechnology = RILConstants.SETUP_DATA_TECH_GSM;
- //TODO(): Add to the APN database, AuthType is set to CHAP/PAP
- authType = (user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP
- : RILConstants.SETUP_DATA_AUTH_NONE;
-
- setupDataCall(Integer.toString(radioTechnology), profile, apn, user,
- password, Integer.toString(authType), result);
-
- }
-
- /**
- * @deprecated
- */
- public void
- deactivateDefaultPDP(int cid, Message result) {
- deactivateDataCall(cid, result);
- }
-
- /**
* The preferred new alternative to setupDefaultPDP that is
* CDMA-compatible.
*
@@ -1329,15 +1302,16 @@ public final class RIL extends BaseCommands implements CommandsInterface {
}
public void
- deactivateDataCall(int cid, Message result) {
+ deactivateDataCall(int cid, int reason, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result);
- rr.mp.writeInt(1);
+ rr.mp.writeInt(2);
rr.mp.writeString(Integer.toString(cid));
+ rr.mp.writeString(Integer.toString(reason));
if (RILJ_LOGD) riljLog(rr.serialString() + "> " +
- requestToString(rr.mRequest) + " " + cid);
+ requestToString(rr.mRequest) + " " + cid + " " + reason);
send(rr);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 888f721..305e15f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -96,6 +96,10 @@ public interface RILConstants {
int SETUP_DATA_AUTH_CHAP = 2;
int SETUP_DATA_AUTH_PAP_CHAP = 3;
+ /* Deactivate data call reasons */
+ int DEACTIVATE_REASON_NONE = 0;
+ int DEACTIVATE_REASON_RADIO_OFF = 1;
+
/*
cat include/telephony/ril.h | \
egrep '^#define' | \
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index b60be6e..de15408 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -569,45 +569,24 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
synchronized (this) {
- if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) {
- /*
- * In 1x CDMA , during radio power off modem will disconnect the
- * data call and sends the power down registration message along
- * with the data call release message to the network
- */
-
- msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X
- dcTracker.sendMessage(msg);
-
- Log.w(LOG_TAG, "Turn off the radio right away");
- hangupAndPowerOff();
- } else {
- if (!mPendingRadioPowerOffAfterDataOff) {
- DataConnectionTracker.State currentState = dcTracker.getState();
- if (currentState != DataConnectionTracker.State.CONNECTED
- && currentState != DataConnectionTracker.State.DISCONNECTING
- && currentState != DataConnectionTracker.State.INITING) {
-
- msg.arg1 = 0; // tearDown is false as it is not needed.
- dcTracker.sendMessage(msg);
-
- if (DBG)
- log("Data disconnected, turn off radio right away.");
- hangupAndPowerOff();
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ DataConnectionTracker.State currentState = dcTracker.getState();
+ if (currentState != DataConnectionTracker.State.CONNECTED
+ && currentState != DataConnectionTracker.State.DISCONNECTING
+ && currentState != DataConnectionTracker.State.INITING) {
+ msg.arg1 = 0; // tearDown is false as it is not needed.
+ dcTracker.sendMessage(msg);
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
+ } else {
+ msg.arg1 = 1; // tearDown is true
+ dcTracker.sendMessage(msg);
+ if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
+ if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
} else {
- // clean data connection
- msg.arg1 = 1; // tearDown is true
- dcTracker.sendMessage(msg);
-
- if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
- if (DBG) {
- log("Wait upto 30s for data to disconnect, then turn off radio.");
- }
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
- hangupAndPowerOff();
- }
+ Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index ed578c8..2e729fe 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -184,19 +184,12 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
public void writeSmsToRuim(int status, String pdu, Message response) {
}
- public void setupDefaultPDP(String apn, String user, String password,
- Message result) {
- }
-
- public void deactivateDefaultPDP(int cid, Message result) {
- }
-
public void setupDataCall(String radioTechnology, String profile,
String apn, String user, String password, String authType,
Message result) {
}
- public void deactivateDataCall(int cid, Message result) {
+ public void deactivateDataCall(int cid, int reason, Message result) {
}
public void setRadioPower(boolean on, Message result) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index ef31ddd..58a4cba 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -393,12 +393,11 @@ public class SipPhone extends SipPhoneBase {
new SipProfile.Builder(calleeSipUri).build();
SipConnection c = new SipConnection(this, callee,
originalNumber);
- connections.add(c);
c.dial();
+ connections.add(c);
setState(Call.State.DIALING);
return c;
} catch (ParseException e) {
- // TODO: notify someone
throw new SipException("dial", e);
}
}
@@ -660,12 +659,6 @@ public class SipPhone extends SipPhoneBase {
@Override
protected void onError(DisconnectCause cause) {
if (DEBUG) Log.d(LOG_TAG, "SIP error: " + cause);
- if (mSipAudioCall.isInCall()
- && (cause != DisconnectCause.LOST_SIGNAL)) {
- // Don't end the call when in a call.
- return;
- }
-
onCallEnded(cause);
}
};
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index fdcf78d..c2f88e5 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -954,20 +954,12 @@ public final class SimulatedCommands extends BaseCommands
unimplemented(response);
}
- @Deprecated
- public void setupDefaultPDP(String apn, String user, String password, Message result) {
- unimplemented(result);
- }
-
public void setupDataCall(String radioTechnology, String profile, String apn, String user,
String password, String authType, Message result) {
unimplemented(result);
}
- public void deactivateDataCall(int cid, Message result) {unimplemented(result);}
-
- @Deprecated
- public void deactivateDefaultPDP(int cid, Message result) {unimplemented(result);}
+ public void deactivateDataCall(int cid, int reason, Message result) {unimplemented(result);}
public void setPreferredNetworkType(int networkType , Message result) {
mNetworkType = networkType;
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index c151251..dc44b25 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -25,7 +25,9 @@
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
- <activity android:name="TestShellActivity" android:launchMode="singleTop"
+ <activity android:name="TestShellActivity"
+ android:launchMode="singleTop"
+ android:hardwareAccelerated="true"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Light"/>
<activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index b901e0d..bc6ad64 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -92,13 +92,6 @@ public class Canvas_Delegate {
return mGraphicsStack.peek();
}
- /**
- * Disposes of the {@link Graphics2D} stack.
- */
- public void dispose() {
-
- }
-
// ---- native methods ----
/*package*/ static boolean isOpaque(Canvas thisCanvas) {
@@ -985,6 +978,16 @@ public class Canvas_Delegate {
}
/*package*/ static void finalizer(int nativeCanvas) {
+ // get the delegate from the native int so that it can be disposed.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return;
+ }
+
+ canvasDelegate.dispose();
+
+ // remove it from the manager.
sManager.removeDelegate(nativeCanvas);
}
@@ -997,6 +1000,15 @@ public class Canvas_Delegate {
private Canvas_Delegate() {
}
+ /**
+ * Disposes of the {@link Graphics2D} stack.
+ */
+ private void dispose() {
+ while (mGraphicsStack.size() > 0) {
+ mGraphicsStack.pop().dispose();
+ }
+ }
+
private void setBitmap(BufferedImage image) {
mBufferedImage = image;
mGraphicsStack.push(mBufferedImage.createGraphics());
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 2de1cbb..392717e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge;
+import com.android.layoutlib.api.Capabilities;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.IProjectCallback;
import com.android.layoutlib.api.IResourceValue;
@@ -23,6 +24,7 @@ import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.LayoutBridge;
import com.android.layoutlib.api.SceneParams;
import com.android.layoutlib.api.SceneResult;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
import com.android.layoutlib.bridge.android.BridgeAssetManager;
import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.LayoutSceneImpl;
@@ -32,12 +34,14 @@ import com.android.tools.layoutlib.create.OverrideMethod;
import android.graphics.Bitmap;
import android.graphics.Typeface_Delegate;
+import android.os.Looper;
import android.util.Finalizers;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
@@ -150,11 +154,19 @@ public final class Bridge extends LayoutBridge {
}
};
+ private EnumSet<Capabilities> mCapabilities;
+
+
@Override
public int getApiLevel() {
return LayoutBridge.API_CURRENT;
}
+ @Override
+ public EnumSet<Capabilities> getCapabilities() {
+ return mCapabilities;
+ }
+
/*
* (non-Javadoc)
* @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map)
@@ -163,6 +175,15 @@ public final class Bridge extends LayoutBridge {
public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
sEnumValueMap = enumValueMap;
+ // don't use EnumSet.allOf(), because the bridge doesn't come with it's specific version
+ // of layoutlib_api. It is provided by the client which could have a more recent version
+ // with newer, unsupported capabilities.
+ mCapabilities = EnumSet.of(
+ Capabilities.RENDER,
+ Capabilities.VIEW_MANIPULATION,
+ Capabilities.ANIMATE);
+
+
Finalizers.init();
BridgeAssetManager.initSystem();
@@ -290,26 +311,31 @@ public final class Bridge extends LayoutBridge {
@Override
public BridgeLayoutScene createScene(SceneParams params) {
try {
- SceneResult lastResult = SceneResult.SUCCESS;
+ SceneResult lastResult = SceneStatus.SUCCESS.getResult();
LayoutSceneImpl scene = new LayoutSceneImpl(params);
try {
- scene.prepareThread();
+ prepareThread();
lastResult = scene.init(params.getTimeout());
- if (lastResult == SceneResult.SUCCESS) {
+ if (lastResult.isSuccess()) {
lastResult = scene.inflate();
- if (lastResult == SceneResult.SUCCESS) {
+ if (lastResult.isSuccess()) {
lastResult = scene.render();
}
}
} finally {
scene.release();
- scene.cleanupThread();
+ cleanupThread();
}
return new BridgeLayoutScene(scene, lastResult);
} catch (Throwable t) {
- t.printStackTrace();
- return new BridgeLayoutScene(null, new SceneResult("error!", t));
+ // get the real cause of the exception.
+ Throwable t2 = t;
+ while (t2.getCause() != null) {
+ t2 = t.getCause();
+ }
+ return new BridgeLayoutScene(null,
+ new SceneResult(SceneStatus.ERROR_UNKNOWN, t2.getMessage(), t2));
}
}
@@ -333,6 +359,31 @@ public final class Bridge extends LayoutBridge {
}
/**
+ * Prepares the current thread for rendering.
+ *
+ * Note that while this can be called several time, the first call to {@link #cleanupThread()}
+ * will do the clean-up, and make the thread unable to do further scene actions.
+ */
+ public static void prepareThread() {
+ // we need to make sure the Looper has been initialized for this thread.
+ // this is required for View that creates Handler objects.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ /**
+ * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
+ * <p>
+ * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
+ * call to this will prevent the thread from doing further scene actions
+ */
+ public static void cleanupThread() {
+ // clean up the looper
+ Looper.sThreadLocal.remove();
+ }
+
+ /**
* Returns details of a framework resource from its integer value.
* @param value the integer value
* @return an array of 2 strings containing the resource name and type, or null if the id
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
index 97bf857..a491901 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
@@ -16,12 +16,16 @@
package com.android.layoutlib.bridge;
+import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.LayoutScene;
import com.android.layoutlib.api.SceneParams;
import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.api.ViewInfo;
import com.android.layoutlib.bridge.impl.LayoutSceneImpl;
+import android.view.View;
+import android.view.ViewGroup;
+
import java.awt.image.BufferedImage;
import java.util.Map;
@@ -60,14 +64,14 @@ public class BridgeLayoutScene extends LayoutScene {
@Override
public SceneResult render(long timeout) {
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(timeout);
- if (mLastResult == SceneResult.SUCCESS) {
+ if (mLastResult.isSuccess()) {
mLastResult = mScene.render();
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
@@ -77,28 +81,94 @@ public class BridgeLayoutScene extends LayoutScene {
public SceneResult animate(Object targetObject, String animationName,
boolean isFrameworkAnimation, IAnimationListener listener) {
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
- if (mLastResult == SceneResult.SUCCESS) {
+ if (mLastResult.isSuccess()) {
mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation,
listener);
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
}
@Override
- public void dispose() {
- // TODO Auto-generated method stub
+ public SceneResult insertChild(Object parentView, IXmlPullParser childXml, int index,
+ IAnimationListener listener) {
+ if (parentView instanceof ViewGroup == false) {
+ throw new IllegalArgumentException("parentView is not a ViewGroup");
+ }
+
+ try {
+ Bridge.prepareThread();
+ mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
+ if (mLastResult.isSuccess()) {
+ mLastResult = mScene.insertChild((ViewGroup) parentView, childXml, index, listener);
+ }
+ } finally {
+ mScene.release();
+ Bridge.cleanupThread();
+ }
+
+ return mLastResult;
+ }
+
+
+ @Override
+ public SceneResult moveChild(Object parentView, Object childView, int index,
+ Map<String, String> layoutParams, IAnimationListener listener) {
+ if (parentView instanceof ViewGroup == false) {
+ throw new IllegalArgumentException("parentView is not a ViewGroup");
+ }
+ if (childView instanceof View == false) {
+ throw new IllegalArgumentException("childView is not a View");
+ }
+
+ try {
+ Bridge.prepareThread();
+ mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
+ if (mLastResult.isSuccess()) {
+ mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView, index,
+ listener);
+ }
+ } finally {
+ mScene.release();
+ Bridge.cleanupThread();
+ }
+
+ return mLastResult;
+ }
+
+ @Override
+ public SceneResult removeChild(Object childView, IAnimationListener listener) {
+ if (childView instanceof View == false) {
+ throw new IllegalArgumentException("childView is not a View");
+ }
+
+ try {
+ Bridge.prepareThread();
+ mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
+ if (mLastResult.isSuccess()) {
+ mLastResult = mScene.removeChild((View) childView, listener);
+ }
+ } finally {
+ mScene.release();
+ Bridge.cleanupThread();
+ }
+ return mLastResult;
+ }
+
+ @Override
+ public void dispose() {
}
/*package*/ BridgeLayoutScene(LayoutSceneImpl scene, SceneResult lastResult) {
mScene = scene;
+ mScene.setScene(this);
mLastResult = lastResult;
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index c20bdfd..2b9d52f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -16,8 +16,11 @@
package com.android.layoutlib.bridge.impl;
+import com.android.layoutlib.api.LayoutScene;
import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.api.LayoutScene.IAnimationListener;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
+import com.android.layoutlib.bridge.Bridge;
import android.animation.Animator;
import android.animation.ValueAnimator;
@@ -57,7 +60,7 @@ public class AnimationThread extends Thread {
@Override
public void run() {
- mScene.prepareThread();
+ Bridge.prepareThread();
try {
Handler_Delegate.setCallback(new IHandlerCallback() {
public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
@@ -75,6 +78,7 @@ public class AnimationThread extends Thread {
mAnimator.start();
// loop the animation
+ LayoutScene scene = mScene.getScene();
do {
// get the next message.
MessageBundle bundle = mQueue.poll();
@@ -88,14 +92,14 @@ public class AnimationThread extends Thread {
try {
sleep(bundle.mUptimeMillis - currentTime);
} catch (InterruptedException e) {
- // TODO Auto-generated catch block
+ // FIXME log/do something/sleep again?
e.printStackTrace();
}
}
// ready to do the work, acquire the scene.
SceneResult result = mScene.acquire(250);
- if (result != SceneResult.SUCCESS) {
+ if (result.isSuccess() == false) {
mListener.done(result);
return;
}
@@ -104,18 +108,18 @@ public class AnimationThread extends Thread {
// the next message, so mQueue will have another one.
try {
bundle.mTarget.handleMessage(bundle.mMessage);
- if (mScene.render() == SceneResult.SUCCESS) {
- mListener.onNewFrame(mScene.getImage());
+ if (mScene.render().isSuccess()) {
+ mListener.onNewFrame(scene);
}
} finally {
mScene.release();
}
} while (mQueue.size() > 0);
- mListener.done(SceneResult.SUCCESS);
+ mListener.done(SceneStatus.SUCCESS.getResult());
} finally {
Handler_Delegate.setCallback(null);
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
}
}
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 0859976..74e7fb2 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
@@ -24,13 +24,16 @@ import com.android.internal.util.XmlUtils;
import com.android.layoutlib.api.IProjectCallback;
import com.android.layoutlib.api.IResourceValue;
import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.LayoutBridge;
+import com.android.layoutlib.api.LayoutScene;
import com.android.layoutlib.api.SceneParams;
import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.api.ViewInfo;
import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
import com.android.layoutlib.api.LayoutScene.IAnimationListener;
import com.android.layoutlib.api.SceneParams.RenderingMode;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
@@ -48,11 +51,11 @@ import android.graphics.Canvas;
import android.graphics.Canvas_Delegate;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
@@ -92,6 +95,7 @@ public class LayoutSceneImpl {
private final SceneParams mParams;
// scene state
+ private LayoutScene mScene;
private BridgeContext mContext;
private BridgeXmlBlockParser mBlockParser;
private BridgeInflater mInflater;
@@ -99,6 +103,9 @@ public class LayoutSceneImpl {
private int mScreenOffset;
private IResourceValue mWindowBackground;
private FrameLayout mViewRoot;
+ private Canvas mCanvas;
+ private int mMeasuredScreenWidth = -1;
+ private int mMeasuredScreenHeight = -1;
// information being returned through the API
private BufferedImage mImage;
@@ -193,32 +200,7 @@ public class LayoutSceneImpl {
mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
mContext, false /* platformResourceFlag */);
- return SceneResult.SUCCESS;
- }
-
- /**
- * Prepares the current thread for rendering.
- *
- * Note that while this can be called several time, the first call to {@link #cleanupThread()}
- * will do the clean-up, and make the thread unable to do further scene actions.
- */
- public void prepareThread() {
- // we need to make sure the Looper has been initialized for this thread.
- // this is required for View that creates Handler objects.
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
- /**
- * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
- * <p>
- * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
- * call to this will prevent the thread from doing further scene actions
- */
- public void cleanupThread() {
- // clean up the looper
- Looper.sThreadLocal.remove();
+ return SceneStatus.SUCCESS.getResult();
}
/**
@@ -360,9 +342,9 @@ public class LayoutSceneImpl {
mViewRoot.setBackgroundDrawable(d);
}
- return SceneResult.SUCCESS;
+ return SceneStatus.SUCCESS.getResult();
} catch (PostInflateException e) {
- return new SceneResult("Error during post inflation process:\n" + e.getMessage());
+ return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e);
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -373,7 +355,7 @@ public class LayoutSceneImpl {
// log it
mParams.getLogger().error(t);
- return new SceneResult("Unknown error during inflation.", t);
+ return new SceneResult(SceneStatus.ERROR_INFLATION, t.getMessage(), t);
}
}
@@ -384,94 +366,110 @@ public class LayoutSceneImpl {
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SceneParams#getRenderingMode()
*/
public SceneResult render() {
checkLock();
try {
- long current = System.currentTimeMillis();
if (mViewRoot == null) {
- return new SceneResult("Layout has not been inflated!");
+ return new SceneResult(SceneStatus.ERROR_NOT_INFLATED);
}
// measure the views
int w_spec, h_spec;
- int renderScreenWidth = mParams.getScreenWidth();
- int renderScreenHeight = mParams.getScreenHeight();
-
RenderingMode renderingMode = mParams.getRenderingMode();
- if (renderingMode != RenderingMode.NORMAL) {
- // measure the full size needed by the layout.
- w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
- renderingMode.isHorizExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY);
- h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
- renderingMode.isVertExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY);
- mViewRoot.measure(w_spec, h_spec);
-
- if (renderingMode.isHorizExpand()) {
- int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
- if (neededWidth > renderScreenWidth) {
- renderScreenWidth = neededWidth;
+ // only do the screen measure when needed.
+ boolean newRenderSize = false;
+ if (mMeasuredScreenWidth == -1) {
+ newRenderSize = true;
+ mMeasuredScreenWidth = mParams.getScreenWidth();
+ mMeasuredScreenHeight = mParams.getScreenHeight();
+
+ if (renderingMode != RenderingMode.NORMAL) {
+ // measure the full size needed by the layout.
+ w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
+ renderingMode.isHorizExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
+ h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
+ renderingMode.isVertExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
+ mViewRoot.measure(w_spec, h_spec);
+
+ if (renderingMode.isHorizExpand()) {
+ int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
+ if (neededWidth > mMeasuredScreenWidth) {
+ mMeasuredScreenWidth = neededWidth;
+ }
}
- }
- if (renderingMode.isVertExpand()) {
- int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
- if (neededHeight > renderScreenHeight - mScreenOffset) {
- renderScreenHeight = neededHeight + mScreenOffset;
+ if (renderingMode.isVertExpand()) {
+ int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
+ if (neededHeight > mMeasuredScreenHeight - mScreenOffset) {
+ mMeasuredScreenHeight = neededHeight + mScreenOffset;
+ }
}
}
}
// remeasure with the size we need
// This must always be done before the call to layout
- w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY);
- h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
+ w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY);
+ h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
MeasureSpec.EXACTLY);
mViewRoot.measure(w_spec, h_spec);
// now do the layout.
- mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight);
+ mViewRoot.layout(0, mScreenOffset, mMeasuredScreenWidth, mMeasuredScreenHeight);
// draw the views
// create the BufferedImage into which the layout will be rendered.
- mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
- BufferedImage.TYPE_INT_ARGB);
-
- if (mParams.isCustomBackgroundEnabled()) {
- Graphics2D gc = mImage.createGraphics();
- gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
- gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset);
- gc.dispose();
- }
+ if (newRenderSize || mCanvas == null) {
+ if (mParams.getImageFactory() != null) {
+ mImage = mParams.getImageFactory().getImage(mMeasuredScreenWidth,
+ mMeasuredScreenHeight - mScreenOffset);
+ } else {
+ mImage = new BufferedImage(mMeasuredScreenWidth,
+ mMeasuredScreenHeight - mScreenOffset, BufferedImage.TYPE_INT_ARGB);
+ }
- // create an Android bitmap around the BufferedImage
- Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
- true /*isMutable*/,
- Density.getEnum(mParams.getDensity()));
+ if (mParams.isCustomBackgroundEnabled()) {
+ Graphics2D gc = mImage.createGraphics();
+ gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
+ gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight - mScreenOffset);
+ gc.dispose();
+ }
+
+ // 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());
+ // create a Canvas around the Android bitmap
+ mCanvas = new Canvas(bitmap);
+ mCanvas.setDensity(mParams.getDensity());
+ }
// to set the logger, get the native delegate
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(mCanvas);
canvasDelegate.setLogger(mParams.getLogger());
- mViewRoot.draw(canvas);
- canvasDelegate.dispose();
+ long preDrawTime = System.currentTimeMillis();
+
+ mViewRoot.draw(mCanvas);
+
+ long drawTime = System.currentTimeMillis();
mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
- System.out.println("rendering (ms): " + (System.currentTimeMillis() - current));
+ System.out.println(String.format("rendering (ms): %03d", drawTime - preDrawTime));
// success!
- return SceneResult.SUCCESS;
+ return SceneStatus.SUCCESS.getResult();
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -482,7 +480,7 @@ public class LayoutSceneImpl {
// log it
mParams.getLogger().error(t);
- return new SceneResult("Unknown error during inflation.", t);
+ return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
}
}
@@ -522,15 +520,93 @@ public class LayoutSceneImpl {
new AnimationThread(this, anim, listener).start();
- return SceneResult.SUCCESS;
+ return SceneStatus.SUCCESS.getResult();
}
} catch (Exception e) {
- e.printStackTrace();
- return new SceneResult("", e);
+ // get the real cause of the exception.
+ Throwable t = e;
+ while (t.getCause() != null) {
+ t = t.getCause();
+ }
+
+ return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
}
}
- return new SceneResult("Failed to find animation");
+ return new SceneResult(SceneStatus.ERROR_ANIM_NOT_FOUND);
+ }
+
+ public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml,
+ int index, IAnimationListener listener) {
+ checkLock();
+
+ // create a block parser for the XML
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(childXml, mContext,
+ false /* platformResourceFlag */);
+
+ // inflate the child without adding it to the root since we want to control where it'll
+ // get added. We do pass the parentView however to ensure that the layoutParams will
+ // be created correctly.
+ View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
+
+ // add it to the parentView in the correct location
+ try {
+ parentView.addView(child, index);
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ }
+
+ invalidateRenderingSize();
+
+ SceneResult result = render();
+ if (result.isSuccess()) {
+ result.setData(child);
+ }
+
+ return result;
+ }
+
+ public SceneResult moveChild(ViewGroup parentView, View childView, int index,
+ IAnimationListener listener) {
+ checkLock();
+
+ try {
+ ViewParent parent = childView.getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup parentGroup = (ViewGroup) parent;
+ parentGroup.removeView(childView);
+ }
+
+ // add it to the parentView in the correct location
+ parentView.addView(childView, index);
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ }
+
+ invalidateRenderingSize();
+
+ return render();
+ }
+
+ public SceneResult removeChild(View childView, IAnimationListener listener) {
+ checkLock();
+
+ try {
+ ViewParent parent = childView.getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup parentGroup = (ViewGroup) parent;
+ parentGroup.removeView(childView);
+ }
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ }
+
+ invalidateRenderingSize();
+
+ return render();
}
/**
@@ -899,6 +975,10 @@ public class LayoutSceneImpl {
return result;
}
+ private void invalidateRenderingSize() {
+ mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
+ }
+
public BufferedImage getImage() {
return mImage;
}
@@ -910,4 +990,12 @@ public class LayoutSceneImpl {
public Map<String, String> getDefaultViewPropertyValues(Object viewObject) {
return mContext.getDefaultPropMap(viewObject);
}
+
+ public void setScene(LayoutScene scene) {
+ mScene = scene;
+ }
+
+ public LayoutScene getScene() {
+ return mScene;
+ }
}
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 6ec223b..097b109 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -16,6 +16,7 @@
#include <ui/KeyCharacterMap.h>
#include <ui/KeyLayoutMap.h>
+#include <ui/VirtualKeyMap.h>
#include <utils/String8.h>
#include <stdio.h>
@@ -30,6 +31,7 @@ enum FileType {
FILETYPE_UNKNOWN,
FILETYPE_KEYLAYOUT,
FILETYPE_KEYCHARACTERMAP,
+ FILETYPE_VIRTUALKEYDEFINITION,
};
@@ -37,8 +39,10 @@ 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);
+ " %s [*.kl] [*.kcm] [virtualkeys.*] [...]\n"
+ " Validates the specified key layouts, key character maps \n"
+ " or virtual key definitions.\n\n",
+ gProgName);
}
static FileType getFileType(const char* filename) {
@@ -51,6 +55,11 @@ static FileType getFileType(const char* filename) {
return FILETYPE_KEYCHARACTERMAP;
}
}
+
+ if (strstr(filename, "virtualkeys.")) {
+ return FILETYPE_VIRTUALKEYDEFINITION;
+ }
+
return FILETYPE_UNKNOWN;
}
@@ -60,7 +69,7 @@ static bool validateFile(const char* filename) {
FileType fileType = getFileType(filename);
switch (fileType) {
case FILETYPE_UNKNOWN:
- fprintf(stderr, "File extension must be .kl or .kcm.\n\n");
+ fprintf(stderr, "Supported file types: *.kl, *.kcm, virtualkeys.*\n\n");
return false;
case FILETYPE_KEYLAYOUT: {
@@ -82,6 +91,16 @@ static bool validateFile(const char* filename) {
}
break;
}
+
+ case FILETYPE_VIRTUALKEYDEFINITION: {
+ VirtualKeyMap* map;
+ status_t status = VirtualKeyMap::load(String8(filename), &map);
+ if (status) {
+ fprintf(stderr, "Error %d parsing virtual key definition file.\n\n", status);
+ return false;
+ }
+ break;
+ }
}
fputs("No errors.\n\n", stdout);
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index 43a3827..a6b54d8 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -21,14 +21,14 @@ import java.util.Map;
/**
* An AudioGroup acts as a router connected to the speaker, the microphone, and
- * {@link AudioStream}s. Its pipeline has four steps. First, for each
- * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
- * packets and stores in its buffer. Then, if the microphone is enabled,
- * processes the recorded audio and stores in its buffer. Third, if the speaker
- * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for
- * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
- * buffers and sends back the encoded packets. An AudioGroup does nothing if
- * there is no AudioStream in it.
+ * {@link AudioStream}s. Its execution loop consists of four steps. First, for
+ * each AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its
+ * incoming packets and stores in its buffer. Then, if the microphone is
+ * enabled, processes the recorded audio and stores in its buffer. Third, if the
+ * speaker is enabled, mixes and playbacks buffers of all AudioStreams. Finally,
+ * for each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all
+ * other buffers and sends back the encoded packets. An AudioGroup does nothing
+ * if there is no AudioStream in it.
*
* <p>Few things must be noticed before using these classes. The performance is
* highly related to the system load and the network bandwidth. Usually a
@@ -47,7 +47,12 @@ import java.util.Map;
* modes other than {@link #MODE_ON_HOLD}. In addition, before adding an
* AudioStream into an AudioGroup, one should always put all other AudioGroups
* into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly
- * initialized.
+ * initialized.</p>
+ *
+ * <p class="note">Using this class requires
+ * {@link android.Manifest.permission#RECORD_AUDIO} permission.</p>
+ *
+ * @see AudioStream
* @hide
*/
public class AudioGroup {
@@ -78,6 +83,8 @@ public class AudioGroup {
*/
public static final int MODE_ECHO_SUPPRESSION = 3;
+ private static final int MODE_LAST = 3;
+
private final Map<AudioStream, Integer> mStreams;
private int mMode = MODE_ON_HOLD;
@@ -94,6 +101,15 @@ public class AudioGroup {
}
/**
+ * Returns the {@link AudioStream}s in this group.
+ */
+ public AudioStream[] getStreams() {
+ synchronized (this) {
+ return mStreams.keySet().toArray(new AudioStream[mStreams.size()]);
+ }
+ }
+
+ /**
* Returns the current mode.
*/
public int getMode() {
@@ -108,49 +124,77 @@ public class AudioGroup {
* @param mode The mode to change to.
* @throws IllegalArgumentException if the mode is invalid.
*/
- public synchronized native void setMode(int mode);
-
- private native void add(int mode, int socket, String remoteAddress,
- int remotePort, String codecSpec, int dtmfType);
+ public void setMode(int mode) {
+ if (mode < 0 || mode > MODE_LAST) {
+ throw new IllegalArgumentException("Invalid mode");
+ }
+ synchronized (this) {
+ nativeSetMode(mode);
+ mMode = mode;
+ }
+ }
- synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) {
- if (!mStreams.containsKey(stream)) {
- try {
- int socket = stream.dup();
- String codecSpec = String.format("%d %s %s", codec.type,
- codec.rtpmap, codec.fmtp);
- add(stream.getMode(), socket,
- stream.getRemoteAddress().getHostAddress(),
- stream.getRemotePort(), codecSpec, dtmfType);
- mStreams.put(stream, socket);
- } catch (NullPointerException e) {
- throw new IllegalStateException(e);
+ private native void nativeSetMode(int mode);
+
+ // Package-private method used by AudioStream.join().
+ void add(AudioStream stream, AudioCodec codec, int dtmfType) {
+ synchronized (this) {
+ if (!mStreams.containsKey(stream)) {
+ try {
+ int socket = stream.dup();
+ String codecSpec = String.format("%d %s %s", codec.type,
+ codec.rtpmap, codec.fmtp);
+ nativeAdd(stream.getMode(), socket,
+ stream.getRemoteAddress().getHostAddress(),
+ stream.getRemotePort(), codecSpec, dtmfType);
+ mStreams.put(stream, socket);
+ } catch (NullPointerException e) {
+ throw new IllegalStateException(e);
+ }
}
}
}
- private native void remove(int socket);
+ private native void nativeAdd(int mode, int socket, String remoteAddress,
+ int remotePort, String codecSpec, int dtmfType);
- synchronized void remove(AudioStream stream) {
- Integer socket = mStreams.remove(stream);
- if (socket != null) {
- remove(socket);
+ // Package-private method used by AudioStream.join().
+ void remove(AudioStream stream) {
+ synchronized (this) {
+ Integer socket = mStreams.remove(stream);
+ if (socket != null) {
+ nativeRemove(socket);
+ }
}
}
+ private native void nativeRemove(int socket);
+
/**
* Sends a DTMF digit to every {@link AudioStream} in this group. Currently
* only event {@code 0} to {@code 15} are supported.
*
* @throws IllegalArgumentException if the event is invalid.
*/
- public native synchronized void sendDtmf(int event);
+ public void sendDtmf(int event) {
+ if (event < 0 || event > 15) {
+ throw new IllegalArgumentException("Invalid event");
+ }
+ synchronized (this) {
+ nativeSendDtmf(event);
+ }
+ }
+
+ private native void nativeSendDtmf(int event);
/**
* Removes every {@link AudioStream} in this group.
*/
- public synchronized void clear() {
- remove(-1);
+ public void clear() {
+ synchronized (this) {
+ mStreams.clear();
+ nativeRemove(-1);
+ }
}
@Override
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index e5197ce..0edae6b 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -34,8 +34,12 @@ import java.net.SocketException;
* of the setter methods are disabled. This is designed to ease the task of
* managing native resources. One can always make an AudioStream leave its
* AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it
- * back after the modification is done.
+ * back after the modification is done.</p>
*
+ * <p class="note">Using this class requires
+ * {@link android.Manifest.permission#INTERNET} permission.</p>
+ *
+ * @see RtpStream
* @see AudioGroup
* @hide
*/
@@ -82,16 +86,18 @@ public class AudioStream extends RtpStream {
* @see AudioGroup
*/
public void join(AudioGroup group) {
- if (mGroup == group) {
- return;
- }
- if (mGroup != null) {
- mGroup.remove(this);
- mGroup = null;
- }
- if (group != null) {
- group.add(this, mCodec, mDtmfType);
- mGroup = group;
+ synchronized (this) {
+ if (mGroup == group) {
+ return;
+ }
+ if (mGroup != null) {
+ mGroup.remove(this);
+ mGroup = null;
+ }
+ if (group != null) {
+ group.add(this, mCodec, mDtmfType);
+ mGroup = group;
+ }
}
}
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
index 23fb258..87d8bc6 100644
--- a/voip/java/android/net/rtp/RtpStream.java
+++ b/voip/java/android/net/rtp/RtpStream.java
@@ -24,6 +24,9 @@ import java.net.SocketException;
/**
* RtpStream represents the base class of streams which send and receive network
* packets with media payloads over Real-time Transport Protocol (RTP).
+ *
+ * <p class="note">Using this class requires
+ * {@link android.Manifest.permission#INTERNET} permission.</p>
* @hide
*/
public class RtpStream {
@@ -43,6 +46,8 @@ public class RtpStream {
*/
public static final int MODE_RECEIVE_ONLY = 2;
+ private static final int MODE_LAST = 2;
+
private final InetAddress mLocalAddress;
private final int mLocalPort;
@@ -129,7 +134,7 @@ public class RtpStream {
if (isBusy()) {
throw new IllegalStateException("Busy");
}
- if (mode != MODE_NORMAL && mode != MODE_SEND_ONLY && mode != MODE_RECEIVE_ONLY) {
+ if (mode < 0 || mode > MODE_LAST) {
throw new IllegalArgumentException("Invalid mode");
}
mMode = mode;
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index f275e39..51236fe 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -594,12 +594,10 @@ public class SipAudioCall {
*/
public void holdCall(int timeout) throws SipException {
synchronized (this) {
- if (mHold) return;
+ if (mHold) return;
mSipSession.changeCall(createHoldOffer().encode(), timeout);
mHold = true;
-
- AudioGroup audioGroup = getAudioGroup();
- if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+ setAudioGroupMode();
}
}
@@ -643,8 +641,7 @@ public class SipAudioCall {
if (!mHold) return;
mSipSession.changeCall(createContinueOffer().encode(), timeout);
mHold = false;
- AudioGroup audioGroup = getAudioGroup();
- if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
+ setAudioGroupMode();
}
}
@@ -767,13 +764,8 @@ public class SipAudioCall {
/** Toggles mute. */
public void toggleMute() {
synchronized (this) {
- AudioGroup audioGroup = getAudioGroup();
- if (audioGroup != null) {
- audioGroup.setMode(mMuted
- ? AudioGroup.MODE_NORMAL
- : AudioGroup.MODE_MUTED);
- mMuted = !mMuted;
- }
+ mMuted = !mMuted;
+ setAudioGroupMode();
}
}
@@ -792,14 +784,22 @@ public class SipAudioCall {
* Puts the device to speaker mode.
* <p class="note"><strong>Note:</strong> Requires the
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
+ *
+ * @param speakerMode set true to enable speaker mode; false to disable
*/
public void setSpeakerMode(boolean speakerMode) {
synchronized (this) {
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
.setSpeakerphoneOn(speakerMode);
+ setAudioGroupMode();
}
}
+ private boolean isSpeakerOn() {
+ return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
+ .isSpeakerphoneOn();
+ }
+
/**
* Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
* event 0--9 maps to decimal
@@ -876,7 +876,11 @@ public class SipAudioCall {
/**
* Sets the {@link AudioGroup} object which the {@link AudioStream} object
* joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
- * will be dynamically created when needed.
+ * will be dynamically created when needed. Note that the mode of the
+ * {@code AudioGroup} is not changed according to the audio settings (i.e.,
+ * hold, mute, speaker phone) of this object. This is mainly used to merge
+ * multiple {@code SipAudioCall} objects to form a conference call. The
+ * settings of the first object (that merges others) override others'.
*
* @see #getAudioStream
* @hide
@@ -992,16 +996,25 @@ public class SipAudioCall {
// AudioGroup logic:
AudioGroup audioGroup = getAudioGroup();
if (mHold) {
- if (audioGroup != null) {
- audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
- }
// don't create an AudioGroup here; doing so will fail if
// there's another AudioGroup out there that's active
} else {
if (audioGroup == null) audioGroup = new AudioGroup();
stream.join(audioGroup);
- if (mMuted) {
+ }
+ setAudioGroupMode();
+ }
+
+ // set audio group mode based on current audio configuration
+ private void setAudioGroupMode() {
+ AudioGroup audioGroup = getAudioGroup();
+ if (audioGroup != null) {
+ if (mHold) {
+ audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+ } else if (mMuted) {
audioGroup.setMode(AudioGroup.MODE_MUTED);
+ } else if (isSpeakerOn()) {
+ audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
} else {
audioGroup.setMode(AudioGroup.MODE_NORMAL);
}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 8aaa805..2e38662 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -314,10 +314,6 @@ public class SipManager {
SipAudioCall call = new SipAudioCall(mContext, localProfile);
call.setListener(listener);
SipSession s = createSipSession(localProfile, null);
- if (s == null) {
- throw new SipException(
- "Failed to create SipSession; network available?");
- }
call.makeCall(peerProfile, s, timeout);
return call;
}
@@ -366,7 +362,9 @@ public class SipManager {
*/
public SipAudioCall takeAudioCall(Intent incomingCallIntent,
SipAudioCall.Listener listener) throws SipException {
- if (incomingCallIntent == null) return null;
+ if (incomingCallIntent == null) {
+ throw new SipException("Cannot retrieve session with null intent");
+ }
String callId = getCallId(incomingCallIntent);
if (callId == null) {
@@ -381,7 +379,9 @@ public class SipManager {
try {
ISipSession session = mSipService.getPendingSession(callId);
- if (session == null) return null;
+ if (session == null) {
+ throw new SipException("No pending session for the call");
+ }
SipAudioCall call = new SipAudioCall(
mContext, session.getLocalProfile());
call.attachCall(new SipSession(session), offerSd);
@@ -526,6 +526,10 @@ public class SipManager {
SipSession.Listener listener) throws SipException {
try {
ISipSession s = mSipService.createSession(localProfile, null);
+ if (s == null) {
+ throw new SipException(
+ "Failed to create SipSession; network unavailable?");
+ }
return new SipSession(s, listener);
} catch (RemoteException e) {
throw new SipException("createSipSession()", e);
@@ -541,7 +545,7 @@ public class SipManager {
try {
return mSipService.getListOfProfiles();
} catch (RemoteException e) {
- return null;
+ return new SipProfile[0];
}
}
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index f480fec..3af6e78 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -135,7 +135,7 @@ public final class SipService extends ISipService.Stub {
switch (state) {
case WifiManager.WIFI_STATE_ENABLED:
mWifiEnabled = true;
- if (anyOpened()) grabWifiLock();
+ if (anyOpenedToReceiveCalls()) grabWifiLock();
break;
case WifiManager.WIFI_STATE_DISABLED:
mWifiEnabled = false;
@@ -231,7 +231,7 @@ public final class SipService extends ISipService.Stub {
notifyProfileRemoved(group.getLocalProfile());
group.close();
- if (!anyOpened()) {
+ if (!anyOpenedToReceiveCalls()) {
releaseWifiLock();
mMyWakeLock.reset(); // in case there's leak
}
@@ -243,7 +243,7 @@ public final class SipService extends ISipService.Stub {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
if (group == null) return false;
if (isCallerCreatorOrRadio(group)) {
- return group.isOpened();
+ return true;
} else {
Log.w(TAG, "only creator or radio can query on the profile");
return false;
@@ -358,9 +358,9 @@ public final class SipService extends ISipService.Stub {
mContext.sendBroadcast(intent);
}
- private boolean anyOpened() {
+ private boolean anyOpenedToReceiveCalls() {
for (SipSessionGroupExt group : mSipGroups.values()) {
- if (group.isOpened()) return true;
+ if (group.isOpenedToReceiveCalls()) return true;
}
return false;
}
@@ -479,7 +479,7 @@ public final class SipService extends ISipService.Stub {
private class SipSessionGroupExt extends SipSessionAdapter {
private SipSessionGroup mSipGroup;
private PendingIntent mIncomingCallPendingIntent;
- private boolean mOpened;
+ private boolean mOpenedToReceiveCalls;
private AutoRegistrationProcess mAutoRegistration =
new AutoRegistrationProcess();
@@ -541,7 +541,7 @@ public final class SipService extends ISipService.Stub {
}
public void openToReceiveCalls() throws SipException {
- mOpened = true;
+ mOpenedToReceiveCalls = true;
if (mConnected) {
mSipGroup.openToReceiveCalls(this);
mAutoRegistration.start(mSipGroup);
@@ -555,9 +555,9 @@ public final class SipService extends ISipService.Stub {
mSipGroup.onConnectivityChanged();
if (connected) {
resetGroup(mLocalIp);
- if (mOpened) openToReceiveCalls();
+ if (mOpenedToReceiveCalls) openToReceiveCalls();
} else {
- // close mSipGroup but remember mOpened
+ // close mSipGroup but remember mOpenedToReceiveCalls
if (DEBUG) Log.d(TAG, " close auto reg temporarily: "
+ getUri() + ": " + mIncomingCallPendingIntent);
mSipGroup.close();
@@ -582,7 +582,7 @@ public final class SipService extends ISipService.Stub {
}
public void close() {
- mOpened = false;
+ mOpenedToReceiveCalls = false;
mSipGroup.close();
mAutoRegistration.stop();
if (DEBUG) Log.d(TAG, " close: " + getUri() + ": "
@@ -629,8 +629,8 @@ public final class SipService extends ISipService.Stub {
+ SipErrorCode.toString(errorCode) + ": " + message);
}
- public boolean isOpened() {
- return mOpened;
+ public boolean isOpenedToReceiveCalls() {
+ return mOpenedToReceiveCalls;
}
public boolean isRegistered() {
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 50ce7dc..30ddfb5 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -190,6 +190,7 @@ class SipSessionGroup implements SipListener {
public synchronized void close() {
Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
+ onConnectivityChanged();
mSessionMap.clear();
closeToNotReceiveCalls();
if (mSipStack != null) {
@@ -526,11 +527,14 @@ class SipSessionGroup implements SipListener {
}
public void answerCall(String sessionDescription, int timeout) {
- try {
- processCommand(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- } catch (SipException e) {
- onError(e);
+ synchronized (SipSessionGroup.this) {
+ if (mPeerProfile == null) return;
+ try {
+ processCommand(new MakeCallCommand(mPeerProfile,
+ sessionDescription, timeout));
+ } catch (SipException e) {
+ onError(e);
+ }
}
}
@@ -539,14 +543,11 @@ class SipSessionGroup implements SipListener {
}
public void changeCall(String sessionDescription, int timeout) {
- doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
- timeout));
- }
-
- public void changeCallWithTimeout(
- String sessionDescription, int timeout) {
- doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
- timeout));
+ synchronized (SipSessionGroup.this) {
+ if (mPeerProfile == null) return;
+ doCommandAsync(new MakeCallCommand(mPeerProfile,
+ sessionDescription, timeout));
+ }
}
public void register(int duration) {
@@ -1162,11 +1163,6 @@ class SipSessionGroup implements SipListener {
mProxy.onCallEstablished(this, mPeerSessionDescription);
}
- private void fallbackToPreviousInCall(int errorCode, String message) {
- mState = SipSession.State.IN_CALL;
- mProxy.onCallChangeFailed(this, errorCode, message);
- }
-
private void endCallNormally() {
reset();
mProxy.onCallEnded(this);
@@ -1190,12 +1186,7 @@ class SipSessionGroup implements SipListener {
onRegistrationFailed(errorCode, message);
break;
default:
- if ((errorCode != SipErrorCode.DATA_CONNECTION_LOST)
- && mInCall) {
- fallbackToPreviousInCall(errorCode, message);
- } else {
- endCallOnError(errorCode, message);
- }
+ endCallOnError(errorCode, message);
}
}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
index 72ee44e..84c7166 100644
--- a/voip/jni/rtp/AmrCodec.cpp
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -73,7 +73,7 @@ int AmrCodec::set(int sampleRate, const char *fmtp)
}
// Handle mode-set and octet-align.
- char *modes = (char*)strcasestr(fmtp, "mode-set=");
+ const char *modes = strcasestr(fmtp, "mode-set=");
if (modes) {
mMode = 0;
mModeSet = 0;
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 0c8a725..cba1123 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -90,6 +90,7 @@ public:
void encode(int tick, AudioStream *chain);
void decode(int tick);
+private:
enum {
NORMAL = 0,
SEND_ONLY = 1,
@@ -97,7 +98,6 @@ public:
LAST_MODE = 2,
};
-private:
int mMode;
int mSocket;
sockaddr_storage mRemote;
@@ -463,6 +463,7 @@ public:
bool add(AudioStream *stream);
bool remove(int socket);
+private:
enum {
ON_HOLD = 0,
MUTED = 1,
@@ -471,7 +472,6 @@ public:
LAST_MODE = 3,
};
-private:
AudioStream *mChain;
int mEventQueue;
volatile int mDtmfEvent;
@@ -948,16 +948,10 @@ void remove(JNIEnv *env, jobject thiz, jint socket)
void setMode(JNIEnv *env, jobject thiz, jint mode)
{
- if (mode < 0 || mode > AudioGroup::LAST_MODE) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
if (group && !group->setMode(mode)) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
}
- env->SetIntField(thiz, gMode, mode);
}
void sendDtmf(JNIEnv *env, jobject thiz, jint event)
@@ -969,10 +963,10 @@ void sendDtmf(JNIEnv *env, jobject thiz, jint event)
}
JNINativeMethod gMethods[] = {
- {"add", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
- {"remove", "(I)V", (void *)remove},
- {"setMode", "(I)V", (void *)setMode},
- {"sendDtmf", "(I)V", (void *)sendDtmf},
+ {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
+ {"nativeRemove", "(I)V", (void *)remove},
+ {"nativeSetMode", "(I)V", (void *)setMode},
+ {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
};
} // namespace
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
index 4cff588..6127d3c 100644
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ b/voip/jni/rtp/EchoSuppressor.cpp
@@ -161,22 +161,27 @@ void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
}
// Compute correlations.
- float corr2 = 0.0f;
int latency = 0;
+ float corr2 = 0.0f;
+ float varX = 0.0f;
float varY = mY2Sum - mWeight * mYSum * mYSum;
for (int i = mTailLength - 1; i >= 0; --i) {
- float varX = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
- float c2 = cov * cov / (varX * varY + 1);
- if (c2 > corr2) {
- corr2 = c2;
- latency = i;
+ if (cov > 0.0f) {
+ float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
+ float corr2i = cov * cov / (varXi * varY + 1);
+ if (corr2i > corr2) {
+ varX = varXi;
+ corr2 = corr2i;
+ latency = i;
+ }
}
}
- //LOGI("correlation^2 = %.10f, latency = %d", corr2, latency * mScale);
+ //LOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY,
+ // latency * mScale);
// Do echo suppression.
- if (corr2 > 0.1f) {
+ if (corr2 > 0.1f && varX > 10000.0f) {
int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
for (int i = 0; i < mSampleCount; ++i) {
recorded[i] = recorded[i] * factor >> 16;