From b798689749c64baba81f02e10cf2157c747d6b46 Mon Sep 17 00:00:00 2001
From: The Android Open Source Project
Date: Fri, 9 Jan 2009 17:51:23 -0800
Subject: [PATCH] auto import from //branches/cupcake/...@125939
---
api/current.xml | 828 +++++++++++++++++-
camera/libcameraservice/CameraHardwareStub.cpp | 4 +
camera/libcameraservice/CameraHardwareStub.h | 1 +
camera/libcameraservice/CameraService.cpp | 62 +-
camera/libcameraservice/CameraService.h | 19 +-
cmds/dumpstate/Android.mk | 17 +-
cmds/dumpstate/dumpstate | 114 ---
cmds/dumpstate/dumpstate.c | 291 +++++++
cmds/dumpstate/dumpstate.h | 120 +++
cmds/dumpstate/utils.c | 230 +++++
cmds/ime/Android.mk | 15 +
cmds/ime/MODULE_LICENSE_APACHE2 | 0
cmds/ime/NOTICE | 190 ++++
cmds/ime/ime | 7 +
cmds/ime/src/com/android/commands/ime/Ime.java | 248 ++++++
cmds/pm/src/com/android/commands/pm/Pm.java | 61 ++
core/java/android/app/Activity.java | 2 -
core/java/android/app/SearchDialog.java | 960 +++++++++------------
core/java/android/app/SearchManager.java | 8 +
core/java/android/bluetooth/BluetoothA2dp.java | 4 +-
core/java/android/content/AbstractTableMerger.java | 12 +-
core/java/android/hardware/Camera.java | 106 ++-
.../IInputMethodSessionWrapper.java | 9 +-
.../inputmethodservice/InputMethodService.java | 177 +++-
core/java/android/inputmethodservice/Keyboard.java | 10 +-
.../android/inputmethodservice/KeyboardView.java | 50 +-
core/java/android/os/HandlerState.java | 33 +
core/java/android/os/HandlerStateMachine.java | 290 +++++++
core/java/android/os/IPowerManager.aidl | 1 +
core/java/android/os/Power.java | 18 +-
.../android/preference/PreferenceGroupAdapter.java | 5 +-
core/java/android/provider/Browser.java | 10 +-
core/java/android/provider/Contacts.java | 30 +-
core/java/android/provider/MediaStore.java | 46 +-
core/java/android/provider/Settings.java | 12 +-
core/java/android/provider/Sync.java | 30 +-
core/java/android/server/BluetoothA2dpService.java | 31 +-
.../java/android/server/search/SearchableInfo.java | 18 +
core/java/android/speech/RecognizerIntent.java | 106 +++
core/java/android/text/InputType.java | 32 +-
core/java/android/text/Layout.java | 4 +-
core/java/android/text/format/DateUtils.java | 201 ++++-
.../text/method/ArrowKeyMovementMethod.java | 36 +-
.../android/text/method/MetaKeyKeyListener.java | 239 ++++-
.../android/text/style/BackgroundColorSpan.java | 2 +-
core/java/android/text/style/CharacterStyle.java | 4 +-
core/java/android/text/style/ClickableSpan.java | 2 +-
.../android/text/style/DynamicDrawableSpan.java | 50 +-
.../android/text/style/ForegroundColorSpan.java | 3 +-
core/java/android/text/style/ImageSpan.java | 46 +-
core/java/android/text/style/MaskFilterSpan.java | 3 +-
core/java/android/text/style/RasterizerSpan.java | 3 +-
.../java/android/text/style/StrikethroughSpan.java | 2 +-
core/java/android/text/style/UnderlineSpan.java | 2 +-
core/java/android/text/style/UpdateAppearance.java | 10 +
core/java/android/text/style/UpdateLayout.java | 7 +-
core/java/android/util/PrintStreamPrinter.java | 40 +
core/java/android/view/Menu.java | 3 -
core/java/android/view/SurfaceView.java | 62 +-
core/java/android/view/View.java | 29 +-
core/java/android/view/ViewRoot.java | 66 +-
core/java/android/view/animation/Animation.java | 37 +-
core/java/android/view/animation/package.html | 236 +----
.../view/inputmethod/BaseInputConnection.java | 21 +-
.../view/inputmethod/DefaultInputMethod.java | 6 +-
.../android/view/inputmethod/InputConnection.java | 11 +
.../view/inputmethod/InputConnectionWrapper.java | 4 +
.../java/android/view/inputmethod/InputMethod.java | 18 +-
.../view/inputmethod/InputMethodManager.java | 232 ++++-
.../view/inputmethod/InputMethodSession.java | 7 +-
core/java/android/view/inputmethod/package.html | 3 +-
core/java/android/webkit/CookieManager.java | 85 +-
core/java/android/webkit/TextDialog.java | 59 +-
core/java/android/webkit/WebView.java | 81 +-
core/java/android/webkit/WebViewCore.java | 79 +-
core/java/android/widget/AbsListView.java | 23 +-
core/java/android/widget/AbsSeekBar.java | 31 +-
core/java/android/widget/AutoCompleteTextView.java | 22 +-
core/java/android/widget/Gallery.java | 52 +-
core/java/android/widget/ProgressBar.java | 112 +--
core/java/android/widget/RatingBar.java | 44 +-
.../java/android/widget/ResourceCursorAdapter.java | 12 +-
core/java/android/widget/SeekBar.java | 29 +-
core/java/android/widget/SimpleCursorAdapter.java | 43 +-
core/java/android/widget/TextView.java | 64 +-
.../internal/app/RingtonePickerActivity.java | 16 +-
.../com/android/internal/os/HandlerCaller.java | 14 +
.../internal/view/IInputConnectionWrapper.java | 9 +
.../com/android/internal/view/IInputContext.aidl | 2 +
.../android/internal/view/IInputMethodManager.aidl | 2 +
.../android/internal/view/IInputMethodSession.aidl | 3 +-
.../internal/view/InputConnectionWrapper.java | 9 +
.../android/internal/view/menu/MenuItemImpl.java | 14 +-
.../internal/widget/EditableInputConnection.java | 93 +-
.../android/gdata/client/AndroidGDataClient.java | 6 +-
.../com/google/android/net/GoogleHttpClient.java | 34 +-
core/jni/Android.mk | 7 +-
core/jni/AndroidRuntime.cpp | 2 +
core/jni/android/graphics/Bitmap.cpp | 2 +-
core/jni/android/graphics/Canvas.cpp | 2 +-
core/jni/android/graphics/Shader.cpp | 2 +-
core/jni/android/opengl/util.cpp | 2 +-
core/jni/android_hardware_Camera.cpp | 34 +
core/jni/android_media_AudioTrack.cpp | 41 +-
core/jni/android_media_JetPlayer.cpp | 485 +++++++++++
core/jni/android_os_Power.cpp | 18 +-
core/jni/android_view_ViewRoot.cpp | 8 +-
core/jni/com_google_android_gles_jni_EGLImpl.cpp | 4 +-
core/res/AndroidManifest.xml | 2 +-
core/res/res/drawable/ic_menu_login.png | Bin 0 -> 2466 bytes
core/res/res/drawable/ic_menu_notifications.png | Bin 0 -> 1771 bytes
core/res/res/drawable/ic_menu_refresh.png | Bin 0 -> 2450 bytes
core/res/res/drawable/ratingbar_full.xml | 6 +-
core/res/res/drawable/ratingbar_full_empty.xml | 34 +
core/res/res/drawable/ratingbar_full_filled.xml | 34 +
core/res/res/drawable/seek_thumb.xml | 34 +
.../{seek_thumb.png => seek_thumb_normal.png} | Bin
core/res/res/drawable/seek_thumb_pressed.png | Bin 0 -> 970 bytes
core/res/res/drawable/seek_thumb_selected.png | Bin 0 -> 970 bytes
core/res/res/layout/search_bar.xml | 27 +-
core/res/res/values-fr/arrays.xml | 4 +
core/res/res/values-fr/strings.xml | 723 ++++++++++++++++
core/res/res/values-it/arrays.xml | 4 +
core/res/res/values-it/strings.xml | 723 ++++++++++++++++
core/res/res/values-mcc204-fr/strings.xml | 6 +
core/res/res/values-mcc204-it/strings.xml | 6 +
core/res/res/values-mcc204-zh-rCN/strings.xml | 6 +
core/res/res/values-mcc230-fr/strings.xml | 6 +
core/res/res/values-mcc230-it/strings.xml | 6 +
core/res/res/values-mcc230-zh-rCN/strings.xml | 6 +
core/res/res/values-mcc232-fr/strings.xml | 6 +
core/res/res/values-mcc232-it/strings.xml | 6 +
core/res/res/values-mcc232-zh-rCN/strings.xml | 6 +
core/res/res/values-mcc234-fr/strings.xml | 6 +
core/res/res/values-mcc234-it/strings.xml | 6 +
core/res/res/values-mcc234-zh-rCN/strings.xml | 6 +
core/res/res/values-mcc260-fr/strings.xml | 6 +
core/res/res/values-mcc260-it/strings.xml | 6 +
core/res/res/values-mcc260-zh-rCN/strings.xml | 6 +
core/res/res/values-mcc262-fr/strings.xml | 6 +
core/res/res/values-mcc262-it/strings.xml | 6 +
core/res/res/values-mcc262-zh-rCN/strings.xml | 6 +
core/res/res/values-zh-rCN/arrays.xml | 4 +
core/res/res/values-zh-rCN/strings.xml | 723 ++++++++++++++++
core/res/res/values/attrs.xml | 24 +-
core/res/res/values/ids.xml | 2 +
core/res/res/values/public.xml | 3 +
core/res/res/values/strings.xml | 62 ++
core/res/res/values/styles.xml | 1 +
core/res/res/xml-en/autotext.xml | 195 +++++
core/res/res/xml/autotext.xml | 175 ----
data/etc/Android.mk | 8 +-
data/etc/{permissions.xml => platform.xml} | 7 +-
data/localization/import-from-xtb | 4 +-
docs/html/community/index.jd | 2 +-
docs/html/images/linearlayout.png | Bin 13576 -> 10100 bytes
.../graphics/drawable/AnimationDrawable.java | 30 +-
.../android/graphics/drawable/BitmapDrawable.java | 18 +
.../android/graphics/drawable/ClipDrawable.java | 10 +-
.../android/graphics/drawable/ColorDrawable.java | 8 +-
.../graphics/drawable/GradientDrawable.java | 32 +-
.../android/graphics/drawable/InsetDrawable.java | 9 +
.../android/graphics/drawable/LayerDrawable.java | 16 +-
.../graphics/drawable/LevelListDrawable.java | 14 +-
.../android/graphics/drawable/RotateDrawable.java | 11 +-
.../android/graphics/drawable/ScaleDrawable.java | 11 +-
.../graphics/drawable/StateListDrawable.java | 56 ++
.../graphics/drawable/TransitionDrawable.java | 16 +-
include/media/JetPlayer.h | 101 +++
include/media/ToneGenerator.h | 1 +
include/media/mediametadataretriever.h | 5 +
include/ui/Camera.h | 5 +
include/ui/CameraHardwareInterface.h | 5 +
include/ui/EGLNativeSurface.h | 13 +-
include/ui/ICamera.h | 9 +
include/ui/IOverlay.h | 2 -
include/ui/ISurface.h | 4 +-
include/ui/Overlay.h | 78 +-
include/ui/PixelFormat.h | 4 +-
include/ui/Region.h | 2 +-
include/utils/MemoryHeapBase.h | 5 +-
include/utils/RefBase.h | 24 +
libs/audioflinger/A2dpAudioInterface.cpp | 49 +-
libs/audioflinger/A2dpAudioInterface.h | 12 +-
libs/audioflinger/AudioDumpInterface.cpp | 16 +-
libs/audioflinger/AudioDumpInterface.h | 2 +-
libs/audioflinger/AudioFlinger.cpp | 135 ++-
libs/audioflinger/AudioFlinger.h | 3 +-
libs/audioflinger/AudioHardwareGeneric.cpp | 12 +-
libs/audioflinger/AudioHardwareGeneric.h | 2 +-
libs/audioflinger/AudioHardwareStub.cpp | 10 +-
libs/audioflinger/AudioHardwareStub.h | 2 +-
libs/surfaceflinger/BootAnimation.cpp | 4 +-
.../DisplayHardware/DisplayHardware.cpp | 4 +-
.../DisplayHardware/DisplayHardware.h | 6 +-
libs/surfaceflinger/Layer.cpp | 4 +-
libs/surfaceflinger/LayerBase.cpp | 17 +-
libs/surfaceflinger/LayerBase.h | 79 +-
libs/surfaceflinger/LayerBlur.cpp | 2 +-
libs/surfaceflinger/LayerBuffer.cpp | 617 ++++++++-----
libs/surfaceflinger/LayerBuffer.h | 134 ++-
libs/surfaceflinger/LayerScreenshot.cpp | 2 +-
libs/surfaceflinger/SurfaceFlinger.cpp | 24 +-
libs/surfaceflinger/SurfaceFlinger.h | 14 +-
libs/surfaceflinger/Transform.h | 2 +-
libs/ui/Camera.cpp | 19 +
libs/ui/ICamera.cpp | 45 +-
libs/ui/IOverlay.cpp | 15 -
libs/ui/ISurface.cpp | 8 +-
libs/ui/Overlay.cpp | 122 ++-
libs/ui/Region.cpp | 2 -
libs/utils/MemoryHeapBase.cpp | 25 +-
.../internal/location/GpsLocationProvider.java | 58 +-
.../internal/location/GpsXtraDownloader.java | 14 +-
.../android/internal/location/LocationCache.java | 18 +-
.../internal/location/LocationMasfClient.java | 10 +
media/java/android/media/AudioManager.java | 29 +
media/java/android/media/AudioTrack.java | 2 +-
media/java/android/media/JetPlayer.java | 334 +++++++
.../java/android/media/MediaMetadataRetriever.java | 18 +-
media/java/android/media/ResampleInputStream.java | 153 ++++
media/jni/Android.mk | 3 +-
media/jni/android_media_MediaMetadataRetriever.cpp | 2 +-
media/jni/android_media_MediaPlayer.cpp | 6 +
media/jni/android_media_ResampleInputStream.cpp | 143 +++
media/libmedia/Android.mk | 5 +-
media/libmedia/AudioTrack.cpp | 22 +-
media/libmedia/IMediaMetadataRetriever.cpp | 2 +-
media/libmedia/JetPlayer.cpp | 428 +++++++++
media/libmedia/ToneGenerator.cpp | 264 +++---
media/libmedia/mediarecorder.cpp | 2 -
preloaded-classes | 69 +-
.../java/com/android/server/HeadsetObserver.java | 19 +-
.../android/server/InputMethodManagerService.java | 104 ++-
.../com/android/server/LocationManagerService.java | 2 +-
services/java/com/android/server/MountService.java | 6 +-
.../com/android/server/PackageManagerService.java | 42 +-
.../com/android/server/PowerManagerService.java | 143 ++-
services/java/com/android/server/Watchdog.java | 197 ++---
services/java/com/android/server/WifiService.java | 147 ++--
.../android/server/am/ActivityManagerService.java | 5 +-
.../com/android/server/am/PendingIntentRecord.java | 4 +-
.../telephony/gsm/ServiceStateTracker.java | 7 +-
.../telephony/gsm/stk/CommandParamsFactory.java | 7 +-
.../internal/telephony/gsm/stk/IconLoader.java | 8 +-
.../telephony/gsm/stk/ImageDescriptor.java | 6 +-
.../telephony/gsm/stk/RilMessageDecoder.java | 138 ++-
.../internal/telephony/gsm/stk/Service.java | 22 +-
.../internal/telephony/gsm/stk/ValueParser.java | 6 +-
test-runner/android/test/TouchUtils.java | 348 +++++++-
.../android/unit_tests/GoogleHttpClientTest.java | 6 +-
.../unit_tests/os/HandlerStateMachineTest.java | 119 +++
tests/CoreTests/android/AndroidManifest.xml | 1 +
tests/CoreTests/android/webkit/CookieTest.java | 4 +
tests/FrameworkTest/AndroidManifest.xml | 8 +
.../res/layout/autocompletetextview_simple.xml | 34 +
.../android/widget/AutoCompleteTextViewSimple.java | 122 +++
.../widget/AutoCompleteTextViewCallbacks.java | 110 +++
.../android/widget/SimpleCursorAdapterTest.java | 104 ++-
tests/SmokeTest/Android.mk | 16 +
tests/SmokeTest/AndroidManifest.xml | 32 +
tests/SmokeTest/README | 8 +
.../com/android/smoketest/SmokeTestActivity.java | 41 +
tests/SmokeTest/tests/Android.mk | 21 +
tests/SmokeTest/tests/AndroidManifest.xml | 39 +
.../com/android/smoketest/ProcessErrorsTest.java | 117 +++
tools/aapt/Images.cpp | 222 ++++-
tools/aapt/Package.cpp | 2 +-
tools/aapt/ResourceTable.cpp | 3 +-
tools/aidl/aidl.cpp | 2 +
tools/aidl/generate_java.cpp | 2 +
tools/aidl/options.cpp | 4 +
tools/aidl/search_path.cpp | 1 +
tools/aidl/search_path.h | 1 +
.../src/com/android/layoutlib/bridge/Bridge.java | 96 ++-
.../tools/layoutlib/create/AsmGenerator.java | 3 +-
.../com/android/tools/layoutlib/create/Main.java | 4 +-
.../tools/layoutlib/create/MethodAdapter.java | 91 ++
.../tools/layoutlib/create/MethodListener.java | 76 ++
.../tools/layoutlib/create/OverrideMethod.java | 102 ++-
.../tools/layoutlib/create/StubMethodAdapter.java | 97 ++-
tools/localize/Perforce.cpp | 2 +
tools/localize/XLIFFFile.cpp | 1 +
tools/localize/XMLHandler.cpp | 1 +
tools/localize/file_utils.cpp | 3 +
tools/localize/localize.cpp | 1 +
wifi/java/android/net/wifi/IWifiManager.aidl | 2 +-
wifi/java/android/net/wifi/WifiManager.java | 81 +-
wifi/java/android/net/wifi/WifiStateTracker.java | 176 ++--
289 files changed, 13302 insertions(+), 3173 deletions(-)
delete mode 100755 cmds/dumpstate/dumpstate
create mode 100644 cmds/dumpstate/dumpstate.c
create mode 100644 cmds/dumpstate/dumpstate.h
create mode 100644 cmds/dumpstate/utils.c
create mode 100644 cmds/ime/Android.mk
create mode 100644 cmds/ime/MODULE_LICENSE_APACHE2
create mode 100644 cmds/ime/NOTICE
create mode 100755 cmds/ime/ime
create mode 100644 cmds/ime/src/com/android/commands/ime/Ime.java
create mode 100644 core/java/android/os/HandlerState.java
create mode 100644 core/java/android/os/HandlerStateMachine.java
create mode 100644 core/java/android/speech/RecognizerIntent.java
create mode 100644 core/java/android/text/style/UpdateAppearance.java
create mode 100644 core/java/android/util/PrintStreamPrinter.java
create mode 100644 core/jni/android_media_JetPlayer.cpp
create mode 100644 core/res/res/drawable/ic_menu_login.png
create mode 100644 core/res/res/drawable/ic_menu_notifications.png
create mode 100644 core/res/res/drawable/ic_menu_refresh.png
create mode 100644 core/res/res/drawable/ratingbar_full_empty.xml
create mode 100644 core/res/res/drawable/ratingbar_full_filled.xml
create mode 100644 core/res/res/drawable/seek_thumb.xml
rename core/res/res/drawable/{seek_thumb.png => seek_thumb_normal.png} (100%)
create mode 100644 core/res/res/drawable/seek_thumb_pressed.png
create mode 100644 core/res/res/drawable/seek_thumb_selected.png
create mode 100644 core/res/res/values-fr/arrays.xml
create mode 100644 core/res/res/values-fr/strings.xml
create mode 100644 core/res/res/values-it/arrays.xml
create mode 100644 core/res/res/values-it/strings.xml
create mode 100644 core/res/res/values-mcc204-fr/strings.xml
create mode 100644 core/res/res/values-mcc204-it/strings.xml
create mode 100644 core/res/res/values-mcc204-zh-rCN/strings.xml
create mode 100644 core/res/res/values-mcc230-fr/strings.xml
create mode 100644 core/res/res/values-mcc230-it/strings.xml
create mode 100644 core/res/res/values-mcc230-zh-rCN/strings.xml
create mode 100644 core/res/res/values-mcc232-fr/strings.xml
create mode 100644 core/res/res/values-mcc232-it/strings.xml
create mode 100644 core/res/res/values-mcc232-zh-rCN/strings.xml
create mode 100644 core/res/res/values-mcc234-fr/strings.xml
create mode 100644 core/res/res/values-mcc234-it/strings.xml
create mode 100644 core/res/res/values-mcc234-zh-rCN/strings.xml
create mode 100644 core/res/res/values-mcc260-fr/strings.xml
create mode 100644 core/res/res/values-mcc260-it/strings.xml
create mode 100644 core/res/res/values-mcc260-zh-rCN/strings.xml
create mode 100644 core/res/res/values-mcc262-fr/strings.xml
create mode 100644 core/res/res/values-mcc262-it/strings.xml
create mode 100644 core/res/res/values-mcc262-zh-rCN/strings.xml
create mode 100644 core/res/res/values-zh-rCN/arrays.xml
create mode 100644 core/res/res/values-zh-rCN/strings.xml
create mode 100644 core/res/res/xml-en/autotext.xml
rename data/etc/{permissions.xml => platform.xml} (96%)
create mode 100644 include/media/JetPlayer.h
create mode 100644 media/java/android/media/JetPlayer.java
create mode 100644 media/java/android/media/ResampleInputStream.java
create mode 100644 media/jni/android_media_ResampleInputStream.cpp
create mode 100644 media/libmedia/JetPlayer.cpp
create mode 100644 tests/AndroidTests/src/com/android/unit_tests/os/HandlerStateMachineTest.java
create mode 100644 tests/FrameworkTest/res/layout/autocompletetextview_simple.xml
create mode 100644 tests/FrameworkTest/src/android/widget/AutoCompleteTextViewSimple.java
create mode 100644 tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java
create mode 100644 tests/SmokeTest/Android.mk
create mode 100644 tests/SmokeTest/AndroidManifest.xml
create mode 100644 tests/SmokeTest/README
create mode 100644 tests/SmokeTest/src/com/android/smoketest/SmokeTestActivity.java
create mode 100644 tests/SmokeTest/tests/Android.mk
create mode 100644 tests/SmokeTest/tests/AndroidManifest.xml
create mode 100644 tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
create mode 100644 tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
create mode 100644 tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
diff --git a/api/current.xml b/api/current.xml
index 880b6e82136c..13ca37036b72 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6044,7 +6044,7 @@
value="16843269"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
@@ -8992,6 +8992,17 @@
visibility="public"
>
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -93808,7 +93882,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
@@ -93824,6 +93898,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -93847,7 +93972,9 @@
deprecated="not deprecated"
visibility="public"
>
-
+
+
+
@@ -93871,6 +93998,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -93892,6 +94040,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -93934,7 +94171,7 @@
deprecated="not deprecated"
visibility="public"
>
-
+
@@ -93949,7 +94186,7 @@
deprecated="not deprecated"
visibility="public"
>
-
+
@@ -93963,7 +94200,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
@@ -93975,7 +94212,7 @@
-
+
+
+
+
+
+
+
+
+
+
@@ -93994,6 +94250,25 @@
+
+
+
+
+
+
+
+
+
+
@@ -94009,6 +94284,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -94034,7 +94356,9 @@
deprecated="not deprecated"
visibility="public"
>
-
+
+
+
@@ -98115,6 +98439,17 @@
visibility="public"
>
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -102247,7 +102622,7 @@
type="int"
transient="false"
volatile="false"
- value="114688"
+ value="524288"
static="true"
final="true"
deprecated="not deprecated"
@@ -102265,6 +102640,17 @@
visibility="public"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -106165,6 +106725,18 @@
>
+
+
+
+
+
+
@@ -106175,10 +106747,38 @@
deprecated="not deprecated"
visibility="public"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -130793,7 +131513,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -130804,7 +131524,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -130815,7 +131535,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -144252,7 +144972,7 @@
-
+
@@ -144992,6 +145712,19 @@
+
+
+
+
-
+
+
+
+
+
+
+
+
+
CameraService::connect(const sp& cameraClient)
}
// create a new Client object
- sp client = new Client(this, cameraClient);
+ sp client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid());
mClient = client;
#if DEBUG_CLIENT_REFERENCES
// Enable tracking for this object, and track increments and decrements of
@@ -151,10 +151,12 @@ void CameraService::removeClient(const sp& cameraClient)
}
CameraService::Client::Client(const sp& cameraService,
- const sp& cameraClient) :
- mCameraService(cameraService), mCameraClient(cameraClient), mHardware(0)
+ const sp& cameraClient, pid_t clientPid)
{
LOGD("Client E constructor");
+ mCameraService = cameraService;
+ mCameraClient = cameraClient;
+ mClientPid = clientPid;
mHardware = openCameraHardware();
// Callback is disabled by default
@@ -162,12 +164,36 @@ CameraService::Client::Client(const sp& cameraService,
LOGD("Client X constructor");
}
+status_t CameraService::Client::checkPid()
+{
+ // zero means the interface is not locked down
+ if (mClientPid == 0) return NO_ERROR;
+ return (int) mClientPid == IPCThreadState::self()->getCallingPid() ? NO_ERROR : -EBUSY;
+}
+
+status_t CameraService::Client::lock()
+{
+ // lock camera to this client
+ status_t result = checkPid();
+ if (result == NO_ERROR) mClientPid = IPCThreadState::self()->getCallingPid();
+ return result;
+}
+
+status_t CameraService::Client::unlock()
+{
+ // allow anyone to use camera
+ status_t result = checkPid();
+ if (result == NO_ERROR) mClientPid = 0;
+ return result;
+}
+
status_t CameraService::Client::connect(const sp& client)
{
- // remvoe old client
- LOGD("connect (new client)");
+ // remove old client
+ LOGV("connect new client to existing camera");
Mutex::Autolock _l(mLock);
mCameraClient = client;
+ mClientPid = IPCThreadState::self()->getCallingPid();
mFrameCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
return NO_ERROR;
}
@@ -209,6 +235,7 @@ void CameraService::Client::disconnect()
{
LOGD("Client E disconnect");
Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
mCameraService->removeClient(mCameraClient);
if (mHardware != 0) {
// Before destroying mHardware, we must make sure it's in the
@@ -228,6 +255,8 @@ status_t CameraService::Client::setPreviewDisplay(const sp& surface)
{
LOGD("setPreviewDisplay(%p)", surface.get());
Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
Mutex::Autolock surfaceLock(mSurfaceLock);
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
@@ -245,6 +274,7 @@ status_t CameraService::Client::setPreviewDisplay(const sp& surface)
void CameraService::Client::setFrameCallbackFlag(int frame_callback_flag)
{
Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
mFrameCallbackFlag = frame_callback_flag;
}
@@ -258,6 +288,8 @@ status_t CameraService::Client::startPreview()
*/
Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
if (mHardware == 0) {
LOGE("mHardware is NULL, returning.");
@@ -269,14 +301,15 @@ status_t CameraService::Client::startPreview()
return INVALID_OPERATION;
}
+ // do nothing if preview is already started
+ if (mHardware->previewEnabled()) return NO_ERROR;
+
// XXX: This needs to be improved. remove all hardcoded stuff
int w, h;
CameraParameters params(mHardware->getParameters());
params.getPreviewSize(&w, &h);
- mSurface->unregisterBuffers();
-
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
debug_frame_cnt = 0;
#endif
@@ -284,6 +317,7 @@ status_t CameraService::Client::startPreview()
status_t ret = mHardware->startPreview(previewCallback,
mCameraService.get());
if (ret == NO_ERROR) {
+ mSurface->unregisterBuffers();
mSurface->registerBuffers(w,h,w,h,
PIXEL_FORMAT_YCbCr_420_SP,
mHardware->getPreviewHeap());
@@ -300,6 +334,7 @@ void CameraService::Client::stopPreview()
LOGD("stopPreview()");
Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
if (mHardware == 0) {
LOGE("mHardware is NULL, returning.");
@@ -315,6 +350,13 @@ void CameraService::Client::stopPreview()
mPreviewBuffer.clear();
}
+bool CameraService::Client::previewEnabled()
+{
+ Mutex::Autolock lock(mLock);
+ if (mHardware == 0) return false;
+ return mHardware->previewEnabled();
+}
+
// Safely retrieves a strong pointer to the client during a hardware callback.
sp CameraService::Client::getClientFromCookie(void* user)
{
@@ -424,6 +466,8 @@ status_t CameraService::Client::autoFocus()
LOGV("autoFocus");
Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
if (mHardware == 0) {
LOGE("mHardware is NULL, returning.");
@@ -440,6 +484,8 @@ status_t CameraService::Client::takePicture()
LOGD("takePicture");
Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
if (mHardware == 0) {
LOGE("mHardware is NULL, returning.");
@@ -580,6 +626,8 @@ status_t CameraService::Client::setParameters(const String8& params)
LOGD("setParameters(%s)", params.string());
Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
if (mHardware == 0) {
LOGE("mHardware is NULL, returning.");
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index b225aa9f3449..cd8c1e9a41cf 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -73,6 +73,12 @@ private:
// connect new client with existing camera remote
virtual status_t connect(const sp& client);
+ // prevent other processes from using this ICamera interface
+ virtual status_t lock();
+
+ // allow other processes to use this ICamera interface
+ virtual status_t unlock();
+
// pass the buffered ISurface to the camera service
virtual status_t setPreviewDisplay(const sp& surface);
@@ -86,6 +92,9 @@ private:
// stop preview mode
virtual void stopPreview();
+ // get preview state
+ virtual bool previewEnabled();
+
// auto focus
virtual status_t autoFocus();
@@ -103,11 +112,14 @@ private:
private:
friend class CameraService;
- Client( const sp& cameraService,
- const sp& cameraClient);
+ Client(const sp& cameraService,
+ const sp& cameraClient,
+ pid_t clientPid);
Client();
virtual ~Client();
+ status_t checkPid();
+
static void previewCallback(const sp& mem, void* user);
static void shutterCallback(void *user);
static void yuvPictureCallback(const sp& mem, void* user);
@@ -132,7 +144,7 @@ private:
// by the CameraHardwareInterface callback, and needs to
// access mSurface. It cannot hold mLock, however, because
// stopPreview() may be holding that lock while attempting
- // top stop preview, and stopPreview itself will block waiting
+ // to stop preview, and stopPreview itself will block waiting
// for a callback from CameraHardwareInterface. If this
// happens, it will cause a deadlock.
mutable Mutex mSurfaceLock;
@@ -146,6 +158,7 @@ private:
// they don't need to be protected by a lock
sp mCameraClient;
sp mHardware;
+ pid_t mClientPid;
};
// ----------------------------------------------------------------------------
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index e101aebf7c1e..f61d7ecb3f5a 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,11 +1,18 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-ALL_PREBUILT += $(TARGET_OUT)/bin/dumpstate
-$(TARGET_OUT)/bin/dumpstate : $(LOCAL_PATH)/dumpstate | $(ACP)
- $(transform-prebuilt-to-target)
+LOCAL_SRC_FILES:= dumpstate.c utils.c
+
+LOCAL_MODULE:= dumpstate
+
+LOCAL_SHARED_LIBRARIES := libcutils
-SYMLINKS := $(TARGET_OUT_EXECUTABLES)/dumpcrash
+include $(BUILD_EXECUTABLE)
+
+COMMANDS = dumpcrash bugreport
+SYMLINKS := $(addprefix $(TARGET_OUT_EXECUTABLES)/,$(COMMANDS))
$(SYMLINKS): DUMPSTATE_BINARY := dumpstate
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
@echo "Symlink: $@ -> $(DUMPSTATE_BINARY)"
@@ -14,3 +21,5 @@ $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
$(hide) ln -sf $(DUMPSTATE_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+endif
diff --git a/cmds/dumpstate/dumpstate b/cmds/dumpstate/dumpstate
deleted file mode 100755
index 8dd7d140dcba..000000000000
--- a/cmds/dumpstate/dumpstate
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/system/bin/sh
-
-# Dumps to /tmp/state by default.
-# Specify "-" to dump to stdout, or any file name to dump to that file.
-
-crashmode=0
-case $0 in
- dumpcrash|*/dumpcrash) crashmode=1 ;;
-esac
-
-case $1 in
- "") echo "DUMPING STATE TO /tmp/state"; exec 1>/tmp/state ;;
- -) ;;
- *) echo "DUMPING STATE TO $1"; exec 1>$1 ;;
-esac
-
-case $crashmode in 0)
- echo "========================================================"
- echo "== dumpstate"
- echo "========================================================"
- echo "------ SYSTEM LOG ------"
- logcat -v time -d '*:v'
- echo "------ VM TRACES ------"
- cat /data/anr/traces.txt
- echo "------ EVENT LOG TAGS ------"
- cat /etc/event-log-tags
- echo "------ EVENT LOG ------"
- logcat -b events -v time -d '*:v'
- echo "------ RADIO LOG ------"
- logcat -b radio -v time -d '*:v'
- echo "------ NETWORK STATE ------"
- echo "Interfaces:"
- netcfg
- echo ""
- echo "Routes:"
- cat /proc/net/route
- echo "------ SYSTEM PROPERTIES ------"
- getprop
- echo "------ KERNEL LOG ------"
- dmesg
- echo "------ KERNEL WAKELOCKS ------"
- cat /proc/wakelocks
- echo ""
- echo "------ PROCESSES ------"
- ps
- echo "------ PROCESSES AND THREADS ------"
- ps -t -p
- echo "------ MEMORY INFO ------"
- cat /proc/meminfo
- echo "------ PSS INFO ------"
- top -n 1 -d 0 -m 15 -s pss
- echo "------ PROCRANK ------"
- procrank
- echo "------ LIBRANK ------"
- librank
- echo "------ VIRTUAL MEMORY STATS ------"
- cat /proc/vmstat
- echo "------ SLAB INFO ------"
- cat /proc/slabinfo
- echo "------ ZONEINFO ------"
- cat /proc/zoneinfo
- echo "------ BINDER FAILED TRANSACTION LOG ------"
- cat /proc/binder/failed_transaction_log
- echo ""
- echo "------ BINDER TRANSACTION LOG ------"
- cat /proc/binder/transaction_log
- echo ""
- echo "------ BINDER TRANSACTIONS ------"
- cat /proc/binder/transactions
- echo ""
- echo "------ BINDER STATS ------"
- cat /proc/binder/stats
- echo ""
- for i in `ls /proc/binder/proc`; do
- echo "------ BINDER PROCESS STATE: $i ------"
- cat /proc/binder/proc/$i
- echo ""
- done
- echo "------ FILESYSTEMS ------"
- df
- echo "------ PACKAGE SETTINGS ------"
- cat /data/system/packages.xml
- echo "------ PACKAGE UID ERRORS ------"
- cat /data/system/uiderrors.txt
- echo "------ LAST KERNEL LOG ------"
- cat /proc/last_kmsg
-;; esac
-
-echo "========================================================"
-echo "== build.prop"
-echo "========================================================"
-
-# the crash server parses key-value pairs between the VERSION INFO and
-# END lines so we can aggregate crash reports based on this data.
-echo "------ VERSION INFO ------"
-echo "currenttime=`date`"
-echo "kernel.version=`cat /proc/version`"
-echo "kernel.cmdline=`cat /proc/cmdline`"
-cat /system/build.prop
-echo "gsm.version.ril-impl=`getprop gsm.version.ril-impl`"
-echo "gsm.version.baseband=`getprop gsm.version.baseband`"
-echo "gsm.imei=`getprop gsm.imei`"
-echo "gsm.sim.operator.numeric=`getprop gsm.sim.operator.numeric`"
-echo "gsm.operator.alpha=`getprop gsm.operator.alpha`"
-echo "------ END ------"
-
-case $crashmode in 0)
- echo "========================================================"
- echo "== dumpsys"
- echo "========================================================"
- dumpsys
-;; esac
-
-exit 0
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
new file mode 100644
index 000000000000..dea269dcf3be
--- /dev/null
+++ b/cmds/dumpstate/dumpstate.c
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "private/android_filesystem_config.h"
+
+#include "dumpstate.h"
+
+static char* const gzip_args[] = { "gzip", "-6", 0 };
+static int start_pattern[] = { 150, 0 };
+static int end_pattern[] = { 75, 50, 75, 50, 75, 0 };
+
+static struct tm now;
+
+/* dumps the current system state to stdout */
+static void dumpstate(int full) {
+ if (full) {
+ PRINT("========================================================");
+ PRINT("== dumpstate");
+ PRINT("========================================================");
+ PRINT("------ SYSTEM LOG ------");
+ EXEC4("logcat", "-v", "time", "-d", "*:v");
+ PRINT("------ VM TRACES ------");
+ DUMP("/data/anr/traces.txt");
+ PRINT("------ EVENT LOG TAGS ------");
+ DUMP("/etc/event-log-tags");
+ PRINT("------ EVENT LOG ------");
+ EXEC6("logcat", "-b", "events", "-v", "time", "-d", "*:v");
+ PRINT("------ RADIO LOG ------");
+ EXEC6("logcat", "-b", "radio", "-v", "time", "-d", "*:v");
+ PRINT("------ NETWORK STATE ------");
+ PRINT("Interfaces:");
+ EXEC("netcfg");
+ PRINT("");
+ PRINT("Routes:");
+ DUMP("/proc/net/route");
+ PRINT("------ SYSTEM PROPERTIES ------");
+ print_properties();
+ PRINT("------ KERNEL LOG ------");
+ EXEC("dmesg");
+ PRINT("------ KERNEL WAKELOCKS ------");
+ DUMP("/proc/wakelocks");
+ PRINT("");
+ PRINT("------ PROCESSES ------");
+ EXEC("ps");
+ PRINT("------ PROCESSES AND THREADS ------");
+ EXEC2("ps", "-t", "-p");
+ PRINT("------ MEMORY INFO ------");
+ DUMP("/proc/meminfo");
+ PRINT("------ PSS INFO ------");
+ EXEC8("top", "-n", "1", "-d", "0", "-m", "15", "-s", "pss");
+ PRINT("------ PROCRANK ------");
+ EXEC("procrank");
+ PRINT("------ LIBRANK ------");
+ EXEC("librank");
+ PRINT("------ VIRTUAL MEMORY STATS ------");
+ DUMP("/proc/vmstat");
+ PRINT("------ SLAB INFO ------");
+ DUMP("/proc/slabinfo");
+ PRINT("------ ZONEINFO ------");
+ DUMP("/proc/zoneinfo");
+ PRINT("------ BINDER FAILED TRANSACTION LOG ------");
+ DUMP("/proc/binder/failed_transaction_log");
+ PRINT("");
+ PRINT("------ BINDER TRANSACTION LOG ------");
+ DUMP("/proc/binder/transaction_log");
+ PRINT("");
+ PRINT("------ BINDER TRANSACTIONS ------");
+ DUMP("/proc/binder/transactions");
+ PRINT("");
+ PRINT("------ BINDER STATS ------");
+ DUMP("/proc/binder/stats");
+ PRINT("");
+ PRINT("------ BINDER PROCESS STATE: $i ------");
+ DUMP_FILES("/proc/binder/proc");
+ PRINT("------ FILESYSTEMS ------");
+ EXEC("df");
+ PRINT("------ PACKAGE SETTINGS ------");
+ DUMP("/data/system/packages.xml");
+ PRINT("------ PACKAGE UID ERRORS ------");
+ DUMP("/data/system/uiderrors.txt");
+ PRINT("------ LAST KERNEL LOG ------");
+ DUMP("/proc/last_kmsg");
+ }
+ PRINT("========================================================");
+ PRINT("== build.prop");
+ PRINT("========================================================");
+
+ /* the crash server parses key-value pairs between the VERSION INFO and
+ * END lines so we can aggregate crash reports based on this data.
+ */
+ PRINT("------ VERSION INFO ------");
+ print_date("currenttime=", &now);
+ DUMP_PROMPT("kernel.version=", "/proc/version");
+ DUMP_PROMPT("kernel.cmdline=", "/proc/cmdline");
+ DUMP("/system/build.prop");
+ PROPERTY("gsm.version.ril-impl");
+ PROPERTY("gsm.version.baseband");
+ PROPERTY("gsm.imei");
+ PROPERTY("gsm.sim.operator.numeric");
+ PROPERTY("gsm.operator.alpha");
+ PRINT("------ END ------");
+
+ if (full) {
+ PRINT("========================================================");
+ PRINT("== dumpsys");
+ PRINT("========================================================");
+ EXEC("dumpsys");
+ }
+}
+
+/* used to check the file name passed via argv[0] */
+static int check_command_name(const char* name, const char* test) {
+ int name_length, test_length;
+
+ if (!strcmp(name, test))
+ return 1;
+
+ name_length = strlen(name);
+ test_length = strlen(test);
+
+ if (name_length > test_length + 2) {
+ name += (name_length - test_length);
+ if (name[-1] != '/')
+ return 0;
+ if (!strcmp(name, test))
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int dumpcrash = check_command_name(argv[0], "dumpcrash");
+ int bugreport = check_command_name(argv[0], "bugreport");
+ int add_date = 0;
+ char* outfile = 0;
+ int vibrate = 0;
+ int compress = 0;
+ int c, fd, vibrate_fd, fds[2];
+ char path[PATH_MAX];
+ pid_t pid;
+
+ /* set as high priority, and protect from OOM killer */
+ setpriority(PRIO_PROCESS, 0, -20);
+ protect_from_oom_killer();
+
+ get_time(&now);
+
+ if (bugreport) {
+ do {
+ c = getopt(argc, argv, "do:vz");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ add_date = 1;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'v':
+ vibrate = 1;
+ break;
+ case 'z':
+ compress = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ }
+
+ /* open vibrator before switching user */
+ if (vibrate) {
+ vibrate_fd = open("/sys/class/timed_output/vibrator/enable", O_WRONLY);
+ if (vibrate_fd > 0)
+ fcntl(vibrate_fd, F_SETFD, FD_CLOEXEC);
+ } else
+ vibrate_fd = -1;
+
+ /* switch to non-root user and group */
+ setgid(AID_LOG);
+ setuid(AID_SHELL);
+
+ /* make it safe to use both printf and STDOUT_FILENO */
+ setvbuf(stdout, 0, _IONBF, 0);
+
+ if (outfile) {
+ if (strlen(outfile) > sizeof(path) - 100)
+ exit(1);
+
+ strcpy(path, outfile);
+ if (add_date) {
+ char date[260];
+ strftime(date, sizeof(date),
+ "-%Y-%m-%d-%H-%M-%S",
+ &now);
+ strcat(path, date);
+ }
+ if (compress)
+ strcat(path, ".gz");
+
+ /* ensure that all directories in the path exist */
+ create_directories(path);
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ return fd;
+
+ if (compress) {
+ pipe(fds);
+
+ /* redirect our stdout to the pipe */
+ dup2(fds[1], STDOUT_FILENO);
+ close(fds[1]);
+
+ if ((pid = fork()) < 0)
+ {
+ fprintf(stderr, "fork error\n");
+ exit(1);
+ }
+
+ if (pid) {
+ /* parent case */
+
+ /* close our copy of the input to gzip */
+ close(fds[0]);
+ /* close our copy of the output file */
+ close(fd);
+ } else {
+ /* child case */
+
+ /* redirect our input pipe to stdin */
+ dup2(fds[0], STDIN_FILENO);
+ close(fds[0]);
+
+ /* redirect stdout to the output file */
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+
+ /* run gzip to postprocess our output */
+ execv("/system/bin/gzip", gzip_args);
+ fprintf(stderr, "execv returned\n");
+ }
+ } else {
+ /* redirect stdout to the output file */
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+ }
+ }
+ /* else everything will print to stdout */
+
+ if (vibrate) {
+ vibrate_pattern(vibrate_fd, start_pattern);
+ }
+ dumpstate(!dumpcrash);
+ if (vibrate) {
+ vibrate_pattern(vibrate_fd, end_pattern);
+ close(vibrate_fd);
+ }
+
+ /* so gzip will terminate */
+ close(STDOUT_FILENO);
+
+ return 0;
+}
+
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
new file mode 100644
index 000000000000..b956f99ce9db
--- /dev/null
+++ b/cmds/dumpstate/dumpstate.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef _DUMPSTATE_H_
+#define _DUMPSTATE_H_
+
+#include
+
+// Commands time out after 15 seconds
+#define TIMEOUT 15
+
+#define PRINT(s) printf("%s\n", s)
+
+#define DUMP(file) dump_file(file)
+
+#define DUMP_FILES(path) dump_files(path)
+
+#define DUMP_PROMPT(prompt, file) \
+{ \
+ printf(prompt); \
+ dump_file(file); \
+}
+
+#define EXEC(cmd) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC2(cmd, a1, a2) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC4(cmd, a1, a2, a3, a4) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC6(cmd, a1, a2, a3, a4, a5, a6) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, a5, a6, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC8(cmd, a1, a2, a3, a4, a5, a6, a7, a8) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, a5, a6, a7, a8, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define PROPERTY(name) print_property(name)
+
+struct Command {
+ const char* path;
+ char* const args[];
+};
+typedef struct Command Command;
+
+/* prints the contents of a file */
+int dump_file(const char* path);
+
+/* prints the contents of all files in a directory */
+void dump_files(const char* path);
+
+/* forks a command and waits for it to finish */
+int run_command(struct Command* cmd, int timeout);
+
+/* reads the current time into tm */
+void get_time(struct tm *tm);
+
+/* prints the date in tm */
+void print_date(const char* prompt, struct tm *tm);
+
+/* prints the name and value of a system property */
+int print_property(const char* name);
+
+/* prints all the system properties */
+void print_properties();
+
+/* creates directories as needed for the given path */
+void create_directories(char *path);
+
+/* runs the vibrator using the given pattern */
+void vibrate_pattern(int fd, int* pattern);
+
+/* prevents the OOM killer from killing us */
+void protect_from_oom_killer();
+
+#endif /* _DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
new file mode 100644
index 000000000000..60d845f61bf3
--- /dev/null
+++ b/cmds/dumpstate/utils.c
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "dumpstate.h"
+
+
+/* prints the contents of a file */
+int dump_file(const char* path) {
+ char buffer[32768];
+ int fd, amount_read;
+ int ret = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ do {
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret > 0)
+ ret = write(STDOUT_FILENO, buffer, ret);
+ } while (ret > 0);
+
+ buffer[0] = '\n';
+ write(STDOUT_FILENO, buffer, 1);
+
+ close(fd);
+ return ret;
+}
+
+/* prints the contents of all files in a directory */
+void dump_files(const char* path) {
+ DIR* dir;
+ struct dirent* entry;
+ char buffer[PATH_MAX];
+
+ dir = opendir(path);
+ if (!dir) {
+ fprintf(stderr, "could not open directory %s\n", path);
+ return;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (entry->d_type == DT_REG) {
+ snprintf(buffer, sizeof(buffer), "%s/%s", path, entry->d_name);
+ dump_file(path);
+ printf("\n");
+ }
+ }
+
+ closedir(dir);
+}
+
+/* prints the name and value of a system property */
+int print_property(const char* name) {
+ char value[PROP_VALUE_MAX];
+
+ __system_property_get(name, value);
+ printf("%s=%s\n", name, value);
+ return 0;
+}
+
+static pid_t alarm_pid = 0;
+static int timed_out = 0;
+static void sig_alarm(int sig)
+{
+ if (alarm_pid) {
+ kill(alarm_pid, SIGKILL);
+ timed_out = 1;
+ alarm_pid = 0;
+ }
+}
+
+/* forks a command and waits for it to finish */
+int run_command(struct Command* cmd, int timeout) {
+ struct sigaction sa;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ /* handle error case */
+ if (pid < 0)
+ return pid;
+
+ /* handle child case */
+ if (pid == 0) {
+ int ret = execv(cmd->path, cmd->args);
+ if (ret)
+ fprintf(stderr, "execv %s returned %d\n", cmd->path, ret);
+ exit(ret);
+ }
+
+ /* handle parent case */
+ timed_out = 0;
+ if (timeout) {
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESETHAND;
+ sa.sa_handler = sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* set an alarm so we don't hang forever */
+ alarm_pid = pid;
+ alarm(timeout);
+ }
+
+ waitpid(pid, &status, 0);
+
+ if (timed_out)
+ printf("ERROR: command %s timed out\n", cmd->path);
+
+ return status;
+}
+
+/* reads the current time into tm */
+void get_time(struct tm *tm) {
+ time_t t;
+
+ tzset();
+ time(&t);
+ localtime_r(&t, tm);
+}
+
+/* prints the date in tm */
+void print_date(const char* prompt, struct tm *tm) {
+ char strbuf[260];
+
+ strftime(strbuf, sizeof(strbuf),
+ "%a %b %e %H:%M:%S %Z %Y",
+ tm);
+ printf("%s%s\n", prompt, strbuf);
+}
+
+
+static void print_prop(const char *key, const char *name,
+ void *user __attribute__((unused)))
+{
+ printf("[%s]: [%s]\n", key, name);
+}
+
+/* prints all the system properties */
+void print_properties() {
+ property_list(print_prop, NULL);
+}
+
+/* creates directories as needed for the given path */
+void create_directories(char *path)
+{
+ char *chp = path;
+
+ /* skip initial slash */
+ if (chp[0] == '/')
+ chp++;
+
+ while (chp && chp[0]) {
+ chp = strchr(chp, '/');
+ if (chp) {
+ *chp = 0;
+ mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ *chp = '/';
+ chp++;
+ }
+ }
+}
+
+/* runs the vibrator using the given pattern */
+void vibrate_pattern(int fd, int* pattern)
+{
+ struct timespec tm;
+ char buffer[10];
+
+ while (*pattern) {
+ /* read vibrate on time */
+ int on_time = *pattern++;
+ snprintf(buffer, sizeof(buffer), "%d", on_time);
+ write(fd, buffer, strlen(buffer));
+
+ /* read vibrate off time */
+ int delay = *pattern++;
+ if (delay) {
+ delay += on_time;
+
+ tm.tv_sec = delay / 1000;
+ tm.tv_nsec = (delay % 1000) * 1000000;
+ nanosleep(&tm, NULL);
+ } else
+ break;
+ }
+}
+
+/* prevents the OOM killer from killing us */
+void protect_from_oom_killer()
+{
+ int fd;
+
+ fd = open("/proc/self/oom_adj", O_WRONLY);
+ if (fd >= 0) {
+ // -17 should make us immune to OOM
+ const char* text = "-17";
+ write(fd, text, strlen(text));
+ close(fd);
+ }
+}
diff --git a/cmds/ime/Android.mk b/cmds/ime/Android.mk
new file mode 100644
index 000000000000..90b1c9106cbc
--- /dev/null
+++ b/cmds/ime/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2007 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := ime
+include $(BUILD_JAVA_LIBRARY)
+
+
+include $(CLEAR_VARS)
+ALL_PREBUILT += $(TARGET_OUT)/bin/ime
+$(TARGET_OUT)/bin/ime : $(LOCAL_PATH)/ime | $(ACP)
+ $(transform-prebuilt-to-target)
+
diff --git a/cmds/ime/MODULE_LICENSE_APACHE2 b/cmds/ime/MODULE_LICENSE_APACHE2
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/cmds/ime/NOTICE b/cmds/ime/NOTICE
new file mode 100644
index 000000000000..c5b1efa7aac7
--- /dev/null
+++ b/cmds/ime/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/ime/ime b/cmds/ime/ime
new file mode 100755
index 000000000000..96c56d3bde0a
--- /dev/null
+++ b/cmds/ime/ime
@@ -0,0 +1,7 @@
+# Script to start "pm" on the device, which has a very rudimentary
+# shell.
+#
+base=/system
+export CLASSPATH=$base/framework/ime.jar
+exec app_process $base/bin com.android.commands.ime.Ime "$@"
+
diff --git a/cmds/ime/src/com/android/commands/ime/Ime.java b/cmds/ime/src/com/android/commands/ime/Ime.java
new file mode 100644
index 000000000000..72a0af6c2d99
--- /dev/null
+++ b/cmds/ime/src/com/android/commands/ime/Ime.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2007 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.commands.ime;
+
+import com.android.internal.view.IInputMethodManager;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.PrintStreamPrinter;
+import android.util.Printer;
+import android.view.inputmethod.InputMethodInfo;
+
+import java.util.List;
+
+public final class Ime {
+ IInputMethodManager mImm;
+
+ private String[] mArgs;
+ private int mNextArg;
+ private String mCurArgData;
+
+ private static final String IMM_NOT_RUNNING_ERR =
+ "Error: Could not access the Input Method Manager. Is the system running?";
+
+ public static void main(String[] args) {
+ new Ime().run(args);
+ }
+
+ public void run(String[] args) {
+ if (args.length < 1) {
+ showUsage();
+ return;
+ }
+
+ mImm = IInputMethodManager.Stub.asInterface(ServiceManager.getService("input_method"));
+ if (mImm == null) {
+ System.err.println(IMM_NOT_RUNNING_ERR);
+ return;
+ }
+
+ mArgs = args;
+ String op = args[0];
+ mNextArg = 1;
+
+ if ("list".equals(op)) {
+ runList();
+ return;
+ }
+
+ if ("enable".equals(op)) {
+ runSetEnabled(true);
+ return;
+ }
+
+ if ("disable".equals(op)) {
+ runSetEnabled(false);
+ return;
+ }
+
+ if ("set".equals(op)) {
+ runSet();
+ return;
+ }
+
+ if (op != null) {
+ System.err.println("Error: unknown command '" + op + "'");
+ }
+ showUsage();
+ }
+
+ /**
+ * Execute the list sub-command.
+ */
+ private void runList() {
+ String opt;
+ boolean all = false;
+ boolean brief = false;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-a")) {
+ all = true;
+ } else if (opt.equals("-s")) {
+ brief = true;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
+ }
+ }
+
+
+ List methods;
+ if (!all) {
+ try {
+ methods = mImm.getEnabledInputMethodList();
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(IMM_NOT_RUNNING_ERR);
+ return;
+ }
+ } else {
+ try {
+ methods = mImm.getInputMethodList();
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(IMM_NOT_RUNNING_ERR);
+ return;
+ }
+ }
+
+ if (methods != null) {
+ Printer pr = new PrintStreamPrinter(System.out);
+ for (int i=0; i= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ if (!arg.startsWith("-")) {
+ return null;
+ }
+ mNextArg++;
+ if (arg.equals("--")) {
+ return null;
+ }
+ if (arg.length() > 1 && arg.charAt(1) != '-') {
+ if (arg.length() > 2) {
+ mCurArgData = arg.substring(2);
+ return arg.substring(0, 2);
+ } else {
+ mCurArgData = null;
+ return arg;
+ }
+ }
+ mCurArgData = null;
+ return arg;
+ }
+
+ private String nextOptionData() {
+ if (mCurArgData != null) {
+ return mCurArgData;
+ }
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String data = mArgs[mNextArg];
+ mNextArg++;
+ return data;
+ }
+
+ private String nextArg() {
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ mNextArg++;
+ return arg;
+ }
+
+ private static void showUsage() {
+ System.err.println("usage: ime list [-a] [-s]");
+ System.err.println(" ime enable ID");
+ System.err.println(" ime disable ID");
+ System.err.println(" ime set ID");
+ System.err.println("");
+ System.err.println("The list command prints all enabled input methods. Use");
+ System.err.println("the -a option to see all input methods. Use");
+ System.err.println("the -s option to see only a single summary line of each.");
+ System.err.println("");
+ System.err.println("The enable command allows the given input method ID to be used.");
+ System.err.println("");
+ System.err.println("The disable command disallows the given input method ID from use.");
+ System.err.println("");
+ System.err.println("The set command switches to the given input method ID.");
+ }
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 09a140b02192..c2d8da519f75 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -94,6 +94,16 @@ public final class Pm {
return;
}
+ if ("enable".equals(op)) {
+ runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ return;
+ }
+
+ if ("disable".equals(op)) {
+ runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ return;
+ }
+
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
@@ -111,6 +121,9 @@ public final class Pm {
}
} finally {
if (validCommand == false) {
+ if (op != null) {
+ System.err.println("Error: unknown command '" + op + "'");
+ }
showUsage();
}
}
@@ -662,6 +675,49 @@ public final class Pm {
return obs.result;
}
+ private static String enabledSettingToString(int state) {
+ switch (state) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ return "default";
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ return "enabled";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ return "disabled";
+ }
+ return "unknown";
+ }
+
+ private void runSetEnabledSetting(int state) {
+ String pkg = nextArg();
+ if (pkg == null) {
+ System.err.println("Error: no package or component specified");
+ showUsage();
+ return;
+ }
+ ComponentName cn = ComponentName.unflattenFromString(pkg);
+ if (cn == null) {
+ try {
+ mPm.setApplicationEnabledSetting(pkg, state, 0);
+ System.err.println("Package " + pkg + " new state: "
+ + enabledSettingToString(
+ mPm.getApplicationEnabledSetting(pkg)));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ } else {
+ try {
+ mPm.setComponentEnabledSetting(cn, state, 0);
+ System.err.println("Component " + cn.toShortString() + " new state: "
+ + enabledSettingToString(
+ mPm.getComponentEnabledSetting(cn)));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+ }
+
/**
* Displays the package file for a package.
* @param pckg
@@ -752,6 +808,8 @@ public final class Pm {
System.err.println(" pm path PACKAGE");
System.err.println(" pm install [-l] [-r] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
+ System.err.println(" pm enable PACKAGE_OR_COMPONENT");
+ System.err.println(" pm disable PACKAGE_OR_COMPONENT");
System.err.println("");
System.err.println("The list packages command prints all packages. Use");
System.err.println("the -f option to see their associated file.");
@@ -780,5 +838,8 @@ public final class Pm {
System.err.println("The uninstall command removes a package from the system. Use");
System.err.println("the -k option to keep the data and cache directories around");
System.err.println("after the package removal.");
+ System.err.println("");
+ System.err.println("The enable and disable commands change the enabled state of");
+ System.err.println("a given package or component (written as \"package/class\").");
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index eafb0488490f..c98cf1bbd356 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2232,8 +2232,6 @@ public class Activity extends ContextThemeWrapper
/**
* Programmatically closes the most recently opened context menu, if showing.
- *
- * @hide pending API council
*/
public void closeContextMenu() {
mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 2ce2db996b89..f1c604cd247e 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -16,14 +16,11 @@
package android.app;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -38,6 +35,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.server.search.SearchableInfo;
import android.text.Editable;
+import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
@@ -45,21 +43,13 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
-import android.view.View.OnFocusChangeListener;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
import android.widget.CursorAdapter;
-import android.widget.EditText;
-import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
@@ -67,8 +57,7 @@ import android.widget.WrapperListAdapter;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
-import java.util.List;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -77,7 +66,7 @@ import java.util.concurrent.atomic.AtomicLong;
*
* @hide
*/
-public class SearchDialog extends Dialog {
+public class SearchDialog extends Dialog implements OnItemClickListener, OnItemSelectedListener {
// Debugging support
final static String LOG_TAG = "SearchDialog";
@@ -87,7 +76,6 @@ public class SearchDialog extends Dialog {
// interaction with runtime
IntentFilter mCloseDialogsFilter;
IntentFilter mPackageFilter;
- private final Handler mHandler = new Handler(); // why isn't Dialog.mHandler shared?
private static final String INSTANCE_KEY_COMPONENT = "comp";
private static final String INSTANCE_KEY_APPDATA = "data";
@@ -102,15 +90,10 @@ public class SearchDialog extends Dialog {
private static final int INSTANCE_SELECTED_QUERY = -1;
// views & widgets
- private View mSearchBarLayout;
private TextView mBadgeLabel;
- private LinearLayout mSearchEditLayout;
- private EditText mSearchTextField;
- private ImageButton mGoButton;
- private ListView mSuggestionsList;
+ private AutoCompleteTextView mSearchTextField;
+ private Button mGoButton;
- private ViewTreeObserver mViewTreeObserver = null;
-
// interaction with searchable application
private ComponentName mLaunchComponent;
private Bundle mAppSearchData;
@@ -121,22 +104,20 @@ public class SearchDialog extends Dialog {
private SearchableInfo mSearchable;
// support for suggestions
- private SuggestionsRunner mSuggestionsRunner;
private String mUserQuery = null;
private int mUserQuerySelStart;
private int mUserQuerySelEnd;
- private boolean mNonUserQuery = false;
private boolean mLeaveJammedQueryOnRefocus = false;
private String mPreviousSuggestionQuery = null;
- private Context mProviderContext;
- private Animation mSuggestionsEntry;
- private Animation mSuggestionsExit;
- private boolean mSkipNextAnimate;
private int mPresetSelection = -1;
private String mSuggestionAction = null;
private Uri mSuggestionData = null;
private String mSuggestionQuery = null;
+ // support for AutoCompleteTextView suggestions display
+ private SuggestionsAdapter mSuggestionsAdapter;
+
+
/**
* Constructor - fires it up and makes it look like the search UI.
*
@@ -167,38 +148,25 @@ public class SearchDialog extends Dialog {
theWindow.setAttributes(lp);
// get the view elements for local access
- mSearchBarLayout = findViewById(com.android.internal.R.id.search_bar);
mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
- mSearchEditLayout = (LinearLayout)findViewById(com.android.internal.R.id.search_edit_frame);
- mSearchTextField = (EditText) findViewById(com.android.internal.R.id.search_src_text);
- mGoButton = (ImageButton) findViewById(com.android.internal.R.id.search_go_btn);
- mSuggestionsList = (ListView) findViewById(com.android.internal.R.id.search_suggest_list);
+ mSearchTextField = (AutoCompleteTextView)
+ findViewById(com.android.internal.R.id.search_src_text);
+ mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
// attach listeners
mSearchTextField.addTextChangedListener(mTextWatcher);
mSearchTextField.setOnKeyListener(mTextKeyListener);
mGoButton.setOnClickListener(mGoButtonClickListener);
mGoButton.setOnKeyListener(mButtonsKeyListener);
- mSuggestionsList.setOnItemClickListener(mSuggestionsListItemClickListener);
- mSuggestionsList.setOnKeyListener(mSuggestionsKeyListener);
- mSuggestionsList.setOnFocusChangeListener(mSuggestFocusListener);
- mSuggestionsList.setOnItemSelectedListener(mSuggestSelectedListener);
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
- mSuggestionsList.setVisibility(View.GONE);
// Additional adjustments to make Dialog work for Search
// Touching outside of the search dialog will dismiss it
setCanceledOnTouchOutside(true);
- // Preload animations
- mSuggestionsEntry = AnimationUtils.loadAnimation(getContext(),
- com.android.internal.R.anim.grow_fade_in);
- mSuggestionsExit = AnimationUtils.loadAnimation(getContext(),
- com.android.internal.R.anim.fade_out);
-
// Set up broadcast filters
mCloseDialogsFilter = new
IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -238,18 +206,10 @@ public class SearchDialog extends Dialog {
}
// OK, we're going to show ourselves
- if (mSuggestionsList != null) {
- mSuggestionsList.setVisibility(View.GONE); // prevent any flicker if was visible
- }
-
super.show();
setupSearchableInfo();
- // start the suggestions thread (which will mainly idle)
- mSuggestionsRunner = new SuggestionsRunner();
- new Thread(mSuggestionsRunner, "SearchSuggestions").start();
-
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
mGlobalSearchMode = globalSearch;
@@ -258,26 +218,21 @@ public class SearchDialog extends Dialog {
getContext().registerReceiver(mBroadcastReceiver, mCloseDialogsFilter);
getContext().registerReceiver(mBroadcastReceiver, mPackageFilter);
- mViewTreeObserver = mSearchBarLayout.getViewTreeObserver();
- mViewTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
+ // configure the autocomplete aspects of the input box
+ mSearchTextField.setOnItemClickListener(this);
+ mSearchTextField.setOnItemSelectedListener(this);
+
+ // attach the suggestions adapter
+ mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable);
+ mSearchTextField.setAdapter(mSuggestionsAdapter);
// finally, load the user's initial text (which may trigger suggestions)
- mNonUserQuery = false;
+ mSuggestionsAdapter.setNonUserQuery(false);
if (initialQuery == null) {
initialQuery = ""; // This forces the preload to happen, triggering suggestions
}
mSearchTextField.setText(initialQuery);
- // If it is not for global search, that means the search dialog is
- // launched to input a web address.
- if (!globalSearch) {
- mSearchTextField.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_URI);
- } else {
- mSearchTextField.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_NORMAL);
- }
-
if (selectInitialQuery) {
mSearchTextField.selectAll();
} else {
@@ -315,21 +270,7 @@ public class SearchDialog extends Dialog {
// This is OK - it just means we didn't have any registered
}
- // ignore layout notifications
- try {
- if (mViewTreeObserver != null) {
- mViewTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
- }
- } catch (RuntimeException e) {
- // This is OK - none registered or observer "dead"
- }
- mViewTreeObserver = null;
-
// dump extra memory we're hanging on to
- if (mSuggestionsRunner != null) {
- mSuggestionsRunner.cancelSuggestions();
- mSuggestionsRunner = null;
- }
mLaunchComponent = null;
mAppSearchData = null;
mSearchable = null;
@@ -337,7 +278,6 @@ public class SearchDialog extends Dialog {
mSuggestionData = null;
mSuggestionQuery = null;
mActivityContext = null;
- mProviderContext = null;
mPreviousSuggestionQuery = null;
mUserQuery = null;
}
@@ -366,9 +306,8 @@ public class SearchDialog extends Dialog {
int selectedElement = INSTANCE_SELECTED_QUERY;
if (mGoButton.isFocused()) {
selectedElement = INSTANCE_SELECTED_BUTTON;
- } else if ((mSuggestionsList.getVisibility() == View.VISIBLE) &&
- mSuggestionsList.isFocused()) {
- selectedElement = mSuggestionsList.getSelectedItemPosition(); // 0..n
+ } else if (mSearchTextField.isPopupShowing()) {
+ selectedElement = 0; // TODO mSearchTextField.getListSelection() // 0..n
}
bundle.putInt(INSTANCE_KEY_SELECTED_ELEMENT, selectedElement);
@@ -403,11 +342,13 @@ public class SearchDialog extends Dialog {
// for some reason, we couldn't re-instantiate
return;
}
- mSkipNextAnimate = true;
- mNonUserQuery = true;
+ mSuggestionsAdapter.setNonUserQuery(true);
mSearchTextField.setText(displayQuery);
- mNonUserQuery = false;
+ // TODO because the new query is (not) processed in another thread, we can't just
+ // take away this flag (yet). The better solution here is going to require a new API
+ // in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
+// mSuggestionsAdapter.setNonUserQuery(false);
// clean up the selection state
switch (selectedElement) {
@@ -425,6 +366,7 @@ public class SearchDialog extends Dialog {
default:
// defer selecting a list element until suggestion list appears
mPresetSelection = selectedElement;
+ // TODO mSearchTextField.setListSelection(selectedElement)
break;
}
}
@@ -436,6 +378,7 @@ public class SearchDialog extends Dialog {
public void onConfigurationChanged(Configuration newConfig) {
if (isShowing()) {
// Redraw (resources may have changed)
+ updateSearchButton();
updateSearchBadge();
updateQueryHint();
}
@@ -448,10 +391,28 @@ public class SearchDialog extends Dialog {
private void setupSearchableInfo() {
if (mSearchable != null) {
mActivityContext = mSearchable.getActivityContext(getContext());
- mProviderContext = mSearchable.getProviderContext(getContext(), mActivityContext);
+ updateSearchButton();
updateSearchBadge();
updateQueryHint();
+
+ // 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
+ // difficult/expensive to know if every last detail has been configured properly, we
+ // can at least see if a suggestions provider has been configured, and use that
+ // as our trigger.
+ int inputType = mSearchable.getInputType();
+ // We only touch this if the input type is set up for text (which it almost certainly
+ // should be, in the case of search!)
+ if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
+ // The existence of a suggestions authority is the proxy for "suggestions
+ // are available here"
+ inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ if (mSearchable.getSuggestAuthority() != null) {
+ inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ }
+ }
+ mSearchTextField.setInputType(inputType);
}
}
@@ -468,6 +429,24 @@ public class SearchDialog extends Dialog {
cancel();
}
+ /**
+ * Update the text in the search button. Note: This is deprecated functionality, for
+ * 1.0 compatibility only.
+ */
+ private void updateSearchButton() {
+ String textLabel = null;
+ Drawable iconLabel = null;
+ int textId = mSearchable.getSearchButtonText();
+ 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);
+ }
+
/**
* Setup the search "Badge" if request by mode flags.
*/
@@ -559,8 +538,8 @@ public class SearchDialog extends Dialog {
}
updateWidgetState();
// Only do suggestions if actually typed by user
- if (!mNonUserQuery) {
- updateSuggestions();
+ if (mSuggestionsAdapter.getNonUserQuery()) {
+ mPreviousSuggestionQuery = s.toString();
mUserQuery = mSearchTextField.getText().toString();
mUserQuerySelStart = mSearchTextField.getSelectionStart();
mUserQuerySelEnd = mSearchTextField.getSelectionEnd();
@@ -582,156 +561,6 @@ public class SearchDialog extends Dialog {
mGoButton.setFocusable(enabled);
}
- /**
- * In response to a change in the query text, update the suggestions
- */
- private void updateSuggestions() {
- final String queryText = mSearchTextField.getText().toString();
- mPreviousSuggestionQuery = queryText;
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("updateSuggestions()");
- }
-
- mSuggestionsRunner.requestSuggestions(mSearchable, queryText);
-
- // For debugging purposes, put in a lot of strings (really fast typist)
- if (DBG_JAM_THREADING > 0) {
- for (int ii = 1; ii < DBG_JAM_THREADING; ++ii) {
- final String jamQuery = queryText + ii;
- mSuggestionsRunner.requestSuggestions(mSearchable, jamQuery);
- }
- // one final (correct) string for cleanup
- mSuggestionsRunner.requestSuggestions(mSearchable, queryText);
- }
- }
-
- /**
- * This class defines a queued message structure for processing user keystrokes, and a
- * thread that allows the suggestions to be gathered out-of-band, and allows us to skip
- * over multiple keystrokes if the typist is faster than the content provider.
- */
- private class SuggestionsRunner implements Runnable {
-
- private class Request {
- final SearchableInfo mSearchableInfo; // query will set these
- final String mQueryText;
- final boolean cancelRequest; // cancellation will set this
-
- // simple constructors
- Request(final SearchableInfo searchable, final String queryText) {
- mSearchableInfo = searchable;
- mQueryText = queryText;
- cancelRequest = false;
- }
-
- Request() {
- mSearchableInfo = null;
- mQueryText = null;
- cancelRequest = true;
- }
- }
-
- private final LinkedBlockingQueue mSuggestionsQueue =
- new LinkedBlockingQueue();
-
- /**
- * Queue up a suggestions request (non-blocking - can safely call from UI thread)
- */
- public void requestSuggestions(final SearchableInfo searchable, final String queryText) {
- Request request = new Request(searchable, queryText);
- try {
- mSuggestionsQueue.put(request);
- } catch (InterruptedException e) {
- // discard the request.
- }
- }
-
- /**
- * Cancel blocking suggestions, discard any results, and shut down the thread.
- * (non-blocking - can safely call from UI thread)
- */
- private void cancelSuggestions() {
- Request request = new Request();
- try {
- mSuggestionsQueue.put(request);
- } catch (InterruptedException e) {
- // discard the request.
- // TODO can we do better here?
- }
- }
-
- /**
- * This runnable implements the logic for decoupling keystrokes from suggestions.
- * The logic isn't quite obvious here, so I'll try to describe it.
- *
- * Normally we simply sleep waiting for a keystroke. When a keystroke arrives,
- * we immediately dispatch a request to gather suggestions.
- *
- * But this can take a while, so by the time it comes back, more keystrokes may have
- * arrived. If anything happened while we were gathering the suggestion, we discard its
- * results, and then use the most recent keystroke to start the next suggestions request.
- *
- * Any request containing cancelRequest == true will cause the thread to immediately
- * terminate.
- */
- public void run() {
- // outer blocking loop simply waits for a suggestion
- while (true) {
- try {
- Request request = mSuggestionsQueue.take();
- if (request.cancelRequest) {
- return;
- }
-
- // since we were idle, what we're really interested is the final element
- // in the queue. So keep pulling until we get the last element.
- // TODO Could we just do some sort of takeHead() here?
- while (! mSuggestionsQueue.isEmpty()) {
- request = mSuggestionsQueue.take();
- if (request.cancelRequest) {
- return;
- }
- }
- final Request useRequest = request;
-
- // now process the final element (unless it's a cancel - that can be discarded)
-
- if (useRequest.mSearchableInfo != null) {
-
- // go get the cursor. this is what takes time.
- final Cursor c = getSuggestions(useRequest.mSearchableInfo,
- useRequest.mQueryText);
-
- // We now have a suggestions result. But, if any new requests have arrived,
- // we're going to discard them - we don't want to waste time displaying
- // out-of-date results, we just want to get going on the next set.
- // Note, null cursor is a valid result (no suggestions). This logic also
- // supports the need to discard the results *and* stop the thread if a kill
- // request arrives during a query.
- if (mSuggestionsQueue.size() > 0) {
- if (c != null) {
- c.close();
- }
- } else {
- mHandler.post(new Runnable() {
- public void run() {
- updateSuggestionsWithCursor(c, useRequest.mSearchableInfo);
- }
- });
- }
- }
- } catch (InterruptedException e) {
- // loop back for more
- }
- // At this point the queue may contain zero-to-many new requests; We simply
- // loop back to handle them (or, block until new requests arrive)
- }
- }
- }
-
- /**
- * Back in the UI thread, handle incoming cursors
- */
private final static String[] ONE_LINE_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1 };
private final static String[] ONE_LINE_ICONS_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_ICON_1,
@@ -755,209 +584,6 @@ public class SearchDialog extends Dialog {
com.android.internal.R.id.icon2};
/**
- * A new cursor (with suggestions) is ready for use. Update the UI.
- */
- void updateSuggestionsWithCursor(Cursor c, final SearchableInfo searchable) {
- ListAdapter adapter = null;
-
- // first, check for various conditions that disqualify this cursor
- if ((c == null) || (c.getCount() == 0)) {
- // no cursor, or cursor with no data
- } else if ((searchable != mSearchable) || !isShowing()) {
- // race condition (suggestions arrived after conditions changed)
- } else {
- // check cursor before trying to create list views from it
- int colId = c.getColumnIndex("_id");
- int col1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
- int col2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
- int colIc1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
- int colIc2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
-
- boolean minimal = (colId >= 0) && (col1 >= 0);
- boolean hasIcons = (colIc1 >= 0) && (colIc2 >= 0);
- boolean has2Lines = col2 >= 0;
-
- if (minimal) {
- int layout;
- String[] from;
- int[] to;
-
- if (hasIcons) {
- if (has2Lines) {
- layout = com.android.internal.R.layout.search_dropdown_item_icons_2line;
- from = TWO_LINE_ICONS_FROM;
- to = TWO_LINE_ICONS_TO;
- } else {
- layout = com.android.internal.R.layout.search_dropdown_item_icons_1line;
- from = ONE_LINE_ICONS_FROM;
- to = ONE_LINE_ICONS_TO;
- }
- } else {
- if (has2Lines) {
- layout = com.android.internal.R.layout.search_dropdown_item_2line;
- from = TWO_LINE_FROM;
- to = TWO_LINE_TO;
- } else {
- layout = com.android.internal.R.layout.search_dropdown_item_1line;
- from = ONE_LINE_FROM;
- to = ONE_LINE_TO;
- }
- }
- try {
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("updateSuggestions(3)");
- }
- adapter = new SuggestionsCursorAdapter(getContext(), layout, c, from, to,
- mProviderContext);
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("updateSuggestions(4)");
- }
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Exception while creating SuggestionsCursorAdapter", e);
- }
- }
-
- // Provide some help for developers instead of just silently discarding
- if ((colIc1 >= 0) != (colIc2 >= 0)) {
- Log.w(LOG_TAG, "Suggestion icon column(s) discarded, must be 0 or 2 columns.");
- } else if (adapter == null) {
- Log.w(LOG_TAG, "Suggestions cursor discarded due to missing required columns.");
- }
- }
-
- // if we have a cursor but we're not using it (e.g. disqualified), close it now
- if ((c != null) && (adapter == null)) {
- c.close();
- c = null;
- }
-
- // we only made an adapter if there were 1+ suggestions. Now, based on the existence
- // of the adapter, we'll also show/hide the list.
- discardListCursor(mSuggestionsList);
- if (adapter == null) {
- showSuggestions(false, !mSkipNextAnimate);
- } else {
- layoutSuggestionsList();
- showSuggestions(true, !mSkipNextAnimate);
- }
- mSkipNextAnimate = false;
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("updateSuggestions(5)");
- }
- mSuggestionsList.setAdapter(adapter);
- // now that we have an adapter, we can actually adjust the selection & scroll positions
- if (mPresetSelection >= 0) {
- boolean bTouchMode = mSuggestionsList.isInTouchMode();
- mSuggestionsList.setSelection(mPresetSelection);
- mPresetSelection = -1;
- }
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("updateSuggestions(6)");
- }
- }
-
- /**
- * Utility for showing & hiding the suggestions list. This is also responsible for triggering
- * animation, if any, at the right time.
- *
- * @param visible If true, show the suggestions, if false, hide them.
- * @param animate If true, use animation. If false, "just do it."
- */
- private void showSuggestions(boolean visible, boolean animate) {
- if (visible) {
- if (animate && (mSuggestionsList.getVisibility() != View.VISIBLE)) {
- mSuggestionsList.startAnimation(mSuggestionsEntry);
- }
- mSuggestionsList.setVisibility(View.VISIBLE);
- } else {
- if (animate && (mSuggestionsList.getVisibility() != View.GONE)) {
- mSuggestionsList.startAnimation(mSuggestionsExit);
- }
- mSuggestionsList.setVisibility(View.GONE);
- }
- }
-
- /**
- * This helper class supports the suggestions list by allowing 3rd party (e.g. app) resources
- * to be used in suggestions
- */
- private static class SuggestionsCursorAdapter extends SimpleCursorAdapter {
-
- private Resources mProviderResources;
-
- public SuggestionsCursorAdapter(Context context, int layout, Cursor c,
- String[] from, int[] to, Context providerContext) {
- super(context, layout, c, from, to);
- mProviderResources = providerContext.getResources();
- }
-
- /**
- * Overriding this allows us to affect the way that an icon is loaded. Specifically,
- * we can be more controlling about the resource path (and allow icons to come from other
- * packages).
- *
- * @param v ImageView to receive an image
- * @param value the value retrieved from the cursor
- */
- @Override
- public void setViewImage(ImageView v, String value) {
- int resID;
- Drawable img = null;
-
- try {
- resID = Integer.parseInt(value);
- if (resID != 0) {
- img = mProviderResources.getDrawable(resID);
- }
- } catch (NumberFormatException nfe) {
- // img = null;
- } catch (NotFoundException e2) {
- // img = null;
- }
-
- // finally, set the image to whatever we've gotten
- v.setImageDrawable(img);
- }
-
- /**
- * This method is overridden purely to provide a bit of protection against
- * flaky content providers.
- */
- @Override
- /**
- * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
- */
- 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 returned exception " + e.toString());
- // what can I return here?
- View v = newView(mContext, mCursor, parent);
- if (v != null) {
- TextView tv = (TextView) v.findViewById(com.android.internal.R.id.text1);
- tv.setText(e.toString());
- }
- return v;
- }
- }
- }
-
- /**
- * Cleanly close the cursor being used by a ListView. Do this before replacing the adapter
- * or before closing the ListView.
- */
- private void discardListCursor(ListView list) {
- CursorAdapter ca = getSuggestionsAdapter(list);
- if (ca != null) {
- Cursor c = ca.getCursor();
- if (c != null) {
- ca.changeCursor(null);
- }
- }
- }
-
- /**
* Safely retrieve the suggestions cursor adapter from the ListView
*
* @param adapterView The ListView containing our adapter
@@ -977,58 +603,6 @@ public class SearchDialog extends Dialog {
}
/**
- * Get the query cursor for the search suggestions.
- *
- * @param query The search text entered (so far)
- * @return Returns a cursor with suggestions, or null if no suggestions
- */
- private Cursor getSuggestions(final SearchableInfo searchable, final String query) {
- Cursor cursor = null;
- if (searchable.getSuggestAuthority() != null) {
- try {
- StringBuilder uriStr = new StringBuilder("content://");
- uriStr.append(searchable.getSuggestAuthority());
-
- // if content path provided, insert it now
- final String contentPath = searchable.getSuggestPath();
- if (contentPath != null) {
- uriStr.append('/');
- uriStr.append(contentPath);
- }
-
- // append standard suggestion query path
- uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
-
- // inject query, either as selection args or inline
- String[] selArgs = null;
- if (searchable.getSuggestSelection() != null) { // if selection provided, use it
- selArgs = new String[] {query};
- } else {
- uriStr.append('/'); // no sel, use REST pattern
- uriStr.append(Uri.encode(query));
- }
-
- // finally, make the query
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("getSuggestions(1)");
- }
- cursor = getContext().getContentResolver().query(
- Uri.parse(uriStr.toString()), null,
- searchable.getSuggestSelection(), selArgs,
- null);
- if (DBG_LOG_TIMING == 1) {
- dbgLogTiming("getSuggestions(2)");
- }
- } catch (RuntimeException e) {
- Log.w(LOG_TAG, "Search Suggestions query returned exception " + e.toString());
- cursor = null;
- }
- }
-
- return cursor;
- }
-
- /**
* React to typing in the GO search button by refocusing to EditText.
* Continue typing the query.
*/
@@ -1094,7 +668,10 @@ public class SearchDialog extends Dialog {
* React to the user typing while the suggestions are focused. First, check for action
* keys. If not handled, try refocusing regular characters into the EditText. In this case,
* replace the query text (start typing fresh text).
+ *
+ * TODO: Move this code into mTextKeyListener, testing for a list entry being hilited
*/
+ /*
View.OnKeyListener mSuggestionsKeyListener = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
boolean handled = false;
@@ -1108,6 +685,7 @@ public class SearchDialog extends Dialog {
return handled;
}
};
+ */
/**
* Per UI design, we're going to "steer" any typed keystrokes back into the EditText
@@ -1140,6 +718,9 @@ public class SearchDialog extends Dialog {
/**
* Update query text based on transitions in and out of suggestions list.
*/
+ /*
+ * TODO - figure out if this logic is required for the autocomplete text view version
+
OnFocusChangeListener mSuggestFocusListener = new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
// also guard against possible race conditions (late arrival after dismiss)
@@ -1170,24 +751,9 @@ public class SearchDialog extends Dialog {
}
};
+ */
/**
- * Update query text based on movement of selection in/out of suggestion list
- */
- OnItemSelectedListener mSuggestSelectedListener = new OnItemSelectedListener() {
- public void onItemSelected(AdapterView parent, View view, int position, long id) {
- // Update query text while user navigates through suggestions list
- // also guard against possible race conditions (late arrival after dismiss)
- if (mSearchable != null && position >= 0 && mSuggestionsList.isFocused()) {
- jamSuggestionQuery(true, parent, position);
- }
- }
-
- // No action needed on this callback
- public void onNothingSelected(AdapterView parent) { }
- };
-
- /**
* This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent. It's an indication that
* we should close ourselves immediately, in order to allow a higher-priority UI to take over
* (e.g. phone call received).
@@ -1225,20 +791,6 @@ public class SearchDialog extends Dialog {
};
/**
- * Listener for layout changes in the main layout. I use this to dynamically clean up
- * the layout of the dropdown and make it "pixel perfect."
- */
- private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener
- = new ViewTreeObserver.OnGlobalLayoutListener() {
-
- // It's very important that layoutSuggestionsList() does not reset
- // the values more than once, or this becomes an infinite loop.
- public void onGlobalLayout() {
- layoutSuggestionsList();
- }
- };
-
- /**
* Various ways to launch searches
*/
@@ -1333,7 +885,7 @@ public class SearchDialog extends Dialog {
* @param jamQuery True means to set the query, false means to reset it to the user's choice
*/
private void jamSuggestionQuery(boolean jamQuery, AdapterView> parent, int position) {
- mNonUserQuery = true; // disables any suggestions processing
+ mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing
if (jamQuery) {
CursorAdapter ca = getSuggestionsAdapter(parent);
Cursor c = ca.getCursor();
@@ -1356,7 +908,10 @@ public class SearchDialog extends Dialog {
}
if (jamText != null) {
mSearchTextField.setText(jamText);
- mSearchTextField.selectAll();
+ /* mSearchTextField.selectAll(); */ // this didn't work anyway in the old UI
+ // TODO this is only needed in the model where we have a selection in the ACTV
+ // and in the dropdown at the same time.
+ mSearchTextField.setSelection(jamText.length());
}
}
} else {
@@ -1372,7 +927,10 @@ public class SearchDialog extends Dialog {
mSearchTextField.selectAll();
}
}
- mNonUserQuery = false;
+ // TODO because the new query is (not) processed in another thread, we can't just
+ // take away this flag (yet). The better solution here is going to require a new API
+ // in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
+// mSuggestionsAdapter.setNonUserQuery(false);
}
/**
@@ -1418,18 +976,6 @@ public class SearchDialog extends Dialog {
}
/**
- * Handler for clicks in the suggestions list
- */
- private OnItemClickListener mSuggestionsListItemClickListener = new OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- // this guard protects against possible race conditions (late arrival of click)
- if (mSearchable != null) {
- launchSuggestion(parent, position);
- }
- }
- };
-
- /**
* Shared code for launching a query from a suggestion.
*
* @param av The AdapterView (really a ListView) containing the suggestions
@@ -1439,6 +985,16 @@ public class SearchDialog extends Dialog {
*/
private boolean launchSuggestion(AdapterView> av, int position) {
CursorAdapter ca = getSuggestionsAdapter(av);
+ return launchSuggestion(ca, position);
+ }
+
+ /**
+ * Shared code for launching a query from a suggestion.
+ * @param ca The cursor adapter containing the suggestions
+ * @param position The suggestion we'll be launching from
+ * @return true if a successful launch, false if could not (e.g. bad position)
+ */
+ private boolean launchSuggestion(CursorAdapter ca, int position) {
Cursor c = ca.getCursor();
if ((c != null) && c.moveToPosition(position)) {
setupSuggestionIntent(c, mSearchable);
@@ -1457,33 +1013,6 @@ public class SearchDialog extends Dialog {
}
/**
- * Manually adjust suggestions list into its perfectly-tweaked position.
- *
- * NOTE: This MUST not adjust the parameters if they are already set correctly,
- * or you create an infinite loop via the ViewTreeObserver.OnGlobalLayoutListener callback.
- */
- private void layoutSuggestionsList() {
- final int FUDGE_SUGG_X = 1;
- final int FUDGE_SUGG_WIDTH = 2;
-
- int[] itemLoc = new int[2];
- mSearchTextField.getLocationOnScreen(itemLoc);
- int x,width;
- x = itemLoc[0] + FUDGE_SUGG_X;
- width = mSearchTextField.getMeasuredWidth() + FUDGE_SUGG_WIDTH;
-
- // now set params and relayout
- ViewGroup.MarginLayoutParams lp;
- lp = (ViewGroup.MarginLayoutParams) mSuggestionsList.getLayoutParams();
- boolean changing = (lp.width != width) || (lp.leftMargin != x);
- if (changing) {
- lp.leftMargin = x;
- lp.width = width;
- mSuggestionsList.setLayoutParams(lp);
- }
- }
-
- /**
* 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
@@ -1585,6 +1114,301 @@ public class SearchDialog extends Dialog {
}
return result;
}
+
+ /**
+ * Support for AutoCompleteTextView-based suggestions
+ */
+ /**
+ * This class provides the filtering-based interface to suggestions providers.
+ * It is hardwired in a couple of places to support GoogleSearch - for example, it supports
+ * two-line suggestions, but it does not support icons.
+ */
+ private static class SuggestionsAdapter extends SimpleCursorAdapter {
+ private final String TAG = "SuggestionsAdapter";
+
+ SearchableInfo mSearchable;
+ private Resources mProviderResources;
+
+ // These private variables are shared by the filter thread and must be protected
+ private WeakReference mRecentCursor = new WeakReference(null);
+ private boolean mNonUserQuery = false;
+
+ public SuggestionsAdapter(Context context, SearchableInfo searchable) {
+ super(context, -1, null, null, null);
+ mSearchable = searchable;
+
+ // set up provider resources (gives us icons, etc.)
+ Context activityContext = mSearchable.getActivityContext(mContext);
+ Context providerContext = mSearchable.getProviderContext(mContext, activityContext);
+ mProviderResources = providerContext.getResources();
+ }
+
+ /**
+ * Set this field (temporarily!) to disable suggestions updating. This allows us
+ * to change the string in the text view without changing the suggestions list.
+ */
+ public void setNonUserQuery(boolean nonUserQuery) {
+ synchronized (this) {
+ mNonUserQuery = nonUserQuery;
+ }
+ }
+
+ public boolean getNonUserQuery() {
+ synchronized (this) {
+ return mNonUserQuery;
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * In order to provide the Search Mgr functionality of seeing your query change as you
+ * scroll through the list, we have to be able to jam new text into the string without
+ * retriggering the suggestions. We do that here via the "nonUserQuery" flag. In that
+ * case we simply return the existing cursor.
+ *
+ * TODO: Dianne suggests that this should simply be promoted into an AutoCompleteTextView
+ * behavior (perhaps optionally).
+ *
+ * TODO: The "nonuserquery" logic has a race condition because it happens in another thread.
+ * This also needs to be fixed.
+ */
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ String query = (constraint == null) ? "" : constraint.toString();
+ Cursor c = null;
+ synchronized (this) {
+ if (mNonUserQuery) {
+ c = mRecentCursor.get();
+ mNonUserQuery = false;
+ }
+ }
+ if (c == null) {
+ c = getSuggestions(mSearchable, query);
+ synchronized (this) {
+ mRecentCursor = new WeakReference(c);
+ }
+ }
+ return c;
+ }
+
+ /**
+ * Overriding changeCursor() allows us to change not only the cursor, but by sampling
+ * the cursor's columns, the actual display characteristics of the list.
+ */
+ @Override
+ public void changeCursor(Cursor c) {
+
+ // first, check for various conditions that disqualify this cursor
+ if ((c == null) || (c.getCount() == 0)) {
+ // no cursor, or cursor with no data
+ changeCursorAndColumns(null, null, null);
+ if (c != null) {
+ c.close();
+ }
+ return;
+ }
+
+ // check cursor before trying to create list views from it
+ int colId = c.getColumnIndex("_id");
+ int col1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ int col2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
+ int colIc1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
+ int colIc2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
+
+ boolean minimal = (colId >= 0) && (col1 >= 0);
+ boolean hasIcons = (colIc1 >= 0) && (colIc2 >= 0);
+ boolean has2Lines = col2 >= 0;
+
+ if (minimal) {
+ int layout;
+ String[] from;
+ int[] to;
+
+ if (hasIcons) {
+ if (has2Lines) {
+ layout = com.android.internal.R.layout.search_dropdown_item_icons_2line;
+ from = TWO_LINE_ICONS_FROM;
+ to = TWO_LINE_ICONS_TO;
+ } else {
+ layout = com.android.internal.R.layout.search_dropdown_item_icons_1line;
+ from = ONE_LINE_ICONS_FROM;
+ to = ONE_LINE_ICONS_TO;
+ }
+ } else {
+ if (has2Lines) {
+ layout = com.android.internal.R.layout.search_dropdown_item_2line;
+ from = TWO_LINE_FROM;
+ to = TWO_LINE_TO;
+ } else {
+ layout = com.android.internal.R.layout.search_dropdown_item_1line;
+ from = ONE_LINE_FROM;
+ to = ONE_LINE_TO;
+ }
+ }
+ // Now actually set up the cursor, columns, and the list view
+ changeCursorAndColumns(c, from, to);
+ setViewResource(layout);
+ } else {
+ // Provide some help for developers instead of just silently discarding
+ Log.w(LOG_TAG, "Suggestions cursor discarded due to missing required columns.");
+ changeCursorAndColumns(null, null, null);
+ c.close();
+ }
+ if ((colIc1 >= 0) != (colIc2 >= 0)) {
+ Log.w(LOG_TAG, "Suggestion icon column(s) discarded, must be 0 or 2 columns.");
+ }
+ }
+
+ /**
+ * Overriding this allows us to write the selected query back into the box.
+ * NOTE: This is a vastly simplified version of SearchDialog.jamQuery() and does
+ * not universally support the search API. But it is sufficient for Google Search.
+ */
+ @Override
+ public CharSequence convertToString(Cursor cursor) {
+ CharSequence result = null;
+ if (cursor != null) {
+ int column = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
+ if (column >= 0) {
+ final String query = cursor.getString(column);
+ if (query != null) {
+ result = query;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the query cursor for the search suggestions.
+ *
+ * TODO this is functionally identical to the version in SearchDialog.java. Perhaps it
+ * could be hoisted into SearchableInfo or some other shared spot.
+ *
+ * @param query The search text entered (so far)
+ * @return Returns a cursor with suggestions, or null if no suggestions
+ */
+ private Cursor getSuggestions(final SearchableInfo searchable, final String query) {
+ Cursor cursor = null;
+ if (searchable.getSuggestAuthority() != null) {
+ try {
+ StringBuilder uriStr = new StringBuilder("content://");
+ uriStr.append(searchable.getSuggestAuthority());
+
+ // if content path provided, insert it now
+ final String contentPath = searchable.getSuggestPath();
+ if (contentPath != null) {
+ uriStr.append('/');
+ uriStr.append(contentPath);
+ }
+
+ // append standard suggestion query path
+ uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+
+ // inject query, either as selection args or inline
+ String[] selArgs = null;
+ if (searchable.getSuggestSelection() != null) { // use selection if provided
+ selArgs = new String[] {query};
+ } else {
+ uriStr.append('/'); // no sel, use REST pattern
+ uriStr.append(Uri.encode(query));
+ }
+
+ // finally, make the query
+ cursor = mContext.getContentResolver().query(
+ Uri.parse(uriStr.toString()), null,
+ searchable.getSuggestSelection(), selArgs,
+ null);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Search Suggestions query returned exception " + e.toString());
+ cursor = null;
+ }
+ }
+
+ return cursor;
+ }
+
+ /**
+ * Overriding this allows us to affect the way that an icon is loaded. Specifically,
+ * we can be more controlling about the resource path (and allow icons to come from other
+ * packages).
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @param v ImageView to receive an image
+ * @param value the value retrieved from the cursor
+ */
+ @Override
+ public void setViewImage(ImageView v, String value) {
+ int resID;
+ Drawable img = null;
+
+ try {
+ resID = Integer.parseInt(value);
+ if (resID != 0) {
+ img = mProviderResources.getDrawable(resID);
+ }
+ } catch (NumberFormatException nfe) {
+ // img = null;
+ } catch (NotFoundException e2) {
+ // img = null;
+ }
+
+ // finally, set the image to whatever we've gotten
+ v.setImageDrawable(img);
+ }
+
+ /**
+ * This method is overridden purely to provide a bit of protection against
+ * flaky content providers.
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @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(TAG, "Search Suggestions cursor returned exception " + e.toString());
+ // what can I return here?
+ View v = newView(mContext, mCursor, parent);
+ if (v != null) {
+ TextView tv = (TextView) v.findViewById(com.android.internal.R.id.text1);
+ tv.setText(e.toString());
+ }
+ return v;
+ }
+ }
+
+ }
+
+ /**
+ * Implements OnItemClickListener
+ */
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+// Log.d(LOG_TAG, "onItemClick() position " + position);
+ launchSuggestion(mSuggestionsAdapter, position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+// Log.d(LOG_TAG, "onItemSelected() position " + position);
+ jamSuggestionQuery(true, parent, position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onNothingSelected(AdapterView> parent) {
+// Log.d(LOG_TAG, "onNothingSelected()");
+ }
/**
* Debugging Support
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 5f25b9060167..0a37e8132022 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -739,6 +739,14 @@ import android.view.KeyEvent;
* No |
*
*
+ * android:inputType |
+ * If provided, supplies a hint about the type of search text the user will be
+ * entering. For most searches, in which free form text is expected, this attribute
+ * need not be provided. Suitable values for this attribute are described in the
+ * inputType attribute. |
+ * No |
+ *
+ *
*
*
*
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d6ea889e8c91..022a87cd443c 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -90,7 +90,7 @@ public class BluetoothA2dp {
}
/** Initiate a connection to an A2DP sink.
- * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when the
+ * Listen for SINK_STATE_CHANGED_ACTION to find out when the
* connection is completed.
* @param address Remote BT address.
* @return Result code, negative indicates an immediate error.
@@ -106,7 +106,7 @@ public class BluetoothA2dp {
}
/** Initiate disconnect from an A2DP sink.
- * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when
+ * Listen for SINK_STATE_CHANGED_ACTION to find out when
* disconnect is completed.
* @param address Remote BT address.
* @return Result code, negative indicates an immediate error.
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
index e1a484e3363d..5511ff6931e7 100644
--- a/core/java/android/content/AbstractTableMerger.java
+++ b/core/java/android/content/AbstractTableMerger.java
@@ -477,7 +477,7 @@ public abstract class AbstractTableMerger
private void fullyDeleteRowsWithSyncId(String syncId, String account, SyncResult syncResult) {
final String[] selectionArgs = new String[]{syncId, account};
// delete the rows explicitly so that the delete operation can be overridden
- Cursor c = mDb.query(mTable, new String[]{"_id"}, SELECT_BY_ID_AND_ACCOUNT,
+ Cursor c = mDb.query(mTable, getDeleteRowProjection(), SELECT_BY_ID_AND_ACCOUNT,
selectionArgs, null, null, null);
try {
c.moveToFirst();
@@ -494,6 +494,16 @@ public abstract class AbstractTableMerger
}
/**
+ * Provides the projection used by
+ * {@link AbstractTableMerger#deleteRow(android.database.Cursor)}.
+ * This should be overridden if the deleteRow implementation requires
+ * additional columns.
+ */
+ protected String[] getDeleteRowProjection() {
+ return new String[]{"_id"};
+ }
+
+ /**
* Converts cursor into a Map, using the correct types for the values.
*/
protected void cursorRowToContentValues(Cursor cursor, ContentValues map) {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index dc757480d9a8..e2d7097af3b7 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -100,10 +100,12 @@ public class Camera {
/**
* Reconnect to the camera after passing it to MediaRecorder. To save
- * setup/teardown time, a client of Camara can pass an initialized Camera
+ * setup/teardown time, a client of Camera can pass an initialized Camera
* object to a MediaRecorder to use for video recording. Once the
* MediaRecorder is done with the Camera, this method can be used to
- * re-establish a connection with the camera hardware.
+ * re-establish a connection with the camera hardware. NOTE: The Camera
+ * object must first be unlocked by the process that owns it before it
+ * can be connected to another proces.
*
* @throws IOException if the method fails.
*
@@ -113,6 +115,34 @@ public class Camera {
public native final void reconnect() throws IOException;
/**
+ * Lock the camera to prevent other processes from accessing it. To save
+ * setup/teardown time, a client of Camera can pass an initialized Camera
+ * object to another process. This method is used to re-lock the Camera
+ * object prevent other processes from accessing it. By default, the
+ * Camera object is locked. Locking it again from the same process will
+ * have no effect. Attempting to lock it from another process if it has
+ * not been unlocked will fail.
+ * Returns 0 if lock was successful.
+ *
+ * FIXME: Unhide after approval
+ * @hide
+ */
+ public native final int lock();
+
+ /**
+ * Unlock the camera to allow aother process to access it. To save
+ * setup/teardown time, a client of Camera can pass an initialized Camera
+ * object to another process. This method is used to unlock the Camera
+ * object before handing off the Camera object to the other process.
+
+ * Returns 0 if unlock was successful.
+ *
+ * FIXME: Unhide after approval
+ * @hide
+ */
+ public native final int unlock();
+
+ /**
* Sets the SurfaceHolder to be used for a picture preview. If the surface
* changed since the last call, the screen will blank. Nothing happens
* if the same surface is re-set.
@@ -152,6 +182,14 @@ public class Camera {
public native final void stopPreview();
/**
+ * Return current preview state.
+ *
+ * FIXME: Unhide before release
+ * @hide
+ */
+ public native final boolean previewEnabled();
+
+ /**
* Can be called at any time to instruct the camera to use a callback for
* each preview frame in addition to displaying it.
*
@@ -242,7 +280,9 @@ public class Camera {
};
/**
- * Registers a callback to be invoked when the auto focus responds.
+ * Starts auto-focus function and registers a callback function to
+ * run when camera is focused. Only valid after startPreview() has
+ * been called.
*
* @param cb the callback to run
*/
@@ -525,6 +565,58 @@ public class Camera {
}
/**
+ * Sets the dimensions for EXIF thumbnails.
+ *
+ * @param width the width of the thumbnail, in pixels
+ * @param height the height of the thumbnail, in pixels
+ *
+ * FIXME: unhide before release
+ * @hide
+ */
+ public void setThumbnailSize(int width, int height) {
+ set("jpeg-thumbnail-width", width);
+ set("jpeg-thumbnail-height", height);
+ }
+
+ /**
+ * Returns the dimensions for EXIF thumbnail
+ *
+ * @return a Size object with the height and width setting
+ * for the EXIF thumbnails
+ *
+ * FIXME: unhide before release
+ * @hide
+ */
+ public Size getThumbnailSize() {
+ return new Size(getInt("jpeg-thumbnail-width"),
+ getInt("jpeg-thumbnail-height"));
+ }
+
+ /**
+ * Sets the quality of the EXIF thumbnail
+ *
+ * @param quality the JPEG quality of the EXIT thumbnail
+ *
+ * FIXME: unhide before release
+ * @hide
+ */
+ public void setThumbnailQuality(int quality) {
+ set("jpeg-thumbnail-quality", quality);
+ }
+
+ /**
+ * Returns the quality setting for the EXIF thumbnail
+ *
+ * @return the JPEG quality setting of the EXIF thumbnail
+ *
+ * FIXME: unhide before release
+ * @hide
+ */
+ public int getThumbnailQuality() {
+ return getInt("jpeg-thumbnail-quality");
+ }
+
+ /**
* Sets the rate at which preview frames are received.
*
* @param fps the frame rate (frames per second)
@@ -547,7 +639,7 @@ public class Camera {
* Sets the image format for preview pictures.
*
* @param pixel_format the desired preview picture format
- * (PixelFormat.YCbCr_422_SP,
+ * (PixelFormat.YCbCr_420_SP,
* PixelFormat.RGB_565, or
* PixelFormat.JPEG)
* @see android.graphics.PixelFormat
@@ -604,7 +696,7 @@ public class Camera {
* Sets the image format for pictures.
*
* @param pixel_format the desired picture format
- * (PixelFormat.YCbCr_422_SP,
+ * (PixelFormat.YCbCr_420_SP,
* PixelFormat.RGB_565, or
* PixelFormat.JPEG)
* @see android.graphics.PixelFormat
@@ -630,6 +722,7 @@ public class Camera {
private String cameraFormatForPixelFormat(int pixel_format) {
switch(pixel_format) {
case PixelFormat.YCbCr_422_SP: return "yuv422sp";
+ case PixelFormat.YCbCr_420_SP: return "yuv420sp";
case PixelFormat.RGB_565: return "rgb565";
case PixelFormat.JPEG: return "jpeg";
default: return null;
@@ -643,6 +736,9 @@ public class Camera {
if (format.equals("yuv422sp"))
return PixelFormat.YCbCr_422_SP;
+ if (format.equals("yuv420sp"))
+ return PixelFormat.YCbCr_420_SP;
+
if (format.equals("rgb565"))
return PixelFormat.RGB_565;
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 40c03cd898f0..5a85c66f4290 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -91,7 +91,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
case DO_UPDATE_SELECTION: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
mInputMethodSession.updateSelection(args.argi1, args.argi2,
- args.argi3, args.argi4);
+ args.argi3, args.argi4, args.argi5, args.argi6);
mCaller.recycleArgs(args);
return;
}
@@ -135,9 +135,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
}
public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIIII(DO_UPDATE_SELECTION,
- oldSelStart, oldSelEnd, newSelStart, newSelEnd));
+ int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
+ oldSelStart, oldSelEnd, newSelStart, newSelEnd,
+ candidatesStart, candidatesEnd));
}
public void updateCursor(Rect newCursor) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9ebf1271ee67..0588beada6cf 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -48,6 +48,132 @@ import android.widget.FrameLayout;
* which final implementations can derive from and customize. See the
* base class {@link AbstractInputMethodService} and the {@link InputMethod}
* interface for more information on the basics of writing input methods.
+ *
+ * An input method has significant discretion in how it goes about its
+ * work: the {@link android.inputmethodservice.InputMethodService} provides
+ * a basic framework for standard UI elements (input view, candidates view,
+ * and running in fullscreen mode), but it is up to a particular implementor
+ * to decide how to use them. For example, one input method could implement
+ * an input area with a keyboard, another could allow the user to draw text,
+ * while a third could have no input area (and thus not be visible to the
+ * user) but instead listen to audio and perform text to speech conversion.
+ *
+ * In the implementation provided here, all of these elements are placed
+ * together in a single window managed by the InputMethodService. It will
+ * execute callbacks as it needs information about them, and provides APIs for
+ * programmatic control over them. They layout of these elements is explicitly
+ * defined:
+ *
+ *
+ * - The soft input view, if available, is placed at the bottom of the
+ * screen.
+ *
- The candidates view, if currently shown, is placed above the soft
+ * input view.
+ *
- If not running fullscreen, the application is moved or resized to be
+ * above these views; if running fullscreen, the window will completely cover
+ * the application and its top part will contain the extract text of what is
+ * currently being edited by the application.
+ *
+ *
+ *
+ *
+ * Soft Input View
+ *
+ * Central to most input methods is the soft input view. This is where most
+ * user interaction occurs: pressing on soft keys, drawing characters, or
+ * however else your input method wants to generate text. Most implementations
+ * will simply have their own view doing all of this work, and return a new
+ * instance of it when {@link #onCreateInputView()} is called. At that point,
+ * as long as the input view is visible, you will see user interaction in
+ * that view and can call back on the InputMethodService to interact with the
+ * application as appropriate.
+ *
+ * There are some situations where you want to decide whether or not your
+ * soft input view should be shown to the user. This is done by implementing
+ * the {@link #onEvaluateInputViewShown()} to return true or false based on
+ * whether it should be shown in the current environment. If any of your
+ * state has changed that may impact this, call
+ * {@link #updateInputViewShown()} to have it re-evaluated. The default
+ * implementation always shows the input view unless there is a hard
+ * keyboard available, which is the appropriate behavior for most input
+ * methods.
+ *
+ *
+ *
+ * Candidates View
+ *
+ * Often while the user is generating raw text, an input method wants to
+ * provide them with a list of possible interpretations of that text that can
+ * be selected for use. This is accomplished with the candidates view, and
+ * like the soft input view you implement {@link #onCreateCandidatesView()}
+ * to instantiate your own view implementing your candidates UI.
+ *
+ * Management of the candidates view is a little different than the input
+ * view, because the candidates view tends to be more transient, being shown
+ * only when there are possible candidates for the current text being entered
+ * by the user. To control whether the candidates view is shown, you use
+ * {@link #setCandidatesViewShown(boolean)}. Note that because the candidate
+ * view tends to be shown and hidden a lot, it does not impact the application
+ * UI in the same way as the soft input view: it will never cause application
+ * windows to resize, only cause them to be panned if needed for the user to
+ * see the current focus.
+ *
+ *
+ *
+ * Fullscreen Mode
+ *
+ * Sometimes your input method UI is too large to integrate with the
+ * application UI, so you just want to take over the screen. This is
+ * accomplished by switching to full-screen mode, causing the input method
+ * window to fill the entire screen and add its own "extracted text" editor
+ * showing the user the text that is being typed. Unlike the other UI elements,
+ * there is a standard implementation for the extract editor that you should
+ * not need to change. The editor is placed at the top of the IME, above the
+ * input and candidates views.
+ *
+ * Similar to the input view, you control whether the IME is running in
+ * fullscreen mode by implementing {@link #onEvaluateFullscreenMode()}
+ * to return true or false based on
+ * whether it should be fullscreen in the current environment. If any of your
+ * state has changed that may impact this, call
+ * {@link #updateFullscreenMode()} to have it re-evaluated. The default
+ * implementation selects fullscreen mode when the screen is in a landscape
+ * orientation, which is appropriate behavior for most input methods that have
+ * a significant input area.
+ *
+ * When in fullscreen mode, you have some special requirements because the
+ * user can not see the application UI. In particular, you should implement
+ * {@link #onDisplayCompletions(CompletionInfo[])} to show completions
+ * generated by your application, typically in your candidates view like you
+ * would normally show candidates.
+ *
+ *
+ *
+ *
Generating Text
+ *
+ * The key part of an IME is of course generating text for the application.
+ * This is done through calls to the
+ * {@link android.view.inputmethod.InputConnection} interface to the
+ * application, which can be retrieved from {@link #getCurrentInputConnection()}.
+ * This interface allows you to generate raw key events or, if the target
+ * supports it, directly edit in strings of candidates and committed text.
+ *
+ * Information about what the target is expected and supports can be found
+ * through the {@link android.view.inputmethod.EditorInfo} class, which is
+ * retrieved with {@link #getCurrentInputEditorInfo()} method. The most
+ * important part of this is {@link android.view.inputmethod.EditorInfo#inputType
+ * EditorInfo.inputType}; in particular, if this is
+ * {@link android.view.inputmethod.EditorInfo#TYPE_NULL EditorInfo.TYPE_NULL},
+ * then the target does not support complex edits and you need to only deliver
+ * raw key events to it. An input method will also want to look at other
+ * values here, to for example detect password mode, auto complete text views,
+ * phone number entry, etc.
+ *
+ * When the user switches between input targets, you will receive calls to
+ * {@link #onFinishInput()} and {@link #onStartInput(EditorInfo, boolean)}.
+ * You can use these to reset and initialize your input state for the current
+ * target. For example, you will often want to clear any input state, and
+ * update a soft keyboard to be appropriate for the new inputType.
*/
public class InputMethodService extends AbstractInputMethodService {
static final String TAG = "InputMethodService";
@@ -68,7 +194,7 @@ public class InputMethodService extends AbstractInputMethodService {
InputBinding mInputBinding;
InputConnection mInputConnection;
boolean mInputStarted;
- EditorInfo mInputInfo;
+ EditorInfo mInputEditorInfo;
boolean mShowInputRequested;
boolean mShowCandidatesRequested;
@@ -210,12 +336,13 @@ public class InputMethodService extends AbstractInputMethodService {
* InputMethodService.onUpdateSelection()}.
*/
public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd) {
+ int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd) {
if (!isEnabled()) {
return;
}
InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
- newSelStart, newSelEnd);
+ newSelStart, newSelEnd, candidatesStart, candidatesEnd);
}
/**
@@ -303,6 +430,7 @@ public class InputMethodService extends AbstractInputMethodService {
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this);
initViews();
+ mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT);
}
void initViews() {
@@ -384,8 +512,8 @@ public class InputMethodService extends AbstractInputMethodService {
return mInputStarted;
}
- public EditorInfo getCurrentInputInfo() {
- return mInputInfo;
+ public EditorInfo getCurrentInputEditorInfo() {
+ return mInputEditorInfo;
}
/**
@@ -459,14 +587,14 @@ public class InputMethodService extends AbstractInputMethodService {
int[] loc = mTmpLocation;
if (mInputFrame.getVisibility() == View.VISIBLE) {
mInputFrame.getLocationInWindow(loc);
- outInsets.contentTopInsets = loc[1];
+ } else {
+ loc[1] = 0;
}
+ outInsets.contentTopInsets = loc[1];
if (mCandidatesFrame.getVisibility() == View.VISIBLE) {
mCandidatesFrame.getLocationInWindow(loc);
- outInsets.visibleTopInsets = loc[1];
- } else {
- outInsets.visibleTopInsets = loc[1];
}
+ outInsets.visibleTopInsets = loc[1];
outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
}
@@ -712,7 +840,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (doShowInput) {
if (mInputStarted) {
if (DEBUG) Log.v(TAG, "showWindow: starting input view");
- onStartInputView(mInputInfo, false);
+ onStartInputView(mInputEditorInfo, false);
}
startExtractingText();
}
@@ -744,11 +872,11 @@ public class InputMethodService extends AbstractInputMethodService {
void doStartInput(EditorInfo attribute, boolean restarting) {
mInputStarted = true;
- mInputInfo = attribute;
+ mInputEditorInfo = attribute;
onStartInput(attribute, restarting);
if (mWindowVisible) {
if (mWindowCreated) {
- onStartInputView(mInputInfo, restarting);
+ onStartInputView(mInputEditorInfo, restarting);
}
startExtractingText();
}
@@ -795,7 +923,8 @@ public class InputMethodService extends AbstractInputMethodService {
* the extract text, if it is being shown.
*/
public void onUpdateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd) {
+ int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd) {
if (mExtractEditText != null && mExtractedText != null) {
final int off = mExtractedText.startOffset;
mExtractEditText.setSelection(newSelStart-off, newSelEnd-off);
@@ -821,10 +950,22 @@ public class InputMethodService extends AbstractInputMethodService {
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mWindowVisible && event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
- dismissSoftInput();
- return true;
+ if (mShowInputRequested) {
+ // If the soft input area is shown, back closes it and we
+ // consume the back key.
+ dismissSoftInput();
+ return true;
+ }
+ if (mShowCandidatesRequested) {
+ // If the candidates are shown, we just want to make sure
+ // they are now hidden but otherwise let the app execute
+ // the back.
+ // XXX this needs better interaction with the soft input
+ // implementation.
+ //setCandidatesViewShown(false);
+ }
}
return false;
}
@@ -857,8 +998,8 @@ public class InputMethodService extends AbstractInputMethodService {
if (mExtractedText != null) {
mExtractEditText.setExtractedText(mExtractedText);
}
- mExtractEditText.setInputType(getCurrentInputInfo().inputType);
- mExtractEditText.setHint(mInputInfo.hintText);
+ mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType);
+ mExtractEditText.setHint(mInputEditorInfo.hintText);
}
}
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 75a2911bcaea..cfd3188bec4d 100755
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -438,7 +438,6 @@ public class Keyboard {
}
}
-
/**
* Returns the square of the distance between the center of the key and the given point.
* @param x the x-coordinate of the point
@@ -446,9 +445,9 @@ public class Keyboard {
* @return the square of the distance of the point from the center of the key
*/
public int squaredDistanceFrom(int x, int y) {
- float xDist = Math.abs((this.x + this.x + width) / 2f - x);
- float yDist = Math.abs((this.y + this.y + height) / 2f - y);
- return (int) (xDist * xDist + yDist * yDist);
+ int xDist = this.x + width / 2 - x;
+ int yDist = this.y + height / 2 - y;
+ return xDist * xDist + yDist * yDist;
}
/**
@@ -749,7 +748,8 @@ public class Keyboard {
if (value.type == TypedValue.TYPE_DIMENSION) {
return a.getDimensionPixelOffset(index, defValue);
} else if (value.type == TypedValue.TYPE_FRACTION) {
- return (int) a.getFraction(index, base, base, defValue);
+ // Round it to avoid values like 47.9999 from getting truncated
+ return Math.round(a.getFraction(index, base, base, defValue));
}
return defValue;
}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 56473da35899..3b5d741aba5f 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard.Key;
@@ -37,6 +38,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.PopupWindow;
@@ -132,6 +134,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private static final int MSG_REMOVE_PREVIEW = 1;
private static final int MSG_REPEAT = 2;
+ private static final int MSG_LONGPRESS = 3;
private int mVerticalCorrection;
private int mProximityThreshold;
@@ -178,6 +181,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
private static final int REPEAT_START_DELAY = 400;
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private Vibrator mVibrator;
private long[] mVibratePattern = new long[] {1, 20};
@@ -206,6 +210,9 @@ public class KeyboardView extends View implements View.OnClickListener {
sendMessageDelayed(repeat, REPEAT_INTERVAL);
}
break;
+ case MSG_LONGPRESS:
+ openPopupIfRequired((MotionEvent) msg.obj);
+ break;
}
}
@@ -308,27 +315,28 @@ public class KeyboardView extends View implements View.OnClickListener {
@Override
public boolean onFling(MotionEvent me1, MotionEvent me2,
float velocityX, float velocityY) {
- if (velocityX > 400 && Math.abs(velocityY) < 400) {
+ final float absX = Math.abs(velocityX);
+ final float absY = Math.abs(velocityY);
+ if (velocityX > 500 && absY < absX) {
swipeRight();
return true;
- } else if (velocityX < -400 && Math.abs(velocityY) < 400) {
+ } else if (velocityX < -500 && absY < absX) {
swipeLeft();
return true;
- } else if (velocityY < -400 && Math.abs(velocityX) < 400) {
+ } else if (velocityY < -500 && absX < absY) {
swipeUp();
return true;
- } else if (velocityY > 400 && Math.abs(velocityX) < 400) {
+ } else if (velocityY > 500 && absX < 200) {
swipeDown();
return true;
+ } else if (absX > 800 || absY > 800) {
+ return true;
}
return false;
}
-
- @Override
- public void onLongPress(MotionEvent me) {
- openPopupIfRequired(me);
- }
});
+
+ mGestureDetector.setIsLongpressEnabled(false);
}
public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
@@ -351,6 +359,9 @@ public class KeyboardView extends View implements View.OnClickListener {
* @param keyboard the keyboard to display in this view
*/
public void setKeyboard(Keyboard keyboard) {
+ if (mKeyboard != null) {
+ showPreview(NOT_A_KEY);
+ }
mKeyboard = keyboard;
requestLayout();
invalidate();
@@ -518,10 +529,10 @@ public class KeyboardView extends View implements View.OnClickListener {
// For characters, use large font. For labels like "Done", use small font.
if (label.length() > 1 && key.codes.length < 2) {
paint.setTextSize(mLabelTextSize);
- paint.setFakeBoldText(true);
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
} else {
paint.setTextSize(mKeyTextSize);
- paint.setFakeBoldText(false);
+ paint.setTypeface(Typeface.DEFAULT);
}
// Draw a drop shadow for the text
paint.setShadowLayer(3f, 0, 0, 0xCC000000);
@@ -878,6 +889,7 @@ public class KeyboardView extends View implements View.OnClickListener {
if (mGestureDetector.onTouchEvent(me)) {
showPreview(NOT_A_KEY);
mHandler.removeMessages(MSG_REPEAT);
+ mHandler.removeMessages(MSG_LONGPRESS);
return true;
}
@@ -907,12 +919,17 @@ public class KeyboardView extends View implements View.OnClickListener {
Message msg = mHandler.obtainMessage(MSG_REPEAT);
mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY);
}
+ if (mCurrentKey != NOT_A_KEY) {
+ Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
+ mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
+ }
showPreview(keyIndex);
playKeyClick();
vibrate();
break;
case MotionEvent.ACTION_MOVE:
+ boolean continueLongPress = false;
if (keyIndex != NOT_A_KEY) {
if (mCurrentKey == NOT_A_KEY) {
mCurrentKey = keyIndex;
@@ -920,6 +937,7 @@ public class KeyboardView extends View implements View.OnClickListener {
} else {
if (keyIndex == mCurrentKey) {
mCurrentKeyTime += eventTime - mLastMoveTime;
+ continueLongPress = true;
} else {
resetMultiTap();
mLastKey = mCurrentKey;
@@ -936,11 +954,21 @@ public class KeyboardView extends View implements View.OnClickListener {
mRepeatKeyIndex = NOT_A_KEY;
}
}
+ if (!continueLongPress) {
+ // Cancel old longpress
+ mHandler.removeMessages(MSG_LONGPRESS);
+ // Start new longpress if key has changed
+ if (keyIndex != NOT_A_KEY) {
+ Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
+ mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
+ }
+ }
showPreview(keyIndex);
break;
case MotionEvent.ACTION_UP:
mHandler.removeMessages(MSG_REPEAT);
+ mHandler.removeMessages(MSG_LONGPRESS);
if (keyIndex == mCurrentKey) {
mCurrentKeyTime += eventTime - mLastMoveTime;
} else {
diff --git a/core/java/android/os/HandlerState.java b/core/java/android/os/HandlerState.java
new file mode 100644
index 000000000000..0708f7d42cb7
--- /dev/null
+++ b/core/java/android/os/HandlerState.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * {@hide}
+ */
+public abstract class HandlerState {
+ public HandlerState() {
+ }
+
+ public void enter(Message message) {
+ }
+
+ public abstract void processMessage(Message message);
+
+ public void exit(Message message) {
+ }
+}
diff --git a/core/java/android/os/HandlerStateMachine.java b/core/java/android/os/HandlerStateMachine.java
new file mode 100644
index 000000000000..d004a25b9259
--- /dev/null
+++ b/core/java/android/os/HandlerStateMachine.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+import android.util.LogPrinter;
+
+/**
+ * {@hide}
+ *
+ * Implement a state machine where each state is an object,
+ * HandlerState. Each HandlerState must implement processMessage
+ * and optionally enter/exit. When a state machine is created
+ * the initial state must be set. When messages are sent to
+ * a state machine the current state's processMessage method is
+ * invoked. If this is the first message for this state the
+ * enter method is called prior to processMessage and when
+ * transtionTo is invoked the state's exit method will be
+ * called after returning from processMessage.
+ *
+ * If a message should be handled in a different state the
+ * processMessage method may call deferMessage. This causes
+ * the message to be saved on a list until transitioning
+ * to a new state, at which time all of the deferred messages
+ * will be put on the front of the state machines queue and
+ * processed by the new current state's processMessage
+ * method.
+ *
+ * Below is an example state machine with two state's, S1 and S2.
+ * The initial state is S1 which defers all messages and only
+ * transition to S2 when message.what == TEST_WHAT_2. State S2
+ * will process each messages until it receives TEST_WHAT_2
+ * where it will transition back to S1:
+
+ class StateMachine1 extends HandlerStateMachine {
+ private static final int TEST_WHAT_1 = 1;
+ private static final int TEST_WHAT_2 = 2;
+
+ StateMachine1(String name) {
+ super(name);
+ setInitialState(mS1);
+ }
+
+ class S1 extends HandlerState {
+ @Override public void enter(Message message) {
+ }
+
+ @Override public void processMessage(Message message) {
+ deferMessage(message);
+ if (message.what == TEST_WHAT_2) {
+ transitionTo(mS2);
+ }
+ }
+
+ @Override public void exit(Message message) {
+ }
+ }
+
+ class S2 extends HandlerState {
+ @Override public void processMessage(Message message) {
+ // Do some processing
+ if (message.what == TEST_WHAT_2) {
+ transtionTo(mS1);
+ }
+ }
+ }
+
+ private S1 mS1 = new S1();
+ private S2 mS2 = new S2();
+ }
+
+ */
+public class HandlerStateMachine {
+
+ private boolean mDbg = false;
+ private static final String TAG = "HandlerStateMachine";
+ private String mName;
+ private SmHandler mHandler;
+ private HandlerThread mHandlerThread;
+
+ /**
+ * Handle messages sent to the state machine by calling
+ * the current state's processMessage. It also handles
+ * the enter/exit calls and placing any deferred messages
+ * back onto the queue when transitioning to a new state.
+ */
+ class SmHandler extends Handler {
+
+ SmHandler(Looper looper) {
+ super(looper);
+ }
+
+ /**
+ * This will dispatch the message to the
+ * current state's processMessage.
+ */
+ @Override
+ final public void handleMessage(Message msg) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage E");
+ if (mDestState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destation call enter");
+ mCurrentState = mDestState;
+ mDestState = null;
+ mCurrentState.enter(msg);
+ }
+ if (mCurrentState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; call processMessage");
+ mCurrentState.processMessage(msg);
+ } else {
+ /* Strange no state to execute */
+ Log.e(TAG, "handleMessage: no current state, did you call setInitialState");
+ }
+
+ if (mDestState != null) {
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destination call exit");
+ mCurrentState.exit(msg);
+
+ /**
+ * Place the messages from the deferred queue:t
+ * on to the Handler's message queue in the
+ * same order that they originally arrived.
+ *
+ * We set cur.when = 0 to circumvent the check
+ * that this message has already been sent.
+ */
+ while (mDeferredMessages != null) {
+ Message cur = mDeferredMessages;
+ mDeferredMessages = mDeferredMessages.next;
+ cur.when = 0;
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage; queue deferred message what="
+ + cur.what + " target=" + cur.target);
+ sendMessageAtFrontOfQueue(cur);
+ }
+ if (mDbg) Log.d(TAG, "SmHandler.handleMessage X");
+ }
+ }
+
+ public HandlerState mCurrentState;
+ public HandlerState mDestState;
+ public Message mDeferredMessages;
+ }
+
+ /**
+ * Create an active StateMachine, one that has a
+ * dedicated thread/looper/queue.
+ */
+ public HandlerStateMachine(String name) {
+ mName = name;
+ mHandlerThread = new HandlerThread(name);
+ mHandlerThread.start();
+ mHandler = new SmHandler(mHandlerThread.getLooper());
+ }
+
+ /**
+ * Get a message and set Message.target = this.
+ */
+ public final Message obtainMessage()
+ {
+ Message msg = Message.obtain(mHandler);
+ if (mDbg) Log.d(TAG, "StateMachine.obtainMessage() EX target=" + msg.target);
+ return msg;
+ }
+
+ /**
+ * Get a message and set Message.target = this and
+ * Message.what = what.
+ */
+ public final Message obtainMessage(int what) {
+ Message msg = Message.obtain(mHandler, what);
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.obtainMessage(what) EX what=" + msg.what +
+ " target=" + msg.target);
+ }
+ return msg;
+ }
+
+ /**
+ * Enqueue a message to this state machine.
+ */
+ public final void sendMessage(Message msg) {
+ if (mDbg) Log.d(TAG, "StateMachine.sendMessage EX msg.what=" + msg.what);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Enqueue a message to this state machine after a delay.
+ */
+ public final void sendMessageDelayed(Message msg, long delayMillis) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.sendMessageDelayed EX msg.what="
+ + msg.what + " delay=" + delayMillis);
+ }
+ mHandler.sendMessageDelayed(msg, delayMillis);
+ }
+
+ /**
+ * Set the initial state. This must be invoked before
+ * and messages are sent to the state machine.
+ */
+ public void setInitialState(HandlerState initialState) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.setInitialState EX initialState"
+ + initialState.getClass().getName());
+ }
+ mHandler.mDestState = initialState;
+ }
+
+ /**
+ * transition to destination state. Upon returning
+ * from processMessage the current state's exit will
+ * be executed and upon the next message arriving
+ * destState.enter will be invoked.
+ */
+ final public void transitionTo(HandlerState destState) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.transitionTo EX destState"
+ + destState.getClass().getName());
+ }
+ mHandler.mDestState = destState;
+ }
+
+ /**
+ * Defer this message until next state transition.
+ * Upon transitioning all deferred messages will be
+ * placed on the queue and reprocessed in the original
+ * order. (i.e. The next state the oldest messages will
+ * be processed first)
+ */
+ final public void deferMessage(Message msg) {
+ if (mDbg) {
+ Log.d(TAG, "StateMachine.deferMessage EX mDeferredMessages="
+ + mHandler.mDeferredMessages);
+ }
+
+ /* Copy the "msg" to "newMsg" as "msg" will be recycled */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(msg);
+
+ /* Place on front of queue */
+ newMsg.next = mHandler.mDeferredMessages;
+ mHandler.mDeferredMessages = newMsg;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * @return Handler
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * @return if debugging is enabled
+ */
+ public boolean isDbg() {
+ return mDbg;
+ }
+
+ /**
+ * Set debug enable/disabled.
+ */
+ public void setDbg(boolean dbg) {
+ mDbg = dbg;
+ if (mDbg) {
+ mHandlerThread.getLooper().setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
+ } else {
+ mHandlerThread.getLooper().setMessageLogging(null);
+ }
+ }
+}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index abc1e2f3105f..e48f1523a6be 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -28,4 +28,5 @@ interface IPowerManager
void setPokeLock(int pokey, IBinder lock, String tag);
void setStayOnSetting(int val);
long getScreenOnTime();
+ void preventScreenOn(boolean prevent);
}
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
index 0794e6de08e2..b53e22704d1f 100644
--- a/core/java/android/os/Power.java
+++ b/core/java/android/os/Power.java
@@ -16,6 +16,8 @@
package android.os;
+import java.io.IOException;
+
/**
* Class that provides access to some of the power management functions.
*
@@ -71,12 +73,12 @@ public class Power
* Brightness value for dim backlight
*/
public static final int BRIGHTNESS_DIM = 20;
-
+
/**
* Brightness value for fully on
*/
public static final int BRIGHTNESS_ON = 255;
-
+
/**
* Brightness value to use when battery is low
*/
@@ -104,11 +106,11 @@ public class Power
public static native int setScreenState(boolean on);
public static native int setLastUserActivityTimeout(long ms);
-
+
/**
* Turn the device off.
- *
- * This method is considered deprecated in favor of
+ *
+ * This method is considered deprecated in favor of
* {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}.
*
* @deprecated
@@ -120,7 +122,9 @@ public class Power
/**
* Reboot the device.
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
+ *
+ * @throws IOException if reboot fails for some reason (eg, lack of
+ * permission)
*/
- public static native void reboot(String reason);
+ public static native void reboot(String reason) throws IOException;
}
-
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index e2a315763278..05c295224374 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -133,11 +133,10 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
if (preferenceAsGroup.isOnSameScreenAsChildren()) {
flattenPreferenceGroup(preferences, preferenceAsGroup);
- preference.setOnPreferenceChangeInternalListener(this);
}
- } else {
- preference.setOnPreferenceChangeInternalListener(this);
}
+
+ preference.setOnPreferenceChangeInternalListener(this);
}
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 7aaed494b93a..76aa51d175c8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -372,15 +372,13 @@ public class Browser {
SEARCHES_WHERE_CLAUSE,
new String [] { search },
null);
+ ContentValues map = new ContentValues();
+ map.put(SearchColumns.SEARCH, search);
+ map.put(SearchColumns.DATE, now);
/* We should only get one answer that is exactly the same. */
if (c.moveToFirst()) {
- ContentValues map = new ContentValues();
- map.put(BookmarkColumns.DATE, now);
- cr.update(BOOKMARKS_URI, map, "_id = " + c.getInt(0), null);
+ cr.update(SEARCHES_URI, map, "_id = " + c.getInt(0), null);
} else {
- ContentValues map = new ContentValues();
- map.put(SearchColumns.SEARCH, search);
- map.put(SearchColumns.DATE, now);
cr.insert(SEARCHES_URI, map);
}
c.deactivate();
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 6d24ba87aa53..085c095569da 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -170,12 +170,20 @@ public class Contacts {
*/
public interface PeopleColumns {
/**
- * The persons name.
+ * The person's name.
* Type: TEXT
*/
public static final String NAME = "name";
/**
+ * Phonetic equivalent of the person's name, in a locale-dependent
+ * character set (e.g. hiragana for Japanese).
+ * Used for pronunciation and/or collation in some languages.
+ * Type: TEXT
+ */
+ public static final String PHONETIC_NAME = "phonetic_name";
+
+ /**
* The display name. If name is not null name, else if number is not null number,
* else if email is not null email.
* Type: TEXT
@@ -1509,6 +1517,12 @@ public class Contacts {
public static final String NAME = "name";
/**
+ * The extra field for the contact phonetic name.
+ * Type: String
+ */
+ public static final String PHONETIC_NAME = "phonetic_name";
+
+ /**
* The extra field for the contact company.
* Type: String
*/
@@ -1535,7 +1549,7 @@ public class Contacts {
/**
* The extra field for the contact phone number type.
* Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String PHONE_TYPE = "phone_type";
@@ -1554,7 +1568,7 @@ public class Contacts {
/**
* The extra field for an optional second contact phone number type.
* Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type";
@@ -1567,7 +1581,7 @@ public class Contacts {
/**
* The extra field for an optional third contact phone number type.
* Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
@@ -1580,7 +1594,7 @@ public class Contacts {
/**
* The extra field for the contact email type.
* Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String EMAIL_TYPE = "email_type";
@@ -1599,7 +1613,7 @@ public class Contacts {
/**
* The extra field for an optional second contact email type.
* Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type";
@@ -1612,7 +1626,7 @@ public class Contacts {
/**
* The extra field for an optional third contact email type.
* Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type";
@@ -1625,7 +1639,7 @@ public class Contacts {
/**
* The extra field for the contact postal address type.
* Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
- * or a string specifying a type and label.
+ * or a string specifying a custom label.
*/
public static final String POSTAL_TYPE = "postal_type";
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index d4b728b437ea..7b2f18ca9235 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -84,13 +84,27 @@ public final class MediaStore
public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
/**
- * The name of the Intent-extra used to control the orientation of a MovieView.
- * This is an int property that overrides the MovieView activity's requestedOrientation.
+ * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
+ * This is an int property that overrides the activity's requestedOrientation.
* @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
*/
public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
/**
+ * The name of the Intent-extra used to control the orientation of a ViewImage.
+ * This is a boolean property that overrides the activity's default fullscreen state.
+ * @hide
+ */
+ public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
+
+ /**
+ * The name of the Intent-extra used to control the orientation of a ViewImage.
+ * This is a boolean property that specifies whether or not to show action icons.
+ * @hide
+ */
+ public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
+
+ /**
* The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
* This is a boolean property that specifies whether or not to finish the MovieView activity
* when the movie completes playing. The default value is true, which means to automatically
@@ -118,6 +132,22 @@ public final class MediaStore
public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
/**
+ * Standard Intent action that can be sent to have the media application
+ * capture an video and return it. The caller may pass in an extra EXTRA_VIDEO_QUALITY
+ * control the video quality.
+ * @hide
+ */
+ public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
+
+ /**
+ * The name of the Intent-extra used to control the quality of a recorded video. This is an
+ * integer property. Currently value 0 means low quality, suitable for MMS messages, and
+ * value 1 means high quality. In the future other quality levels may be added.
+ * @hide
+ */
+ public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
+
+ /**
* Common fields for most MediaProvider tables
*/
@@ -1162,13 +1192,21 @@ public final class MediaStore
public static final class Video {
/**
- * The default sort order for this table
+ * deprecated Replaced by DEFAULT_SORT_ORDER2
+ * This variable is a mistake that is retained for backwards compatibility.
+ * (There is no "name" column in the Video table.)
*/
public static final String DEFAULT_SORT_ORDER = "name ASC";
+ /**
+ * The default sort order for this table
+ * @hide
+ */
+ public static final String DEFAULT_SORT_ORDER2 = MediaColumns.DISPLAY_NAME;
+
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
{
- return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
+ return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER2);
}
public interface VideoColumns extends MediaColumns {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e93bbeb95cc0..91624adce91a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1772,9 +1772,19 @@ public final class Settings {
/**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
+ * @deprecated This identifier is poorly initialized and has
+ * many collisions. It should not be used.
*/
+ @Deprecated
public static final String LOGGING_ID = "logging_id";
-
+
+ /**
+ * The Logging ID (a unique 64-bit value) as a hex string.
+ * Used as a pseudonymous identifier for logging.
+ * @hide
+ */
+ public static final String LOGGING_ID2 = "logging_id2";
+
/**
* User preference for which network(s) should be used. Only the
* connectivity service should touch this.
diff --git a/core/java/android/provider/Sync.java b/core/java/android/provider/Sync.java
index 2086a5dd4aa6..94bf80777d0f 100644
--- a/core/java/android/provider/Sync.java
+++ b/core/java/android/provider/Sync.java
@@ -25,7 +25,6 @@ import android.os.Handler;
import java.util.Map;
-
/**
* The Sync provider stores information used in managing the syncing of the device,
* including the history and pending syncs.
@@ -500,6 +499,9 @@ public final class Sync {
/** controls whether or not the individual provider is synced when tickles are received */
public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_";
+ /** query column project */
+ private static final String[] PROJECTION = { KEY, VALUE };
+
/**
* Convenience function for updating a single settings value as a
* boolean. This will either create a new entry in the table if the
@@ -521,6 +523,32 @@ public final class Sync {
}
/**
+ * Convenience function for getting a setting value as a boolean without using the
+ * QueryMap for light-weight setting querying.
+ * @param contentResolver The ContentResolver for querying the setting.
+ * @param name The name of the setting to query
+ * @param def The default value for the setting.
+ * @return The value of the setting.
+ */
+ static public boolean getBoolean(ContentResolver contentResolver,
+ String name, boolean def) {
+ Cursor cursor = contentResolver.query(
+ CONTENT_URI,
+ PROJECTION,
+ KEY + "=?",
+ new String[] { name },
+ null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ return Boolean.parseBoolean(cursor.getString(1));
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ return def;
+ }
+
+ /**
* A convenience method to set whether or not the provider is synced when
* it receives a network tickle.
*
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 3cbb855d08c9..ded50e457aca 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -45,7 +45,7 @@ import java.util.HashMap;
import java.util.Iterator;
public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
- private static final String TAG = "BluetoothDeviceService";
+ private static final String TAG = "BluetoothA2dpService";
private static final boolean DBG = true;
public static final String BLUETOOTH_A2DP_SERVICE = "bluetooth_a2dp";
@@ -143,12 +143,27 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (path == null) {
return BluetoothError.ERROR;
}
- if (!connectSinkNative(path)) {
+
+ SinkState sink = mAudioDevices.get(path);
+ int state = BluetoothA2dp.STATE_DISCONNECTED;
+ if (sink != null) {
+ state = sink.state;
+ }
+ switch (state) {
+ case BluetoothA2dp.STATE_CONNECTED:
+ case BluetoothA2dp.STATE_PLAYING:
+ case BluetoothA2dp.STATE_DISCONNECTING:
return BluetoothError.ERROR;
- } else {
- updateState(path, BluetoothA2dp.STATE_CONNECTING);
+ case BluetoothA2dp.STATE_CONNECTING:
return BluetoothError.SUCCESS;
}
+
+ // State is DISCONNECTED
+ if (!connectSinkNative(path)) {
+ return BluetoothError.ERROR;
+ }
+ updateState(path, BluetoothA2dp.STATE_CONNECTING);
+ return BluetoothError.SUCCESS;
}
public synchronized int disconnectSink(String address) {
@@ -165,6 +180,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (path == null) {
return BluetoothError.ERROR;
}
+ switch (mAudioDevices.get(path).state) {
+ case BluetoothA2dp.STATE_DISCONNECTED:
+ return BluetoothError.ERROR;
+ case BluetoothA2dp.STATE_DISCONNECTING:
+ return BluetoothError.SUCCESS;
+ }
+
+ // State is CONNECTING or CONNECTED or PLAYING
if (!disconnectSinkNative(path)) {
return BluetoothError.ERROR;
} else {
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index 6c8f55487c0e..ac4cdb9d0cfc 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -31,6 +31,7 @@ import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.InputType;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -75,6 +76,7 @@ public final class SearchableInfo implements Parcelable {
public boolean mQueryRewriteFromText = false;
private int mIconId = 0;
private int mSearchButtonText = 0;
+ private int mSearchInputType = 0;
private String mSuggestAuthority = null;
private String mSuggestPath = null;
private String mSuggestSelection = null;
@@ -415,6 +417,10 @@ public final class SearchableInfo implements Parcelable {
mIconId = a.getResourceId(com.android.internal.R.styleable.Searchable_icon, 0);
mSearchButtonText = a.getResourceId(
com.android.internal.R.styleable.Searchable_searchButtonText, 0);
+ mSearchInputType = a.getInt(com.android.internal.R.styleable.Searchable_inputType,
+ InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_SEARCH |
+ InputType.TYPE_TEXT_VARIATION_SEARCH_STRING);
setSearchModeFlags();
if (DBG_INHIBIT_SUGGESTIONS == 0) {
@@ -657,6 +663,16 @@ public final class SearchableInfo implements Parcelable {
}
/**
+ * Return the input type as specified in the searchable attributes. This will default to
+ * InputType.TYPE_CLASS_TEXT if not specified (which is appropriate for free text input).
+ *
+ * @return the input type
+ */
+ public int getInputType() {
+ return mSearchInputType;
+ }
+
+ /**
* Return the list of searchable activities, for use in the drop-down.
*/
public static ArrayList getSearchablesList() {
@@ -694,6 +710,7 @@ public final class SearchableInfo implements Parcelable {
mSearchMode = in.readInt();
mIconId = in.readInt();
mSearchButtonText = in.readInt();
+ mSearchInputType = in.readInt();
setSearchModeFlags();
mSuggestAuthority = in.readString();
@@ -722,6 +739,7 @@ public final class SearchableInfo implements Parcelable {
dest.writeInt(mSearchMode);
dest.writeInt(mIconId);
dest.writeInt(mSearchButtonText);
+ dest.writeInt(mSearchInputType);
dest.writeString(mSuggestAuthority);
dest.writeString(mSuggestPath);
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
new file mode 100644
index 000000000000..abbf8a77c31a
--- /dev/null
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+
+/**
+ * Constants for supporting speech recognition through starting an {@link Intent}
+ *
+ * @hide {pending API council review}
+ */
+public class RecognizerIntent {
+ private RecognizerIntent() {
+ // Not for instantiating.
+ }
+
+ /**
+ * Starts an activity that will prompt the user for speech and sends it through a
+ * speech recognizer.
+ *
+ * Required extras:
+ *
+ * - {@link #EXTRA_LANGUAGE_MODEL}
+ *
+ *
+ * Optional extras:
+ *
+ * - {@link Intent#EXTRA_PROMPT}
+ *
- {@link #EXTRA_LANGUAGE}
+ *
- {@link #EXTRA_MAX_RESULTS}
+ *
+ *
+ * Result extras:
+ *
+ * - {@link #EXTRA_RESULTS}
+ *
+ *
+ * NOTE: There may not be any applications installed to handle this action, so you should
+ * make sure to catch {@link ActivityNotFoundException}.
+ */
+ public static final String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
+
+ /**
+ * Informs the recognizer which speech model to prefer when performing
+ * {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this
+ * information to fine tune the results. This extra is required. Activities implementing
+ * {@link #ACTION_RECOGNIZE_SPEECH} may interpret the values as they see fit.
+ *
+ * @see #LANGUAGE_MODEL_FREE_FORM
+ * @see #LANGUAGE_MODEL_WEB_SEARCH
+ */
+ public static final String EXTRA_LANGUAGE_MODEL = "language_model";
+
+ /** Free form speech recognition */
+ public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
+ /** Use a language model based on web search terms */
+ public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
+
+ /** Optional text prompt to show to the user when asking them to speak. */
+ public static final String EXTRA_PROMPT = "prompt";
+
+ /**
+ * Optional language override to inform the recognizer that it should expect speech in
+ * a language different than the one set in the {@link java.util.Locale#getDefault()}.
+ */
+ public static final String EXTRA_LANGUAGE = "lang";
+
+ /**
+ * Optional limit on the maximum number of results to return. If omitted the recognizer
+ * will choose how many results to return. Must be an integer.
+ */
+ public static final String EXTRA_MAX_RESULTS = "max_results";
+
+ /** Result code returned when no matches are found for the given speech */
+ public static final int RESULT_NO_MATCH = Activity.RESULT_FIRST_USER;
+ /** Result code returned when there is a generic client error */
+ public static final int RESULT_CLIENT_ERROR = Activity.RESULT_FIRST_USER + 1;
+ /** Result code returned when the recognition server returns an error */
+ public static final int RESULT_SERVER_ERROR = Activity.RESULT_FIRST_USER + 2;
+ /** Result code returned when a network error was encountered */
+ public static final int RESULT_NETWORK_ERROR = Activity.RESULT_FIRST_USER + 3;
+ /** Result code returned when an audio error was encountered */
+ public static final int RESULT_AUDIO_ERROR = Activity.RESULT_FIRST_USER + 4;
+
+ /**
+ * An ArrayList of the potential results when performing
+ * {@link #ACTION_RECOGNIZE_SPEECH}. Only present when {@link Activity#RESULT_OK} is returned.
+ */
+ public static final String EXTRA_RESULTS = "results";
+}
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index 0ffe4acaffbf..bd868340dcaf 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.text;
import android.text.TextUtils;
@@ -104,6 +120,11 @@ public interface InputType {
*/
public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000;
+ /**
+ * Flag for {@link #TYPE_CLASS_TEXT}: flags any text being used as a search string
+ */
+ public static final int TYPE_TEXT_FLAG_SEARCH = 0x00040000;
+
// ----------------------------------------------------------------------
/**
@@ -139,8 +160,7 @@ public interface InputType {
public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000050;
/**
- * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing
- * address.
+ * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address.
*/
public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000060;
@@ -150,14 +170,12 @@ public interface InputType {
public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000070;
/**
- * Variation of {@link #TYPE_CLASS_TEXT}: entering a search string
- * for a web search.
+ * Variation of {@link #TYPE_CLASS_TEXT}: entering a simple text search (e.g. web search)
*/
- public static final int TYPE_TEXT_VARIATION_WEB_SEARCH = 0x00000080;
+ public static final int TYPE_TEXT_VARIATION_SEARCH_STRING = 0x00000080;
/**
- * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of
- * a web form.
+ * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form.
*/
public static final int TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x00000090;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 346db4944b88..95acf9d23617 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1074,7 +1074,9 @@ public abstract class Layout {
float h2 = getSecondaryHorizontal(point) - 0.5f;
int caps = TextKeyListener.getMetaState(editingBuffer,
- KeyEvent.META_SHIFT_ON);
+ KeyEvent.META_SHIFT_ON) |
+ TextKeyListener.getMetaState(editingBuffer,
+ TextKeyListener.META_SELECTING);
int fn = TextKeyListener.getMetaState(editingBuffer,
KeyEvent.META_ALT_ON);
int dist = 0;
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 48f65c6c3d64..feae6cf52a6f 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -146,26 +146,26 @@ public class DateUtils
// The following FORMAT_* symbols are used for specifying the format of
// dates and times in the formatDateRange method.
- public static final int FORMAT_SHOW_TIME = 0x00001;
- public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
- public static final int FORMAT_SHOW_YEAR = 0x00004;
- public static final int FORMAT_NO_YEAR = 0x00008;
- public static final int FORMAT_SHOW_DATE = 0x00010;
- public static final int FORMAT_NO_MONTH_DAY = 0x00020;
- public static final int FORMAT_12HOUR = 0x00040;
- public static final int FORMAT_24HOUR = 0x00080;
- public static final int FORMAT_CAP_AMPM = 0x00100;
- public static final int FORMAT_NO_NOON = 0x00200;
- public static final int FORMAT_CAP_NOON = 0x00400;
- public static final int FORMAT_NO_MIDNIGHT = 0x00800;
- public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
- public static final int FORMAT_UTC = 0x02000;
- public static final int FORMAT_ABBREV_TIME = 0x04000;
+ public static final int FORMAT_SHOW_TIME = 0x00001;
+ public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
+ public static final int FORMAT_SHOW_YEAR = 0x00004;
+ public static final int FORMAT_NO_YEAR = 0x00008;
+ public static final int FORMAT_SHOW_DATE = 0x00010;
+ public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+ public static final int FORMAT_12HOUR = 0x00040;
+ public static final int FORMAT_24HOUR = 0x00080;
+ public static final int FORMAT_CAP_AMPM = 0x00100;
+ public static final int FORMAT_NO_NOON = 0x00200;
+ public static final int FORMAT_CAP_NOON = 0x00400;
+ public static final int FORMAT_NO_MIDNIGHT = 0x00800;
+ public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
+ public static final int FORMAT_UTC = 0x02000;
+ public static final int FORMAT_ABBREV_TIME = 0x04000;
public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
- public static final int FORMAT_ABBREV_MONTH = 0x10000;
- public static final int FORMAT_NUMERIC_DATE = 0x20000;
- public static final int FORMAT_ABBREV_ALL = (FORMAT_ABBREV_TIME
- | FORMAT_ABBREV_WEEKDAY | FORMAT_ABBREV_MONTH);
+ public static final int FORMAT_ABBREV_MONTH = 0x10000;
+ public static final int FORMAT_NUMERIC_DATE = 0x20000;
+ public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
+ public static final int FORMAT_ABBREV_ALL = 0x80000;
public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);
public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);
@@ -233,18 +233,20 @@ public class DateUtils
};
/**
- * Request the full spelled-out name.
- * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
- * @more
- * e.g. "Sunday" or "January"
+ * Request the full spelled-out name. For use with the 'abbrev' parameter of
+ * {@link #getDayOfWeekString} and {@link #getMonthString}.
+ *
+ * @more
+ * e.g. "Sunday" or "January"
*/
public static final int LENGTH_LONG = 10;
/**
- * Request an abbreviated version of the name.
- * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
- * @more
- *
e.g. "Sun" or "Jan"
+ * Request an abbreviated version of the name. For use with the 'abbrev'
+ * parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
+ *
+ * @more
+ * e.g. "Sun" or "Jan"
*/
public static final int LENGTH_MEDIUM = 20;
@@ -364,53 +366,162 @@ public class DateUtils
* 0, MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, WEEK_IN_MILLIS
*/
public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution) {
- Resources r = Resources.getSystem();
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;
+ return getRelativeTimeSpanString(time, now, minResolution, flags);
+ }
+ /**
+ * Returns a string describing 'time' as a time relative to 'now'.
+ *
+ * Time spans in the past are formatted like "42 minutes ago". Time spans in
+ * the future are formatted like "in 42 minutes".
+ *
+ * Can use {@link #FORMAT_ABBREV_RELATIVE} flag to use abbreviated relative
+ * times, like "42 mins ago".
+ *
+ * @param time the time to describe, in milliseconds
+ * @param now the current time in milliseconds
+ * @param minResolution the minimum timespan to report. For example, a time
+ * 3 seconds in the past will be reported as "0 minutes ago" if
+ * this is set to MINUTE_IN_MILLIS. Pass one of 0,
+ * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS,
+ * WEEK_IN_MILLIS
+ * @param flags a bit mask of formatting options, such as
+ * {@link #FORMAT_NUMERIC_DATE} or
+ * {@link #FORMAT_ABBREV_RELATIVE}
+ */
+ public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution,
+ int flags) {
+ Resources r = Resources.getSystem();
+ boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0;
+
boolean past = (now >= time);
long duration = Math.abs(now - time);
-
+
int resId;
long count;
if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
count = duration / SECOND_IN_MILLIS;
if (past) {
- resId = com.android.internal.R.plurals.num_seconds_ago;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_num_seconds_ago;
+ } else {
+ resId = com.android.internal.R.plurals.num_seconds_ago;
+ }
} else {
- resId = com.android.internal.R.plurals.in_num_seconds;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_in_num_seconds;
+ } else {
+ resId = com.android.internal.R.plurals.in_num_seconds;
+ }
}
} else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
count = duration / MINUTE_IN_MILLIS;
if (past) {
- resId = com.android.internal.R.plurals.num_minutes_ago;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_num_minutes_ago;
+ } else {
+ resId = com.android.internal.R.plurals.num_minutes_ago;
+ }
} else {
- resId = com.android.internal.R.plurals.in_num_minutes;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_in_num_minutes;
+ } else {
+ resId = com.android.internal.R.plurals.in_num_minutes;
+ }
}
} else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
count = duration / HOUR_IN_MILLIS;
if (past) {
- resId = com.android.internal.R.plurals.num_hours_ago;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_num_hours_ago;
+ } else {
+ resId = com.android.internal.R.plurals.num_hours_ago;
+ }
} else {
- resId = com.android.internal.R.plurals.in_num_hours;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_in_num_hours;
+ } else {
+ resId = com.android.internal.R.plurals.in_num_hours;
+ }
}
} else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
count = duration / DAY_IN_MILLIS;
if (past) {
- resId = com.android.internal.R.plurals.num_days_ago;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_num_days_ago;
+ } else {
+ resId = com.android.internal.R.plurals.num_days_ago;
+ }
} else {
- resId = com.android.internal.R.plurals.in_num_days;
+ if (abbrevRelative) {
+ resId = com.android.internal.R.plurals.abbrev_in_num_days;
+ } else {
+ resId = com.android.internal.R.plurals.in_num_days;
+ }
}
} else {
- // Longer than a week ago, so just show the date.
- int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;
-
// We know that we won't be showing the time, so it is safe to pass
// in a null context.
return formatDateRange(null, time, time, flags);
}
-
+
String format = r.getQuantityString(resId, (int) count);
return String.format(format, count);
}
+
+ /**
+ * Return string describing the elapsed time since startTime formatted like
+ * "[relative time/date], [time]".
+ *
+ * Example output strings for the US date format.
+ *
+ * - 3 mins ago, 10:15 AM
+ * - yesterday, 12:20 PM
+ * - Dec 12, 4:12 AM
+ * - 11/14/2007, 8:20 AM
+ *
+ *
+ * @param time some time in the past.
+ * @param minResolution the minimum elapsed time (in milliseconds) to report
+ * when showing relative times. For example, a time 3 seconds in
+ * the past will be reported as "0 minutes ago" if this is set to
+ * {@link #MINUTE_IN_MILLIS}.
+ * @param transitionResolution the elapsed time (in milliseconds) at which
+ * to stop reporting relative measurements. Elapsed times greater
+ * than this resolution will default to normal date formatting.
+ * For example, will transition from "6 days ago" to "Dec 12"
+ * when using {@link #WEEK_IN_MILLIS}.
+ */
+ public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution,
+ long transitionResolution, int flags) {
+ Resources r = Resources.getSystem();
+
+ long now = System.currentTimeMillis();
+ long duration = Math.abs(now - time);
+
+ // getRelativeTimeSpanString() doesn't correctly format relative dates
+ // above a week or exact dates below a day, so clamp
+ // transitionResolution as needed.
+ if (transitionResolution > WEEK_IN_MILLIS) {
+ transitionResolution = WEEK_IN_MILLIS;
+ } else if (transitionResolution < DAY_IN_MILLIS) {
+ transitionResolution = DAY_IN_MILLIS;
+ }
+
+ CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME);
+
+ String result;
+ if (duration < transitionResolution) {
+ CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags);
+ result = r.getString(com.android.internal.R.string.relative_time, relativeClause, timeClause);
+ } else {
+ CharSequence dateClause = getRelativeTimeSpanString(c, time, false);
+ result = r.getString(com.android.internal.R.string.date_time, dateClause, timeClause);
+ }
+
+ return result;
+ }
/**
* Returns a string describing a day relative to the current day. For example if the day is
@@ -1005,8 +1116,8 @@ public class DateUtils
boolean showYear = (flags & FORMAT_SHOW_YEAR) != 0;
boolean noYear = (flags & FORMAT_NO_YEAR) != 0;
boolean useUTC = (flags & FORMAT_UTC) != 0;
- boolean abbrevWeekDay = (flags & FORMAT_ABBREV_WEEKDAY) != 0;
- boolean abbrevMonth = (flags & FORMAT_ABBREV_MONTH) != 0;
+ boolean abbrevWeekDay = (flags & (FORMAT_ABBREV_WEEKDAY | FORMAT_ABBREV_ALL)) != 0;
+ boolean abbrevMonth = (flags & (FORMAT_ABBREV_MONTH | FORMAT_ABBREV_ALL)) != 0;
boolean noMonthDay = (flags & FORMAT_NO_MONTH_DAY) != 0;
boolean numericDate = (flags & FORMAT_NUMERIC_DATE) != 0;
@@ -1087,7 +1198,7 @@ public class DateUtils
startTimeFormat = HOUR_MINUTE_24;
endTimeFormat = HOUR_MINUTE_24;
} else {
- boolean abbrevTime = (flags & FORMAT_ABBREV_TIME) != 0;
+ boolean abbrevTime = (flags & (FORMAT_ABBREV_TIME | FORMAT_ABBREV_ALL)) != 0;
boolean capAMPM = (flags & FORMAT_CAP_AMPM) != 0;
boolean noNoon = (flags & FORMAT_NO_NOON) != 0;
boolean capNoon = (flags & FORMAT_CAP_NOON) != 0;
@@ -1419,7 +1530,6 @@ public class DateUtils
long now = System.currentTimeMillis();
long span = now - millis;
- Resources res = c.getResources();
if (sNowTime == null) {
sNowTime = new Time();
sThenTime = new Time();
@@ -1449,6 +1559,7 @@ public class DateUtils
prepositionId = R.string.preposition_for_date;
}
if (withPreposition) {
+ Resources res = c.getResources();
result = res.getString(prepositionId, result);
}
return result;
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 652413eb168a..7457439a6a8c 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -31,8 +31,10 @@ ArrowKeyMovementMethod
implements MovementMethod
{
private boolean up(TextView widget, Spannable buffer) {
- boolean cap = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1;
+ boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
+ KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0);
boolean alt = MetaKeyKeyListener.getMetaState(buffer,
KeyEvent.META_ALT_ON) == 1;
Layout layout = widget.getLayout();
@@ -55,8 +57,10 @@ implements MovementMethod
}
private boolean down(TextView widget, Spannable buffer) {
- boolean cap = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1;
+ boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
+ KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0);
boolean alt = MetaKeyKeyListener.getMetaState(buffer,
KeyEvent.META_ALT_ON) == 1;
Layout layout = widget.getLayout();
@@ -79,8 +83,10 @@ implements MovementMethod
}
private boolean left(TextView widget, Spannable buffer) {
- boolean cap = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1;
+ boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
+ KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0);
boolean alt = MetaKeyKeyListener.getMetaState(buffer,
KeyEvent.META_ALT_ON) == 1;
Layout layout = widget.getLayout();
@@ -101,8 +107,10 @@ implements MovementMethod
}
private boolean right(TextView widget, Spannable buffer) {
- boolean cap = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1;
+ boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
+ KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0);
boolean alt = MetaKeyKeyListener.getMetaState(buffer,
KeyEvent.META_ALT_ON) == 1;
Layout layout = widget.getLayout();
@@ -141,6 +149,13 @@ implements MovementMethod
case KeyEvent.KEYCODE_DPAD_RIGHT:
handled |= right(widget, buffer);
break;
+
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
+ if (widget.showContextMenu()) {
+ handled = true;
+ }
+ }
}
if (handled) {
@@ -179,7 +194,10 @@ implements MovementMethod
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
- boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0;
+ boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
+ KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0);
if (cap) {
Selection.extendSelection(buffer, off);
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index f0305d9480e2..d5a473b0d6d9 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -22,7 +22,8 @@ import android.text.*;
/**
* This base class encapsulates the behavior for handling the meta keys
- * (caps, fn, sym). Key listener that care about meta state should
+ * (shift and alt) and the pseudo-meta state of selecting text.
+ * Key listeners that care about meta state should
* inherit from it; you should not instantiate this class directly in a client.
*/
@@ -31,13 +32,49 @@ public abstract class MetaKeyKeyListener {
public static final int META_ALT_ON = KeyEvent.META_ALT_ON;
public static final int META_SYM_ON = KeyEvent.META_SYM_ON;
- public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << 8;
- public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << 8;
- public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << 8;
+ private static final int LOCKED_SHIFT = 8;
+
+ public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << LOCKED_SHIFT;
+ public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << LOCKED_SHIFT;
+ public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << LOCKED_SHIFT;
+
+ /**
+ * @hide pending API review
+ */
+ public static final int META_SELECTING = 1 << 16;
+
+ private static final int USED_SHIFT = 24;
+
+ private static final long META_CAP_USED = ((long)KeyEvent.META_SHIFT_ON) << USED_SHIFT;
+ private static final long META_ALT_USED = ((long)KeyEvent.META_ALT_ON) << USED_SHIFT;
+ private static final long META_SYM_USED = ((long)KeyEvent.META_SYM_ON) << USED_SHIFT;
+
+ private static final int PRESSED_SHIFT = 32;
+
+ private static final long META_CAP_PRESSED = ((long)KeyEvent.META_SHIFT_ON) << PRESSED_SHIFT;
+ private static final long META_ALT_PRESSED = ((long)KeyEvent.META_ALT_ON) << PRESSED_SHIFT;
+ private static final long META_SYM_PRESSED = ((long)KeyEvent.META_SYM_ON) << PRESSED_SHIFT;
+ private static final int RELEASED_SHIFT = 40;
+
+ private static final long META_CAP_RELEASED = ((long)KeyEvent.META_SHIFT_ON) << RELEASED_SHIFT;
+ private static final long META_ALT_RELEASED = ((long)KeyEvent.META_ALT_ON) << RELEASED_SHIFT;
+ private static final long META_SYM_RELEASED = ((long)KeyEvent.META_SYM_ON) << RELEASED_SHIFT;
+
+ private static final long META_SHIFT_MASK = META_SHIFT_ON
+ | META_CAP_LOCKED | META_CAP_USED
+ | META_CAP_PRESSED | META_CAP_RELEASED;
+ private static final long META_ALT_MASK = META_ALT_ON
+ | META_ALT_LOCKED | META_ALT_USED
+ | META_ALT_PRESSED | META_ALT_RELEASED;
+ private static final long META_SYM_MASK = META_SYM_ON
+ | META_SYM_LOCKED | META_SYM_USED
+ | META_SYM_PRESSED | META_SYM_RELEASED;
+
private static final Object CAP = new Object();
private static final Object ALT = new Object();
private static final Object SYM = new Object();
+ private static final Object SELECTING = new Object();
/**
* Resets all meta state to inactive.
@@ -46,6 +83,7 @@ public abstract class MetaKeyKeyListener {
text.removeSpan(CAP);
text.removeSpan(ALT);
text.removeSpan(SYM);
+ text.removeSpan(SELECTING);
}
/**
@@ -59,13 +97,14 @@ public abstract class MetaKeyKeyListener {
public static final int getMetaState(CharSequence text) {
return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) |
getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) |
- getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED);
+ getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED) |
+ getActive(text, SELECTING, META_SELECTING, META_SELECTING);
}
/**
* Gets the state of a particular meta key.
*
- * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON
+ * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON, or META_SELECTING
* @param text the buffer in which the meta key would have been pressed.
*
* @return 0 if inactive, 1 if active, 2 if locked.
@@ -81,6 +120,9 @@ public abstract class MetaKeyKeyListener {
case META_SYM_ON:
return getActive(text, SYM, 1, 2);
+ case META_SELECTING:
+ return getActive(text, SELECTING, 1, 2);
+
default:
return 0;
}
@@ -120,7 +162,8 @@ public abstract class MetaKeyKeyListener {
* keep track of meta state in the specified text.
*/
public static boolean isMetaTracker(CharSequence text, Object what) {
- return what == CAP || what == ALT || what == SYM;
+ return what == CAP || what == ALT || what == SYM ||
+ what == SELECTING;
}
private static void adjust(Spannable content, Object what) {
@@ -140,6 +183,7 @@ public abstract class MetaKeyKeyListener {
resetLock(content, CAP);
resetLock(content, ALT);
resetLock(content, SYM);
+ resetLock(content, SELECTING);
}
private static void resetLock(Spannable content, Object what) {
@@ -189,6 +233,23 @@ public abstract class MetaKeyKeyListener {
}
/**
+ * Start selecting text.
+ * @hide pending API review
+ */
+ public static void startSelecting(View view, Spannable content) {
+ content.setSpan(SELECTING, 0, 0, PRESSED);
+ }
+
+ /**
+ * Stop selecting text. This does not actually collapse the selection;
+ * call {@link android.text.Selection#setSelection} too.
+ * @hide pending API review
+ */
+ public static void stopSelecting(View view, Spannable content) {
+ content.removeSpan(SELECTING);
+ }
+
+ /**
* Handles release of the meta keys.
*/
public boolean onKeyUp(View view, Editable content, int keyCode,
@@ -225,6 +286,170 @@ public abstract class MetaKeyKeyListener {
if ((states&META_SHIFT_ON) != 0) resetLock(content, CAP);
if ((states&META_ALT_ON) != 0) resetLock(content, ALT);
if ((states&META_SYM_ON) != 0) resetLock(content, SYM);
+ if ((states&META_SELECTING) != 0) resetLock(content, SELECTING);
+ }
+
+ /**
+ * Call this if you are a method that ignores the locked meta state
+ * (arrow keys, for example) and you handle a key.
+ */
+ public static long resetLockedMeta(long state) {
+ state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
+ state = resetLock(state, META_ALT_ON, META_ALT_MASK);
+ state = resetLock(state, META_SYM_ON, META_SYM_MASK);
+ return state;
+ }
+
+ private static long resetLock(long state, int what, long mask) {
+ if ((state&(((long)what)<= size()}
- * @hide pending API council
*/
public MenuItem getItem(int index);
/**
* Closes the menu, if open.
- *
- * @hide pending API council
*/
public void close();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0d9e2218329c..e928998b6d00 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
+import java.lang.ref.WeakReference;
/**
* Provides a dedicated drawing surface embedded inside of a view hierarchy.
@@ -308,7 +309,7 @@ public class SurfaceView extends View {
mLayout.memoryType = mRequestedType;
if (mWindow == null) {
- mWindow = new MyWindow();
+ mWindow = new MyWindow(this);
mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
mLayout.gravity = Gravity.LEFT|Gravity.TOP;
mSession.add(mWindow, mLayout,
@@ -392,33 +393,45 @@ public class SurfaceView extends View {
mNewSurfaceNeeded = true;
updateWindow(false);
}
-
- private class MyWindow extends IWindow.Stub {
+
+ private static class MyWindow extends IWindow.Stub {
+ private WeakReference mSurfaceView;
+
+ public MyWindow(SurfaceView surfaceView) {
+ mSurfaceView = new WeakReference(surfaceView);
+ }
+
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw) {
- if (localLOGV) Log.v(
- "SurfaceView", SurfaceView.this + " got resized: w=" +
- w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
- synchronized (this) {
- if (mCurWidth != w || mCurHeight != h) {
- mCurWidth = w;
- mCurHeight = h;
- }
- if (reportDraw) {
- try {
- mSession.finishDrawing(mWindow);
- } catch (RemoteException e) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ if (localLOGV) Log.v(
+ "SurfaceView", surfaceView + " got resized: w=" +
+ w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+ synchronized (this) {
+ if (mCurWidth != w || mCurHeight != h) {
+ mCurWidth = w;
+ mCurHeight = h;
+ }
+ if (reportDraw) {
+ try {
+ surfaceView.mSession.finishDrawing(surfaceView.mWindow);
+ } catch (RemoteException e) {
+ }
}
}
}
}
public void dispatchKey(KeyEvent event) {
- //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
- if (mSession != null && mSurface != null) {
- try {
- mSession.finishKey(mWindow);
- } catch (RemoteException ex) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
+ if (surfaceView.mSession != null && surfaceView.mSurface != null) {
+ try {
+ surfaceView.mSession.finishKey(surfaceView.mWindow);
+ } catch (RemoteException ex) {
+ }
}
}
}
@@ -446,10 +459,13 @@ public class SurfaceView extends View {
public void dispatchAppVisibility(boolean visible) {
// The point of SurfaceView is to let the app control the surface.
}
-
+
public void dispatchGetNewSurface() {
- Message msg = mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
- mHandler.sendMessage(msg);
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+ surfaceView.mHandler.sendMessage(msg);
+ }
}
public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f948b33fb179..1cc7b60ac8ea 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3562,7 +3562,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
*
* @param outAttrs Fill in with attribute information about the connection.
*/
- public InputConnection createInputConnection(EditorInfo outAttrs) {
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return null;
}
@@ -4315,12 +4315,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
- handler = ViewRoot.sUiThreads.get();
- if (handler == null) {
- // Assume that post will succeed later
- ViewRoot.sRunQueue.post(action);
- return true;
- }
+ // Assume that post will succeed later
+ ViewRoot.getRunQueue().post(action);
+ return true;
}
return handler.post(action);
@@ -4347,12 +4344,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
- handler = ViewRoot.sUiThreads.get();
- if (handler == null) {
- // Assume that post will succeed later
- ViewRoot.sRunQueue.postDelayed(action, delayMillis);
- return true;
- }
+ // Assume that post will succeed later
+ ViewRoot.getRunQueue().postDelayed(action, delayMillis);
+ return true;
}
return handler.postDelayed(action, delayMillis);
@@ -4373,12 +4367,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
- handler = ViewRoot.sUiThreads.get();
- if (handler == null) {
- // Assume that post will succeed later
- ViewRoot.sRunQueue.removeCallbacks(action);
- return true;
- }
+ // Assume that post will succeed later
+ ViewRoot.getRunQueue().removeCallbacks(action);
+ return true;
}
handler.removeCallbacks(action);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index db0b368ebcf7..a254edb98777 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -34,6 +34,7 @@ import android.util.Log;
import android.util.EventLog;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
+import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.content.pm.PackageManager;
@@ -94,8 +95,7 @@ public final class ViewRoot extends Handler implements ViewParent,
static final Object mStaticInit = new Object();
static boolean mInitialized = false;
- static final ThreadLocal sUiThreads = new ThreadLocal();
- static final RunQueue sRunQueue = new RunQueue();
+ static final ThreadLocal sRunQueues = new ThreadLocal();
long mLastTrackballTime = 0;
final TrackballAxis mTrackballAxisX = new TrackballAxis();
@@ -222,13 +222,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mFirst = true; // true for the first time the view is added
mSurface = new Surface();
mAdded = false;
-
- Handler handler = sUiThreads.get();
- if (handler == null) {
- handler = new RootHandler();
- sUiThreads.set(handler);
- }
- mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, handler, this);
+ mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
}
@Override
@@ -617,7 +611,7 @@ public final class ViewRoot extends Handler implements ViewParent,
attachInfo.mKeepScreenOn = false;
viewVisibilityChanged = false;
host.dispatchAttachedToWindow(attachInfo, 0);
- sRunQueue.executeActions(attachInfo.mHandler);
+ getRunQueue().executeActions(attachInfo.mHandler);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
desiredWindowWidth = mWinFrame.width();
@@ -1400,10 +1394,22 @@ public final class ViewRoot extends Handler implements ViewParent,
public final static int DISPATCH_APP_VISIBILITY = 1008;
public final static int DISPATCH_GET_NEW_SURFACE = 1009;
public final static int FINISHED_EVENT = 1010;
+ public final static int DISPATCH_KEY_FROM_IME = 1011;
+ public final static int FINISH_INPUT_CONNECTION = 1012;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case View.AttachInfo.INVALIDATE_MSG:
+ ((View) msg.obj).invalidate();
+ break;
+ case View.AttachInfo.INVALIDATE_RECT_MSG:
+ int left = msg.arg1 >>> 16;
+ int top = msg.arg1 & 0xFFFF;
+ int right = msg.arg2 >>> 16;
+ int bottom = msg.arg2 & 0xFFFF;
+ ((View) msg.obj).invalidate(left, top, right, bottom);
+ break;
case DO_TRAVERSAL:
if (mProfile) {
Debug.startMethodTracing("ViewRoot");
@@ -1583,6 +1589,18 @@ public final class ViewRoot extends Handler implements ViewParent,
case DIE:
dispatchDetachedFromWindow();
break;
+ case DISPATCH_KEY_FROM_IME:
+ if (LOCAL_LOGV) Log.v(
+ "ViewRoot", "Dispatching key "
+ + msg.obj + " from IME to " + mView);
+ deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
+ break;
+ case FINISH_INPUT_CONNECTION: {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.reportFinishInputConnection((InputConnection)msg.obj);
+ }
+ } break;
}
}
@@ -2614,24 +2632,6 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- private static final class RootHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case View.AttachInfo.INVALIDATE_MSG:
- ((View) msg.obj).invalidate();
- break;
- case View.AttachInfo.INVALIDATE_RECT_MSG:
- int left = msg.arg1 >>> 16;
- int top = msg.arg1 & 0xFFFF;
- int right = msg.arg2 >>> 16;
- int bottom = msg.arg2 & 0xFFFF;
- ((View) msg.obj).invalidate(left, top, right, bottom);
- break;
- }
- }
- }
-
private SurfaceHolder mHolder = new SurfaceHolder() {
// we only need a SurfaceHolder for opengl. it would be nice
// to implement everything else though, especially the callback
@@ -2681,6 +2681,16 @@ public final class ViewRoot extends Handler implements ViewParent,
}
};
+ static RunQueue getRunQueue() {
+ RunQueue rq = sRunQueues.get();
+ if (rq != null) {
+ return rq;
+ }
+ rq = new RunQueue();
+ sRunQueues.set(rq);
+ return rq;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 9f3650b3ce5c..d1d549c76a27 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -118,7 +118,7 @@ public abstract class Animation {
* Indicates whether the animation transformation should be applied after the
* animation ends.
*/
- boolean mFillAfter = false;
+ boolean mFillAfter = true;
/**
* The time in milliseconds at which the animation must start;
@@ -292,12 +292,18 @@ public abstract class Animation {
}
/**
- * How long this animation should last.
+ * How long this animation should last. The duration cannot be negative.
*
* @param durationMillis Duration in milliseconds
+ *
+ * @throw java.lang.IllegalArgumentException if the duration is < 0
+ *
* @attr ref android.R.styleable#Animation_duration
*/
public void setDuration(long durationMillis) {
+ if (durationMillis < 0) {
+ throw new IllegalArgumentException("Animation duration cannot be negative");
+ }
mDuration = durationMillis;
}
@@ -401,7 +407,7 @@ public abstract class Animation {
/**
* If fillBefore is true, this animation will apply its transformation
- * before the start time of the animation. Defaults to false if not set.
+ * before the start time of the animation. Defaults to true if not set.
* Note that this applies when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
* animations. The transformation is not applied before the AnimationSet
@@ -416,7 +422,7 @@ public abstract class Animation {
/**
* If fillAfter is true, the transformation that this animation performed
- * will persist when it is finished. Defaults to false if not set.
+ * will persist when it is finished. Defaults to true if not set.
* Note that this applies when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
* animations. The transformation is not applied before the AnimationSet
@@ -607,12 +613,17 @@ public abstract class Animation {
}
final long startOffset = getStartOffset();
- float normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
- (float) mDuration;
+ final long duration = mDuration;
+ float normalizedTime;
+ if (duration != 0) {
+ normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
+ (float) duration;
+ } else {
+ // time is a step-change with a zero duration
+ normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
+ }
boolean expired = normalizedTime >= 1.0f;
- // Pin time to 0.0 to 1.0 range
- normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
mMore = !expired;
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
@@ -623,6 +634,9 @@ public abstract class Animation {
mStarted = true;
}
+ // Pin time to 0.0 to 1.0 range
+ normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
+
if (mCycleFlip) {
normalizedTime = 1.0f - normalizedTime;
}
@@ -788,16 +802,15 @@ public abstract class Animation {
void onAnimationStart(Animation animation);
/**
- * Notifies the end of the animation. This callback is invoked
- * only for animation with repeat mode set to NO_REPEAT.
+ * Notifies the end of the animation. This callback is not invoked
+ * for animations with repeat count set to INFINITE.
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animation animation);
/**
- * Notifies the repetition of the animation. This callback is invoked
- * only for animation with repeat mode set to RESTART or REVERSE.
+ * Notifies the repetition of the animation.
*
* @param animation The animation which was repeated.
*/
diff --git a/core/java/android/view/animation/package.html b/core/java/android/view/animation/package.html
index c35804742402..87c99bb2f2de 100755
--- a/core/java/android/view/animation/package.html
+++ b/core/java/android/view/animation/package.html
@@ -5,240 +5,16 @@
that you can use to create simple animations: tweened
animation, in which you tell Android to perform a series of simple
transformations (position, size, rotation, and so on) to the content of a
- View; and frame
- by frame animation, which loads a series of Drawable resources
+ View; and frame-by-frame animation, which loads a series of Drawable resources
one after the other. Both animation types can be used in any View object
to provide simple rotating timers, activity icons, and other useful UI elements.
- Tweened animation is handled by this package; frame by frame animation is
+ Tweened animation is handled by this package (android.view.animation); frame-by-frame animation is
handled by the {@link android.graphics.drawable.AnimationDrawable} class.
- Animations do not have a pause method.
-Tweened Animation
- Android can perform simple visual transformations for you, including straight
- line motion, size change, and transparency change, on the contents of a {@link
- android.view.View View} object. These transformations are represented by the
- following classes:
-
- - {@link android.view.animation.AlphaAnimation AlphaAnimation} (transparency
- changes)
- - {@link android.view.animation.RotateAnimation RotateAnimation} (rotations)
- - {@link android.view.animation.ScaleAnimation ScaleAnimation} (growing
- or shrinking)
- - {@link android.view.animation.TranslateAnimation TranslateAnimation}
- (position changes)
-
-Note: tweened animation does not provide tools to help you draw shapes. Tweened
- animation is the act of applying one or more of these
- transformations applied to the contents of a View object. So, if you have a TextView
- with text, you can move, rotate, grow, or shrink the text. If it has a background
- image, the background image will also be transformed along with the text.
-Animations are drawn in the area designated for the View at the start of the animation;
- this area does not change to accommodate size or movement, so if your animation
- moves or expands outside the original boundaries of your object, it will be clipped
- to the size of the original canvas, even if the object's LayoutParams are
- set to WRAP_CONTENT (the object will not resize to accommodate moving or expanding/shrinking
- animations).
-Step 1: Define your animation
-The first step in creating a tweened animation is to define the transformations.
- This can be done either in XML or in code. You define an animation by defining
- the transformations that you want to occur, when they will occur, and how long
- they should take to apply. Transformations
- can be sequential or simultaneous—so, for example, you can have the contents
- of a TextView move from left to right, and then rotate 180 degrees, or you can
- have the text move and rotate simultaneously. Each transformation takes a set
- of parameters specific for that transformation (starting size and ending size
- for size change, starting angle and ending angle for rotation, and so on), and
- also a set of common parameters (for instance, start time and duration). To make
- several transformations happen simultaneously, give them the same start time;
- to make them sequential, calculate the start time plus the duration of the preceding
- transformation.
-Screen coordinates are (0,0) at the upper left hand corner, and increase as you
- go down and to the right.
-Some values, such as pivotX, can be specified relative to the object itself or
- relative to the parent. Be sure to use the proper format for what you want ("50"
- for 50% relative to the parent, "50%" for 50% relative to itself).
-You can determine how a transformation is applied over time by assigning an Interpolator
- to it. Android includes several Interpolator subclasses that specify various
- speed curves: for instance, AccelerateInterpolator tells a transformation
- to start slow and speed up; DecelerateInterpolator tells it to start fast than slow
- down, and so on.
-If
- you want a group of transformations to share a set of parameters (for example,
- start time and duration), you can bundle them into an AnimationSet, which
- defines the common parameters for all its children (and overrides any
- values explicitly set by the children). Add your AnimationSet as
- a child to the root AnimationSet (which serves to wrap all transformations into
- the final animation).
- Here is the XML that defines a simple animation. The object will first move
- to the right, then rotate and double in size, then move up. Note the
- transformation start times.
-
-
- XML |
- Equivalent Java |
-
-
- <set android:shareInterpolator="true"
- android:interpolator="@android:anim/accelerate_interpolator">
+
- <translate android:fromXDelta="0"
- android:toXDelta="30"
- android:duration="800"
- android:fillAfter="true"/>
-
- <set android:duration="800"
- android:pivotX="50%"
- android:pivotY="50%" >
+For more information on creating tweened or frame-by-frame animations, read the discussion in the
+2D Graphics
+Dev Guide.
- <rotate android:fromDegrees="0"
- android:toDegrees="-90"
- android:fillAfter="true"
- android:startOffset="800"/>
-
- <scale android:fromXScale="1.0"
- android:toXScale="2.0"
- android:fromYScale="1.0"
- android:toYScale="2.0"
- android:startOffset="800" />
- </set>
-
- <translate android:toYDelta="-100"
- android:fillAfter="true"
- android:duration="800"
- android:startOffset="1600"/>
-</set> |
- // Create root AnimationSet.
-AnimationSet rootSet = new AnimationSet(true);
-rootSet.setInterpolator(new AccelerateInterpolator());
-rootSet.setRepeatMode(Animation.NO_REPEAT);
-
-// Create and add first child, a motion animation.
-TranslateAnimation trans1 = new TranslateAnimation(0, 30, 0, 0);
-trans1.setStartOffset(0);
-trans1.setDuration(800);
-trans1.setFillAfter(true);
-rootSet.addAnimation(trans1);
-
-// Create a rotate and a size animation.
-RotateAnimation rotate = new RotateAnimation(
- 0,
- -90,
- RotateAnimation.RELATIVE_TO_SELF, 0.5f,
- RotateAnimation.RELATIVE_TO_SELF, 0.5f);
- rotate.setFillAfter(true);
- rotate.setDuration(800);
-
-ScaleAnimation scale = new ScaleAnimation(
- 1, 2, 1, 2, // From x, to x, from y, to y
- ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
- ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
- scale.setDuration(800);
- scale.setFillAfter(true);
-
-// Add rotate and size animations to a new set,
-// then add the set to the root set.
-AnimationSet childSet = new AnimationSet(true);
-childSet.setStartOffset(800);
-childSet.addAnimation(rotate);
-childSet.addAnimation(scale);
-rootSet.addAnimation(childSet);
-
-// Add a final motion animation to the root set.
-TranslateAnimation trans2 = new TranslateAnimation(0, 0, 0, -100);
-trans2.setFillAfter(true);
-trans2.setDuration(800);
-trans2.setStartOffset(1600);
-rootSet.addAnimation(trans2);
-
-// Start the animation.
-animWindow.startAnimation(rootSet); |
-
-
-
-The following diagram shows the animation drawn from this code:
-![A tweened animation: move right, turn and grow, move up.]({@docRoot}images/tweening_example.png)
-The previous diagram shows a few important things. One is the animation itself,
- and the other is that the animation can get cropped if it moves out of its originally
- defined area. To avoid this, we could have sized the TextView to fill_parent
- for its height.
-If you define your animation in XML, save it in the res/anim/ folder as described
- in Resources. That topic
- also describes the XML tags and attributes you can use to specify transformations.
-Animations
- have the following common parameters (from the Animation interface).
- If a group of animations share the same values, you can bundle them into an AnimationSet
- so you don't have to set these values on each one individually.
-
-
- Property |
- XML Attribute |
- Java Method / |
- Description |
-
-
- Start time |
- android:startOffset |
- Animation.setStartOffset() (or setStartTime() for absolute time) |
- The start time (in milliseconds) of a transformation, where 0 is the
- start time of the root animation set. |
-
-
- Duration |
- android:duration |
- Animation.setDuration() |
- The duration (in milliseconds) of a transformation. |
-
-
- Fill before |
- android:fillBefore |
- Animation.setFillBefore() |
- True if you want this transformation to be applied at time zero, regardless
- of your start time value (you will probably never need this). |
-
-
- Fill after |
- android:fillAfter |
- Animation.SetFillAfter() |
- Whether you want the transform you apply to continue after the duration
- of the transformation has expired. If false, the original value will
- immediately be applied when the transformation is done. So, for example,
- if you want to make a dot move down, then right in an "L" shape, if this
- value is not true, at the end of the down motion the text box will immediately
- jump back to the top before moving right. |
-
-
- Interpolator |
- android:interpolator |
- Animation.SetInterpolator() |
- Which interpolator to use. |
-
-
- Repeat mode |
- Cannot be set in XML |
- Animation.SetRepeatMode() |
- Whether and how the animation should repeat. |
-
-
-
-Step 2: Load and start your animation
-
- - If you've created your transformation in XML, you'll need to load it in Java
- by calling {@link android.view.animation.AnimationUtils#loadAnimation(android.content.Context,
- int) AnimationUtils.loadAnimation()}.
- - Either start the animation immediately by calling {@link android.view.View#startAnimation(android.view.animation.Animation)
- View.startAnimation()}, or if you have specified a start time in the animation
- parameters, you can call
- {@link android.view.View#setAnimation(android.view.animation.Animation)
- View.setCurrentAnimation()}.
-
-The following code demonstrates loading and starting an animation.
-// Hook into the object to be animated.
-TextView animWindow = (TextView)findViewById(R.id.anim);
-
-// Load the animation from XML (XML file is res/anim/move_animation.xml).
-Animation anim = AnimationUtils.loadAnimation(AnimationSample.this, R.anim.move_animation);
-anim.setRepeatMode(Animation.NO_REPEAT);
-
-// Play the animation.
-animWindow.startAnimation(anim);