OSDN Git Service

auto import from //branches/cupcake/...@125939
authorThe Android Open Source Project <initial-contribution@android.com>
Sat, 10 Jan 2009 01:51:23 +0000 (17:51 -0800)
committerThe Android Open Source Project <initial-contribution@android.com>
Sat, 10 Jan 2009 01:51:23 +0000 (17:51 -0800)
289 files changed:
api/current.xml
camera/libcameraservice/CameraHardwareStub.cpp
camera/libcameraservice/CameraHardwareStub.h
camera/libcameraservice/CameraService.cpp
camera/libcameraservice/CameraService.h
cmds/dumpstate/Android.mk
cmds/dumpstate/dumpstate [deleted file]
cmds/dumpstate/dumpstate.c [new file with mode: 0644]
cmds/dumpstate/dumpstate.h [new file with mode: 0644]
cmds/dumpstate/utils.c [new file with mode: 0644]
cmds/ime/Android.mk [new file with mode: 0644]
cmds/ime/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
cmds/ime/NOTICE [new file with mode: 0644]
cmds/ime/ime [new file with mode: 0755]
cmds/ime/src/com/android/commands/ime/Ime.java [new file with mode: 0644]
cmds/pm/src/com/android/commands/pm/Pm.java
core/java/android/app/Activity.java
core/java/android/app/SearchDialog.java
core/java/android/app/SearchManager.java
core/java/android/bluetooth/BluetoothA2dp.java
core/java/android/content/AbstractTableMerger.java
core/java/android/hardware/Camera.java
core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
core/java/android/inputmethodservice/InputMethodService.java
core/java/android/inputmethodservice/Keyboard.java
core/java/android/inputmethodservice/KeyboardView.java
core/java/android/os/HandlerState.java [new file with mode: 0644]
core/java/android/os/HandlerStateMachine.java [new file with mode: 0644]
core/java/android/os/IPowerManager.aidl
core/java/android/os/Power.java
core/java/android/preference/PreferenceGroupAdapter.java
core/java/android/provider/Browser.java
core/java/android/provider/Contacts.java
core/java/android/provider/MediaStore.java
core/java/android/provider/Settings.java
core/java/android/provider/Sync.java
core/java/android/server/BluetoothA2dpService.java
core/java/android/server/search/SearchableInfo.java
core/java/android/speech/RecognizerIntent.java [new file with mode: 0644]
core/java/android/text/InputType.java
core/java/android/text/Layout.java
core/java/android/text/format/DateUtils.java
core/java/android/text/method/ArrowKeyMovementMethod.java
core/java/android/text/method/MetaKeyKeyListener.java
core/java/android/text/style/BackgroundColorSpan.java
core/java/android/text/style/CharacterStyle.java
core/java/android/text/style/ClickableSpan.java
core/java/android/text/style/DynamicDrawableSpan.java
core/java/android/text/style/ForegroundColorSpan.java
core/java/android/text/style/ImageSpan.java
core/java/android/text/style/MaskFilterSpan.java
core/java/android/text/style/RasterizerSpan.java
core/java/android/text/style/StrikethroughSpan.java
core/java/android/text/style/UnderlineSpan.java
core/java/android/text/style/UpdateAppearance.java [new file with mode: 0644]
core/java/android/text/style/UpdateLayout.java
core/java/android/util/PrintStreamPrinter.java [new file with mode: 0644]
core/java/android/view/Menu.java
core/java/android/view/SurfaceView.java
core/java/android/view/View.java
core/java/android/view/ViewRoot.java
core/java/android/view/animation/Animation.java
core/java/android/view/animation/package.html
core/java/android/view/inputmethod/BaseInputConnection.java
core/java/android/view/inputmethod/DefaultInputMethod.java
core/java/android/view/inputmethod/InputConnection.java
core/java/android/view/inputmethod/InputConnectionWrapper.java
core/java/android/view/inputmethod/InputMethod.java
core/java/android/view/inputmethod/InputMethodManager.java
core/java/android/view/inputmethod/InputMethodSession.java
core/java/android/view/inputmethod/package.html
core/java/android/webkit/CookieManager.java
core/java/android/webkit/TextDialog.java
core/java/android/webkit/WebView.java
core/java/android/webkit/WebViewCore.java
core/java/android/widget/AbsListView.java
core/java/android/widget/AbsSeekBar.java
core/java/android/widget/AutoCompleteTextView.java
core/java/android/widget/Gallery.java
core/java/android/widget/ProgressBar.java
core/java/android/widget/RatingBar.java
core/java/android/widget/ResourceCursorAdapter.java
core/java/android/widget/SeekBar.java
core/java/android/widget/SimpleCursorAdapter.java
core/java/android/widget/TextView.java
core/java/com/android/internal/app/RingtonePickerActivity.java
core/java/com/android/internal/os/HandlerCaller.java
core/java/com/android/internal/view/IInputConnectionWrapper.java
core/java/com/android/internal/view/IInputContext.aidl
core/java/com/android/internal/view/IInputMethodManager.aidl
core/java/com/android/internal/view/IInputMethodSession.aidl
core/java/com/android/internal/view/InputConnectionWrapper.java
core/java/com/android/internal/view/menu/MenuItemImpl.java
core/java/com/android/internal/widget/EditableInputConnection.java
core/java/com/google/android/gdata/client/AndroidGDataClient.java
core/java/com/google/android/net/GoogleHttpClient.java
core/jni/Android.mk
core/jni/AndroidRuntime.cpp
core/jni/android/graphics/Bitmap.cpp
core/jni/android/graphics/Canvas.cpp
core/jni/android/graphics/Shader.cpp
core/jni/android/opengl/util.cpp
core/jni/android_hardware_Camera.cpp
core/jni/android_media_AudioTrack.cpp
core/jni/android_media_JetPlayer.cpp [new file with mode: 0644]
core/jni/android_os_Power.cpp
core/jni/android_view_ViewRoot.cpp
core/jni/com_google_android_gles_jni_EGLImpl.cpp
core/res/AndroidManifest.xml
core/res/res/drawable/ic_menu_login.png [new file with mode: 0644]
core/res/res/drawable/ic_menu_notifications.png [new file with mode: 0644]
core/res/res/drawable/ic_menu_refresh.png [new file with mode: 0644]
core/res/res/drawable/ratingbar_full.xml
core/res/res/drawable/ratingbar_full_empty.xml [new file with mode: 0644]
core/res/res/drawable/ratingbar_full_filled.xml [new file with mode: 0644]
core/res/res/drawable/seek_thumb.xml [new file with mode: 0644]
core/res/res/drawable/seek_thumb_normal.png [moved from core/res/res/drawable/seek_thumb.png with 100% similarity]
core/res/res/drawable/seek_thumb_pressed.png [new file with mode: 0644]
core/res/res/drawable/seek_thumb_selected.png [new file with mode: 0644]
core/res/res/layout/search_bar.xml
core/res/res/values-fr/arrays.xml [new file with mode: 0644]
core/res/res/values-fr/strings.xml [new file with mode: 0644]
core/res/res/values-it/arrays.xml [new file with mode: 0644]
core/res/res/values-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc204-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc204-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc204-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-mcc230-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc230-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc230-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-mcc232-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc232-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc232-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-mcc234-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc234-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc234-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-mcc260-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc260-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc260-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-mcc262-fr/strings.xml [new file with mode: 0644]
core/res/res/values-mcc262-it/strings.xml [new file with mode: 0644]
core/res/res/values-mcc262-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values-zh-rCN/arrays.xml [new file with mode: 0644]
core/res/res/values-zh-rCN/strings.xml [new file with mode: 0644]
core/res/res/values/attrs.xml
core/res/res/values/ids.xml
core/res/res/values/public.xml
core/res/res/values/strings.xml
core/res/res/values/styles.xml
core/res/res/xml-en/autotext.xml [new file with mode: 0644]
core/res/res/xml/autotext.xml
data/etc/Android.mk
data/etc/platform.xml [moved from data/etc/permissions.xml with 96% similarity]
data/localization/import-from-xtb
docs/html/community/index.jd
docs/html/images/linearlayout.png
graphics/java/android/graphics/drawable/AnimationDrawable.java
graphics/java/android/graphics/drawable/BitmapDrawable.java
graphics/java/android/graphics/drawable/ClipDrawable.java
graphics/java/android/graphics/drawable/ColorDrawable.java
graphics/java/android/graphics/drawable/GradientDrawable.java
graphics/java/android/graphics/drawable/InsetDrawable.java
graphics/java/android/graphics/drawable/LayerDrawable.java
graphics/java/android/graphics/drawable/LevelListDrawable.java
graphics/java/android/graphics/drawable/RotateDrawable.java
graphics/java/android/graphics/drawable/ScaleDrawable.java
graphics/java/android/graphics/drawable/StateListDrawable.java
graphics/java/android/graphics/drawable/TransitionDrawable.java
include/media/JetPlayer.h [new file with mode: 0644]
include/media/ToneGenerator.h
include/media/mediametadataretriever.h
include/ui/Camera.h
include/ui/CameraHardwareInterface.h
include/ui/EGLNativeSurface.h
include/ui/ICamera.h
include/ui/IOverlay.h
include/ui/ISurface.h
include/ui/Overlay.h
include/ui/PixelFormat.h
include/ui/Region.h
include/utils/MemoryHeapBase.h
include/utils/RefBase.h
libs/audioflinger/A2dpAudioInterface.cpp
libs/audioflinger/A2dpAudioInterface.h
libs/audioflinger/AudioDumpInterface.cpp
libs/audioflinger/AudioDumpInterface.h
libs/audioflinger/AudioFlinger.cpp
libs/audioflinger/AudioFlinger.h
libs/audioflinger/AudioHardwareGeneric.cpp
libs/audioflinger/AudioHardwareGeneric.h
libs/audioflinger/AudioHardwareStub.cpp
libs/audioflinger/AudioHardwareStub.h
libs/surfaceflinger/BootAnimation.cpp
libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
libs/surfaceflinger/DisplayHardware/DisplayHardware.h
libs/surfaceflinger/Layer.cpp
libs/surfaceflinger/LayerBase.cpp
libs/surfaceflinger/LayerBase.h
libs/surfaceflinger/LayerBlur.cpp
libs/surfaceflinger/LayerBuffer.cpp
libs/surfaceflinger/LayerBuffer.h
libs/surfaceflinger/LayerScreenshot.cpp
libs/surfaceflinger/SurfaceFlinger.cpp
libs/surfaceflinger/SurfaceFlinger.h
libs/surfaceflinger/Transform.h
libs/ui/Camera.cpp
libs/ui/ICamera.cpp
libs/ui/IOverlay.cpp
libs/ui/ISurface.cpp
libs/ui/Overlay.cpp
libs/ui/Region.cpp
libs/utils/MemoryHeapBase.cpp
location/java/com/android/internal/location/GpsLocationProvider.java
location/java/com/android/internal/location/GpsXtraDownloader.java
location/java/com/android/internal/location/LocationCache.java
location/java/com/android/internal/location/LocationMasfClient.java
media/java/android/media/AudioManager.java
media/java/android/media/AudioTrack.java
media/java/android/media/JetPlayer.java [new file with mode: 0644]
media/java/android/media/MediaMetadataRetriever.java
media/java/android/media/ResampleInputStream.java [new file with mode: 0644]
media/jni/Android.mk
media/jni/android_media_MediaMetadataRetriever.cpp
media/jni/android_media_MediaPlayer.cpp
media/jni/android_media_ResampleInputStream.cpp [new file with mode: 0644]
media/libmedia/Android.mk
media/libmedia/AudioTrack.cpp
media/libmedia/IMediaMetadataRetriever.cpp
media/libmedia/JetPlayer.cpp [new file with mode: 0644]
media/libmedia/ToneGenerator.cpp
media/libmedia/mediarecorder.cpp
preloaded-classes
services/java/com/android/server/HeadsetObserver.java
services/java/com/android/server/InputMethodManagerService.java
services/java/com/android/server/LocationManagerService.java
services/java/com/android/server/MountService.java
services/java/com/android/server/PackageManagerService.java
services/java/com/android/server/PowerManagerService.java
services/java/com/android/server/Watchdog.java
services/java/com/android/server/WifiService.java
services/java/com/android/server/am/ActivityManagerService.java
services/java/com/android/server/am/PendingIntentRecord.java
telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java
telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java
telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java
telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java
telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java
telephony/java/com/android/internal/telephony/gsm/stk/Service.java
telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java
test-runner/android/test/TouchUtils.java
tests/AndroidTests/src/com/android/unit_tests/GoogleHttpClientTest.java
tests/AndroidTests/src/com/android/unit_tests/os/HandlerStateMachineTest.java [new file with mode: 0644]
tests/CoreTests/android/AndroidManifest.xml
tests/CoreTests/android/webkit/CookieTest.java
tests/FrameworkTest/AndroidManifest.xml
tests/FrameworkTest/res/layout/autocompletetextview_simple.xml [new file with mode: 0644]
tests/FrameworkTest/src/android/widget/AutoCompleteTextViewSimple.java [new file with mode: 0644]
tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java [new file with mode: 0644]
tests/FrameworkTest/tests/src/android/widget/SimpleCursorAdapterTest.java
tests/SmokeTest/Android.mk [new file with mode: 0644]
tests/SmokeTest/AndroidManifest.xml [new file with mode: 0644]
tests/SmokeTest/README [new file with mode: 0644]
tests/SmokeTest/src/com/android/smoketest/SmokeTestActivity.java [new file with mode: 0644]
tests/SmokeTest/tests/Android.mk [new file with mode: 0644]
tests/SmokeTest/tests/AndroidManifest.xml [new file with mode: 0644]
tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java [new file with mode: 0644]
tools/aapt/Images.cpp
tools/aapt/Package.cpp
tools/aapt/ResourceTable.cpp
tools/aidl/aidl.cpp
tools/aidl/generate_java.cpp
tools/aidl/options.cpp
tools/aidl/search_path.cpp
tools/aidl/search_path.h
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java [new file with mode: 0644]
tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java [new file with mode: 0644]
tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
tools/localize/Perforce.cpp
tools/localize/XLIFFFile.cpp
tools/localize/XMLHandler.cpp
tools/localize/file_utils.cpp
tools/localize/localize.cpp
wifi/java/android/net/wifi/IWifiManager.aidl
wifi/java/android/net/wifi/WifiManager.java
wifi/java/android/net/wifi/WifiStateTracker.java

index 880b6e8..13ca370 100644 (file)
  value="16843269"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
  visibility="public"
 >
 </field>
+<field name="ic_menu_login"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301665"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ic_menu_manage"
  type="int"
  transient="false"
  visibility="public"
 >
 </field>
+<field name="ic_menu_notifications"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301667"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ic_menu_preferences"
  type="int"
  transient="false"
  visibility="public"
 >
 </field>
+<field name="ic_menu_refresh"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301666"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ic_menu_report_image"
  type="int"
  transient="false"
 <parameter name="params" type="android.view.ViewGroup.LayoutParams">
 </parameter>
 </method>
+<method name="closeContextMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="closeOptionsMenu"
  return="void"
  abstract="false"
  visibility="public"
 >
 </method>
-<method name="getCurrentInputInfo"
+<method name="getCurrentInputEditorInfo"
  return="android.view.inputmethod.EditorInfo"
  abstract="false"
  native="false"
 </parameter>
 <parameter name="newSelEnd" type="int">
 </parameter>
+<parameter name="candidatesStart" type="int">
+</parameter>
+<parameter name="candidatesEnd" type="int">
+</parameter>
 </method>
 <method name="setCandidatesView"
  return="void"
 </parameter>
 <parameter name="newSelEnd" type="int">
 </parameter>
+<parameter name="candidatesStart" type="int">
+</parameter>
+<parameter name="candidatesEnd" type="int">
+</parameter>
 </method>
 </class>
 <class name="InputMethodService.Insets"
  visibility="public"
 >
 </field>
+<field name="PHONETIC_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PHONE_ISPRIMARY"
  type="java.lang.String"
  transient="false"
  visibility="public"
 >
 </field>
+<field name="PHONETIC_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PHOTO_VERSION"
  type="java.lang.String"
  transient="false"
  value="&quot;logging_id&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 <parameter name="stepCount" type="int">
 </parameter>
 </method>
+<method name="drag"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="fromX" type="float">
+</parameter>
+<parameter name="toX" type="float">
+</parameter>
+<parameter name="fromY" type="float">
+</parameter>
+<parameter name="toY" type="float">
+</parameter>
+<parameter name="stepCount" type="int">
+</parameter>
+</method>
+<method name="dragQuarterScreenDown"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+</parameter>
+</method>
 <method name="dragQuarterScreenDown"
  return="void"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+</method>
+<method name="dragQuarterScreenUp"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 </parameter>
 </method>
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
 </parameter>
 </method>
 <method name="dragViewBy"
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 <parameter name="deltaY" type="int">
 </parameter>
 </method>
+<method name="dragViewBy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="gravity" type="int">
+</parameter>
+<parameter name="deltaX" type="int">
+</parameter>
+<parameter name="deltaY" type="int">
+</parameter>
+</method>
 <method name="dragViewTo"
  return="int"
  abstract="false"
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 <parameter name="toY" type="int">
 </parameter>
 </method>
+<method name="dragViewTo"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="gravity" type="int">
+</parameter>
+<parameter name="toX" type="int">
+</parameter>
+<parameter name="toY" type="int">
+</parameter>
+</method>
+<method name="dragViewToBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
 <method name="dragViewToBottom"
  return="void"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
+<method name="dragViewToBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.View">
 </parameter>
+<parameter name="stepCount" type="int">
+</parameter>
 </method>
 <method name="dragViewToBottom"
  return="void"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="stepCount" type="int">
+</parameter>
+</method>
+<method name="dragViewToTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
+<method name="dragViewToTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.View">
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+<parameter name="test" type="android.test.InstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.View">
 </parameter>
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+<parameter name="test" type="android.test.InstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.View">
 </parameter>
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 <parameter name="toX" type="int">
 </parameter>
 </method>
-<method name="dragViewToY"
+<method name="dragViewToX"
  return="int"
  abstract="false"
  native="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="gravity" type="int">
+</parameter>
+<parameter name="toX" type="int">
+</parameter>
+</method>
+<method name="dragViewToY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.View">
 <parameter name="toY" type="int">
 </parameter>
 </method>
+<method name="dragViewToY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="gravity" type="int">
+</parameter>
+<parameter name="toY" type="int">
+</parameter>
+</method>
 <method name="longClickView"
  return="void"
  abstract="false"
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 <parameter name="v" type="android.view.View">
 </parameter>
 </method>
+<method name="longClickView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+</method>
+<method name="scrollToBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+</parameter>
+<parameter name="v" type="android.view.ViewGroup">
+</parameter>
+</method>
 <method name="scrollToBottom"
  return="void"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="v" type="android.view.ViewGroup">
+</parameter>
+</method>
+<method name="scrollToTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="test" type="android.test.ActivityInstrumentationTestCase">
 </parameter>
 <parameter name="v" type="android.view.ViewGroup">
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="test" type="android.test.ActivityInstrumentationTestCase">
+<parameter name="test" type="android.test.InstrumentationTestCase">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
 </parameter>
 <parameter name="v" type="android.view.ViewGroup">
 </parameter>
  visibility="public"
 >
 </field>
+<field name="TYPE_TEXT_FLAG_SEARCH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TYPE_TEXT_VARIATION_EMAIL_ADDRESS"
  type="int"
  transient="false"
  visibility="public"
 >
 </field>
-<field name="TYPE_TEXT_VARIATION_URI"
+<field name="TYPE_TEXT_VARIATION_SEARCH_STRING"
  type="int"
  transient="false"
  volatile="false"
- value="16"
+ value="128"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 </field>
-<field name="TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"
+<field name="TYPE_TEXT_VARIATION_URI"
  type="int"
  transient="false"
  volatile="false"
- value="144"
+ value="16"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 </field>
-<field name="TYPE_TEXT_VARIATION_WEB_SEARCH"
+<field name="TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"
  type="int"
  transient="false"
  volatile="false"
- value="128"
+ value="144"
  static="true"
  final="true"
  deprecated="not deprecated"
 <parameter name="abbrev" type="int">
 </parameter>
 </method>
+<method name="getRelativeDateTimeString"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="c" type="android.content.Context">
+</parameter>
+<parameter name="time" type="long">
+</parameter>
+<parameter name="minResolution" type="long">
+</parameter>
+<parameter name="transitionResolution" type="long">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="getRelativeTimeSpanString"
  return="java.lang.CharSequence"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="time" type="long">
+</parameter>
+<parameter name="now" type="long">
+</parameter>
+<parameter name="minResolution" type="long">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="getRelativeTimeSpanString"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 <parameter name="c" type="android.content.Context">
 </parameter>
 <parameter name="millis" type="long">
  type="int"
  transient="false"
  volatile="false"
- value="114688"
+ value="524288"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 </field>
+<field name="FORMAT_ABBREV_RELATIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FORMAT_ABBREV_TIME"
  type="int"
  transient="false"
 <parameter name="content" type="android.text.Spannable">
 </parameter>
 </method>
+<method name="adjustMetaAfterKeypress"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+</method>
 <method name="clearMetaKeyState"
  return="void"
  abstract="false"
 <parameter name="states" type="int">
 </parameter>
 </method>
+<method name="clearMetaKeyState"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+<parameter name="which" type="int">
+</parameter>
+</method>
 <method name="getMetaState"
  return="int"
  abstract="false"
 <parameter name="meta" type="int">
 </parameter>
 </method>
+<method name="getMetaState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+</method>
+<method name="getMetaState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+<parameter name="meta" type="int">
+</parameter>
+</method>
+<method name="handleKeyDown"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="handleKeyUp"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
 <method name="isMetaTracker"
  return="boolean"
  abstract="false"
 <parameter name="content" type="android.text.Spannable">
 </parameter>
 </method>
+<method name="resetLockedMeta"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="long">
+</parameter>
+</method>
 <method name="resetMetaState"
  return="void"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="BackgroundColorSpan"
  type="android.text.style.BackgroundColorSpan"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="ClickableSpan"
  type="android.text.style.ClickableSpan"
  static="false"
  visibility="public"
 >
 </constructor>
+<constructor name="DynamicDrawableSpan"
+ type="android.text.style.DynamicDrawableSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="verticalAlignment" type="int">
+</parameter>
+</constructor>
 <method name="draw"
  return="void"
  abstract="false"
 <parameter name="fm" type="android.graphics.Paint.FontMetricsInt">
 </parameter>
 </method>
+<method name="getVerticalAlignment"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="ALIGN_BASELINE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ALIGN_BOTTOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVerticalAlignment"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
 </class>
 <class name="ForegroundColorSpan"
  extends="android.text.style.CharacterStyle"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="ForegroundColorSpan"
  type="android.text.style.ForegroundColorSpan"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="b" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="verticalAlignment" type="int">
+</parameter>
+</constructor>
+<constructor name="ImageSpan"
+ type="android.text.style.ImageSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 <parameter name="d" type="android.graphics.drawable.Drawable">
 </parameter>
 </constructor>
 >
 <parameter name="d" type="android.graphics.drawable.Drawable">
 </parameter>
+<parameter name="verticalAlignment" type="int">
+</parameter>
+</constructor>
+<constructor name="ImageSpan"
+ type="android.text.style.ImageSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
 <parameter name="source" type="java.lang.String">
 </parameter>
 </constructor>
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+<parameter name="source" type="java.lang.String">
+</parameter>
+<parameter name="verticalAlignment" type="int">
+</parameter>
+</constructor>
+<constructor name="ImageSpan"
+ type="android.text.style.ImageSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<constructor name="ImageSpan"
+ type="android.text.style.ImageSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 <parameter name="context" type="android.content.Context">
 </parameter>
 <parameter name="uri" type="android.net.Uri">
 </parameter>
+<parameter name="verticalAlignment" type="int">
+</parameter>
 </constructor>
 <constructor name="ImageSpan"
  type="android.text.style.ImageSpan"
 <parameter name="resourceId" type="int">
 </parameter>
 </constructor>
+<constructor name="ImageSpan"
+ type="android.text.style.ImageSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="resourceId" type="int">
+</parameter>
+<parameter name="verticalAlignment" type="int">
+</parameter>
+</constructor>
 <method name="getDrawable"
  return="android.graphics.drawable.Drawable"
  abstract="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="MaskFilterSpan"
  type="android.text.style.MaskFilterSpan"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="RasterizerSpan"
  type="android.text.style.RasterizerSpan"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="StrikethroughSpan"
  type="android.text.style.StrikethroughSpan"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 <constructor name="UnderlineSpan"
  type="android.text.style.UnderlineSpan"
  static="false"
 </parameter>
 </method>
 </class>
+<interface name="UpdateAppearance"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</interface>
 <interface name="UpdateLayout"
  abstract="true"
  static="false"
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.text.style.UpdateAppearance">
+</implements>
 </interface>
 <interface name="WrapTogetherSpan"
  abstract="true"
 >
 </method>
 </class>
+<class name="PrintStreamPrinter"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.util.Printer">
+</implements>
+<constructor name="PrintStreamPrinter"
+ type="android.util.PrintStreamPrinter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pw" type="java.io.PrintStream">
+</parameter>
+</constructor>
+<method name="println"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="x" type="java.lang.String">
+</parameter>
+</method>
+</class>
 <class name="PrintWriterPrinter"
  extends="java.lang.Object"
  abstract="false"
  visibility="public"
 >
 </method>
+<method name="close"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="findItem"
  return="android.view.MenuItem"
  abstract="true"
 <parameter name="id" type="int">
 </parameter>
 </method>
+<method name="getItem"
+ return="android.view.MenuItem"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
 <method name="hasVisibleItems"
  return="boolean"
  abstract="true"
 <parameter name="menu" type="android.view.ContextMenu">
 </parameter>
 </method>
-<method name="createInputConnection"
- return="android.view.inputmethod.InputConnection"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="outAttrs" type="android.view.inputmethod.EditorInfo">
-</parameter>
-</method>
 <method name="destroyDrawingCache"
  return="void"
  abstract="false"
 <parameter name="extraSpace" type="int">
 </parameter>
 </method>
+<method name="onCreateInputConnection"
+ return="android.view.inputmethod.InputConnection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="outAttrs" type="android.view.inputmethod.EditorInfo">
+</parameter>
+</method>
 <method name="onDetachedFromWindow"
  return="void"
  abstract="false"
 <parameter name="rightLength" type="int">
 </parameter>
 </method>
+<method name="finishComposingText"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getCursorCapsMode"
  return="int"
  abstract="true"
 <parameter name="rightLength" type="int">
 </parameter>
 </method>
+<method name="finishComposingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getCursorCapsMode"
  return="int"
  abstract="false"
 </parameter>
 <parameter name="selEnd" type="int">
 </parameter>
+<parameter name="candidatesStart" type="int">
+</parameter>
+<parameter name="candidatesEnd" type="int">
+</parameter>
 </method>
 <method name="updateStatusIcon"
  return="void"
 </parameter>
 <parameter name="newSelEnd" type="int">
 </parameter>
+<parameter name="candidatesStart" type="int">
+</parameter>
+<parameter name="candidatesEnd" type="int">
+</parameter>
 </method>
 </interface>
 <interface name="InputMethodSession.EventCallback"
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="host" type="java.lang.String">
+<parameter name="schemePlusHost" type="java.lang.String">
 </parameter>
 <parameter name="username" type="java.lang.String">
 </parameter>
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
 </parameter>
 <parameter name="rating" type="float">
 </parameter>
-<parameter name="fromTouch" type="boolean">
+<parameter name="fromUser" type="boolean">
 </parameter>
 </method>
 </interface>
 <parameter name="dropDownLayout" type="int">
 </parameter>
 </method>
+<method name="setViewResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="layout" type="int">
+</parameter>
+</method>
 </class>
 <class name="ResourceCursorTreeAdapter"
  extends="android.widget.CursorTreeAdapter"
 </parameter>
 <parameter name="progress" type="int">
 </parameter>
-<parameter name="fromTouch" type="boolean">
+<parameter name="fromUser" type="boolean">
 </parameter>
 </method>
 <method name="onStartTrackingTouch"
 <parameter name="cursor" type="android.database.Cursor">
 </parameter>
 </method>
+<method name="changeCursorAndColumns"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="from" type="java.lang.String[]">
+</parameter>
+<parameter name="to" type="int[]">
+</parameter>
+</method>
 <method name="getCursorToStringConverter"
  return="android.widget.SimpleCursorAdapter.CursorToStringConverter"
  abstract="false"
index ea21af8..3d6b0b1 100644 (file)
@@ -183,6 +183,10 @@ void CameraHardwareStub::stopPreview()
     mPreviewThread.clear();
 }
 
+bool CameraHardwareStub::previewEnabled() {
+    return mPreviewThread != 0;
+}
+
 // ---------------------------------------------------------------------------
 
 int CameraHardwareStub::beginAutoFocusThread(void *cookie)
index 5b445d3..9f5ddf1 100644 (file)
@@ -33,6 +33,7 @@ public:
 
     virtual status_t    startPreview(preview_callback cb, void* user);
     virtual void        stopPreview();
+    virtual bool        previewEnabled();
     virtual status_t    autoFocus(autofocus_callback, void *user);
     virtual status_t    takePicture(shutter_callback,
                                     raw_callback,
index 800ffa4..33987c3 100644 (file)
@@ -107,7 +107,7 @@ sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
     }
 
     // create a new Client object
-    sp<Client> client = new Client(this, cameraClient);
+    sp<Client> 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<ICameraClient>& cameraClient)
 }
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
-        const sp<ICameraClient>& cameraClient) :
-    mCameraService(cameraService), mCameraClient(cameraClient), mHardware(0)
+        const sp<ICameraClient>& 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>& 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<ICameraClient>& 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<ISurface>& 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<ISurface>& 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> 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.");
index b225aa9..cd8c1e9 100644 (file)
@@ -73,6 +73,12 @@ private:
         // connect new client with existing camera remote
         virtual status_t        connect(const sp<ICameraClient>& 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<ISurface>& 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>& cameraService,
-                                        const sp<ICameraClient>& cameraClient);
+                                Client(const sp<CameraService>& cameraService,
+                                        const sp<ICameraClient>& cameraClient,
+                                        pid_t clientPid);
                                 Client();
         virtual                 ~Client();
 
+                    status_t    checkPid();
+
         static      void        previewCallback(const sp<IMemory>& mem, void* user);
         static      void        shutterCallback(void *user);
         static      void        yuvPictureCallback(const sp<IMemory>& 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<ICameraClient>           mCameraClient;
                     sp<CameraHardwareInterface> mHardware;
+                    pid_t                       mClientPid;
     };
 
 // ----------------------------------------------------------------------------
index e101aeb..f61d7ec 100644 (file)
@@ -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 (executable)
index 8dd7d14..0000000
+++ /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 (file)
index 0000000..dea269d
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#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 (file)
index 0000000..b956f99
--- /dev/null
@@ -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 <time.h>
+
+// 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 (file)
index 0000000..60d845f
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <cutils/properties.h>
+#include <sys/system_properties.h>
+
+#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 (file)
index 0000000..90b1c91
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/cmds/ime/NOTICE b/cmds/ime/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
@@ -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 (executable)
index 0000000..96c56d3
--- /dev/null
@@ -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 (file)
index 0000000..72a0af6
--- /dev/null
@@ -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<InputMethodInfo> 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<methods.size(); i++) {
+                InputMethodInfo imi = methods.get(i);
+                if (brief) {
+                    System.out.println(imi.getId());
+                } else {
+                    System.out.println(imi.getId() + ":");
+                    imi.dump(pr, "  ");
+                }
+            }
+        }
+    }
+    
+    private void runSetEnabled(boolean state) {
+        String id = nextArg();
+        if (id == null) {
+            System.err.println("Error: no input method ID specified");
+            showUsage();
+            return;
+        }
+        
+        try {
+            boolean res = mImm.setInputMethodEnabled(id, state);
+            if (state) {
+                System.out.println("Input method " + id + ": "
+                        + (res ? "already enabled" : "now enabled"));
+            } else {
+                System.out.println("Input method " + id + ": "
+                        + (res ? "now disabled" : "already disabled"));
+            }
+        } catch (IllegalArgumentException e) {
+            System.err.println("Error: " + e.getMessage());
+            return;
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(IMM_NOT_RUNNING_ERR);
+            return;
+        }
+    }
+    
+    private void runSet() {
+        String id = nextArg();
+        if (id == null) {
+            System.err.println("Error: no input method ID specified");
+            showUsage();
+            return;
+        }
+        
+        try {
+            mImm.setInputMethod(null, id);
+            System.out.println("Input method " + id + " selected");
+        } catch (IllegalArgumentException e) {
+            System.err.println("Error: " + e.getMessage());
+            return;
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(IMM_NOT_RUNNING_ERR);
+            return;
+        }
+    }
+    
+    private String nextOption() {
+        if (mNextArg >= 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.");
+    }
+}
index 09a140b..c2d8da5 100644 (file)
@@ -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\").");
     }
 }
index eafb048..c98cf1b 100644 (file)
@@ -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);
index 2ce2db9..f1c604c 100644 (file)
 
 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<Request> mSuggestionsQueue = 
-                                                            new LinkedBlockingQueue<Request>();
-
-        /**
-         * 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<Cursor> mRecentCursor = new WeakReference<Cursor>(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<Cursor>(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
index 5f25b90..0a37e81 100644 (file)
@@ -739,6 +739,14 @@ import android.view.KeyEvent;
  *         <td align="center">No</td>
  *     </tr>
  *     
+ *     <tr><th>android:inputType</th>
+ *         <td>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
+ *             <a href="../R.attr.html#inputType">inputType</a> attribute.</td>
+ *         <td align="center">No</td>
+ *     </tr>
+ *     
  *     </tbody>
  * </table>
  * 
index d6ea889..022a87c 100644 (file)
@@ -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.
index e1a484e..5511ff6 100644 (file)
@@ -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) {
index dc75748..e2d7097 100644 (file)
@@ -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 
-         *                     (<var>PixelFormat.YCbCr_422_SP</var>,
+         *                     (<var>PixelFormat.YCbCr_420_SP</var>,
          *                      <var>PixelFormat.RGB_565</var>, or
          *                      <var>PixelFormat.JPEG</var>)
          * @see android.graphics.PixelFormat
@@ -604,7 +696,7 @@ public class Camera {
          * Sets the image format for pictures.
          * 
          * @param pixel_format the desired picture format 
-         *                     (<var>PixelFormat.YCbCr_422_SP</var>,
+         *                     (<var>PixelFormat.YCbCr_420_SP</var>,
          *                      <var>PixelFormat.RGB_565</var>, or
          *                      <var>PixelFormat.JPEG</var>)
          * @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;
 
index 40c03cd..5a85c66 100644 (file)
@@ -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) {
index 9ebf127..0588bea 100644 (file)
@@ -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.
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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:</p>
+ * 
+ * <ul>
+ * <li>The soft input view, if available, is placed at the bottom of the
+ * screen.
+ * <li>The candidates view, if currently shown, is placed above the soft
+ * input view.
+ * <li>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.
+ * </ul>
+ * 
+ * 
+ * <a name="SoftInputView"></a>
+ * <h3>Soft Input View</h3>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.</p>
+ * 
+ * 
+ * <a name="CandidatesView"></a>
+ * <h3>Candidates View</h3>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.</p>
+ * 
+ * 
+ * <a name="FullscreenMode"></a>
+ * <h3>Fullscreen Mode</h3>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.
+ * 
+ * 
+ * <a name="GeneratingText"></a>
+ * <h3>Generating Text</h3>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.</p>
+ * 
+ * <p>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.</p>
  */
 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);
         }
     }
 }
index 75a2911..cfd3188 100755 (executable)
@@ -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;
     }
index 56473da..3b5d741 100755 (executable)
@@ -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 (file)
index 0000000..0708f7d
--- /dev/null
@@ -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 (file)
index 0000000..d004a25
--- /dev/null
@@ -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:
+<code>
+     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();
+    }
+</code>
+ */
+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);
+        }
+   }
+}
index abc1e2f..e48f152 100644 (file)
@@ -28,4 +28,5 @@ interface IPowerManager
     void setPokeLock(int pokey, IBinder lock, String tag);
     void setStayOnSetting(int val);
     long getScreenOnTime();
+    void preventScreenOn(boolean prevent);
 }
index 0794e6d..b53e227 100644 (file)
@@ -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;
 }
-
index e2a3157..05c2952 100644 (file)
@@ -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);
         }
     }
 
index 7aaed49..76aa51d 100644 (file)
@@ -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();
index 6d24ba8..085c095 100644 (file)
@@ -170,12 +170,20 @@ public class Contacts {
      */
     public interface PeopleColumns {
         /**
-         * The persons name.
+         * The person's name.
          * <P>Type: TEXT</P>
          */
         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.
+         * <p>Type: TEXT</P>
+         */
+        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.
          * <P>Type: TEXT</P>
@@ -1509,6 +1517,12 @@ public class Contacts {
             public static final String NAME = "name";
 
             /**
+             * The extra field for the contact phonetic name.
+             * <P>Type: String</P>
+             */
+            public static final String PHONETIC_NAME = "phonetic_name";
+
+            /**
              * The extra field for the contact company.
              * <P>Type: String</P>
              */
@@ -1535,7 +1549,7 @@ public class Contacts {
             /**
              * The extra field for the contact phone number type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             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.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
-             *  or a string specifying a type and label.</P>
+             *  or a string specifying a custom label.</P>
              */
             public static final String POSTAL_TYPE = "postal_type";
 
index d4b728b..7b2f18c 100644 (file)
@@ -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 {
index e93bbeb..91624ad 100644 (file)
@@ -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.
index 2086a5d..94bf807 100644 (file)
@@ -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.
          *
index 3cbb855..ded50e4 100644 (file)
@@ -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 {
index 6c8f554..ac4cdb9 100644 (file)
@@ -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<SearchableInfo> 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 (file)
index 0000000..abbf8a7
--- /dev/null
@@ -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.
+     * 
+     * <p>Required extras:
+     * <ul>
+     *   <li>{@link #EXTRA_LANGUAGE_MODEL}
+     * </ul>
+     * 
+     * <p>Optional extras:
+     * <ul>
+     *   <li>{@link Intent#EXTRA_PROMPT}
+     *   <li>{@link #EXTRA_LANGUAGE}
+     *   <li>{@link #EXTRA_MAX_RESULTS}
+     * </ul>
+     * 
+     * <p> Result extras:
+     * <ul>
+     *   <li>{@link #EXTRA_RESULTS}
+     * </ul>
+     * 
+     * <p>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<String> 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";
+}
index 0ffe4ac..bd86834 100644 (file)
@@ -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;
     
index 346db49..95acf9d 100644 (file)
@@ -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;
index 48f65c6..feae6cf 100644 (file)
@@ -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
-     * <p>e.g. "Sunday" or "January"
+     * Request the full spelled-out name. For use with the 'abbrev' parameter of
+     * {@link #getDayOfWeekString} and {@link #getMonthString}.
+     * 
+     * @more <p>
+     *       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
-     * <p>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 <p>
+     *       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'.
+     * <p>
+     * Time spans in the past are formatted like "42 minutes ago". Time spans in
+     * the future are formatted like "in 42 minutes".
+     * <p>
+     * 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]".
+     * <p>
+     * Example output strings for the US date format.
+     * <ul>
+     * <li>3 mins ago, 10:15 AM</li>
+     * <li>yesterday, 12:20 PM</li>
+     * <li>Dec 12, 4:12 AM</li>
+     * <li>11/14/2007, 8:20 AM</li>
+     * </ul>
+     * 
+     * @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;
index 652413e..7457439 100644 (file)
@@ -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);
index f0305d9..d5a473b 100644 (file)
@@ -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)<<LOCKED_SHIFT)) != 0) {
+            state &= ~mask;
+        }
+        return state;
+    }
+
+    // ---------------------------------------------------------------------
+    // Version of API that operates on a state bit mask
+    // ---------------------------------------------------------------------
+
+    /**
+     * Gets the state of the meta keys.
+     * 
+     * @param state the current meta state bits.
+     *
+     * @return an integer in which each bit set to one represents a pressed
+     *         or locked meta key.
+     */
+    public static final int getMetaState(long state) {
+        return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) |
+               getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) |
+               getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED);
+    }
+
+    /**
+     * Gets the state of a particular meta key.
+     *
+     * @param state the current state bits.
+     * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON
+     *
+     * @return 0 if inactive, 1 if active, 2 if locked.
+     */
+    public static final int getMetaState(long state, int meta) {
+        switch (meta) {
+            case META_SHIFT_ON:
+                return getActive(state, meta, 1, 2);
+
+            case META_ALT_ON:
+                return getActive(state, meta, 1, 2);
+
+            case META_SYM_ON:
+                return getActive(state, meta, 1, 2);
+
+            default:
+                return 0;
+        }
+    }
+
+    private static int getActive(long state, int meta, int on, int lock) {
+        if ((state&(meta<<LOCKED_SHIFT)) != 0) {
+            return lock;
+        } else if ((state&meta) != 0) {
+            return on;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Call this method after you handle a keypress so that the meta
+     * state will be reset to unshifted (if it is not still down)
+     * or primed to be reset to unshifted (once it is released).  Takes
+     * the current state, returns the new state.
+     */
+    public static long adjustMetaAfterKeypress(long state) {
+        state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK);
+        state = adjust(state, META_ALT_ON, META_ALT_MASK);
+        state = adjust(state, META_SYM_ON, META_SYM_MASK);
+        return state;
+    }
+
+    private static long adjust(long state, int what, long mask) {
+        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
+            return (state&~mask) | what | ((long)what)<<USED_SHIFT;
+        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
+            return state & ~mask;
+        return state;
+    }
+
+    /**
+     * Handles presses of the meta keys.
+     */
+    public static long handleKeyDown(long state, int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
+            return press(state, META_SHIFT_ON, META_SHIFT_MASK);
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
+                || keyCode == KeyEvent.KEYCODE_NUM) {
+            return press(state, META_ALT_ON, META_ALT_MASK);
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_SYM) {
+            return press(state, META_SYM_ON, META_SYM_MASK);
+        }
+
+        return state;
+    }
+
+    private static long press(long state, int what, long mask) {
+        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
+            ; // repeat before use
+        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
+            state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT);
+        else if ((state&(((long)what)<<USED_SHIFT)) != 0)
+            ; // repeat after use
+        else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0)
+            state = state&~mask;
+        else
+            state = state | what | (((long)what)<<PRESSED_SHIFT);
+        return state;
+    }
+
+    /**
+     * Handles release of the meta keys.
+     */
+    public static long handleKeyUp(long state, int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
+            return release(state, META_SHIFT_ON, META_SHIFT_MASK);
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
+                || keyCode == KeyEvent.KEYCODE_NUM) {
+            return release(state, META_ALT_ON, META_ALT_MASK);
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_SYM) {
+            return release(state, META_SYM_ON, META_SYM_MASK);
+        }
+
+        return state;
+    }
+
+    private static long release(long state, int what, long mask) {
+        if ((state&(((long)what)<<USED_SHIFT)) != 0)
+            state = state&~mask;
+        else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
+            state = state | what | (((long)what)<<RELEASED_SHIFT);
+        return state;
+    }
+
+    public long clearMetaKeyState(long state, int which) {
+        if ((which&META_SHIFT_ON) != 0)
+            state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
+        if ((which&META_ALT_ON) != 0)
+            state = resetLock(state, META_ALT_ON, META_ALT_MASK);
+        if ((which&META_SYM_ON) != 0)
+            state = resetLock(state, META_SYM_ON, META_SYM_MASK);
+        return state;
     }
     
     /**
index be6ef77..27eda69 100644 (file)
@@ -18,7 +18,7 @@ package android.text.style;
 
 import android.text.TextPaint;
 
-public class BackgroundColorSpan extends CharacterStyle {
+public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance {
 
     private int mColor;
 
index 7620d29..14dfddd 100644 (file)
 
 package android.text.style;
 
-import android.graphics.Paint;
 import android.text.TextPaint;
 
 /**
  * The classes that affect character-level text formatting extend this
- * class.  Most also extend {@link MetricAffectingSpan}.
+ * class.  Most extend its subclass {@link MetricAffectingSpan}, but simple
+ * ones may just implement {@link UpdateAppearance}.
  */
 public abstract class CharacterStyle {
        public abstract void updateDrawState(TextPaint tp);
index a232ed7..989ef54 100644 (file)
@@ -25,7 +25,7 @@ import android.view.View;
  * text can be selected.  If clicked, the {@link #onClick} method will
  * be called.
  */
-public abstract class ClickableSpan extends CharacterStyle {
+public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
 
     /**
      * Performs the click action associated with this span.
index dd89b68..89dc45b 100644 (file)
 package android.text.style;
 
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.Paint.Style;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
 
 import java.lang.ref.WeakReference;
 
 /**
  *
  */
-public abstract class DynamicDrawableSpan
-extends ReplacementSpan
-{
+public abstract class DynamicDrawableSpan extends ReplacementSpan {
+    private static final String TAG = "DynamicDrawableSpan";
+    
+    /**
+     * A constant indicating that the bottom of this span should be aligned
+     * with the bottom of the surrounding text, i.e., at the same level as the
+     * lowest descender in the text.
+     */
+    public static final int ALIGN_BOTTOM = 0;
+    
+    /**
+     * A constant indicating that the bottom of this span should be aligned
+     * with the baseline of the surrounding text.
+     */
+    public static final int ALIGN_BASELINE = 1;
+    
+    protected final int mVerticalAlignment;
+    
+    public DynamicDrawableSpan() {
+        mVerticalAlignment = ALIGN_BOTTOM;
+    }
+
+    /**
+     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+     */
+    protected DynamicDrawableSpan(int verticalAlignment) {
+        mVerticalAlignment = verticalAlignment;
+    }
+
+    /**
+     * Returns the vertical alignment of this span, one of {@link #ALIGN_BOTTOM} or
+     * {@link #ALIGN_BASELINE}.
+     */
+    public int getVerticalAlignment() {
+        return mVerticalAlignment;
+    }
+
     /**
      * Your subclass must implement this method to provide the bitmap   
      * to be drawn.  The dimensions of the bitmap must be the same
@@ -61,7 +98,12 @@ extends ReplacementSpan
         Drawable b = getCachedDrawable();
         canvas.save();
         
-        canvas.translate(x, bottom - b.getBounds().bottom);
+        int transY = bottom - b.getBounds().bottom;
+        if (mVerticalAlignment == ALIGN_BASELINE) {
+            transY -= paint.getFontMetricsInt().descent;
+        }
+
+        canvas.translate(x, transY);
         b.draw(canvas);
         canvas.restore();
     }
index 5cccd9c..99b3381 100644 (file)
 
 package android.text.style;
 
-import android.graphics.Paint;
 import android.text.TextPaint;
 
-public class ForegroundColorSpan extends CharacterStyle {
+public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance {
 
     private int mColor;
 
index de067b1..2eebc0d 100644 (file)
@@ -32,29 +32,73 @@ public class ImageSpan extends DynamicDrawableSpan {
     private int mResourceId;
     private Context mContext;
     private String mSource;
-    
 
     public ImageSpan(Bitmap b) {
+        this(b, ALIGN_BOTTOM);
+    }
+
+    /**
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     */
+    public ImageSpan(Bitmap b, int verticalAlignment) {
+        super(verticalAlignment);
         mDrawable = new BitmapDrawable(b);
         mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(),
                 mDrawable.getIntrinsicHeight());
     }
 
     public ImageSpan(Drawable d) {
+        this(d, ALIGN_BOTTOM);
+    }
+
+    /**
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     */
+    public ImageSpan(Drawable d, int verticalAlignment) {
+        super(verticalAlignment);
         mDrawable = d;
     }
 
     public ImageSpan(Drawable d, String source) {
+        this(d, source, ALIGN_BOTTOM);
+    }
+
+    /**
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     */
+    public ImageSpan(Drawable d, String source, int verticalAlignment) {
+        super(verticalAlignment);
         mDrawable = d;
         mSource = source;
     }
 
     public ImageSpan(Context context, Uri uri) {
+        this(context, uri, ALIGN_BOTTOM);
+    }
+
+    /**
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     */
+    public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+        super(verticalAlignment);
         mContext = context;
         mContentUri = uri;
     }
 
     public ImageSpan(Context context, int resourceId) {
+        this(context, resourceId, ALIGN_BOTTOM);
+    }
+
+    /**
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     */
+    public ImageSpan(Context context, int resourceId, int verticalAlignment) {
+        super(verticalAlignment);
         mContext = context;
         mResourceId = resourceId;
     }
index 781bcec..64ab0d8 100644 (file)
 
 package android.text.style;
 
-import android.graphics.Paint;
 import android.graphics.MaskFilter;
 import android.text.TextPaint;
 
-public class MaskFilterSpan extends CharacterStyle {
+public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
 
        private MaskFilter mFilter;
 
index 193c700..75b5bcc 100644 (file)
 
 package android.text.style;
 
-import android.graphics.Paint;
 import android.graphics.Rasterizer;
 import android.text.TextPaint;
 
-public class RasterizerSpan extends CharacterStyle {
+public class RasterizerSpan extends CharacterStyle implements UpdateAppearance {
 
        private Rasterizer mRasterizer;
 
index 01ae38c..dd430e5 100644 (file)
@@ -18,7 +18,7 @@ package android.text.style;
 
 import android.text.TextPaint;
 
-public class StrikethroughSpan extends CharacterStyle {
+public class StrikethroughSpan extends CharacterStyle implements UpdateAppearance {
 
        @Override
        public void updateDrawState(TextPaint ds) {
index 5dce0f2..ca6f10c 100644 (file)
@@ -18,7 +18,7 @@ package android.text.style;
 
 import android.text.TextPaint;
 
-public class UnderlineSpan extends CharacterStyle {
+public class UnderlineSpan extends CharacterStyle implements UpdateAppearance {
 
        @Override
        public void updateDrawState(TextPaint ds) {
diff --git a/core/java/android/text/style/UpdateAppearance.java b/core/java/android/text/style/UpdateAppearance.java
new file mode 100644 (file)
index 0000000..198e4fa
--- /dev/null
@@ -0,0 +1,10 @@
+package android.text.style;
+
+/**
+ * The classes that affect character-level text in a way that modifies their
+ * appearance when one is added or removed must implement this interface.  Note
+ * that if the class also impacts size or other metrics, it should instead
+ * implement {@link UpdateLayout}.
+ */
+public interface UpdateAppearance {
+}
index 211685a..591075e 100644 (file)
@@ -18,7 +18,8 @@ package android.text.style;
 
 /**
  * The classes that affect character-level text formatting in a way that
- * triggers a text layout update when one is added or remove must implement
- * this interface.
+ * triggers a text layout update when one is added or removed must implement
+ * this interface.  This interface also includes {@link UpdateAppearance}
+ * since such a change implicitly also impacts the appearance.
  */
-public interface UpdateLayout { }
+public interface UpdateLayout extends UpdateAppearance { }
diff --git a/core/java/android/util/PrintStreamPrinter.java b/core/java/android/util/PrintStreamPrinter.java
new file mode 100644 (file)
index 0000000..1c11f15
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.util;
+
+import java.io.PrintStream;
+
+/**
+ * Implementation of a {@link android.util.Printer} that sends its output
+ * to a {@link java.io.PrintStream}.
+ */
+public class PrintStreamPrinter implements Printer {
+    private final PrintStream mPS;
+    
+    /**
+     * Create a new Printer that sends to a PrintWriter object.
+     * 
+     * @param pw The PrintWriter where you would like output to go.
+     */
+    public PrintStreamPrinter(PrintStream pw) {
+        mPS = pw;
+    }
+    
+    public void println(String x) {
+        mPS.println(x);
+    }
+}
index ca2140b..97825e6 100644 (file)
@@ -385,14 +385,11 @@ public interface Menu {
      * @return The menu item.
      * @exception IndexOutOfBoundsException
      *                when {@code index < 0 || >= size()}
-     * @hide pending API council
      */
     public MenuItem getItem(int index);
     
     /**
      * Closes the menu, if open.
-     * 
-     * @hide pending API council
      */
     public void close();
     
index 0d9e221..e928998 100644 (file)
@@ -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<SurfaceView> mSurfaceView;
+
+        public MyWindow(SurfaceView surfaceView) {
+            mSurfaceView = new WeakReference<SurfaceView>(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) {
index f948b33..1cc7b60 100644 (file)
@@ -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);
index db0b368..a254edb 100644 (file)
@@ -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<Handler> sUiThreads = new ThreadLocal<Handler>();
-    static final RunQueue sRunQueue = new RunQueue();
+    static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
 
     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
      */
index 9f3650b..d1d549c 100644 (file)
@@ -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);
 
         /**
-         * <p>Notifies the end of the animation. This callback is invoked
-         * only for animation with repeat mode set to NO_REPEAT.</p>
+         * <p>Notifies the end of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
          *
          * @param animation The animation which reached its end.
          */
         void onAnimationEnd(Animation animation);
 
         /**
-         * <p>Notifies the repetition of the animation. This callback is invoked
-         * only for animation with repeat mode set to RESTART or REVERSE.</p>
+         * <p>Notifies the repetition of the animation.</p>
          *
          * @param animation The animation which was repeated.
          */
index c358047..87c99bb 100755 (executable)
     that you can use to create simple animations: <strong>tweened
         animation</strong>, 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 <strong>frame
-        by frame animation</strong>, which loads a series of Drawable resources
+    View; and <strong>frame-by-frame animation</strong>, 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.</p>
-<h2>Tweened Animation<a name="tweened"></a></h2>
-<p> 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:</p>
-<ul>
-    <li>    {@link android.view.animation.AlphaAnimation AlphaAnimation} (transparency
-        changes) </li>
-    <li>{@link android.view.animation.RotateAnimation        RotateAnimation} (rotations) </li>
-    <li>        {@link android.view.animation.ScaleAnimation ScaleAnimation} (growing
-        or shrinking) </li>
-    <li>{@link android.view.animation.TranslateAnimation        TranslateAnimation}
-        (position changes) </li>
-</ul>
-<p><em>Note: tweened animation does not provide tools to help you draw shapes.</em> 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. </p>
-<p>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).</p>
-<h3>Step 1: Define your animation </h3>
-<p>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&mdash;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. </p>
-<p>Screen coordinates are (0,0) at the upper left hand corner, and increase as you
-    go down and to the right. </p>
-<p>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 (&quot;50&quot;
-    for 50% relative to the parent, &quot;50%&quot; for 50% relative to itself).</p>
-<p>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. </p>
-<p>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). </p>
-<p>    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.</p>
-<table width="100%" border="1">
-    <tr>
-        <th scope="col">XML</th>
-        <th scope="col">Equivalent Java </th>
-    </tr>
-    <tr>
-        <td><pre>&lt;set android:shareInterpolator=&quot;true&quot; 
-     android:interpolator=&quot;@android:anim/accelerate_interpolator&quot;&gt;
+    </p>
 
-    &lt;translate android:fromXDelta=&quot;0&quot;
-               android:toXDelta=&quot;30&quot;
-               android:duration=&quot;800&quot;
-               android:fillAfter=&quot;true&quot;/&gt;
-    
-    &lt;set android:duration="800" 
-         android:pivotX=&quot;50%&quot;
-         android:pivotY=&quot;50%&quot; &gt;
+<p>For more information on creating tweened or frame-by-frame animations, read the discussion in the
+<a href="{@docRoot}guide/topics/graphics/2d-graphics.html#tween-animation">2D Graphics</a>
+Dev Guide.</p>
 
-        &lt;rotate android:fromDegrees=&quot;0&quot;
-                android:toDegrees=&quot;-90&quot; 
-                android:fillAfter=&quot;true&quot;
-                android:startOffset=&quot;800&quot;/&gt;
-    
-        &lt;scale android:fromXScale=&quot;1.0&quot;
-                android:toXScale=&quot;2.0&quot;
-                android:fromYScale=&quot;1.0&quot;
-                android:toYScale=&quot;2.0&quot;
-                android:startOffset=&quot;800&quot; /&gt;
-    &lt;/set&gt;
-
-    &lt;translate android:toYDelta=&quot;-100&quot;
-               android:fillAfter=&quot;true&quot;
-               android:duration=&quot;800&quot;
-               android:startOffset=&quot;1600&quot;/&gt;
-&lt;/set&gt;</pre></td>
-        <td><pre>// 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);</pre></td>
-    </tr>
-</table>
-<p>&nbsp;</p>
-<p>The following diagram shows the animation drawn from this code: </p>
-<p><img src="{@docRoot}images/tweening_example.png" alt="A tweened animation: move right, turn and grow, move up." ></p>
-<p>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. </p>
-<p>If you define your animation in XML, save it in the res/anim/ folder as described
-    in <a href="{@docRoot}reference/available-resources.html#tweenedanimation">Resources</a>. That topic
-    also describes the XML tags and attributes you can use to specify transformations. </p>
-<p>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.</p>
-<table width="100%" border="1">
-    <tr>
-        <th scope="col">Property</th>
-        <th scope="col">XML Attribute</th>
-        <th scope="col">Java Method / </th>
-        <th scope="col">Description</th>
-    </tr>
-    <tr>
-        <td>Start time </td>
-        <td><code>android:startOffset</code></td>
-        <td><code>Animation.setStartOffset()</code> (or <code>setStartTime()</code> for absolute time)</td>
-        <td>The start time (in milliseconds) of a transformation, where 0 is the
-        start time of the root animation set. </td>
-    </tr>
-    <tr>
-        <td>Duration</td>
-        <td><code>android:duration</code></td>
-        <td><code>Animation.setDuration()</code></td>
-        <td>The duration (in milliseconds) of a transformation. </td>
-    </tr>
-    <tr>
-        <td>Fill before </td>
-        <td><code>android:fillBefore</code></td>
-        <td><code>Animation.setFillBefore()</code></td>
-        <td>True if you want this transformation to be applied at time zero, regardless
-        of your start time value (you will probably never need this). </td>
-    </tr>
-    <tr>
-        <td>Fill after </td>
-        <td><code>android:fillAfter</code></td>
-        <td><code>Animation.SetFillAfter()</code></td>
-        <td>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 &quot;L&quot; 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. </td>
-    </tr>
-    <tr>
-        <td>Interpolator</td>
-        <td><code>android:interpolator</code></td>
-        <td><code>Animation.SetInterpolator()</code></td>
-        <td>Which interpolator to use. </td>
-    </tr>
-    <tr>
-        <td>Repeat mode </td>
-        <td>Cannot be set in XML </td>
-        <td><code>Animation.SetRepeatMode()</code></td>
-        <td>Whether and how the animation should repeat. </td>
-    </tr>
-</table>
-<p>&nbsp; </p>
-<h3>Step 2: Load and start your animation </h3>
-<ol>
-    <li>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()}. </li>
-    <li>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()}.</li>
-</ol>
-<p>The following code demonstrates loading and starting an animation. </p>
-<pre>// 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);</pre>
 </body>
 </html>
index 4416ee5..a6ce293 100644 (file)
@@ -57,8 +57,9 @@ public abstract class BaseInputConnection implements InputConnection {
                     h = mIMM.mServedView.getHandler();
                 }
             }
-            if (h != null && mTargetView != null) {
-                h.post(new DispatchKey(event, mTargetView.getRootView()));
+            if (h != null) {
+                h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+                        event));
             }
         }
         return false;
@@ -81,18 +82,4 @@ public abstract class BaseInputConnection implements InputConnection {
         mIMM.updateStatusIcon(resId, packageName);
         return true;
     }
-    
-    static class DispatchKey implements Runnable {
-        KeyEvent mEvent;
-        View mView;
-        
-        DispatchKey(KeyEvent event, View v) {
-            mEvent = event;
-            mView = v;
-        }
-        
-        public void run() {
-            mView.dispatchKeyEvent(mEvent);
-        }
-    }
-}
\ No newline at end of file
+}
index da5cab5..e92cbad 100644 (file)
@@ -67,7 +67,7 @@ public class DefaultInputMethod implements InputMethod, InputMethodSession {
     }
     
     public void updateSelection(int oldSelStart, int oldSelEnd,
-            int newSelStart, int newSelEnd) {
+            int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
     }
 
     public void updateCursor(Rect newCursor) {
@@ -119,9 +119,9 @@ class SimpleInputMethod extends IInputMethod.Stub {
         }
         
         public void updateSelection(int oldSelStart, int oldSelEnd,
-                int newSelStart, int newSelEnd) {
+                int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
             mSession.updateSelection(oldSelStart, oldSelEnd,
-                    newSelStart, newSelEnd);
+                    newSelStart, newSelEnd, candidatesStart, candidatesEnd);
         }
 
         public void updateCursor(Rect newCursor) {
index 5f8ba1f..27461ff 100644 (file)
@@ -26,6 +26,9 @@ import android.view.KeyEvent;
  * {@link InputMethod} back to the application that is receiving its input. It
  * is used to perform such things as reading text around the cursor,
  * committing text to the text box, and sending raw key events to the application.
+ * 
+ * <p>Implementations of this interface should generally be done by
+ * subclassing {@link BaseInputConnection}.
  */
 public interface InputConnection {
     /**
@@ -137,6 +140,14 @@ public interface InputConnection {
     public boolean setComposingText(CharSequence text, int newCursorPosition);
 
     /**
+     * Have the text editor finish whatever composing text is currently
+     * active.  This simple leaves the text as-is, removing any special
+     * composing styling or other state that was around it.  The cursor
+     * position remains unchanged.
+     */
+    public boolean finishComposingText();
+    
+    /**
      * Commit text to the text box and set the new cursor position.
      * Any composing text set previously will be removed
      * automatically.
index f150ad6..a41955c 100644 (file)
@@ -65,6 +65,10 @@ public class InputConnectionWrapper implements InputConnection {
         return mBase.setComposingText(text, newCursorPosition);
     }
 
+    public boolean finishComposingText() {
+        return mBase.finishComposingText();
+    }
+    
     public boolean commitText(CharSequence text, int newCursorPosition) {
         return mBase.commitText(text, newCursorPosition);
     }
index 259e759..ad61f94 100644 (file)
 
 package android.view.inputmethod;
 
-import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
-import android.os.Bundle;
 import android.os.IBinder;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 
 /**
  * The InputMethod interface represents an input method which can generate key
  * events and text, such as digital, email addresses, CJK characters, other
  * language characters, and etc., while handling various input events, and send
- * the text back to the application that requests text input.
- * 
+ * the text back to the application that requests text input.  See
+ * {@link InputMethodManager} for more general information about the
+ * architecture.
+ *
  * <p>Applications will not normally use this interface themselves, instead
  * relying on the standard interaction provided by
  * {@link android.widget.TextView} and {@link android.widget.EditText}.
@@ -42,6 +40,14 @@ import android.view.MotionEvent;
  * {@link android.Manifest.permission#BIND_INPUT_METHOD} in order to interact
  * with the service; if this is not required, the system will not use that
  * input method, because it can not trust that it is not compromised.
+ * 
+ * <p>The InputMethod interface is actually split into two parts: the interface
+ * here is the top-level interface to the input method, providing all
+ * access to it, which only the system can access (due to the BIND_INPUT_METHOD
+ * permission requirement).  In addition its method
+ * {@link #createSession(android.view.inputmethod.InputMethod.SessionCallback)}
+ * can be called to instantate a secondary {@link InputMethodSession} interface
+ * which is what clients use to communicate with the input method.
  */
 public interface InputMethod {
     /**
index da82593..a9c46c3 100644 (file)
@@ -17,8 +17,6 @@
 package android.view.inputmethod;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -31,6 +29,7 @@ import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewRoot;
 
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
@@ -43,9 +42,142 @@ import com.android.internal.view.InputBindResult;
 import java.util.List;
 
 /**
- * Public interface to the global input method manager.  You can retrieve
- * an instance of this interface with
+ * Central system API to the overall input method framework (IMF) architecture,
+ * which arbitrates interaction between applications and the current input method.
+ * You can retrieve an instance of this interface with
  * {@link Context#getSystemService(String) Context.getSystemService()}.
+ * 
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#ArchitectureOverview">Architecture Overview</a>
+ * </ol>
+ * 
+ * <a name="ArchitectureOverview"></a>
+ * <h3>Architecture Overview</h3>
+ * 
+ * <p>There are three primary parties involved in the input method
+ * framework (IMF) architecture:</p>
+ * 
+ * <ul>
+ * <li> The <strong>input method manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts.  It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> An <strong>input method (IME)</strong> implements a particular
+ * interaction model allowing the user to generate text.  The system binds
+ * to the current input method that is use, causing it to be created and run,
+ * and tells it when to hide and show its UI.  Only one IME is running at a time.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the input
+ * method manager for input focus and control over the state of the IME.  Only
+ * one such client is ever active (working with the IME) at a time.
+ * </ul>
+ * 
+ * 
+ * <a name="Applications"></a>
+ * <h3>Applications</h3>
+ * 
+ * <p>In most cases, applications that are using the standard
+ * {@link android.widget.TextView} or its subclasses will have little they need
+ * to do to work well with soft input methods.  The main things you need to
+ * be aware of are:</p>
+ * 
+ * <ul>
+ * <li> Properly set the {@link android.R.attr#inputType} if your editable
+ * text views, so that the input method will have enough context to help the
+ * user in entering text into them.
+ * <li> Deal well with losing screen space when the input method is
+ * displayed.  Ideally an application should handle its window being resized
+ * smaller, but it can rely on the system performing panning of the window
+ * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
+ * attribute on your activity or the corresponding values on windows you
+ * create to help the system determine whether to pan or resize (it will
+ * try to determine this automatically but may get it wrong).
+ * <li> You can also control the preferred soft input state (open, closed, etc)
+ * for your window using the same {@link android.R.attr#windowSoftInputMode}
+ * attribute.
+ * </ul>
+ * 
+ * <p>More finer-grained control is available through the APIs here to directly
+ * interact with the IMF and its IME -- either showing or hiding the input
+ * area, letting the user pick an input method, etc.</p>
+ * 
+ * <p>For the rare people amongst us writing their own text editors, you
+ * will need to implement {@link android.view.View#onCreateInputConnection}
+ * to return a new instance of your own {@link InputConnection} interface
+ * allowing the IME to interact with your editor.</p>
+ * 
+ * 
+ * <a name="InputMethods"></a>
+ * <h3>Input Methods</h3>
+ * 
+ * <p>An input method (IME) is implemented
+ * as a {@link android.app.Service}, typically deriving from
+ * {@link android.inputmethodservice.InputMethodService}.  It must provide
+ * the core {@link InputMethod} interface, though this is normally handled by
+ * {@link android.inputmethodservice.InputMethodService} and implementors will
+ * only need to deal with the higher-level API there.</p>
+ * 
+ * See the {@link android.inputmethodservice.InputMethodService} class for
+ * more information on implementing IMEs.
+ * 
+ * 
+ * <a name="Security"></a>
+ * <h3>Security</h3>
+ * 
+ * <p>There are a lot of security issues associated with input methods,
+ * since they essentially have freedom to completely drive the UI and monitor
+ * everything the user enters.  The Android input method framework also allows
+ * arbitrary third party IMEs, so care must be taken to restrict their
+ * selection and interactions.</p>
+ * 
+ * <p>Here are some key points about the security architecture behind the
+ * IMF:</p>
+ * 
+ * <ul>
+ * <li> <p>Only the system is allowed to directly access an IME's
+ * {@link InputMethod} interface, via the
+ * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
+ * enforced in the system by not binding to an input method service that does
+ * not require this permission, so the system can guarantee no other untrusted
+ * clients are accessing the current input method outside of its control.</p>
+ * 
+ * <li> <p>There may be many client processes of the IMF, but only one may
+ * be active at a time.  The inactive clients can not interact with key
+ * parts of the IMF through the mechanisms described below.</p>
+ * 
+ * <li> <p>Clients of an input method are only given access to its
+ * {@link InputMethodSession} interface.  One instance of this interface is
+ * created for each client, and only calls from the session associated with
+ * the active client will be processed by the current IME.  This is enforced
+ * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
+ * IMEs, but must be explicitly handled by an IME that is customizing the
+ * raw {@link InputMethodSession} implementation.</p>
+ * 
+ * <li> <p>Only the active client's {@link InputConnection} will accept
+ * operations.  The IMF tells each client process whether it is active, and
+ * the framework enforces that in inactive processes calls on to the current
+ * InputConnection will be ignored.  This ensures that the current IME can
+ * only deliver events and text edits to the UI that the user sees as
+ * being in focus.</p>
+ * 
+ * <li> <p>An IME can never interact with an {@link InputConnection} while
+ * the screen is off.  This is enforced by making all clients inactive while
+ * the screen is off, and prevents bad IMEs from driving the UI when the user
+ * can not be aware of its behavior.</p>
+ * 
+ * <li> <p>A client application can ask that the system let the user pick a
+ * new IME, but can not programmatically switch to one itself.  This avoids
+ * malicious applications from switching the user to their own IME, which
+ * remains running when the user navigates away to another application.  An
+ * IME, on the other hand, <em>is</em> allowed to programmatically switch
+ * the system to another IME, since it already has full control of user
+ * input.</p>
+ * 
+ * <li> <p>The user must explicitly enable a new IME in settings before
+ * they can switch to it, to confirm with the system that they know about it
+ * and want to make it available for use.</p>
+ * </ul>
  */
 public final class InputMethodManager {
     static final boolean DEBUG = false;
@@ -118,6 +250,8 @@ public final class InputMethodManager {
     Rect mCursorRect = new Rect();
     int mCursorSelStart;
     int mCursorSelEnd;
+    int mCursorCandStart;
+    int mCursorCandEnd;
 
     // -----------------------------------------------------------
     
@@ -203,6 +337,10 @@ public final class InputMethodManager {
             return false;
         }
 
+        public boolean finishComposingText() {
+            return false;
+        }
+
         public boolean showStatusIcon(String packageName, int resId) {
             return false;
         }
@@ -294,6 +432,9 @@ public final class InputMethodManager {
         public boolean setComposingText(CharSequence text, int newCursorPosition) {
             return false;
         }
+        public boolean finishComposingText() {
+            return false;
+        }
     };
     
     InputMethodManager(IInputMethodManager service, Looper looper) {
@@ -430,23 +571,45 @@ public final class InputMethodManager {
      * Disconnect any existing input connection, clearing the served view.
      */
     void finishInputLocked() {
-        synchronized (mH) {
-            if (mServedView != null) {
-                if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
-                updateStatusIcon(0, null);
-                
-                if (mCurrentTextBoxAttribute != null) {
-                    try {
-                        mService.finishInput(mClient);
-                    } catch (RemoteException e) {
-                    }
+        if (mServedView != null) {
+            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
+            updateStatusIcon(0, null);
+            
+            if (mCurrentTextBoxAttribute != null) {
+                try {
+                    mService.finishInput(mClient);
+                } catch (RemoteException e) {
                 }
-                
-                mServedView = null;
-                mCompletions = null;
-                mServedConnecting = false;
-                clearConnectionLocked();
             }
+            
+            if (mServedInputConnection != null) {
+                // We need to tell the previously served view that it is no
+                // longer the input target, so it can reset its state.  Schedule
+                // this call on its window's Handler so it will be on the correct
+                // thread and outside of our lock.
+                Handler vh = mServedView.getHandler();
+                if (vh != null) {
+                    // This will result in a call to reportFinishInputConnection()
+                    // below.
+                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
+                            mServedInputConnection));
+                }
+            }
+            
+            mServedView = null;
+            mCompletions = null;
+            mServedConnecting = false;
+            clearConnectionLocked();
+        }
+    }
+    
+    /**
+     * Called from the FINISH_INPUT_CONNECTION message above.
+     * @hide
+     */
+    public void reportFinishInputConnection(InputConnection ic) {
+        if (mServedInputConnection != ic) {
+            ic.finishComposingText();
         }
     }
     
@@ -584,7 +747,7 @@ public final class InputMethodManager {
         // do its stuff.
         // Life is good: let's hook everything up!
         EditorInfo tba = new EditorInfo();
-        InputConnection ic = view.createInputConnection(tba);
+        InputConnection ic = view.onCreateInputConnection(tba);
         if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
         
         synchronized (mH) {
@@ -609,6 +772,8 @@ public final class InputMethodManager {
             if (ic != null) {
                 mCursorSelStart = tba.initialSelStart;
                 mCursorSelEnd = tba.initialSelEnd;
+                mCursorCandStart = -1;
+                mCursorCandEnd = -1;
                 mCursorRect.setEmpty();
                 setCurrentInputConnection(ic);
             } else {
@@ -678,16 +843,24 @@ public final class InputMethodManager {
      * @hide
      */
     public void focusOut(View view) {
+        InputConnection ic = null;
         synchronized (mH) {
             if (DEBUG) Log.v(TAG, "focusOut: " + view
                     + " mServedView=" + mServedView
                     + " winFocus=" + view.hasWindowFocus());
-            if (mServedView == view && view.hasWindowFocus()) {
-                mLastServedView = view;
-                mH.removeMessages(MSG_CHECK_FOCUS);
-                mH.sendEmptyMessage(MSG_CHECK_FOCUS);
+            if (mServedView == view) {
+                ic = mServedInputConnection;
+                if (view.hasWindowFocus()) {
+                    mLastServedView = view;
+                    mH.removeMessages(MSG_CHECK_FOCUS);
+                    mH.sendEmptyMessage(MSG_CHECK_FOCUS);
+                }
             }
         }
+        
+        if (ic != null) {
+            ic.finishComposingText();
+        }
     }
 
     void checkFocus() {
@@ -733,22 +906,27 @@ public final class InputMethodManager {
     /**
      * Report the current selection range.
      */
-    public void updateSelection(View view, int selStart, int selEnd) {
+    public void updateSelection(View view, int selStart, int selEnd,
+            int candidatesStart, int candidatesEnd) {
         synchronized (mH) {
             if (mServedView != view || mCurrentTextBoxAttribute == null
                     || mCurMethod == null) {
                 return;
             }
             
-            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd) {
+            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
+                    || mCursorCandStart != candidatesStart
+                    || mCursorCandEnd != candidatesEnd) {
                 if (DEBUG) Log.d(TAG, "updateSelection");
 
                 try {
                     if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
                     mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
-                            selStart, selEnd);
+                            selStart, selEnd, candidatesStart, candidatesEnd);
                     mCursorSelStart = selStart;
                     mCursorSelEnd = selEnd;
+                    mCursorCandStart = candidatesStart;
+                    mCursorCandEnd = candidatesEnd;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId, e);
                 }
index 603da13..b5bbaff 100644 (file)
@@ -53,9 +53,14 @@ public interface InputMethodSession {
      * start position.
      * @param newSelEnd The new text offset of the cursor selection
      * end position.
+     * @param candidatesStart The text offset of the current candidate
+     * text start position.
+     * @param candidatesEnd The text offset of the current candidate
+     * text end position.
      */
     public void updateSelection(int oldSelStart, int oldSelEnd,
-            int newSelStart, int newSelEnd);
+            int newSelStart, int newSelEnd,
+            int candidatesStart, int candidatesEnd);
 
     /**
      * This method is called when cursor location of the target input field
index 348aba6..328c7b3 100644 (file)
@@ -1,7 +1,8 @@
 <html>
 <body>
 Framework classes for interaction between views and input methods (such
-as soft keyboards).  In most cases the main classes here are not needed for
+as soft keyboards).  See {@link android.view.inputmethod.InputMethodManager} for
+an overview.  In most cases the main classes here are not needed for
 most applications, since they are dealt with for you by
 {@link android.widget.TextView}.  When implementing a custom text editor,
 however, you will need to implement the
index 00b17d2..5a37f04 100644 (file)
@@ -172,7 +172,7 @@ public final class CookieManager {
             if (urlPath.startsWith(path)) {
                 int len = path.length();
                 int urlLen = urlPath.length();
-                if (urlLen > len) {
+                if (path.charAt(len-1) != PATH_DELIM && urlLen > len) {
                     // make sure /wee doesn't match /we
                     return urlPath.charAt(len) == PATH_DELIM;
                 }
@@ -440,29 +440,43 @@ public final class CookieManager {
     /**
      * Remove all session cookies, which are cookies without expiration date
      */
-    public synchronized void removeSessionCookie() {
-        Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
-        Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
-        while (listIter.hasNext()) {
-            ArrayList<Cookie> list = listIter.next();
-            Iterator<Cookie> iter = list.iterator();
-            while (iter.hasNext()) {
-                Cookie cookie = iter.next();
-                if (cookie.expires == -1) {
-                    iter.remove();
+    public void removeSessionCookie() {
+        final Runnable clearCache = new Runnable() {
+            public void run() {
+                synchronized(CookieManager.this) {
+                    Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
+                    Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
+                    while (listIter.hasNext()) {
+                        ArrayList<Cookie> list = listIter.next();
+                        Iterator<Cookie> iter = list.iterator();
+                        while (iter.hasNext()) {
+                            Cookie cookie = iter.next();
+                            if (cookie.expires == -1) {
+                                iter.remove();
+                            }
+                        }
+                    }
+                    CookieSyncManager.getInstance().clearSessionCookies();
                 }
             }
-        }
-        CookieSyncManager.getInstance().clearSessionCookies();
+        };
+        new Thread(clearCache).start();
     }
 
     /**
      * Remove all cookies
      */
-    public synchronized void removeAllCookie() {
-        mCookieMap = new LinkedHashMap<String, ArrayList<Cookie>>(
-                MAX_DOMAIN_COUNT, 0.75f, true);
-        CookieSyncManager.getInstance().clearAllCookies();
+    public void removeAllCookie() {
+        final Runnable clearCache = new Runnable() {
+            public void run() {
+                synchronized(CookieManager.this) {
+                    mCookieMap = new LinkedHashMap<String, ArrayList<Cookie>>(
+                            MAX_DOMAIN_COUNT, 0.75f, true);
+                    CookieSyncManager.getInstance().clearAllCookies();
+                }
+            }
+        };
+        new Thread(clearCache).start();
     }
 
     /**
@@ -475,23 +489,30 @@ public final class CookieManager {
     /**
      * Remove all expired cookies
      */
-    public synchronized void removeExpiredCookie() {
-        long now = System.currentTimeMillis();
-        Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
-        Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
-        while (listIter.hasNext()) {
-            ArrayList<Cookie> list = listIter.next();
-            Iterator<Cookie> iter = list.iterator();
-            while (iter.hasNext()) {
-                Cookie cookie = iter.next();
-                // expires == -1 means no expires defined. Otherwise negative
-                // means far future
-                if (cookie.expires > 0 && cookie.expires < now) {
-                    iter.remove();
+    public void removeExpiredCookie() {
+        final Runnable clearCache = new Runnable() {
+            public void run() {
+                synchronized(CookieManager.this) {
+                    long now = System.currentTimeMillis();
+                    Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
+                    Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
+                    while (listIter.hasNext()) {
+                        ArrayList<Cookie> list = listIter.next();
+                        Iterator<Cookie> iter = list.iterator();
+                        while (iter.hasNext()) {
+                            Cookie cookie = iter.next();
+                            // expires == -1 means no expires defined. Otherwise 
+                            // negative means far future
+                            if (cookie.expires > 0 && cookie.expires < now) {
+                                iter.remove();
+                            }
+                        }
+                    }
+                    CookieSyncManager.getInstance().clearExpiredCookies(now);
                 }
             }
-        }
-        CookieSyncManager.getInstance().clearExpiredCookies(now);
+        };
+        new Thread(clearCache).start();
     }
 
     /**
index 4e9370c..30b519a 100644 (file)
@@ -37,6 +37,7 @@ import android.text.method.MetaKeyKeyListener;
 import android.text.method.MovementMethod;
 import android.text.method.PasswordTransformationMethod;
 import android.text.method.TextKeyListener;
+import android.view.inputmethod.EditorInfo;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -265,13 +266,20 @@ import android.widget.AutoCompleteTextView;
         if (mGotEnterDown && !down) {
             return true;
         }
-        // WebView check the trackballtime in onKeyDown to avoid calling native
-        // from both trackball and key handling. As this is called from 
-        // TextDialog, we always want WebView to check with native. Reset
-        // trackballtime to ensure it.
-        mWebView.resetTrackballTime();
-        return down ? mWebView.onKeyDown(keyCode, event) : 
-                mWebView.onKeyUp(keyCode, event);
+        // if it is a navigation key, pass it to WebView
+        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
+                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+                || keyCode == KeyEvent.KEYCODE_DPAD_UP
+                || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+            // WebView check the trackballtime in onKeyDown to avoid calling
+            // native from both trackball and key handling. As this is called 
+            // from TextDialog, we always want WebView to check with native. 
+            // Reset trackballtime to ensure it.
+            mWebView.resetTrackballTime();
+            return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                    .onKeyUp(keyCode, event);
+        }
+        return false;
     }
     
     /**
@@ -315,31 +323,30 @@ import android.widget.AutoCompleteTextView;
             updateCachedTextfield();
             return;
         }
-        // In this case, replace before with all but the last character of the 
-        // new text.
-        if (count > 1) {
-            String replace = s.subSequence(start, start + count - 1).toString();
+        // Find the last character being replaced.  If it can be represented by
+        // events, we will pass them to native (after replacing the beginning
+        // of the changed text), so we can see javascript events.
+        // Otherwise, replace the text being changed (including the last
+        // character) in the textfield.
+        TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
+        KeyCharacterMap kmap =
+                KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+        KeyEvent[] events = kmap.getEvents(mCharacter);
+        boolean cannotUseKeyEvents = null == events;
+        int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
+        if (count > 1 || cannotUseKeyEvents) {
+            String replace = s.subSequence(start,
+                    start + count - charactersFromKeyEvents).toString();
             mWebView.replaceTextfieldText(start, start + before, replace,
-                    start + count - 1, start + count - 1);
+                    start + count - charactersFromKeyEvents,
+                    start + count - charactersFromKeyEvents);
         } else {
             // This corrects the selection which may have been affected by the 
             // trackball or auto-correct.
             mWebView.setSelection(start, start + before);
         }
-        // Whether the text to be added is only one character, or we already
-        // added all but the last character, we now figure out the DOM events
-        // for the last character, and pass them down.
-        TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
-        // We only care about the events that translate directly into 
-        // characters. Should we be using KeyCharacterMap.BUILT_IN_KEYBOARD?
-        // The comment makes it sound like it may not be directly related to 
-        // the keys. However, KeyCharacterMap.ALPHA says it has "maybe some
-        // numbers."  Not sure if that will have the numbers we may need.
-        KeyCharacterMap kmap =
-                KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
-        KeyEvent[] events = kmap.getEvents(mCharacter);
         updateCachedTextfield();
-        if (null == events) {
+        if (cannotUseKeyEvents) {
             return;
         }
         int length = events.length;
@@ -433,6 +440,8 @@ import android.widget.AutoCompleteTextView;
             method = null;
         }
         setTransformationMethod(method);
+        setInputType(inPassword ? EditorInfo.TYPE_TEXT_VARIATION_PASSWORD :
+                EditorInfo.TYPE_CLASS_TEXT);
     }
 
     /* package */ void setMaxLength(int maxLength) {
index 7467b83..bd910b5 100644 (file)
@@ -2841,11 +2841,6 @@ public class WebView extends AbsoluteLayout
     // a center key.  Does not affect long press with the trackball/touch.
     private boolean mGotEnterDown = false;
 
-    // Enable copy/paste with trackball here.
-    // This should be left disabled until the framework can guarantee
-    // delivering matching key-up and key-down events for the shift key
-    private static final boolean ENABLE_COPY_PASTE = true;
-
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (LOGV_ENABLED) {
@@ -2876,7 +2871,7 @@ public class WebView extends AbsoluteLayout
             return false;
         }
 
-        if (ENABLE_COPY_PASTE && mShiftIsPressed == false 
+        if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
                 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
             mExtendSelection = false;
@@ -2911,10 +2906,6 @@ public class WebView extends AbsoluteLayout
                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
                         .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
                 nativeRecordButtons(true, true);
-                // FIXME, currently in webcore keydown it doesn't do anything.
-                // In keyup, it calls both keydown and keyup, we should fix it.
-                mWebViewCore.sendMessage(EventHub.KEY_DOWN, keyCode,
-                        EventHub.KEYEVENT_UNHANDLED_TYPE, event);
                 return true;
             }
             // Bubble up the key event as WebView doesn't handle it
@@ -2949,15 +2940,10 @@ public class WebView extends AbsoluteLayout
             }
         }
 
-        if (nativeFocusNodeWantsKeyEvents()) {
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, keyCode,
-                                 EventHub.KEYEVENT_FOCUS_NODE_TYPE, event);
-            // return true as DOM handles the key
-            return true;
-        } else if (false) { // reserved to check the meta tag
+        // TODO: should we pass all the keys to DOM or check the meta tag
+        if (nativeFocusNodeWantsKeyEvents() || true) {
             // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, keyCode,
-                                 EventHub.KEYEVENT_UNHANDLED_TYPE, event);
+            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
             // return true as DOM handles the key
             return true;
         }
@@ -3008,8 +2994,8 @@ public class WebView extends AbsoluteLayout
             return false;
         }
 
-        if (ENABLE_COPY_PASTE && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
-                || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
+        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
+                || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (commitCopy()) {
                 return true;
             }
@@ -3050,25 +3036,18 @@ public class WebView extends AbsoluteLayout
             Rect visibleRect = sendOurVisibleRect();
             // Note that sendOurVisibleRect calls viewToContent, so the
             // coordinates should be in content coordinates.
-            boolean nodeOnScreen = false;
-            boolean isTextField = false;
-            boolean isTextArea = false;
-            FocusNode node = null;
             if (nativeUpdateFocusNode()) {
-                node = mFocusNode;
-                isTextField = node.mIsTextField;
-                isTextArea = node.mIsTextArea;
-                nodeOnScreen = Rect.intersects(node.mBounds, visibleRect);
-            }
-            if (nodeOnScreen && !isTextField && !isTextArea) {
-                nativeSetFollowedLink(true);
-                mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
-                        EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
-                        new WebViewCore.FocusData(mFocusData));
-                playSoundEffect(SoundEffectConstants.CLICK);
-                if (!mCallbackProxy.uiOverrideUrlLoading(node.mText)) {
-                    mWebViewCore.sendMessage(EventHub.KEY_UP, keyCode,
-                            EventHub.KEYEVENT_UNHANDLED_TYPE, event);
+                if (Rect.intersects(mFocusNode.mBounds, visibleRect)) {
+                    nativeSetFollowedLink(true);
+                    mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
+                            EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
+                            new WebViewCore.FocusData(mFocusData));
+                    playSoundEffect(SoundEffectConstants.CLICK);
+                    if (!mCallbackProxy.uiOverrideUrlLoading(mFocusNode.mText)) {
+                        // use CLICK instead of KEY_DOWN/KEY_UP so that we can
+                        // trigger mouse click events
+                        mWebViewCore.sendMessage(EventHub.CLICK);
+                    }
                 }
                 return true;
             }
@@ -3076,15 +3055,10 @@ public class WebView extends AbsoluteLayout
             return false;
         }
 
-        if (nativeFocusNodeWantsKeyEvents()) {
-            mWebViewCore.sendMessage(EventHub.KEY_UP, keyCode,
-                    EventHub.KEYEVENT_FOCUS_NODE_TYPE, event);
-            // return true as DOM handles the key
-            return true;
-        } else if (false) { // reserved to check the meta tag
+        // TODO: should we pass all the keys to DOM or check the meta tag
+        if (nativeFocusNodeWantsKeyEvents() || true) {
             // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_UP, keyCode,
-                    EventHub.KEYEVENT_UNHANDLED_TYPE, event);
+            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
             // return true as DOM handles the key
             return true;
         }
@@ -3637,12 +3611,7 @@ public class WebView extends AbsoluteLayout
     private long mTrackballUpTime = 0;
     private long mLastFocusTime = 0;
     private Rect mLastFocusBounds;
-    
-    // Used to determine that the trackball is down AND that it has not
-    // been moved while down, whereas mTrackballDown is true until we
-    // receive an ACTION_UP
-    private boolean mTrackTrackball = false;
-    
+
     // Set by default; BrowserActivity clears to interpret trackball data
     // directly for movement. Currently, the framework only passes 
     // arrow key events, not trackball events, from one child to the next
@@ -3666,7 +3635,6 @@ public class WebView extends AbsoluteLayout
         }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
-            mTrackTrackball = true;
             mTrackballDown = true;
             if (mNativeClass != 0) {
                 nativeRecordButtons(true, true);
@@ -3681,12 +3649,10 @@ public class WebView extends AbsoluteLayout
                         + " mLastFocusTime=" + mLastFocusTime);
             }
             return false; // let common code in onKeyDown at it
-        } else if (mTrackTrackball) {
-            // LONG_PRESS_ENTER is set in common onKeyDown
-            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
-            mTrackTrackball = false;
         } 
         if (ev.getAction() == MotionEvent.ACTION_UP) {
+            // LONG_PRESS_ENTER is set in common onKeyDown
+            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
             mTrackballDown = false;
             mTrackballUpTime = time;
             if (mShiftIsPressed) {
@@ -4511,7 +4477,6 @@ public class WebView extends AbsoluteLayout
                     // as this is shared by keydown and trackballdown, reset all
                     // the states
                     mGotEnterDown = false;
-                    mTrackTrackball = false;
                     mTrackballDown = false;
                     // LONG_PRESS_ENTER is sent as a delayed message. If we
                     // switch to windows overview, the WebView will be
index 9e413f9..323b44d 100644 (file)
@@ -25,7 +25,6 @@ import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -300,14 +299,10 @@ final class WebViewCore {
      */
     private native void nativeSplitContent();
 
-    // these must be kept lock-step with the KeyState enum in WebViewCore.h
-    static private final int KEY_ACTION_DOWN = 0;
-    static private final int KEY_ACTION_UP = 1;
+    private native boolean nativeKey(int keyCode, int unichar,
+            int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
 
-    private native boolean nativeSendKeyToFocusNode(int keyCode, int unichar,
-                int repeatCount, boolean isShift, boolean isAlt, int keyAction);
-
-    private native boolean nativeKeyUp(int keycode, int keyvalue);
+    private native boolean nativeClick();
 
     private native void nativeSendListBoxChoices(boolean[] choices, int size);
 
@@ -527,6 +522,7 @@ final class WebViewCore {
         static final int PASS_TO_JS = 115;
         static final int SET_GLOBAL_BOUNDS = 116;
         static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
+        static final int CLICK = 118;
         static final int DOC_HAS_IMAGES = 120;
         static final int SET_SNAP_ANCHOR = 121;
         static final int DELETE_SELECTION = 122;
@@ -573,19 +569,6 @@ final class WebViewCore {
         static final int NO_FOCUS_CHANGE_BLOCK = 0;
         static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
 
-        /*  The KEY_DOWN and KEY_UP messages pass the keyCode in arg1, and a
-            "type" in arg2. These are the types, and they describe what the
-            circumstances were that prompted the UI thread to send the keyevent
-            to webkit.
-         
-            FOCUS_NODE - the currently focused node says it wants key events
-                         (e.g. plugins)
-            UNHANDLED - the UI side did not handle the key, so we give webkit
-                        a shot at it.
-         */
-        static final int KEYEVENT_FOCUS_NODE_TYPE = 0;
-        static final int KEYEVENT_UNHANDLED_TYPE = 1;
-
         // Private handler for WebCore messages.
         private Handler mHandler;
         // Message queue for containing messages before the WebCore thread is
@@ -680,11 +663,15 @@ final class WebViewCore {
                             break;
 
                         case KEY_DOWN:
-                            keyDown(msg.arg1, msg.arg2, (KeyEvent) msg.obj);
+                            key((KeyEvent) msg.obj, true);
                             break;
 
                         case KEY_UP:
-                            keyUp(msg.arg1, msg.arg2, (KeyEvent) msg.obj);
+                            key((KeyEvent) msg.obj, false);
+                            break;
+
+                        case CLICK:
+                            nativeClick();
                             break;
 
                         case VIEW_SIZE_CHANGED:
@@ -1130,49 +1117,19 @@ final class WebViewCore {
         mBrowserFrame.loadUrl(url);
     }
 
-    private void keyDown(int code, int target, KeyEvent event) {
+    private void key(KeyEvent evt, boolean isDown) {
         if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "CORE keyDown at " + System.currentTimeMillis()
-                    + ", " + event);
+            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+                    + evt);
         }
-        switch (target) {
-            case EventHub.KEYEVENT_UNHANDLED_TYPE:
-                break;
-            case EventHub.KEYEVENT_FOCUS_NODE_TYPE:
-                if (nativeSendKeyToFocusNode(code, event.getUnicodeChar(),
-                                             event.getRepeatCount(),
-                                             event.isShiftPressed(),
-                                             event.isAltPressed(),
-                                             KEY_ACTION_DOWN)) {
-                    return;
-                }
-                break;
+        if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
+                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
+                isDown)) {
+            // bubble up the event handling
+            mCallbackProxy.onUnhandledKeyEvent(evt);
         }
-        // If we get here, no one handled it, so call our proxy
-        mCallbackProxy.onUnhandledKeyEvent(event);
     }
 
-    private void keyUp(int code, int target, KeyEvent event) {
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "CORE keyUp at " + System.currentTimeMillis()
-                    + ", " + event);
-        }
-        switch (target) {
-            case EventHub.KEYEVENT_UNHANDLED_TYPE:
-                if (!nativeKeyUp(code, event.getUnicodeChar())) {
-                    mCallbackProxy.onUnhandledKeyEvent(event);
-                }
-                break;
-            case EventHub.KEYEVENT_FOCUS_NODE_TYPE:
-                nativeSendKeyToFocusNode(code, event.getUnicodeChar(),
-                                         event.getRepeatCount(),
-                                         event.isShiftPressed(),
-                                         event.isAltPressed(),
-                                         KEY_ACTION_UP);
-                break;
-            }
-        }
-
     // These values are used to avoid requesting a layout based on old values
     private int mCurrentViewWidth = 0;
     private int mCurrentViewHeight = 0;
index 1440522..c22023c 100644 (file)
@@ -896,13 +896,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
     public void setFilterText(String filterText) {
         if (mTextFilterEnabled && filterText != null && filterText.length() > 0) {
             createTextFilter(false);
-            // This is going to call our listener onTextChanged, but we are
-            // not ready to bring up a window yet
+            // This is going to call our listener onTextChanged, but we might not
+            // be ready to bring up a window yet
             mTextFilter.setText(filterText);
             mTextFilter.setSelection(filterText.length());
             if (mAdapter instanceof Filterable) {
-                Filter f = ((Filterable) mAdapter).getFilter();
-                f.filter(filterText);
+                // if mPopup is non-null, then onTextChanged will do the filtering
+                if (mPopup == null) {
+                    Filter f = ((Filterable) mAdapter).getFilter();
+                    f.filter(filterText);
+                }
                 // Set filtered to true so we will display the filter window when our main
                 // window is ready
                 mFiltered = true;
@@ -1361,7 +1364,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         Rect selectorRect = mSelectorRect;
         if (selector != null && (isFocused() || touchModeDrawsInPressedState())
                 && selectorRect != null && !selectorRect.isEmpty()) {
+
+            final View v = getChildAt(mSelectedPosition - mFirstPosition);
+
+            if (v != null) v.setPressed(true);
             setPressed(true);
+
             final boolean longClickable = isLongClickable();
             Drawable d = selector.getCurrent();
             if (d != null && d instanceof TransitionDrawable) {
@@ -1641,9 +1649,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         case KeyEvent.KEYCODE_ENTER:
             if (isPressed() && mSelectedPosition >= 0 && mAdapter != null &&
                     mSelectedPosition < mAdapter.getCount()) {
-                final int index = mSelectedPosition - mFirstPosition;
-                performItemClick(getChildAt(index), mSelectedPosition, mSelectedRowId);
+                final View view = getChildAt(mSelectedPosition - mFirstPosition);
+                performItemClick(view, mSelectedPosition, mSelectedRowId);
                 setPressed(false);
+                if (view != null) view.setPressed(false);
                 return true;
             }
         }
@@ -1763,7 +1772,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                 handler.removeCallbacks(mPendingCheckForLongPress);
             }
             setPressed(false);
-            View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+            View motionView = getChildAt(mMotionPosition - mFirstPosition);
             if (motionView != null) {
                 motionView.setPressed(false);
             }
index 65ca885..b046a6b 100644 (file)
@@ -22,6 +22,7 @@ import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 
 public abstract class AbsSeekBar extends ProgressBar {
@@ -54,7 +55,6 @@ public abstract class AbsSeekBar extends ProgressBar {
     public AbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.SeekBar, defStyle, 0);
         Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
@@ -114,10 +114,15 @@ public abstract class AbsSeekBar extends ProgressBar {
         if (progressDrawable != null) {
             progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
         }
+        
+        if (mThumb != null && mThumb.isStateful()) {
+            int[] state = getDrawableState();
+            mThumb.setState(state);
+        }
     }
     
     @Override
-    void onProgressRefresh(float scale, boolean fromTouch) { 
+    void onProgressRefresh(float scale, boolean fromUser) { 
         Drawable thumb = mThumb;
         if (thumb != null) {
             setThumbPos(getWidth(), getHeight(), thumb, scale, Integer.MIN_VALUE);
@@ -236,6 +241,7 @@ public abstract class AbsSeekBar extends ProgressBar {
         
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
+                setPressed(true);
                 onStartTrackingTouch();
                 trackTouchEvent(event);
                 break;
@@ -248,10 +254,12 @@ public abstract class AbsSeekBar extends ProgressBar {
             case MotionEvent.ACTION_UP:
                 trackTouchEvent(event);
                 onStopTrackingTouch();
+                setPressed(false);
                 break;
                 
             case MotionEvent.ACTION_CANCEL:
                 onStopTrackingTouch();
+                setPressed(false);
                 break;
         }
         return true;
@@ -306,4 +314,23 @@ public abstract class AbsSeekBar extends ProgressBar {
     void onStopTrackingTouch() {
     }
 
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        int progress = getProgress();
+        
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (progress <= 0) break;
+                setProgress(progress - 1, true);
+                return true;
+        
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (progress >= getMax()) break;
+                setProgress(progress + 1, true);
+                return true;
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
 }
index d8fa603..1591791 100644 (file)
@@ -336,7 +336,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (isPopupShowing()) {
+        if (isPopupShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
             boolean consumed = mDropDownList.onKeyUp(keyCode, event);
             if (consumed) {
                 switch (keyCode) {
@@ -359,13 +359,20 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
         if (isPopupShowing()) {
             // the key events are forwarded to the list in the drop down view
             // note that ListView handles space but we don't want that to happen
-            if (keyCode != KeyEvent.KEYCODE_SPACE) {
+            // also if selection is not currently in the drop down, then don't
+            // let center or enter presses go there since that would cause it
+            // to select one of its items
+            if (keyCode != KeyEvent.KEYCODE_SPACE
+                    && (mDropDownList.getSelectedItemPosition() >= 0
+                            || (keyCode != KeyEvent.KEYCODE_ENTER
+                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
                 int curIndex = mDropDownList.getSelectedItemPosition();
                 boolean consumed;
                 if (keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= 0) {
                     // When the selection is at the top, we block the key
                     // event to prevent focus from moving.
                     mDropDownList.hideSelector();
+                    mDropDownList.requestLayout();
                     mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
                     mPopup.update();
                     return true;
@@ -548,17 +555,21 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
     private void performCompletion(View selectedView, int position, long id) {
         if (isPopupShowing()) {
             Object selectedItem;
-            if (position == -1) {
+            if (position < 0) {
                 selectedItem = mDropDownList.getSelectedItem();
             } else {
                 selectedItem = mAdapter.getItem(position);
             }
+            if (selectedItem == null) {
+                Log.w(TAG, "performCompletion: no selected item");
+                return;
+            }
             replaceText(convertSelectionToString(selectedItem));
 
             if (mItemClickListener != null) {
                 final DropDownListView list = mDropDownList;
 
-                if (selectedView == null || position == -1) {
+                if (selectedView == null || position < 0) {
                     selectedView = list.getSelectedView();
                     position = list.getSelectedItemPosition();
                     id = list.getSelectedItemId();
@@ -665,8 +676,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
             mPopup.setOutsideTouchable(true);
             mPopup.setTouchInterceptor(new PopupTouchIntercepter());
             mPopup.showAsDropDown(this, mDropDownHorizontalOffset, mDropDownVerticalOffset);
+            mDropDownList.setSelection(ListView.INVALID_POSITION);
             mDropDownList.hideSelector();
-            mDropDownList.setSelection(0);
             mDropDownList.requestFocus();
             post(mHideSelector);
         }
@@ -828,6 +839,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
         public void run() {
             if (mDropDownList != null) {
                 mDropDownList.hideSelector();
+                mDropDownList.requestLayout();
             }
         }
     }
index d886155..cabca40 100644 (file)
@@ -56,7 +56,13 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
     private static final String TAG = "Gallery";
 
     private static final boolean localLOGV = Config.LOGV;
-    
+
+    /**
+     * Duration in milliseconds from the start of a scroll during which we're
+     * unsure whether the user is scrolling or flinging.
+     */
+    private static final int SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT = 250;
+
     /**
      * Horizontal spacing between items.
      */
@@ -104,6 +110,17 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
      * Executes the delta scrolls from a fling or scroll movement. 
      */
     private FlingRunnable mFlingRunnable = new FlingRunnable();
+
+    /**
+     * Sets mSuppressSelectionChanged = false. This is used to set it to false
+     * in the future. It will also trigger a selection changed.
+     */
+    private Runnable mDisableSuppressSelectionChangedRunnable = new Runnable() {
+        public void run() {
+            mSuppressSelectionChanged = false;
+            selectionChanged();
+        }
+    };
     
     /**
      * When fling runnable runs, it resets this to false. Any method along the
@@ -142,6 +159,12 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
     private boolean mReceivedInvokeKeyDown;
     
     private AdapterContextMenuInfo mContextMenuInfo;
+
+    /**
+     * If true, this onScroll is the first for this user's drag (remember, a
+     * drag sends many onScrolls).
+     */
+    private boolean mIsFirstScroll;
     
     public Gallery(Context context) {
         this(context, null);
@@ -861,8 +884,13 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
         
         if (!mShouldCallbackDuringFling) {
+            // We want to suppress selection changes
+            
+            // Remove any future code to set mSuppressSelectionChanged = false
+            removeCallbacks(mDisableSuppressSelectionChangedRunnable);
+
             // This will get reset once we scroll into slots
-            mSuppressSelectionChanged = true;
+            if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true;
         }
         
         // Fling the gallery!
@@ -891,13 +919,24 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
         
         // As the user scrolls, we want to callback selection changes so related-
         // info on the screen is up-to-date with the gallery's selection
-        if (mSuppressSelectionChanged) {
-            mSuppressSelectionChanged = false;
+        if (!mShouldCallbackDuringFling) {
+            if (mIsFirstScroll) {
+                /*
+                 * We're not notifying the client of selection changes during
+                 * the fling, and this scroll could possibly be a fling. Don't
+                 * do selection changes until we're sure it is not a fling.
+                 */
+                if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true;
+                postDelayed(mDisableSuppressSelectionChangedRunnable, SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT);
+            }
+        } else {
+            if (mSuppressSelectionChanged) mSuppressSelectionChanged = false;
         }
         
         // Track the motion
         trackMotionScroll(-1 * (int) distanceX);
-        
+       
+        mIsFirstScroll = false;
         return true;
     }
     
@@ -917,6 +956,9 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
             mDownTouchView.setPressed(true);
         }
         
+        // Reset the multiple-scroll tracking state
+        mIsFirstScroll = true;
+        
         // Must return true to get matching events for this down event.
         return true;
     }
index abba6d0..2e04b5d 100644 (file)
@@ -29,6 +29,7 @@ import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
 import android.util.AttributeSet;
@@ -174,7 +175,7 @@ public class ProgressBar extends View {
         
         Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
         if (drawable != null) {
-            drawable = tileify(drawable);
+            drawable = tileify(drawable, false);
             setProgressDrawable(drawable);
         }
 
@@ -217,18 +218,12 @@ public class ProgressBar extends View {
         a.recycle();
     }
 
-    /*
-     * TODO: This is almost ready to be removed. This was used to support our
-     * old style of progress bars with the ticks. Need to check with designers
-     * on whether they can give us a transparent 'tick' overlay tile for our new
-     * gradient-based progress bars. (We still need the ticked progress bar for
-     * media player apps.) I'll remove this and add XML support if they want to
-     * do the overlay approach. If they want to just have a separate style for
-     * this legacy stuff, then we can keep it around.
+    /**
+     * Converts a drawable to a tiled version of itself. It will recursively
+     * traverse layer and state list drawables.
      */
-    
-    // TODO Remove all this once ShapeDrawable + shaders are supported through XML
-    private Drawable tileify(Drawable drawable) {
+    private Drawable tileify(Drawable drawable, boolean clip) {
+        
         if (drawable instanceof LayerDrawable) {
             LayerDrawable background = (LayerDrawable) drawable;
             final int N = background.getNumberOfLayers();
@@ -236,7 +231,7 @@ public class ProgressBar extends View {
             
             for (int i = 0; i < N; i++) {
                 int id = background.getId(i);
-                outDrawables[i] = createDrawableForTile(background.getDrawable(i),
+                outDrawables[i] = tileify(background.getDrawable(i),
                         (id == R.id.progress || id == R.id.secondaryProgress));
             }
 
@@ -246,30 +241,36 @@ public class ProgressBar extends View {
                 newBg.setId(i, background.getId(i));
             }
             
-            drawable = newBg;
-        }
-        return drawable;
-    }
+            return newBg;
+            
+        } else if (drawable instanceof StateListDrawable) {
+            StateListDrawable in = (StateListDrawable) drawable;
+            StateListDrawable out = new StateListDrawable();
+            int numStates = in.getStateCount();
+            for (int i = 0; i < numStates; i++) {
+                out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
+            }
+            return out;
+            
+        } else if (drawable instanceof BitmapDrawable) {
+            final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
+            if (mSampleTile == null) {
+                mSampleTile = tileBitmap;
+            }
+            
+            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
 
-    // TODO Remove all this once ShapeDrawable + shaders are supported through XML
-    private Drawable createDrawableForTile(Drawable tileDrawable, boolean clip) {
-        if (!(tileDrawable instanceof BitmapDrawable)) return tileDrawable;
+            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
+                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
+            shapeDrawable.getPaint().setShader(bitmapShader);
 
-        final Bitmap tileBitmap = ((BitmapDrawable) tileDrawable).getBitmap();
-        if (mSampleTile == null) {
-            mSampleTile = tileBitmap;
+            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
+                    ClipDrawable.HORIZONTAL) : shapeDrawable;
         }
         
-        final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
-
-        final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
-                Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
-        shapeDrawable.getPaint().setShader(bitmapShader);
-
-        return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
-                ClipDrawable.HORIZONTAL) : shapeDrawable;
+        return drawable;
     }
-    
+
     Shape getDrawableShape() {
         final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
         return new RoundRectShape(roundedCorners, null, null);
@@ -288,7 +289,7 @@ public class ProgressBar extends View {
             newBg.setOneShot(background.isOneShot());
             
             for (int i = 0; i < N; i++) {
-                Drawable frame = createDrawableForTile(background.getFrame(i), true);
+                Drawable frame = tileify(background.getFrame(i), true);
                 frame.setLevel(10000);
                 newBg.addFrame(frame, background.getDuration(i));
             }
@@ -448,29 +449,29 @@ public class ProgressBar extends View {
 
         private int mId;
         private int mProgress;
-        private boolean mFromTouch;
+        private boolean mFromUser;
         
-        RefreshProgressRunnable(int id, int progress, boolean fromTouch) {
+        RefreshProgressRunnable(int id, int progress, boolean fromUser) {
             mId = id;
             mProgress = progress;
-            mFromTouch = fromTouch;
+            mFromUser = fromUser;
         }
         
         public void run() {
-            doRefreshProgress(mId, mProgress, mFromTouch);
+            doRefreshProgress(mId, mProgress, mFromUser);
             // Put ourselves back in the cache when we are done
             mRefreshProgressRunnable = this;
         }
         
-        public void setup(int id, int progress, boolean fromTouch) {
+        public void setup(int id, int progress, boolean fromUser) {
             mId = id;
             mProgress = progress;
-            mFromTouch = fromTouch;
+            mFromUser = fromUser;
         }
         
     }
     
-    private synchronized void doRefreshProgress(int id, int progress, boolean fromTouch) {
+    private synchronized void doRefreshProgress(int id, int progress, boolean fromUser) {
         float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
         final Drawable d = mCurrentDrawable;
         if (d != null) {
@@ -487,16 +488,16 @@ public class ProgressBar extends View {
         }
         
         if (id == R.id.progress) {
-            onProgressRefresh(scale, fromTouch);
+            onProgressRefresh(scale, fromUser);
         }
     }
     
-    void onProgressRefresh(float scale, boolean fromTouch) {        
+    void onProgressRefresh(float scale, boolean fromUser) {        
     }
 
-    private synchronized void refreshProgress(int id, int progress, boolean fromTouch) {
+    private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
         if (mUiThreadId == Thread.currentThread().getId()) {
-            doRefreshProgress(id, progress, fromTouch);
+            doRefreshProgress(id, progress, fromUser);
         } else {
             RefreshProgressRunnable r;
             if (mRefreshProgressRunnable != null) {
@@ -504,10 +505,10 @@ public class ProgressBar extends View {
                 r = mRefreshProgressRunnable;
                 // Uncache it
                 mRefreshProgressRunnable = null;
-                r.setup(id, progress, fromTouch);
+                r.setup(id, progress, fromUser);
             } else {
                 // Make a new one
-                r = new RefreshProgressRunnable(id, progress, fromTouch);
+                r = new RefreshProgressRunnable(id, progress, fromUser);
             }
             post(r);
         }
@@ -528,7 +529,7 @@ public class ProgressBar extends View {
         setProgress(progress, false);
     }
     
-    synchronized void setProgress(int progress, boolean fromTouch) {
+    synchronized void setProgress(int progress, boolean fromUser) {
         if (mIndeterminate) {
             return;
         }
@@ -543,7 +544,7 @@ public class ProgressBar extends View {
 
         if (progress != mProgress) {
             mProgress = progress;
-            refreshProgress(R.id.progress, mProgress, fromTouch);
+            refreshProgress(R.id.progress, mProgress, fromUser);
         }
     }
 
@@ -836,6 +837,21 @@ public class ProgressBar extends View {
                 resolveSize(dh, heightMeasureSpec));
     }
     
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        
+        int[] state = getDrawableState();
+        
+        if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
+            mProgressDrawable.setState(state);
+        }
+        
+        if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
+            mIndeterminateDrawable.setState(state);
+        }
+    }
+
     static class SavedState extends BaseSavedState {
         int progress;
         int secondaryProgress;
index 845b542..ad5ca07 100644 (file)
@@ -26,10 +26,14 @@ import com.android.internal.R;
 
 /**
  * A RatingBar is an extension of SeekBar and ProgressBar that shows a rating in
- * stars. The user can touch and/or drag to set the rating when using the
- * default size RatingBar. The smaller RatingBar style ({@link android.R.attr#ratingBarStyleSmall})
- * and the larger indicator-only style ({@link android.R.attr#ratingBarStyleIndicator})
- * do not support user interaction and should only be used as indicators.
+ * stars. The user can touch/drag or use arrow keys to set the rating when using
+ * the default size RatingBar. The smaller RatingBar style (
+ * {@link android.R.attr#ratingBarStyleSmall}) and the larger indicator-only
+ * style ({@link android.R.attr#ratingBarStyleIndicator}) do not support user
+ * interaction and should only be used as indicators.
+ * <p>
+ * When using a RatingBar that supports user interaction, placing widgets to the
+ * left or right of the RatingBar is discouraged.
  * <p>
  * The number of stars set (via {@link #setNumStars(int)} or in an XML layout)
  * will be shown when the layout width is set to wrap content (if another layout
@@ -44,17 +48,18 @@ import com.android.internal.R;
  * @attr ref android.R.styleable#RatingBar_isIndicator
  */
 public class RatingBar extends AbsSeekBar {
-    
+
     /**
-     * A callback that notifies clients when the rating has been changed. This 
-     * includes changes that were initiated by the user through a touch gesture as well
-     * as changes that were initiated programmatically.
+     * A callback that notifies clients when the rating has been changed. This
+     * includes changes that were initiated by the user through a touch gesture
+     * or arrow key/trackball as well as changes that were initiated
+     * programmatically.
      */
     public interface OnRatingBarChangeListener {
         
         /**
          * Notification that the rating has changed. Clients can use the
-         * fromTouch parameter to distinguish user-initiated changes from those
+         * fromUser parameter to distinguish user-initiated changes from those
          * that occurred programmatically. This will not be called continuously
          * while the user is dragging, only when the user finalizes a rating by
          * lifting the touch.
@@ -62,10 +67,10 @@ public class RatingBar extends AbsSeekBar {
          * @param ratingBar The RatingBar whose rating has changed.
          * @param rating The current rating. This will be in the range
          *            0..numStars.
-         * @param fromTouch True if the rating change was initiated by a user's
-         *            touch gesture.
+         * @param fromUser True if the rating change was initiated by a user's
+         *            touch gesture or arrow key/horizontal trackbell movement.
          */
-        void onRatingChanged(RatingBar ratingBar, float rating, boolean fromTouch);
+        void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser);
 
     }
 
@@ -138,6 +143,7 @@ public class RatingBar extends AbsSeekBar {
      */
     public void setIsIndicator(boolean isIndicator) {
         mIsUserSeekable = !isIndicator;
+        setFocusable(!isIndicator);
     }
     
     /**
@@ -179,7 +185,7 @@ public class RatingBar extends AbsSeekBar {
      * @param rating The rating to set.
      */
     public void setRating(float rating) {
-        setProgress((int) (rating * getProgressPerStar()));
+        setProgress(Math.round(rating * getProgressPerStar()));
     }
 
     /**
@@ -235,14 +241,14 @@ public class RatingBar extends AbsSeekBar {
     }
 
     @Override
-    void onProgressRefresh(float scale, boolean fromTouch) {
-        super.onProgressRefresh(scale, fromTouch);
+    void onProgressRefresh(float scale, boolean fromUser) {
+        super.onProgressRefresh(scale, fromUser);
 
         // Keep secondary progress in sync with primary
         updateSecondaryProgress(getProgress());
         
-        if (!fromTouch) {
-            // Callback for non-touch rating changes
+        if (!fromUser) {
+            // Callback for non-user rating changes
             dispatchRatingChange(false);
         }
     }
@@ -291,10 +297,10 @@ public class RatingBar extends AbsSeekBar {
         }
     }
     
-    void dispatchRatingChange(boolean fromTouch) {
+    void dispatchRatingChange(boolean fromUser) {
         if (mOnRatingBarChangeListener != null) {
             mOnRatingBarChangeListener.onRatingChanged(this, getRating(),
-                    fromTouch);
+                    fromUser);
         }
     }
 
index 456d58d..9052ae3 100644 (file)
@@ -40,7 +40,8 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
      * @param context The context where the ListView associated with this
      *            SimpleListItemFactory is running
      * @param layout resource identifier of a layout file that defines the views
-     *            for this list item.
+     *            for this list item.  Unless you override them later, this will
+     *            define both the item views and the drop down views.
      */
     public ResourceCursorAdapter(Context context, int layout, Cursor c) {
         super(context, c);
@@ -65,6 +66,15 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
     }
 
     /**
+     * <p>Sets the layout resource of the item views.</p>
+     *
+     * @param layout the layout resources used to create item views
+     */
+    public void setViewResource(int layout) {
+        mLayout = layout;
+    }
+    
+    /**
      * <p>Sets the layout resource of the drop down views.</p>
      *
      * @param dropDownLayout the layout resources used to create drop down views
index e87dc2d..dfee29b 100644 (file)
@@ -22,33 +22,35 @@ import android.util.AttributeSet;
 
 
 /**
- * A Seekbar is an extension of ProgressBar that adds a draggable thumb. The user can touch
- * the thumb and drag left or right to set the current progress level. 
- * 
+ * A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch
+ * the thumb and drag left or right to set the current progress level or use the arrow keys.
+ * Placing focusable widgets to the left or right of a SeekBar is discouraged. 
+ * <p>
+ * Clients of the SeekBar can attach a {@link SeekBar.OnSeekBarChangeListener} to
  * be notified of the user's actions.
- * Clients of the Seekbar can attach a {@link SeekBar.OnSeekBarChangeListener} to
  *
  * @attr ref android.R.styleable#SeekBar_thumb
  */
 public class SeekBar extends AbsSeekBar {
 
     /**
-     * A callback that notifies clients when the progress level has been changed. This 
-     * includes changes that were initiated by the user through a touch gesture as well
-     * as changes that were initiated programmatically. 
+     * A callback that notifies clients when the progress level has been
+     * changed. This includes changes that were initiated by the user through a
+     * touch gesture or arrow key/trackball as well as changes that were initiated
+     * programmatically.
      */
     public interface OnSeekBarChangeListener {
         
         /**
-         * Notification that the progress level has changed. Clients can use the fromTouch parameter
+         * Notification that the progress level has changed. Clients can use the fromUser parameter
          * to distinguish user-initiated changes from those that occurred programmatically.
          * 
          * @param seekBar The SeekBar whose progress has changed
          * @param progress The current progress level. This will be in the range 0..max where max
          *        was set by {@link ProgressBar#setMax(int)}. (The default value for max is 100.)
-         * @param fromTouch True if the progress change was initiated by a user's touch gesture.
+         * @param fromUser True if the progress change was initiated by the user.
          */
-        void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
+        void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
     
         /**
          * Notification that the user has started a touch gesture. Clients may want to use this
@@ -80,11 +82,11 @@ public class SeekBar extends AbsSeekBar {
     }
 
     @Override
-    void onProgressRefresh(float scale, boolean fromTouch) {
-        super.onProgressRefresh(scale, fromTouch);
+    void onProgressRefresh(float scale, boolean fromUser) {
+        super.onProgressRefresh(scale, fromUser);
 
         if (mOnSeekBarChangeListener != null) {
-            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromTouch);
+            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
         }
     }
 
@@ -113,4 +115,5 @@ public class SeekBar extends AbsSeekBar {
             mOnSeekBarChangeListener.onStopTrackingTouch(this);
         }
     }
+    
 }
index 74a9964..c1595ea 100644 (file)
@@ -74,11 +74,12 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
      *            for this list item. Thelayout file should include at least
      *            those named views defined in "to"
      * @param c The database cursor.  Can be null if the cursor is not available yet.
-     * @param from A list of column names representing the data to bind to the UI
+     * @param from A list of column names representing the data to bind to the UI.  Can be null 
+     *            if the cursor is not available yet.
      * @param to The views that should display column in the "from" parameter.
      *            These should all be TextViews. The first N views in this list
      *            are given the values of the first N columns in the from
-     *            parameter.
+     *            parameter.  Can be null if the cursor is not available yet.
      */
     public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
         super(context, layout, c);
@@ -318,20 +319,24 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
         return super.convertToString(cursor);
     }
 
+    /**
+     * Create a map from an array of strings to an array of column-id integers in mCursor.
+     * If mCursor is null, the array will be discarded.
+     * 
+     * @param from the Strings naming the columns of interest
+     */
     private void findColumns(String[] from) {
-        int i;
-        int count = from.length;
-        if (mFrom == null) {
-            mFrom = new int[count];
-        }
         if (mCursor != null) {
+            int i;
+            int count = from.length;
+            if (mFrom == null || mFrom.length != count) {
+                mFrom = new int[count];
+            }
             for (i = 0; i < count; i++) {
                 mFrom[i] = mCursor.getColumnIndexOrThrow(from[i]);
             }
         } else {
-            for (i = 0; i < count; i++) {
-                mFrom[i] = -1;
-            }
+            mFrom = null;
         }
     }
 
@@ -341,6 +346,24 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
         // rescan columns in case cursor layout is different
         findColumns(mOriginalFrom);
     }
+    
+    /**
+     * Change the cursor and change the column-to-view mappings at the same time.
+     *  
+     * @param c The database cursor.  Can be null if the cursor is not available yet.
+     * @param from A list of column names representing the data to bind to the UI.  Can be null 
+     *            if the cursor is not available yet.
+     * @param to The views that should display column in the "from" parameter.
+     *            These should all be TextViews. The first N views in this list
+     *            are given the values of the first N columns in the from
+     *            parameter.  Can be null if the cursor is not available yet.
+     */
+    public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
+        mOriginalFrom = from;
+        mTo = to;
+        super.changeCursor(c);        
+        findColumns(mOriginalFrom);
+    }
 
     /**
      * This class can be used by external clients of SimpleCursorAdapter
index 9e5f019..73c2b3e 100644 (file)
@@ -68,6 +68,7 @@ import android.text.method.TextKeyListener;
 import android.text.method.TransformationMethod;
 import android.text.style.ParagraphStyle;
 import android.text.style.URLSpan;
+import android.text.style.UpdateAppearance;
 import android.text.style.UpdateLayout;
 import android.text.util.Linkify;
 import android.util.AttributeSet;
@@ -3505,7 +3506,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
         InputMethodManager imm = InputMethodManager.peekInstance();
         if (highlight != null && mInputMethodState != null && imm != null) {
-            imm.updateSelection(this, selStart, selEnd);
+            if (imm.isActive(this)) {
+                int candStart = -1;
+                int candEnd = -1;
+                if (mText instanceof Spannable) {
+                    Spannable sp = (Spannable)mText;
+                    candStart = EditableInputConnection.getComposingSpanStart(sp);
+                    candEnd = EditableInputConnection.getComposingSpanEnd(sp);
+                }
+                imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
+            }
             
             if (imm.isWatchingCursor(this)) {
                 final InputMethodState ims = mInputMethodState;
@@ -3762,7 +3772,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         return super.onKeyUp(keyCode, event);
     }
 
-    @Override public InputConnection createInputConnection(EditorInfo outAttrs) {
+    @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
         if (mInputType != EditorInfo.TYPE_NULL) {
             if (mInputMethodState == null) {
                 mInputMethodState = new InputMethodState();
@@ -5137,7 +5147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             }
         }
 
-        if (what instanceof UpdateLayout ||
+        if (what instanceof UpdateAppearance ||
             what instanceof ParagraphStyle) {
             invalidate();
             mHighlightPathBogus = true;
@@ -5167,6 +5177,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
         public void afterTextChanged(Editable buffer) {
             TextView.this.sendAfterTextChanged(buffer);
+
+            if (MetaKeyKeyListener.getMetaState(buffer,
+                                 MetaKeyKeyListener.META_SELECTING) != 0) {
+                MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
+            }
+
             TextView.this.reportExtractedText();
         }
 
@@ -5550,6 +5566,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         return false;
     }
 
+    private boolean canSelectText() {
+        if (mText instanceof Spannable && mText.length() != 0 &&
+            mMovement != null && mMovement.canSelectArbitrarily()) {
+            return true;
+        }
+
+        return false;
+    }
+
     private boolean canCut() {
         if (mTransformation instanceof PasswordTransformationMethod) {
             return false;
@@ -5623,6 +5648,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
         boolean selection = getSelectionStart() != getSelectionEnd();
 
+        if (canSelectText()) {
+            if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
+                menu.add(0, ID_STOP_SELECTING_TEXT, 0,
+                        com.android.internal.R.string.stopSelectingText).
+                    setOnMenuItemClickListener(handler);
+                added = true;
+            } else {
+                menu.add(0, ID_SELECT_TEXT, 0,
+                        com.android.internal.R.string.selectText).
+                    setOnMenuItemClickListener(handler);
+                added = true;
+            }
+        }
+
         if (canCut()) {
             int name;
             if (selection) {
@@ -5688,6 +5727,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     }
 
     private static final int ID_SELECT_ALL = com.android.internal.R.id.selectAll;
+    private static final int ID_SELECT_TEXT = com.android.internal.R.id.selectText;
+    private static final int ID_STOP_SELECTING_TEXT = com.android.internal.R.id.stopSelectingText;
     private static final int ID_CUT = com.android.internal.R.id.cut;
     private static final int ID_COPY = com.android.internal.R.id.copy;
     private static final int ID_PASTE = com.android.internal.R.id.paste;
@@ -5728,7 +5769,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                         mText.length());
                 return true;
 
+            case ID_SELECT_TEXT:
+                MetaKeyKeyListener.startSelecting(this, (Spannable) mText);
+                return true;
+
+            case ID_STOP_SELECTING_TEXT:
+                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+                Selection.setSelection((Spannable) mText, getSelectionEnd());
+                return true;
+
             case ID_CUT:
+                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+
                 if (min == max) {
                     min = 0;
                     max = mText.length();
@@ -5739,6 +5791,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 return true;
 
             case ID_COPY:
+                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+
                 if (min == max) {
                     min = 0;
                     max = mText.length();
@@ -5748,6 +5802,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 return true;
 
             case ID_PASTE:
+                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+
                 CharSequence paste = clip.getText();
 
                 if (paste != null) {
@@ -5758,6 +5814,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 return true;
 
             case ID_COPY_URL:
+                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+
                 URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
                                                        URLSpan.class);
                 if (urls.length == 1) {
index 9c83aa3..1f8e6f0 100644 (file)
@@ -169,6 +169,14 @@ public final class RingtonePickerActivity extends AlertActivity implements
 
     public void onPrepareListView(ListView listView) {
         
+        if (mHasDefaultItem) {
+            mDefaultRingtonePos = addDefaultRingtoneItem(listView);
+            
+            if (RingtoneManager.isDefault(mExistingUri)) {
+                mClickedPos = mDefaultRingtonePos;
+            }
+        }
+        
         if (mHasSilentItem) {
             mSilentPos = addSilentItem(listView);
             
@@ -178,14 +186,6 @@ public final class RingtonePickerActivity extends AlertActivity implements
             }
         }
 
-        if (mHasDefaultItem) {
-            mDefaultRingtonePos = addDefaultRingtoneItem(listView);
-            
-            if (RingtoneManager.isDefault(mExistingUri)) {
-                mClickedPos = mDefaultRingtonePos;
-            }
-        }
-        
         if (mClickedPos == -1) {
             mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri));
         }
index fd8fd5a..e88a36f 100644 (file)
@@ -26,6 +26,8 @@ public class HandlerCaller {
         public int argi2;
         public int argi3;
         public int argi4;
+        public int argi5;
+        public int argi6;
     }
     
     static final int ARGS_POOL_MAX_SIZE = 10;
@@ -153,6 +155,18 @@ public class HandlerCaller {
         return mH.obtainMessage(what, 0, 0, args);
     }
     
+    public Message obtainMessageIIIIII(int what, int arg1, int arg2,
+            int arg3, int arg4, int arg5, int arg6) {
+        SomeArgs args = obtainArgs();
+        args.argi1 = arg1;
+        args.argi2 = arg2;
+        args.argi3 = arg3;
+        args.argi4 = arg4;
+        args.argi5 = arg5;
+        args.argi6 = arg6;
+        return mH.obtainMessage(what, 0, 0, args);
+    }
+    
     public Message obtainMessageIIIIO(int what, int arg1, int arg2,
             int arg3, int arg4, Object arg5) {
         SomeArgs args = obtainArgs();
index c5966ee..d604259 100644 (file)
@@ -23,6 +23,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
     private static final int DO_COMMIT_TEXT = 50;
     private static final int DO_COMMIT_COMPLETION = 55;
     private static final int DO_SET_COMPOSING_TEXT = 60;
+    private static final int DO_FINISH_COMPOSING_TEXT = 65;
     private static final int DO_SEND_KEY_EVENT = 70;
     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
     private static final int DO_HIDE_STATUS_ICON = 100;
@@ -89,6 +90,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
     }
 
+    public void finishComposingText() {
+        dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
+    }
+
     public void sendKeyEvent(KeyEvent event) {
         dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
     }
@@ -181,6 +186,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
                 mInputConnection.setComposingText((CharSequence)msg.obj, msg.arg1);
                 return;
             }
+            case DO_FINISH_COMPOSING_TEXT: {
+                mInputConnection.finishComposingText();
+                return;
+            }
             case DO_SEND_KEY_EVENT: {
                 mInputConnection.sendKeyEvent((KeyEvent)msg.obj);
                 return;
index 7ea65a0..55a9784 100644 (file)
@@ -42,6 +42,8 @@ import com.android.internal.view.IInputContextCallback;
 
     void setComposingText(CharSequence text, int newCursorPosition);
 
+    void finishComposingText();
+    
     void commitText(CharSequence text, int newCursorPosition);
 
     void commitCompletion(in CompletionInfo completion);
index b4cfe26..1c9e797 100644 (file)
@@ -47,5 +47,7 @@ interface IInputMethodManager {
     void setInputMethod(in IBinder token, String id);
     void hideMySoftInput(in IBinder token);
     void updateStatusIcon(int iconId, String iconPackage);
+    
+    boolean setInputMethodEnabled(String id, boolean enabled);
 }
 
index 4f28593..8a44976 100644 (file)
@@ -34,7 +34,8 @@ oneway interface IInputMethodSession {
     void updateExtractedText(int token, in ExtractedText text);
     
     void updateSelection(int oldSelStart, int oldSelEnd,
-            int newSelStart, int newSelEnd);
+            int newSelStart, int newSelEnd,
+            int candidatesStart, int candidatesEnd);
     
     void updateCursor(in Rect newCursor);
     
index 5bfcfe9..1cfaf17 100644 (file)
@@ -250,6 +250,15 @@ public class InputConnectionWrapper implements InputConnection {
         }
     }
 
+    public boolean finishComposingText() {
+        try {
+            mIInputContext.finishComposingText();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     public boolean sendKeyEvent(KeyEvent event) {
         try {
             mIInputContext.sendKeyEvent(event);
index 43dba6f..1543b62 100644 (file)
@@ -547,19 +547,7 @@ public final class MenuItemImpl implements MenuItem {
     boolean setVisibleInt(boolean shown) {
         final int oldFlags = mFlags;
         mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
-        if (oldFlags != mFlags) {
-            final int visibility = (shown) ? View.VISIBLE : View.GONE;
-            
-            for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
-                if (hasItemView(i)) {
-                    ((View) mItemViews[i].get()).setVisibility(visibility);
-                }
-            }
-         
-            return true;
-        }
-        
-        return false;
+        return oldFlags != mFlags;
     }
     
     public MenuItem setVisible(boolean shown) {
index efe15f3..263220b 100644 (file)
@@ -44,22 +44,74 @@ public class EditableInputConnection extends BaseInputConnection {
     public static final Object COMPOSING = new ComposingText();
     
     private final TextView mTextView;
-    private final Handler mUiHandler;
 
     private Object[] mDefaultComposingSpans;
     
     public EditableInputConnection(TextView textview) {
         super(textview);
         mTextView = textview;
-        mUiHandler = textview.getHandler();
     }
 
+    public static final void removeComposingSpans(Spannable text) {
+        text.removeSpan(COMPOSING);
+        Object[] sps = text.getSpans(0, text.length(), Object.class);
+        if (sps != null) {
+            for (int i=sps.length-1; i>=0; i--) {
+                Object o = sps[i];
+                if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
+                    text.removeSpan(o);
+                }
+            }
+        }
+    }
+    
+    public static void setComposingSpans(Spannable text) {
+        final Object[] sps = text.getSpans(0, text.length(), Object.class);
+        if (sps != null) {
+            for (int i=sps.length-1; i>=0; i--) {
+                final Object o = sps[i];
+                if (o == COMPOSING) {
+                    text.removeSpan(o);
+                    continue;
+                }
+                final int fl = text.getSpanFlags(o);
+                if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) 
+                        != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
+                    text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+                            (fl&Spanned.SPAN_POINT_MARK_MASK)
+                                    | Spanned.SPAN_COMPOSING
+                                    | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+            }
+        }
+        
+        text.setSpan(COMPOSING, 0, text.length(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+    }
+    
+    public static int getComposingSpanStart(Spannable text) {
+        return text.getSpanStart(COMPOSING);
+    }
+    
+    public static int getComposingSpanEnd(Spannable text) {
+        return text.getSpanEnd(COMPOSING);
+    }
+    
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "setComposingText " + text);
         replaceText(text, newCursorPosition, true);
         return true;
     }
 
+    public boolean finishComposingText() {
+        if (DEBUG) Log.v(TAG, "finishComposingText");
+        final Editable content = getEditable();
+        if (content != null) {
+            removeComposingSpans(content);
+        }
+        return true;
+    }
+    
     public boolean commitText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "commitText " + text);
         replaceText(text, newCursorPosition, false);
@@ -212,43 +264,6 @@ public class EditableInputConnection extends BaseInputConnection {
         return null;
     }
     
-    public static void setComposingSpans(Spannable text) {
-        final Object[] sps = text.getSpans(0, text.length(), Object.class);
-        if (sps != null) {
-            for (int i=sps.length-1; i>=0; i--) {
-                final Object o = sps[i];
-                if (o == COMPOSING) {
-                    text.removeSpan(o);
-                    continue;
-                }
-                final int fl = text.getSpanFlags(o);
-                if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) 
-                        != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
-                    text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
-                            (fl&Spanned.SPAN_POINT_MARK_MASK)
-                                    | Spanned.SPAN_COMPOSING
-                                    | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                }
-            }
-        }
-        
-        text.setSpan(COMPOSING, 0, text.length(),
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
-    }
-    
-    public static final void removeComposingSpans(Spannable text) {
-        text.removeSpan(COMPOSING);
-        Object[] sps = text.getSpans(0, text.length(), Object.class);
-        if (sps != null) {
-            for (int i=sps.length-1; i>=0; i--) {
-                Object o = sps[i];
-                if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
-                    text.removeSpan(o);
-                }
-            }
-        }
-    }
-    
     private void replaceText(CharSequence text, int newCursorPosition,
             boolean composing) {
         final Editable content = getEditable();
index 48d0233..17f86d6 100644 (file)
@@ -49,8 +49,7 @@ public class AndroidGDataClient implements GDataClient {
     private static final String X_HTTP_METHOD_OVERRIDE =
         "X-HTTP-Method-Override";
 
-    private static final String USER_AGENT_GZIP = 
-            GoogleHttpClient.getGzipCapableUserAgent("Android-GData/1.0");
+    private static final String USER_AGENT_APP_VERSION = "Android-GData/1.0";
 
     private static final int MAX_REDIRECTS = 10;
 
@@ -123,7 +122,8 @@ public class AndroidGDataClient implements GDataClient {
      * through the Android proxy server, using null to indicate not using proxy.
      */
     public AndroidGDataClient(ContentResolver resolver) {
-        mHttpClient = new GoogleHttpClient(resolver, USER_AGENT_GZIP);
+        mHttpClient = new GoogleHttpClient(resolver, USER_AGENT_APP_VERSION,
+                true /* gzip capable */);
         mResolver = resolver;
     }
 
index bc6eaee..4656aff 100644 (file)
@@ -38,13 +38,12 @@ import java.net.URISyntaxException;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.os.SystemClock;
+import android.os.Build;
 import android.net.http.AndroidHttpClient;
 import android.provider.Checkin;
 import android.util.Config;
 import android.util.Log;
 
-import com.android.internal.os.RuntimeInit;
-
 /**
  * {@link AndroidHttpClient} wrapper that uses {@link UrlRules} to rewrite URLs
  * and otherwise tweak HTTP requests.
@@ -69,6 +68,7 @@ public class GoogleHttpClient implements HttpClient {
      * Create an HTTP client.  Normally one client is shared throughout an app.
      * @param resolver to use for accessing URL rewriting rules.
      * @param userAgent to report in your HTTP requests.
+     * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)} 
      */
     public GoogleHttpClient(ContentResolver resolver, String userAgent) {
         mClient = AndroidHttpClient.newInstance(userAgent);
@@ -77,6 +77,35 @@ public class GoogleHttpClient implements HttpClient {
     }
 
     /**
+     * Create an HTTP client.  Normaly this client is shared throughout an app.
+     * The HTTP client will construct its User-Agent as follows:
+     *
+     * <appAndVersion> (<build device> <build id>)
+     * or
+     * <appAndVersion> (<build device> <build id>); gzip
+     * (if gzip capable)
+     *
+     * @param resolver to use for acccessing URL rewriting rules.
+     * @param appAndVersion Base app and version to use in the User-Agent.
+     * e.g., "MyApp/1.0"
+     * @param gzipCapable Whether or not this client is able to consume gzip'd
+     * responses.  Only used to modify the User-Agent, not other request
+     * headers.  Needed because Google servers require gzip in the User-Agent
+     * in order to return gzip'd content.
+     */
+    public GoogleHttpClient(ContentResolver resolver, String appAndVersion,
+            boolean gzipCapable) {
+        String userAgent = appAndVersion
+                + " (" + Build.DEVICE + " " + Build.ID + ")";
+        if (gzipCapable) {
+            userAgent = userAgent + "; gzip";
+        }
+        mClient = AndroidHttpClient.newInstance(userAgent);
+        mResolver = resolver;
+        mUserAgent = userAgent;
+    }
+
+    /**
      * Release resources associated with this client.  You must call this,
      * or significant resources (sockets and memory) may be leaked.
      */
@@ -181,6 +210,7 @@ public class GoogleHttpClient implements HttpClient {
      *
      * @param originalUserAgent to modify (however you identify yourself)
      * @return user agent with a "yes, I really can handle gzip" token added.
+     * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)} 
      */
     public static String getGzipCapableUserAgent(String originalUserAgent) {
         return originalUserAgent + "; gzip";
index 2c74ab7..c074b69 100644 (file)
@@ -91,6 +91,7 @@ LOCAL_SRC_FILES:= \
        android_media_AudioRecord.cpp \
        android_media_AudioSystem.cpp \
        android_media_AudioTrack.cpp \
+       android_media_JetPlayer.cpp \
        android_media_ToneGenerator.cpp \
        android_hardware_Camera.cpp \
        android_hardware_SensorManager.cpp \
@@ -116,10 +117,14 @@ LOCAL_SRC_FILES:= \
 LOCAL_C_INCLUDES += \
        $(JNI_H_INCLUDE) \
        $(LOCAL_PATH)/android/graphics \
-       $(call include-path-for, bluedroid corecg graphics) \
+       $(call include-path-for, bluedroid) \
        $(call include-path-for, libhardware)/hardware \
        $(LOCAL_PATH)/../../include/ui \
        $(LOCAL_PATH)/../../include/utils \
+       external/skia/include/core \
+       external/skia/include/effects \
+       external/skia/include/images \
+       external/skia/include/utils \
        external/sqlite/dist \
        external/sqlite/android \
        external/expat/lib \
index f85f7d5..097ffac 100644 (file)
@@ -74,6 +74,7 @@ extern int register_android_hardware_SensorManager(JNIEnv *env);
 extern int register_android_media_AudioRecord(JNIEnv *env);
 extern int register_android_media_AudioSystem(JNIEnv *env);
 extern int register_android_media_AudioTrack(JNIEnv *env);
+extern int register_android_media_JetPlayer(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
 
 extern int register_android_message_digest_sha1(JNIEnv *env);
@@ -1073,6 +1074,7 @@ static const RegJNIRec gRegJNI[] = {
     REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioSystem),
     REG_JNI(register_android_media_AudioTrack),
+    REG_JNI(register_android_media_JetPlayer),
     REG_JNI(register_android_media_ToneGenerator),
 
     REG_JNI(register_android_opengl_classes),
index 27a6349..329a695 100644 (file)
@@ -1,5 +1,5 @@
 #include "SkBitmap.h"\r
-#include "SkImageDecoder.h"\r
+#include "SkImageEncoder.h"\r
 #include "SkColorPriv.h"\r
 #include "GraphicsJNI.h"\r
 #include "SkDither.h"\r
index 841fe49..b9e5f67 100644 (file)
@@ -800,7 +800,7 @@ public:
                               jobject bounds) {
         SkRect   r;
         SkIRect ir;
-        bool     result = canvas->getClipBounds(&r);
+        bool     result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType);
 
         r.round(&ir);
         (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
index eef8bb2..b28eb90 100644 (file)
@@ -4,7 +4,7 @@
 #include "SkShader.h"
 #include "SkGradientShader.h"
 #include "SkPorterDuff.h"
-#include "SkShaderExtras.h"
+#include "SkComposeShader.h"
 #include "SkTemplates.h"
 #include "SkXfermode.h"
 
index 5cd2ceb..a1059e5 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <GLES/gl.h>
 
-#include <graphics/SkBitmap.h>
+#include <core/SkBitmap.h>
 
 #include "android_runtime/AndroidRuntime.h"
 
index 4b126a6..e1ef459 100644 (file)
@@ -225,6 +225,15 @@ static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz)
     c->stopPreview();
 }
 
+static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz)
+{
+    sp<Camera> c = get_native_camera(env, thiz);
+    if (c == 0)
+        return false;
+
+    return c->previewEnabled();
+}
+
 static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed)
 {
     sp<Camera> c = get_native_camera(env, thiz);
@@ -382,6 +391,22 @@ static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
     }
 }
 
+static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz)
+{
+    sp<Camera> c = get_native_camera(env, thiz);
+    if (c == 0)
+        return INVALID_OPERATION;
+    return (jint) c->lock();
+}
+
+static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz)
+{
+    sp<Camera> c = get_native_camera(env, thiz);
+    if (c == 0)
+        return INVALID_OPERATION;
+    return (jint) c->lock();
+}
+
 //-------------------------------------------------
 
 static JNINativeMethod camMethods[] = {
@@ -400,6 +425,9 @@ static JNINativeMethod camMethods[] = {
   { "stopPreview",
     "()V",
     (void *)android_hardware_Camera_stopPreview },
+  { "previewEnabled",
+    "()Z",
+    (void *)android_hardware_Camera_previewEnabled },
   { "setHasPreviewCallback",
     "(Z)V",
     (void *)android_hardware_Camera_setHasPreviewCallback },
@@ -418,6 +446,12 @@ static JNINativeMethod camMethods[] = {
   { "reconnect",
     "()V",
     (void*)android_hardware_Camera_reconnect },
+  { "lock",
+    "()I",
+    (void*)android_hardware_Camera_lock },
+  { "unlock",
+    "()I",
+    (void*)android_hardware_Camera_unlock },
 };
 
 struct field {
index 4a98fcc..6bbcaee 100644 (file)
@@ -52,6 +52,7 @@ struct fields_t {
     int       STREAM_RING;           //...  stream type constants
     int       STREAM_MUSIC;          //...  stream type constants
     int       STREAM_ALARM;          //...  stream type constants
+    int       STREAM_NOTIFICATION;   //...  stream type constants
     int       MODE_STREAM;           //...  memory mode
     int       MODE_STATIC;           //...  memory mode
     jfieldID  nativeTrackInJavaObj; // stores in Java the native AudioTrack object
@@ -192,6 +193,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
         atStreamType = AudioTrack::MUSIC;
     } else if (streamType == javaAudioTrackFields.STREAM_ALARM) {
         atStreamType = AudioTrack::ALARM;
+    } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
+        atStreamType = AudioTrack::NOTIFICATION;
     } else {
         LOGE("Error creating AudioTrack: unknown stream type.");
         return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
@@ -696,22 +699,23 @@ static JNINativeMethod gMethods[] = {
 
 
 // field names found in android/media/AudioTrack.java
-#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
-#define JAVA_CONST_PCM16_NAME        "ENCODING_PCM_16BIT"
-#define JAVA_CONST_PCM8_NAME         "ENCODING_PCM_8BIT"
-#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT"
-#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL"
-#define JAVA_CONST_STREAM_SYSTEM_NAME     "STREAM_SYSTEM"
-#define JAVA_CONST_STREAM_RING_NAME       "STREAM_RING"
-#define JAVA_CONST_STREAM_MUSIC_NAME      "STREAM_MUSIC"
-#define JAVA_CONST_STREAM_ALARM_NAME      "STREAM_ALARM"
-#define JAVA_CONST_MODE_STREAM_NAME       "MODE_STREAM"
-#define JAVA_CONST_MODE_STATIC_NAME       "MODE_STATIC"
-#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
-#define JAVA_JNIDATA_FIELD_NAME              "mJniData"
-
-#define JAVA_AUDIOFORMAT_CLASS_NAME  "android/media/AudioFormat"
-#define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager"
+#define JAVA_POSTEVENT_CALLBACK_NAME            "postEventFromNative"
+#define JAVA_CONST_PCM16_NAME                   "ENCODING_PCM_16BIT"
+#define JAVA_CONST_PCM8_NAME                    "ENCODING_PCM_8BIT"
+#define JAVA_CONST_BUFFER_COUNT_NAME            "BUFFER_COUNT"
+#define JAVA_CONST_STREAM_VOICE_CALL_NAME       "STREAM_VOICE_CALL"
+#define JAVA_CONST_STREAM_SYSTEM_NAME           "STREAM_SYSTEM"
+#define JAVA_CONST_STREAM_RING_NAME             "STREAM_RING"
+#define JAVA_CONST_STREAM_MUSIC_NAME            "STREAM_MUSIC"
+#define JAVA_CONST_STREAM_ALARM_NAME            "STREAM_ALARM"
+#define JAVA_CONST_STREAM_NOTIFICATION_NAME     "STREAM_NOTIFICATION"
+#define JAVA_CONST_MODE_STREAM_NAME             "MODE_STREAM"
+#define JAVA_CONST_MODE_STATIC_NAME             "MODE_STATIC"
+#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME    "mNativeTrackInJavaObj"
+#define JAVA_JNIDATA_FIELD_NAME                 "mJniData"
+
+#define JAVA_AUDIOFORMAT_CLASS_NAME             "android/media/AudioFormat"
+#define JAVA_AUDIOMANAGER_CLASS_NAME            "android/media/AudioManager"
 
 // ----------------------------------------------------------------------------
 // preconditions:
@@ -820,7 +824,10 @@ int register_android_media_AudioTrack(JNIEnv *env)
                JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING))
           || !android_media_getIntConstantFromClass(env, audioManagerClass,
                JAVA_AUDIOMANAGER_CLASS_NAME, 
-               JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM)) ) {
+               JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM))
+          || !android_media_getIntConstantFromClass(env, audioManagerClass,
+               JAVA_AUDIOMANAGER_CLASS_NAME, 
+               JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) ) {
        // error log performed in android_media_getIntConstantFromClass()
        return -1;
     }
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
new file mode 100644 (file)
index 0000000..994f161
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * 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.
+ */
+
+//FIXME: remove log before release
+#define LOG_NDEBUG 0
+#define LOG_TAG "JET_JNI"
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include "utils/Log.h"
+#include "media/JetPlayer.h"
+
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/JetPlayer";
+
+// ----------------------------------------------------------------------------
+struct fields_t {
+    // these fields provide access from C++ to the...
+    jclass    jetClass;              // JetPlayer java class global ref
+    jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
+    jfieldID  nativePlayerInJavaObj; // stores in Java the native JetPlayer object
+};
+
+static fields_t javaJetPlayerFields;
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+/*
+ * This function is called from JetPlayer instance's render thread
+ */
+static void
+jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if(env) {
+        env->CallStaticVoidMethod(
+            javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
+            javaTarget,
+            what, arg1, arg2);
+        if (env->ExceptionCheck()) {
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+        }
+    } else {
+        LOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
+        return;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+static jboolean
+android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+    jint maxTracks, jint trackBufferSize)
+{
+    //LOGV("android_media_JetPlayer_setup(): entering.");
+    JetPlayer* lpJet = new JetPlayer(weak_this, maxTracks, trackBufferSize);
+
+    EAS_RESULT result = lpJet->init();
+
+    if(result==EAS_SUCCESS) {
+        // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field 
+        // of the Java object (in mNativePlayerInJavaObj)
+        env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (int)lpJet);
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
+        delete lpJet;
+        env->SetIntField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
+{
+    LOGV("android_media_JetPlayer_finalize(): entering.");
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if(lpJet != NULL) {
+        lpJet->release();
+        delete lpJet;
+    }
+
+    LOGV("android_media_JetPlayer_finalize(): exiting.");
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
+{
+    android_media_JetPlayer_finalize(env, thiz);
+    env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
+    LOGV("android_media_JetPlayer_release() done");   
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    // set up event callback function
+    lpJet->setEventCallback(jetPlayerEventCallback);
+
+
+    const char *pathStr = env->GetStringUTFChars(path, NULL);
+    if (pathStr == NULL) {  // Out of memory
+        LOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
+        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+        return JNI_FALSE;
+    }
+
+    LOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
+    EAS_RESULT result = lpJet->openFile(pathStr);
+    env->ReleaseStringUTFChars(path, pathStr);
+
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_openFile(): file successfully opened");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
+            (int)result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    if( lpJet->closeFile()==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_closeFile(): file successfully closed");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_closeFile(): failed to close file");
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result = lpJet->play();
+    if( result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_play(): play successful");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld", 
+            result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result = lpJet->pause();
+    if( result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_pause(): pause successful");
+        return JNI_TRUE;
+    } else {
+        if(result==EAS_ERROR_QUEUE_IS_EMPTY) {
+            LOGV("android_media_JetPlayer_pause(): paused with an empty queue");
+            return JNI_TRUE;
+        } else
+            LOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
+                result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
+        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
+        jbyte userID)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result
+        = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
+            result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
+        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
+        jbyte userID)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result=EAS_FAILURE;
+
+    jboolean *muteTracks = NULL;
+    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
+    if (muteTracks == NULL) {
+        LOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
+        return JNI_FALSE;
+    }
+
+    EAS_U32 muteMask=0;
+    int maxTracks = lpJet->getMaxTracks();
+    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
+        if(muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
+            muteMask = (muteMask << 1) | 0x00000001;
+        else
+            muteMask = muteMask << 1;
+    }
+    //LOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
+
+    result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
+
+    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld", 
+            result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
+         jint muteFlags /*unsigned?*/, jboolean bSync)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result;
+    result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
+        jbooleanArray muteArray, jboolean bSync)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result=EAS_FAILURE;
+
+    jboolean *muteTracks = NULL;
+    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
+    if (muteTracks == NULL) {
+        LOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
+        return JNI_FALSE;
+    }
+
+    EAS_U32 muteMask=0;
+    int maxTracks = lpJet->getMaxTracks();
+    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
+        if(muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
+            muteMask = (muteMask << 1) | 0x00000001;
+        else
+            muteMask = muteMask << 1;
+    }
+    //LOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
+
+    result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
+
+    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_setMuteArray(): failed to update mute flags with EAS error code %ld", result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
+         jint trackId, jboolean muteFlag, jboolean bSync)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result;
+    result = lpJet->setMuteFlag(trackId, 
+        muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
+                trackId, result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
+{
+    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
+        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+    if (lpJet == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve JetPlayer pointer for openFile()");
+    }
+    
+    EAS_RESULT result;
+    result = lpJet->triggerClip(clipId);
+    if(result==EAS_SUCCESS) {
+        //LOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
+        return JNI_TRUE;
+    } else {
+        LOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
+                clipId, result);
+        return JNI_FALSE;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+static JNINativeMethod gMethods[] = {
+    // name,               signature,               funcPtr
+    {"native_setup",       "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
+    {"native_finalize",    "()V",                   (void *)android_media_JetPlayer_finalize},
+    {"native_release",     "()V",                   (void *)android_media_JetPlayer_release},
+    {"native_openJetFile", "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_openFile},
+    {"native_closeJetFile","()Z",                   (void *)android_media_JetPlayer_closeFile},
+    {"native_playJet",     "()Z",                   (void *)android_media_JetPlayer_play},
+    {"native_pauseJet",    "()Z",                   (void *)android_media_JetPlayer_pause},
+    {"native_queueJetSegment",  
+                           "(IIIIIB)Z",             (void *)android_media_JetPlayer_queueSegment},
+    {"native_queueJetSegmentMuteArray", 
+                           "(IIII[ZB)Z",     (void *)android_media_JetPlayer_queueSegmentMuteArray},
+    {"native_setMuteFlags","(IZ)Z",                 (void *)android_media_JetPlayer_setMuteFlags},
+    {"native_setMuteArray","([ZZ)Z",                (void *)android_media_JetPlayer_setMuteArray},
+    {"native_setMuteFlag", "(IZZ)Z",                (void *)android_media_JetPlayer_setMuteFlag},
+    {"native_triggerClip", "(I)Z",                  (void *)android_media_JetPlayer_triggerClip},
+};
+
+#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
+#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
+
+
+int register_android_media_JetPlayer(JNIEnv *env)
+{
+    jclass jetPlayerClass = NULL;
+    javaJetPlayerFields.jetClass = NULL;
+    javaJetPlayerFields.postNativeEventInJava = NULL;
+    javaJetPlayerFields.nativePlayerInJavaObj = NULL;
+    
+    // Get the JetPlayer java class
+    jetPlayerClass = env->FindClass(kClassPathName);
+    if (jetPlayerClass == NULL) {
+        LOGE("Can't find %s", kClassPathName);
+        return -1;
+    }
+    javaJetPlayerFields.jetClass = (jclass)env->NewGlobalRef(jetPlayerClass);
+
+    // Get the mNativePlayerInJavaObj variable field
+    javaJetPlayerFields.nativePlayerInJavaObj = env->GetFieldID(
+            jetPlayerClass,
+            JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "I");
+    if (javaJetPlayerFields.nativePlayerInJavaObj == NULL) {
+        LOGE("Can't find AudioTrack.%s", JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME);
+        return -1;
+    }
+
+    // Get the callback to post events from this native code to Java
+    javaJetPlayerFields.postNativeEventInJava = env->GetStaticMethodID(javaJetPlayerFields.jetClass,
+            JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;III)V");
+    if (javaJetPlayerFields.postNativeEventInJava == NULL) {
+        LOGE("Can't find Jet.%s", JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME);
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+            kClassPathName, gMethods, NELEM(gMethods));
+}
index 02a4083..4e70f63 100644 (file)
@@ -2,19 +2,20 @@
 **
 ** Copyright 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 
+** 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 
+**     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 
+** 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 "JNIHelp.h"
 #include "jni.h"
 #include "android_runtime/AndroidRuntime.h"
 #include <utils/misc.h>
@@ -100,6 +101,7 @@ static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)
                  LINUX_REBOOT_CMD_RESTART2, (char*) chars);
         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.
     }
+    jniThrowIOException(env, errno);
 #endif
 }
 
index 843d293..9d62d89 100644 (file)
 #include <stdio.h>
 #include <assert.h>
 
-#include <graphics/SkCanvas.h>
-#include <graphics/SkDevice.h>
-#include <graphics/SkGLCanvas.h>
-#include <graphics/SkPaint.h>
+#include <core/SkCanvas.h>
+#include <core/SkDevice.h>
+#include <core/SkPaint.h>
+#include <utils/SkGLCanvas.h>
 #include "GraphicsJNI.h"
 
 #include "jni.h"
index a985c24..8bacc74 100644 (file)
@@ -23,8 +23,8 @@
 
 #include <ui/EGLNativeWindowSurface.h>
 #include <ui/Surface.h>
-#include <graphics/SkBitmap.h>
-#include <graphics/SkPixelRef.h>
+#include <SkBitmap.h>
+#include <SkPixelRef.h>
 
 namespace android {
 
index 2e04885..29c1f52 100644 (file)
     <!-- Allows access to hardware peripherals.  Intended only for hardware testing -->
     <permission android:name="android.permission.HARDWARE_TEST"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="normal"
+        android:protectionLevel="signature"
         android:label="@string/permlab_hardware_test"
         android:description="@string/permdesc_hardware_test" />
 
diff --git a/core/res/res/drawable/ic_menu_login.png b/core/res/res/drawable/ic_menu_login.png
new file mode 100644 (file)
index 0000000..2b856bc
Binary files /dev/null and b/core/res/res/drawable/ic_menu_login.png differ
diff --git a/core/res/res/drawable/ic_menu_notifications.png b/core/res/res/drawable/ic_menu_notifications.png
new file mode 100644 (file)
index 0000000..866d4e0
Binary files /dev/null and b/core/res/res/drawable/ic_menu_notifications.png differ
diff --git a/core/res/res/drawable/ic_menu_refresh.png b/core/res/res/drawable/ic_menu_refresh.png
new file mode 100644 (file)
index 0000000..77d70dd
Binary files /dev/null and b/core/res/res/drawable/ic_menu_refresh.png differ
index 875b0ff..3bd1a6a 100644 (file)
@@ -15,8 +15,8 @@
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+android:id/background" android:drawable="@android:drawable/btn_rating_star_off_normal" />
-    <item android:id="@+android:id/secondaryProgress" android:drawable="@android:drawable/btn_rating_star_off_normal" />
-    <item android:id="@+android:id/progress" android:drawable="@android:drawable/btn_rating_star_on_normal" />
+    <item android:id="@+android:id/background" android:drawable="@android:drawable/ratingbar_full_empty" />
+    <item android:id="@+android:id/secondaryProgress" android:drawable="@android:drawable/ratingbar_full_empty" />
+    <item android:id="@+android:id/progress" android:drawable="@android:drawable/ratingbar_full_filled" />
 </layer-list>
 
diff --git a/core/res/res/drawable/ratingbar_full_empty.xml b/core/res/res/drawable/ratingbar_full_empty.xml
new file mode 100644 (file)
index 0000000..527efc3
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the rating bar drawable that is used to a show a filled star. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_off_pressed" />
+
+    <item android:state_focused="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_off_selected" />
+
+    <item android:state_selected="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_off_selected" />
+
+    <item android:drawable="@drawable/btn_rating_star_off_normal" />
+
+</selector>
diff --git a/core/res/res/drawable/ratingbar_full_filled.xml b/core/res/res/drawable/ratingbar_full_filled.xml
new file mode 100644 (file)
index 0000000..e95ba6d
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the rating bar drawable that is used to a show a filled star. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_on_pressed" />
+
+    <item android:state_focused="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_on_selected" />
+
+    <item android:state_selected="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/btn_rating_star_on_selected" />
+
+    <item android:drawable="@drawable/btn_rating_star_on_normal" />
+
+</selector>
diff --git a/core/res/res/drawable/seek_thumb.xml b/core/res/res/drawable/seek_thumb.xml
new file mode 100644 (file)
index 0000000..7fe51b3
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the rating bar drawable that is used to a show a filled star. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/seek_thumb_pressed" />
+
+    <item android:state_focused="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/seek_thumb_selected" />
+
+    <item android:state_selected="true"
+          android:state_window_focused="true"
+          android:drawable="@drawable/seek_thumb_selected" />
+
+    <item android:drawable="@drawable/seek_thumb_normal" />
+
+</selector>
diff --git a/core/res/res/drawable/seek_thumb_pressed.png b/core/res/res/drawable/seek_thumb_pressed.png
new file mode 100644 (file)
index 0000000..dbaae91
Binary files /dev/null and b/core/res/res/drawable/seek_thumb_pressed.png differ
diff --git a/core/res/res/drawable/seek_thumb_selected.png b/core/res/res/drawable/seek_thumb_selected.png
new file mode 100644 (file)
index 0000000..dbaae91
Binary files /dev/null and b/core/res/res/drawable/seek_thumb_selected.png differ
index ac3777c..c88b0a2 100644 (file)
     android:id="@+id/search_bar"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
-    android:paddingBottom="14dip"
+    android:paddingBottom="200dip"
     android:orientation="vertical" 
     android:focusable="true"
     android:descendantFocusability="afterDescendants">
+    <!-- android:paddingBottom="14dip"  TODO MUST FIX - it's a hack to get the popup to show -->
 
     <!-- Outer layout defines the entire search bar at the top of the screen -->
     <!-- Bottom padding of 16 is due to the graphic, with 9 extra pixels of drop
             android:gravity="center_vertical"
             android:baselineAligned="false" >
 
-            <EditText
+            <AutoCompleteTextView
                 android:id="@+id/search_src_text"
                 android:layout_height="wrap_content"
                 android:layout_width="0dip"
                 android:layout_weight="1.0"
                 android:paddingLeft="8dip"
                 android:paddingRight="6dip"
-                android:inputType="text|textAutoComplete" />
+                android:inputType="text|textAutoComplete"
+                android:completionThreshold="1" />
+                <!-- android:focusableInTouchMode="false" -->
+                <!-- android:singleLine="true" -->
+                <!-- android:selectAllOnFocus="true" -->
                 
-            <ImageButton android:id="@+id/search_go_btn"
+            <!-- This button can switch between text and icon "modes" -->
+            <Button 
+                android:id="@+id/search_go_btn"
                 android:layout_marginLeft="1dip"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:src="@android:drawable/ic_btn_search"
+                android:drawableLeft="@android:drawable/ic_btn_search"
             />
 
         </LinearLayout>
         
     </LinearLayout>
 
-    <!-- The margintop of -21 adjusts the listview to abut the bottom of the edittext. -->
-    <ListView 
-        android:id="@+id/search_suggest_list"
-        android:layout_marginTop="-21dip"
-        android:layout_width="200dip"
-        android:layout_height="fill_parent"
-        android:background="@android:drawable/spinner_dropdown_background_down"
-        android:divider="@android:drawable/divider_horizontal_bright" 
-        android:cacheColorHint="#FFFFFFFF" />
-
 </LinearLayout>
diff --git a/core/res/res/values-fr/arrays.xml b/core/res/res/values-fr/arrays.xml
new file mode 100644 (file)
index 0000000..f9c904b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
new file mode 100644 (file)
index 0000000..4f21761
--- /dev/null
@@ -0,0 +1,723 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="byteShort">"O"</string>
+    <string name="kilobyteShort">"Ko"</string>
+    <string name="megabyteShort">"Mo"</string>
+    <string name="gigabyteShort">"Go"</string>
+    <string name="terabyteShort">"To"</string>
+    <string name="petabyteShort">"Po"</string>
+    <string name="untitled">"&lt;sans titre&gt;"</string>
+    <string name="ellipsis">"…"</string>
+    <string name="emptyPhoneNumber">"(Aucun numéro de téléphone)"</string>
+    <string name="unknownName">"(Inconnu)"</string>
+    <string name="defaultVoiceMailAlphaTag">"Messagerie vocale"</string>
+    <string name="defaultMsisdnAlphaTag">"MSISDN1"</string>
+    <string name="mmiError">"Problème de connexion ou code MMI non valide."</string>
+    <string name="serviceEnabled">"Le service a été activé."</string>
+    <string name="serviceEnabledFor">"Ce service a été activé pour :"</string>
+    <string name="serviceDisabled">"Ce service a été désactivé."</string>
+    <string name="serviceRegistered">"Enregistrement réussi."</string>
+    <string name="serviceErased">"Effacement réussi."</string>
+    <string name="passwordIncorrect">"Le mot de passe est incorrect."</string>
+    <string name="mmiComplete">"MMI terminé."</string>
+    <string name="badPin">"L\'ancien code PIN saisi est incorrect."</string>
+    <string name="badPuk">"Le code PUK saisi est incorrect."</string>
+    <string name="mismatchPin">"Les codes PIN saisis ne correspondent pas."</string>
+    <string name="invalidPin">"Saisissez un code PIN comptant 4 à 8 chiffres."</string>
+    <!-- no translation found for needPuk (919668385956251611) -->
+    <skip />
+    <string name="needPuk2">"Saisissez le code PUK2 pour débloquer la carte SIM."</string>
+    <string name="ClipMmi">"Identifiant des appels entrants"</string>
+    <string name="ClirMmi">"Identifiant des appels sortants"</string>
+    <string name="CfMmi">"Transfert d\'appel"</string>
+    <string name="CwMmi">"Appel en attente"</string>
+    <string name="BaMmi">"Interdiction d\'appel"</string>
+    <string name="PwdMmi">"Modification du mot de passe"</string>
+    <string name="PinMmi">"Modification du code PIN"</string>
+    <string name="CLIRDefaultOnNextCallOn">"Par défaut, les identifiants d\'appel sont restreints. Appel suivant : restreint"</string>
+    <string name="CLIRDefaultOnNextCallOff">"Par défaut, les identifiants d\'appel sont restreints. Appel suivant : non restreint"</string>
+    <string name="CLIRDefaultOffNextCallOn">"Par défaut, les identifiants d\'appel ne sont pas restreints. Appel suivant : restreint"</string>
+    <string name="CLIRDefaultOffNextCallOff">"Par défaut, les identifiants d\'appel ne sont pas restreints. Appel suivant : non restreint"</string>
+    <string name="serviceNotProvisioned">"Ce service n\'est pas pris en charge."</string>
+    <string name="CLIRPermanent">"Le paramètre Identifiant d\'appel ne peut pas être modifié."</string>
+    <string name="serviceClassVoice">"Voix"</string>
+    <string name="serviceClassData">"Données"</string>
+    <string name="serviceClassFAX">"Télécopie"</string>
+    <string name="serviceClassSMS">"SMS"</string>
+    <string name="serviceClassDataAsync">"Asynchronisées"</string>
+    <string name="serviceClassDataSync">"Synchronisées"</string>
+    <string name="serviceClassPacket">"Paquet"</string>
+    <string name="serviceClassPAD">"PAD"</string>
+    <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
+    <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
+    <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
+    <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
+    <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
+    <string name="httpErrorOk">"OK"</string>
+    <string name="httpError">"La page Internet contient une erreur."</string>
+    <string name="httpErrorLookup">"L\'URL n\'a pas pu être trouvée."</string>
+    <string name="httpErrorUnsupportedAuthScheme">"Le processus d\'authentification du site n\'est pas pris en charge."</string>
+    <string name="httpErrorAuth">"Échec de l\'authentification."</string>
+    <string name="httpErrorProxyAuth">"Échec de l\'authentification par un serveur proxy."</string>
+    <string name="httpErrorConnect">"Échec de la connexion au serveur."</string>
+    <string name="httpErrorIO">"Échec de la communication avec le serveur. Merci de réessayer ultérieurement."</string>
+    <string name="httpErrorTimeout">"Délai de connexion au serveur dépassé."</string>
+    <string name="httpErrorRedirectLoop">"Cette page contient trop de redirections de serveurs."</string>
+    <string name="httpErrorUnsupportedScheme">"Ce protocole n\'est pas pris en charge."</string>
+    <string name="httpErrorFailedSslHandshake">"Aucune connexion sécurisée n\'a pu être établie."</string>
+    <string name="httpErrorBadUrl">"Cette page n\'a pas pu être ouverte car l\'URL n\'est pas valide."</string>
+    <string name="httpErrorFile">"Impossible d\'accéder au fichier."</string>
+    <string name="httpErrorFileNotFound">"Le fichier demandé n\'a pas été trouvé."</string>
+    <string name="httpErrorTooManyRequests">"Trop de requêtes sont en cours de traitement. Merci de réessayer ultérieurement."</string>
+    <string name="contentServiceSync">"Synchronisation"</string>
+    <string name="contentServiceSyncNotificationTitle">"Synchroniser"</string>
+    <string name="contentServiceTooManyDeletesNotificationDesc">"Trop de contenus effacés (<xliff:g id="CONTENT_TYPE">%s</xliff:g>)."</string>
+    <string name="low_memory">"La mémoire du téléphone est pleine ! Effacez des fichiers pour libérer de l\'espace."</string>
+    <string name="me">"Moi"</string>
+    <string name="power_dialog">"Options du téléphone"</string>
+    <string name="silent_mode">"Mode silencieux"</string>
+    <string name="turn_on_radio">"Activer le mode sans fil"</string>
+    <string name="turn_off_radio">"Désactiver le mode sans fil"</string>
+    <string name="screen_lock">"Verrouillage de l\'écran"</string>
+    <string name="power_off">"Éteindre"</string>
+    <string name="shutdown_progress">"Le téléphone s\'éteint..."</string>
+    <string name="shutdown_confirm">"Votre téléphone s\'éteindra."</string>
+    <string name="no_recent_tasks">"Aucune application récente."</string>
+    <string name="global_actions">"Options du téléphone"</string>
+    <string name="global_action_lock">"Verrouillage de l\'écran"</string>
+    <string name="global_action_power_off">"Éteindre"</string>
+    <string name="global_action_toggle_silent_mode">"Mode silencieux"</string>
+    <string name="global_action_silent_mode_on_status">"Le son est désactivé."</string>
+    <string name="global_action_silent_mode_off_status">"Le son est activé."</string>
+    <string name="safeMode">"Mode sécurisé"</string>
+    <string name="permgrouplab_costMoney">"Services payants"</string>
+    <string name="permgroupdesc_costMoney">"Permettre aux applications d\'effectuer des opérations payantes."</string>
+    <string name="permgrouplab_messages">"Vos messages"</string>
+    <string name="permgroupdesc_messages">"Lire et rédiger des SMS, e-mails et autres messages."</string>
+    <string name="permgrouplab_personalInfo">"Vos informations personnelles"</string>
+    <string name="permgroupdesc_personalInfo">"Accédez directement aux contacts et au calendrier enregistrés sur votre téléphone."</string>
+    <string name="permgrouplab_location">"Votre position géographique"</string>
+    <string name="permgroupdesc_location">"Suivre votre position géographique"</string>
+    <string name="permgrouplab_network">"Communications réseau"</string>
+    <string name="permgroupdesc_network">"Permettre à des applications d\'accéder à différentes fonctionnalités réseau."</string>
+    <string name="permgrouplab_accounts">"Vos comptes Google"</string>
+    <string name="permgroupdesc_accounts">"Accédez aux comptes Google disponibles."</string>
+    <string name="permgrouplab_hardwareControls">"Contrôles du matériel"</string>
+    <string name="permgroupdesc_hardwareControls">"Accéder directement au matériel de l\'appareil."</string>
+    <string name="permgrouplab_phoneCalls">"Appels"</string>
+    <string name="permgroupdesc_phoneCalls">"Suivre, enregistrer et traiter les appels téléphoniques"</string>
+    <string name="permgrouplab_systemTools">"Outils système"</string>
+    <string name="permgroupdesc_systemTools">"Accès de faible niveau et contrôle du système."</string>
+    <string name="permgrouplab_developmentTools">"Outils de développement"</string>
+    <string name="permgroupdesc_developmentTools">"Ces fonctionnalités sont réservées aux développeurs d\'applications."</string>
+    <string name="permlab_statusBar">"désactiver ou modifier la barre d\'état"</string>
+    <string name="permdesc_statusBar">"Permettre à une application de désactiver la barre d\'état ou d\'ajouter/supprimer des icônes du système."</string>
+    <string name="permlab_expandStatusBar">"agrandir/réduire la barre d\'état"</string>
+    <string name="permdesc_expandStatusBar">"Permettre à l\'application de réduire ou d\'agrandir la barre d\'état."</string>
+    <string name="permlab_processOutgoingCalls">"intercepter les appels sortants"</string>
+    <string name="permdesc_processOutgoingCalls">"Permettre à l\'application de traiter des appels en cours et de modifier le numéro à composer. Des applications malveillantes peuvent suivre, rediriger ou empêcher des appels sortants."</string>
+    <string name="permlab_receiveSms">"recevoir SMS"</string>
+    <string name="permdesc_receiveSms">"Permettre à une application de recevoir et traiter des messages SMS. Des applications malveillantes peuvent suivre vos messages ou les effacer sans que vous en ayez pris connaissance."</string>
+    <string name="permlab_receiveMms">"recevoir des MMS"</string>
+    <string name="permdesc_receiveMms">"Permettre à une application de recevoir et traiter des messages MMS. Des applications malveillantes peuvent utiliser cette fonctionnalité pour suivre vos messages ou les effacer sans que vous en ayez pris connaissance."</string>
+    <string name="permlab_sendSms">"envoyer des messages SMS"</string>
+    <string name="permdesc_sendSms">"Permettre aux applications d\'envoyer des messages SMS. Des applications malveillantes peuvent entraîner des frais en envoyant des messages sans vous demander confirmation."</string>
+    <string name="permlab_readSms">"lire les SMS ou MMS"</string>
+    <string name="permdesc_readSms">"Permettre à l\'application de lire les messages SMS enregistrés dans la mémoire de votre téléphone ou sur votre carte SD. Des applications malveillantes peuvent lire vos messages confidentiels."</string>
+    <string name="permlab_writeSms">"modifier un SMS ou un MMS"</string>
+    <string name="permdesc_writeSms">"Permettre à une application de manipuler des messages SMS enregistrés sur votre téléphone ou sur votre carte SIM. Des applications malveillantes peuvent ainsi supprimer vos messages."</string>
+    <string name="permlab_receiveWapPush">"recevoir WAP"</string>
+    <string name="permdesc_receiveWapPush">"Permettre à l\'application de recevoir et de traiter des messages WAP. Des applications malveillantes peuvent ainsi suivre vos messages ou les effacer sans que vous en ayez pris connaissance."</string>
+    <string name="permlab_getTasks">"récupérer les applications en cours d\'exécution"</string>
+    <string name="permdesc_getTasks">"Permettre à l\'application de récupérer des informations sur des tâches en cours d\'exécution ou récemment utilisées. Des applications malveillantes peuvent ainsi obtenir des informations d\'ordre privé concernant d\'autres applications."</string>
+    <string name="permlab_reorderTasks">"trier les applications en cours d\'exécution"</string>
+    <string name="permdesc_reorderTasks">"Permettre à une application de placer des tâches au premier plan ou en arrière-plan. Des applications malveillantes peuvent se placer inopinément au premier plan sans votre autorisation."</string>
+    <string name="permlab_setDebugApp">"activer le débogage de l\'application"</string>
+    <string name="permdesc_setDebugApp">"Permettre à une application d\'activer le mode de débogage d\'une autre application. Des applications malveillantes peuvent utiliser cette fonctionnalité pour interrompre d\'autres applications de façon inopinée."</string>
+    <string name="permlab_changeConfiguration">"modifier les paramètres de l\'IU"</string>
+    <string name="permdesc_changeConfiguration">"Permettre à une application de modifier la configuration actuelle (par ex. : la taille de la police générale ou des paramètres locaux)."</string>
+    <string name="permlab_restartPackages">"relancer d\'autres applications"</string>
+    <string name="permdesc_restartPackages">"Permettre à une application de forcer le lancement d\'autres applications."</string>
+    <string name="permlab_setProcessForeground">"empêcher l\'interruption"</string>
+    <string name="permdesc_setProcessForeground">"Permettre à une application de placer tout processus au premier plan afin qu\'il ne puisse pas être interrompu. Les applications normales ne devraient jamais nécessiter cette fonctionnalité."</string>
+    <string name="permlab_forceBack">"obliger l\'application à se fermer"</string>
+    <string name="permdesc_forceBack">"Permettre à une application de forcer une autre application au premier plan à se fermer et à passer en arrière-plan. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_dump">"récupérer l\'état interne du système"</string>
+    <string name="permdesc_dump">"Permettre à l\'application de récupérer l\'état interne du système. Des applications malveillantes peuvent obtenir de nombreuses informations personnelles et sécurisées auxquelles elles ne devraient pas avoir accès."</string>
+    <string name="permlab_addSystemService">"éditer des services à faible niveau"</string>
+    <string name="permdesc_addSystemService">"Permettre à l\'application d\'éditer ses propres services de système de faible niveau. Des applications malveillantes peuvent prendre le contrôle du système et voler ou corrompre les données qu\'il contient."</string>
+    <string name="permlab_runSetActivityWatcher">"suivre et contrôler tout lancement d\'application"</string>
+    <string name="permdesc_runSetActivityWatcher">"Permettre à une application de suivre et de contrôler la façon dont le système lance des activités. Des applications malveillantes peuvent entièrement déstabiliser le système. Cette autorisation est uniquement nécessaire au développement et non pour l\'utilisation normale du téléphone."</string>
+    <string name="permlab_broadcastPackageRemoved">"envoyer une diffusion de paquet supprimé"</string>
+    <string name="permdesc_broadcastPackageRemoved">"Permettre à une application de diffuser une notification lorsqu\'un paquet d\'application a été supprimé. Des applications malveillantes peuvent utiliser cette fonctionnalité pour interrompre d\'autres applications en cours d\'exécution."</string>
+    <string name="permlab_broadcastSmsReceived">"envoyer une diffusion de SMS reçu"</string>
+    <string name="permdesc_broadcastSmsReceived">"Permettre à une application de diffuser une notification lors de la réception d\'un message SMS. Des applications malveillantes peuvent utiliser cette fonctionnalité pour falsifier des messages SMS entrants."</string>
+    <string name="permlab_broadcastWapPush">"envoyer une diffusion de réception de WAP PUSH"</string>
+    <string name="permdesc_broadcastWapPush">"Permettre à une application d\'envoyer une notification lorsqu\'un message WAP PUSH a été reçu. Des applications malveillantes peuvent utiliser cette fonctionnalité pour créer de faux accusés de réception de MMS ou remplacer le contenu de toute page Internet par des données malveillantes."</string>
+    <string name="permlab_setProcessLimit">"limiter le nombre de processus en cours d\'exécution"</string>
+    <string name="permdesc_setProcessLimit">"Permettre à une application de contrôler le nombre de processus maximal exécutés en même temps. Les applications normales n\'ont jamais recours à cette fonctionnalité."</string>
+    <string name="permlab_setAlwaysFinish">"fermer toutes les applications en tâche de fond"</string>
+    <string name="permdesc_setAlwaysFinish">"Permettre à une application de vérifier si des activités sont systématiquement interrompues lorsqu\'elles sont placées en tâche de fond. Cette fonctionnalité n\'est jamais utilisée par les applications normales."</string>
+    <string name="permlab_fotaUpdate">"installer automatiquement les mises à jour système"</string>
+    <string name="permdesc_fotaUpdate">"Permettre à une application de recevoir des notifications concernant des mises à jour système imminentes et de lancer leur installation. Des applications malveillantes peuvent utiliser cette fonctionnalité pour corrompre le système avec des mises à jour non autorisées ou plus généralement, pour interférer avec le processus de mise à jour."</string>
+    <string name="permlab_batteryStats">"modifier les statistiques de la batterie"</string>
+    <string name="permdesc_batteryStats">"Autoriser la modification des statistiques de la batterie. Cette fonctionnalité ne concerne pas les applications normales."</string>
+    <string name="permlab_internalSystemWindow">"afficher les fenêtres non autorisées"</string>
+    <string name="permdesc_internalSystemWindow">"Permettre de créer des fenêtres conçues pour l\'interface utilisateur du système interne. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
+    <string name="permlab_systemAlertWindow">"afficher les alertes au niveau du système"</string>
+    <string name="permdesc_systemAlertWindow">"Permettre à une application d\'afficher des fenêtres d\'alerte système. Des applications malveillantes peuvent masquer la totalité de l\'écran du téléphone."</string>
+    <string name="permlab_setAnimationScale">"modifier la vitesse générale des animations"</string>
+    <string name="permdesc_setAnimationScale">"Permettre à une application de modifier à tout moment la vitesse globale des animations (pour les rendre plus lentes ou plus rapides)."</string>
+    <string name="permlab_manageAppTokens">"gérer les repères des applications"</string>
+    <string name="permdesc_manageAppTokens">"Permettre à des applications de créer et gérer leurs propres repères en contournant leur axe de profondeur normal. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_injectEvents">"appuyer sur des touches ou contrôler des commandes"</string>
+    <string name="permdesc_injectEvents">"Permettre à une application de fournir ces propres commandes (touches enfoncées, etc.) à d\'autres applications. Des applications malveillantes peuvent utiliser cette fonctionnalité pour prendre le contrôle de votre téléphone."</string>
+    <string name="permlab_readInputState">"enregistrer le texte saisi et les actions effectuées"</string>
+    <string name="permdesc_readInputState">"Permettre à des applications d\'identifier les touches sur lesquelles vous appuyez même lorsque vous utilisez une autre application (lors de la saisie d\'un mot de passe, par exemple). Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <!-- no translation found for permlab_bindInputMethod (3360064620230515776) -->
+    <skip />
+    <!-- no translation found for permdesc_bindInputMethod (3734838321027317228) -->
+    <skip />
+    <string name="permlab_setOrientation">"modifier l\'orientation de l\'écran"</string>
+    <string name="permdesc_setOrientation">"Permettre à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_signalPersistentProcesses">"envoyer des signaux Linux aux applications"</string>
+    <string name="permdesc_signalPersistentProcesses">"Permettre à une application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
+    <string name="permlab_persistentActivity">"exécuter l\'application en continu"</string>
+    <string name="permdesc_persistentActivity">"Permettre à une application de perdurer en partie afin que le système ne puisse pas l\'utiliser pour d\'autres applications."</string>
+    <string name="permlab_deletePackages">"effacer des applications"</string>
+    <string name="permdesc_deletePackages">"Permettre à une application d\'effacer des paquets de données Android. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer des applications importantes."</string>
+    <string name="permlab_clearAppUserData">"effacer les données d\'autres applications"</string>
+    <string name="permdesc_clearAppUserData">"Permettre à une application d\'effacer les données de l\'utilisateur."</string>
+    <string name="permlab_deleteCacheFiles">"effacer le cache d\'autres applications"</string>
+    <string name="permdesc_deleteCacheFiles">"Permettre à une application d\'effacer des fichiers du cache."</string>
+    <string name="permlab_getPackageSize">"évaluer l\'espace de stockage de l\'application"</string>
+    <string name="permdesc_getPackageSize">"Permettre à une application de récupérer la taille de son code, de ses données et de son cache."</string>
+    <string name="permlab_installPackages">"installer directement les applications"</string>
+    <string name="permdesc_installPackages">"Permettre à une application d\'installer des nouveaux paquets de données ou des mises à jour Android. Des applications malveillantes peuvent utiliser cette fonctionnalité pour ajouter de nouvelles applications disposant d\'autorisations anormalement élevées."</string>
+    <string name="permlab_clearAppCache">"effacer les données du cache de toutes les applications"</string>
+    <string name="permdesc_clearAppCache">"Permettre à une application de libérer de l\'espace dans la mémoire du téléphone en supprimant des fichiers du cache des applications. Cet accès, en général très limité, est réservé aux processus système."</string>
+    <string name="permlab_readLogs">"lire les fichiers journaux du système"</string>
+    <string name="permdesc_readLogs">"Permettre à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone, mais sans récupérer des informations d\'ordre personnel ou privé."</string>
+    <string name="permlab_diagnostic">"lire/écrire dans les ressources appartenant au diag"</string>
+    <string name="permdesc_diagnostic">"Permettre à une application de lire et écrire toute ressource appartenant au groupe diag (par exemple, les fichiers in/dev). Ceci peut affecter la stabilité et la sécurité du système. Cette fonctionnalité est UNIQUEMENT réservée aux diagnostics matériels effectués par le fabricant ou l\'opérateur."</string>
+    <string name="permlab_changeComponentState">"activer ou désactiver des éléments de l\'application"</string>
+    <!-- no translation found for permdesc_changeComponentState (4569107043246700630) -->
+    <skip />
+    <string name="permlab_setPreferredApplications">"définir les applications préférées"</string>
+    <string name="permdesc_setPreferredApplications">"Permettre à une application de modifier vos applications préférées. Des applications malveillantes peuvent utiliser cette fonctionnalité pour modifier discrètement les applications en cours d\'exécution, en imitant vos applications existantes afin de récupérer des données personnelles vous concernant."</string>
+    <string name="permlab_writeSettings">"modifier les paramètres système généraux"</string>
+    <string name="permdesc_writeSettings">"Permettre à une application de modifier les données des paramètres du système. Des applications malveillantes peuvent utiliser cette fonctionnalité pour corrompre la configuration de votre système."</string>
+    <!-- no translation found for permlab_writeSecureSettings (204676251876718288) -->
+    <skip />
+    <!-- no translation found for permdesc_writeSecureSettings (4116616249170428132) -->
+    <skip />
+    <string name="permlab_writeGservices">"modifier la carte des services Google"</string>
+    <string name="permdesc_writeGservices">"Permettre à une application de modifier la carte des services Google. Cette fonctionnalité n\'est pas conçue pour les applications normales."</string>
+    <string name="permlab_receiveBootCompleted">"lancer automatiquement au démarrage"</string>
+    <string name="permdesc_receiveBootCompleted">"Permettre à une application de se lancer dès la fin du démarrage du système. Ceci peut rallonger le temps nécessaire au téléphone pour démarrer. L\'application étant alors constamment en cours d\'exécution, le fonctionnement général du téléphone peut s\'en trouver ralenti."</string>
+    <string name="permlab_broadcastSticky">"envoyer une diffusion persistante"</string>
+    <string name="permdesc_broadcastSticky">"Permettre à une application d\'envoyer des diffusions \"persistantes\", qui perdurent après la fin de la diffusion. Des applications malveillantes peuvent ainsi ralentir le téléphone ou le rendre instable en l\'obligeant à utiliser trop de mémoire."</string>
+    <string name="permlab_readContacts">"lire les données des contacts"</string>
+    <string name="permdesc_readContacts">"Permettre à une application de lire toutes les données des contacts (adresses) enregistrées sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonctionnalité pour envoyer vos données à d\'autres personnes."</string>
+    <string name="permlab_writeContacts">"écrire les données du contact"</string>
+    <string name="permdesc_writeContacts">"Permettre à une application de modifier toutes les données de contacts (adresses) enregistrées sur le téléphone. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier vos données."</string>
+    <string name="permlab_writeOwnerData">"écrire les données du propriétaire"</string>
+    <string name="permdesc_writeOwnerData">"Permettre à une application de modifier les données du propriétaire du téléphone enregistrées sur votre appareil. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier ces données."</string>
+    <string name="permlab_readOwnerData">"lire des données du propriétaire"</string>
+    <string name="permdesc_readOwnerData">"Permettre à une application de lire les données du propriétaire du téléphone enregistrées sur votre appareil. Des applications malveillantes peuvent utiliser cette fonctionnalité pour lire ces données."</string>
+    <string name="permlab_readCalendar">"lire les données du calendrier"</string>
+    <string name="permdesc_readCalendar">"Permettre à une application de lire tous les événements du calendrier enregistré sur le téléphone. Des applications malveillantes peuvent utiliser cette fonctionnalité pour envoyer les événements de votre calendrier à d\'autres personnes."</string>
+    <string name="permlab_writeCalendar">"écrire les données du calendrier"</string>
+    <string name="permdesc_writeCalendar">"Permettre à une application de modifier les événements du calendrier enregistré sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier les données de votre calendrier."</string>
+    <string name="permlab_accessMockLocation">"créer de fausses sources géographiques à des fins de test"</string>
+    <string name="permdesc_accessMockLocation">"Créer des fausses sources de position géographique à des fins de test. Des applications malveillantes peuvent utiliser cette fonctionnalité pour contourner la position géographique et/ou l\'état fournis par des sources réelles comme le GPS ou les fournisseurs d\'accès."</string>
+    <string name="permlab_accessLocationExtraCommands">"accéder à des commandes de fournisseur de position géographique supplémentaires"</string>
+    <string name="permdesc_accessLocationExtraCommands">"Accéder à des commandes de fournisseur de position géographique supplémentaires. Des applications malveillantes peuvent utiliser cette fonctionnalité pour interférer avec l\'utilisation du GPS ou d\'autres sources de positionnement géographique."</string>
+    <string name="permlab_accessFineLocation">"position géographique précise (GPS)"</string>
+    <string name="permdesc_accessFineLocation">"Accéder à des sources de positionnement géographique précises comme le Global Positioning System (GPS) sur le téléphone, lorsque ces services sont disponibles. Des applications malveillantes peuvent utiliser cette fonctionnalité pour déterminer l\'endroit où vous vous trouvez et puiser davantage dans les réserves de la batterie."</string>
+    <string name="permlab_accessCoarseLocation">"position géographique approximative (selon le réseau)"</string>
+    <string name="permdesc_accessCoarseLocation">"Accéder à des sources de positionnement géographique approximatif (par ex. des bases de données de réseaux mobiles) pour déterminer la position géographique du téléphone, lorsque cette option est disponible. Des applications malveillantes peuvent utiliser cette fonctionnalité pour déterminer approximativement l\'endroit où vous vous trouvez."</string>
+    <string name="permlab_accessSurfaceFlinger">"accéder à SurfaceFlinger"</string>
+    <string name="permdesc_accessSurfaceFlinger">"Permettre à certaines applications d\'utiliser les fonctionnalités SurfaceFlinger de faible niveau."</string>
+    <string name="permlab_readFrameBuffer">"lire le tampon graphique"</string>
+    <string name="permdesc_readFrameBuffer">"Permettre aux applications de lire/utiliser le contenu du tampon graphique."</string>
+    <string name="permlab_modifyAudioSettings">"modifier les paramètres audio"</string>
+    <string name="permdesc_modifyAudioSettings">"Permettre à l\'application de modifier les paramètres audio généraux (par ex. : le volume et le transfert)."</string>
+    <string name="permlab_recordAudio">"enregistrer un fichier audio"</string>
+    <string name="permdesc_recordAudio">"Permettre à l\'application d\'accéder au chemin d\'enregistrement audio."</string>
+    <string name="permlab_camera">"prendre des photos"</string>
+    <string name="permdesc_camera">"Permettre à l\'application de prendre des clichés avec l\'appareil photo. Cette fonctionnalité permet à l\'application de récupérer à tout moment les images perçues par l\'appareil."</string>
+    <string name="permlab_brick">"désactiver le téléphone de façon permanente"</string>
+    <string name="permdesc_brick">"Permettre à l\'application de désactiver définitivement le téléphone. Cette fonctionnalité est très dangereuse."</string>
+    <string name="permlab_reboot">"forcer le redémarrage du téléphone"</string>
+    <string name="permdesc_reboot">"Permettre à l\'application d\'obliger le téléphone à redémarrer."</string>
+    <string name="permlab_mount_unmount_filesystems">"monter et démonter des systèmes de fichiers"</string>
+    <string name="permdesc_mount_unmount_filesystems">"Permettre à l\'application de monter et démonter des systèmes de fichiers pour des périphériques de stockage amovibles."</string>
+    <string name="permlab_vibrate">"contrôler le mode vibration"</string>
+    <string name="permdesc_vibrate">"Permettre à l\'application de contrôler le mode vibration."</string>
+    <string name="permlab_flashlight">"contrôler la lampe de poche"</string>
+    <string name="permdesc_flashlight">"Permettre à l\'application de contrôler la lampe de poche."</string>
+    <string name="permlab_hardware_test">"tester le matériel"</string>
+    <string name="permdesc_hardware_test">"Permettre à l\'application de contrôler différents périphériques à des fins de test matériel."</string>
+    <string name="permlab_callPhone">"appeler directement des numéros de téléphone"</string>
+    <string name="permdesc_callPhone">"Permettre à l\'application d\'appeler des numéros de téléphone sans votre intervention. Des applications malveillantes peuvent ainsi ajouter des appels non désirés à votre facture téléphonique. Sachez que cette fonctionnalité ne permet pas à l\'application d\'appeler des numéros d\'urgence."</string>
+    <string name="permlab_callPrivileged">"appeler directement tout numéro de téléphone"</string>
+    <string name="permdesc_callPrivileged">"Permettre à une application d\'appeler tout numéro de téléphone (y compris les numéros d\'urgence) sans votre intervention. Des applications malveillantes peuvent passer des appels non nécessaires ou illégitimes à des services d\'urgence."</string>
+    <string name="permlab_locationUpdates">"contrôler les notifications de mise à jour de position géographique"</string>
+    <string name="permdesc_locationUpdates">"Autoriser l\'activation/la désactivation des notifications de mises à jour de la position géographique provenant de la radio. Cette fonctionnalité n\'est pas conçue pour les applications normales."</string>
+    <string name="permlab_checkinProperties">"accéder aux propriétés d\'enregistrement"</string>
+    <string name="permdesc_checkinProperties">"Donner un accès en lecture/écriture à des propriétés chargées par le service d\'inscription. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_modifyPhoneState">"modifier l\'état du téléphone"</string>
+    <string name="permdesc_modifyPhoneState">"Permettre à une application de contrôler les fonctionnalités téléphoniques de l\'appareil. Une application bénéficiant de cette autorisation peut changer de réseau, éteindre et allumer la radio du téléphone, etc. sans vous en notifier."</string>
+    <string name="permlab_readPhoneState">"lire l\'état du téléphone"</string>
+    <string name="permdesc_readPhoneState">"Permettre à l\'application d\'accéder aux fonctionnalités d\'appel du téléphone. L\'application peut alors déterminer le numéro de l\'appareil, savoir si un appel est en cours, identifier le numéro appelé, etc."</string>
+    <string name="permlab_wakeLock">"empêcher le téléphone de passer en mode veille"</string>
+    <string name="permdesc_wakeLock">"Permettre à une application d\'empêcher votre téléphone de passer en mode veille."</string>
+    <string name="permlab_devicePower">"éteindre ou allumer le téléphone"</string>
+    <string name="permdesc_devicePower">"Permettre à l\'application d\'éteindre et d\'allumer le téléphone."</string>
+    <string name="permlab_factoryTest">"exécuter en mode Test d\'usine"</string>
+    <string name="permdesc_factoryTest">"Exécuter en tant que test fabricant de faible niveau et autoriser l\'accès au matériel du téléphone. Cette fonctionnalité est uniquement disponible lorsque le téléphone est en mode de test fabricant."</string>
+    <string name="permlab_setWallpaper">"configurer le fond d\'écran"</string>
+    <string name="permdesc_setWallpaper">"Permettre à une application de configurer le fond d\'écran du système."</string>
+    <string name="permlab_setWallpaperHints">"configurer les conseils de taille du fond d\'écran."</string>
+    <string name="permdesc_setWallpaperHints">"Permettre à une application de configurer les conseils de taille du fond d\'écran du système."</string>
+    <string name="permlab_masterClear">"réinitialiser le système à ses valeurs d\'usine"</string>
+    <string name="permdesc_masterClear">"Permettre à une application de réinitialiser entièrement le système afin qu\'il récupère ses valeurs d\'usine, et d\'effacer toutes les données, configurations et applications installées."</string>
+    <string name="permlab_setTimeZone">"configurer le fuseau horaire"</string>
+    <string name="permdesc_setTimeZone">"Permettre à l\'application de modifier le fuseau horaire du téléphone."</string>
+    <string name="permlab_getAccounts">"identifier des comptes connus"</string>
+    <string name="permdesc_getAccounts">"Permettre à une application d\'obtenir la liste des comptes connus du téléphone."</string>
+    <string name="permlab_accessNetworkState">"afficher l\'état du réseau"</string>
+    <string name="permdesc_accessNetworkState">"Permettre à une application d\'afficher l\'état de tous les réseaux."</string>
+    <string name="permlab_createNetworkSockets">"accès Internet complet"</string>
+    <string name="permdesc_createNetworkSockets">"Permettre à une application de créer des connecteurs réseau."</string>
+    <string name="permlab_writeApnSettings">"écrire les paramètres de Nom des points d\'accès"</string>
+    <string name="permdesc_writeApnSettings">"Permettre à une application de modifier les paramètres Nom des points d\'accès, comme le proxy ou le port de tout point d\'accès."</string>
+    <string name="permlab_changeNetworkState">"modifier la connectivité du réseau"</string>
+    <string name="permdesc_changeNetworkState">"Permettre à une application de modifier la connectivité du réseau."</string>
+    <string name="permlab_accessWifiState">"afficher l\'état du Wi-Fi"</string>
+    <string name="permdesc_accessWifiState">"Permettre à une application d\'afficher des informations concernant l\'état du Wi-Fi."</string>
+    <string name="permlab_changeWifiState">"modifier l\'état du Wi-Fi"</string>
+    <string name="permdesc_changeWifiState">"Permettre à une application de se connecter à des points d\'accès Wi-Fi, de s\'en déconnecter et de modifier des réseaux Wi-Fi configurés."</string>
+    <string name="permlab_bluetoothAdmin">"administration Bluetooth"</string>
+    <string name="permdesc_bluetoothAdmin">"Permettre à une application de configurer le téléphone Bluetooth local, d\'identifier des périphériques distants et de les associer au téléphone."</string>
+    <string name="permlab_bluetooth">"créer des connexions Bluetooth"</string>
+    <string name="permdesc_bluetooth">"Permettre à une application d\'obtenir la configuration du téléphone Bluetooth local et de créer et accepter des connexions à des appareils associés."</string>
+    <string name="permlab_disableKeyguard">"désactiver le verrouillage des touches"</string>
+    <string name="permdesc_disableKeyguard">"Permettre à une application de désactiver le  age des touches et toute sécurité par mot de passe. Un exemple légitime : votre téléphone désactive le clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
+    <string name="permlab_readSyncSettings">"lire les paramètres de synchronisation"</string>
+    <string name="permdesc_readSyncSettings">"Permettre à une application de lire les paramètres de synchronisation (par ex. : savoir si la synchronisation est activée pour les Contacts)."</string>
+    <string name="permlab_writeSyncSettings">"écrire les paramètres de synchronisation"</string>
+    <string name="permdesc_writeSyncSettings">"Permettre à une application de modifier les paramètres de synchronisation (par ex. si la synchronisation est activée pour les contacts)."</string>
+    <string name="permlab_readSyncStats">"lire les statistiques de synchronisation"</string>
+    <string name="permdesc_readSyncStats">"Permettre à une application de lire les statistiques de synchronisation (par ex. l\'historique des synchronisations effectuées)."</string>
+    <string name="permlab_subscribedFeedsRead">"lire les feeds auxquels vous êtes abonné"</string>
+    <string name="permdesc_subscribedFeedsRead">"Permettre à une application d\'obtenir des informations sur les feeds synchronisés récemment."</string>
+    <string name="permlab_subscribedFeedsWrite">"écrire les feeds auxquels vous êtes abonné"</string>
+    <string name="permdesc_subscribedFeedsWrite">"Permettre à une application de modifier vos feeds synchronisés actuels. Grâce à cette fonctionnalité, des applications malveillantes peuvent modifier vos feeds synchronisés."</string>
+    <!-- no translation found for phoneTypes:7 (9192514806975898961) -->
+    <!-- no translation found for emailAddressTypes:3 (2374913952870110618) -->
+    <!-- no translation found for postalAddressTypes:3 (4932682847595299369) -->
+    <!-- no translation found for imAddressTypes:3 (3145118944639869809) -->
+    <!-- no translation found for organizationTypes:2 (3455047468583965104) -->
+  <string-array name="imProtocols">
+    <item>"AIM"</item>
+    <item>"Windows Live"</item>
+    <item>"Yahoo"</item>
+    <item>"Skype"</item>
+    <item>"QQ"</item>
+    <item>"Google Talk"</item>
+    <item>"ICQ"</item>
+    <item>"Jabber"</item>
+  </string-array>
+    <!-- no translation found for keyguard_password_enter_pin_code (3731488827218876115) -->
+    <skip />
+    <string name="keyguard_password_wrong_pin_code">"Le code PIN est incorrect !"</string>
+    <string name="keyguard_label_text">"Pour débloquer le clavier, appuyez sur Menu puis sur 0."</string>
+    <string name="emergency_call_dialog_number_for_display">"Numéro d\'urgence"</string>
+    <string name="lockscreen_carrier_default">"(Aucun service)"</string>
+    <!-- no translation found for lockscreen_screen_locked (7288443074806832904) -->
+    <skip />
+    <string name="lockscreen_instructions_when_pattern_enabled">"Appuyez sur Menu pour débloquer le téléphone ou appeler un numéro d\'urgence"</string>
+    <string name="lockscreen_instructions_when_pattern_disabled">"Appuyez sur Menu pour débloquer le téléphone."</string>
+    <!-- no translation found for lockscreen_pattern_instructions (7478703254964810302) -->
+    <skip />
+    <string name="lockscreen_emergency_call">"Appel d\'urgence"</string>
+    <string name="lockscreen_pattern_correct">"Forme validée !"</string>
+    <!-- no translation found for lockscreen_pattern_wrong (4817583279053112312) -->
+    <skip />
+    <string name="lockscreen_plugged_in">"Rechargement (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string>
+    <string name="lockscreen_low_battery">"Branchez votre chargeur."</string>
+    <string name="lockscreen_missing_sim_message_short">"Aucune carte SIM n\'a été trouvée."</string>
+    <string name="lockscreen_missing_sim_message">"Aucune carte SIM n\'est insérée dans le téléphone."</string>
+    <string name="lockscreen_missing_sim_instructions">"Insérez une carte SIM."</string>
+    <string name="lockscreen_network_locked_message">"Réseau bloqué"</string>
+    <string name="lockscreen_sim_puk_locked_message">"La carte SIM est bloquée par code PUK."</string>
+    <string name="lockscreen_sim_puk_locked_instructions">"Veuillez contacter l\'assistance clientèle."</string>
+    <string name="lockscreen_sim_locked_message">"La carte SIM est bloquée."</string>
+    <string name="lockscreen_sim_unlock_progress_dialog_message">"Déblocage de la carte SIM..."</string>
+    <string name="lockscreen_too_many_failed_attempts_dialog_message">"Vous avez mal reproduit la forme de déblocage <xliff:g id="NUMBER_0">%d</xliff:g> fois. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin">"Vous avez mal dessiné la forme de déblocage <xliff:g id="NUMBER_0">%d</xliff:g> fois. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> tentatives supplémentaires, vous devrez débloquer votre téléphone avec votre identifiant Google."\n\n"Merci de réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+    <string name="lockscreen_too_many_failed_attempts_countdown">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+    <string name="lockscreen_forgot_pattern_button_text">"Vous avez oublié la forme à dessiner ?"</string>
+    <string name="lockscreen_glogin_too_many_attempts">"Trop de tentatives !"</string>
+    <!-- no translation found for lockscreen_glogin_instructions (7400120254204758548) -->
+    <skip />
+    <string name="lockscreen_glogin_username_hint">"Nom d\'utilisateur (e-mail)"</string>
+    <string name="lockscreen_glogin_password_hint">"Mot de passe"</string>
+    <string name="lockscreen_glogin_submit_button">"Se connecter"</string>
+    <string name="lockscreen_glogin_invalid_input">"Nom d\'utilisateur ou mot de passe non valide."</string>
+    <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string>
+    <!-- no translation found for hour_minute_ampm (7044207493989843593) -->
+    <skip />
+    <!-- no translation found for hour_minute_cap_ampm (5778825208801303756) -->
+    <skip />
+    <!-- no translation found for hour_ampm (7618670480400517084) -->
+    <skip />
+    <!-- no translation found for hour_cap_ampm (5117798389811605468) -->
+    <skip />
+    <string name="status_bar_clear_all_button">"Effacer les notifications"</string>
+    <string name="status_bar_no_notifications_title">"Aucune notification"</string>
+    <string name="status_bar_ongoing_events_title">"En cours"</string>
+    <string name="status_bar_latest_events_title">"Notifications"</string>
+    <!-- no translation found for battery_status_text_percent_format (8818848472818880005) -->
+    <skip />
+    <string name="battery_status_charging">"Rechargement..."</string>
+    <string name="battery_low_title">"Veuillez brancher le chargeur"</string>
+    <string name="battery_low_subtitle">"La batterie commence à faiblir :"</string>
+    <string name="battery_low_percent_format">"Batterie restante : <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+    <string name="factorytest_failed">"Échec du test usine"</string>
+    <string name="factorytest_not_system">"L\'action FACTORY_TEST est uniquement prise en charge pour les paquets de données installés dans in/system/app."</string>
+    <string name="factorytest_no_action">"Aucun paquet permettant de prendre en charge l\'action FACTORY_TEST n\'a été trouvé."</string>
+    <string name="factorytest_reboot">"Redémarrer"</string>
+    <string name="save_password_label">"Confirmer"</string>
+    <string name="save_password_message">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
+    <string name="save_password_notnow">"Pas maintenant"</string>
+    <string name="save_password_remember">"Se souvenir du mot de passe"</string>
+    <string name="save_password_never">"Jamais"</string>
+    <string name="open_permission_deny">"Vous n\'avez pas l\'autorisation d\'ouvrir cette page."</string>
+    <string name="text_copied">"Le texte a été copié dans le presse-papier."</string>
+    <string name="more_item_label">"Plus"</string>
+    <string name="prepend_shortcut_label">"Menu+"</string>
+    <string name="menu_space_shortcut_label">"espace"</string>
+    <string name="menu_enter_shortcut_label">"entrée"</string>
+    <string name="menu_delete_shortcut_label">"effacer"</string>
+    <string name="search_go">"Rechercher"</string>
+    <string name="today">"Aujourd\'hui"</string>
+    <string name="yesterday">"Hier"</string>
+    <string name="tomorrow">"Demain"</string>
+    <string name="oneMonthDurationPast">"Il y a 1 mois"</string>
+    <string name="beforeOneMonthDurationPast">"Il y a plus d\'un mois"</string>
+  <plurals name="num_seconds_ago">
+    <item quantity="one">"Il y a 1 seconde"</item>
+    <item quantity="other">"Il y a <xliff:g id="COUNT">%d</xliff:g> secondes"</item>
+  </plurals>
+  <plurals name="num_minutes_ago">
+    <item quantity="one">"Il y a 1 minute"</item>
+    <item quantity="other">"Il y a <xliff:g id="COUNT">%d</xliff:g> minutes"</item>
+  </plurals>
+  <plurals name="num_hours_ago">
+    <item quantity="one">"il y a 1 heure"</item>
+    <item quantity="other">"Il y a <xliff:g id="COUNT">%d</xliff:g> heures"</item>
+  </plurals>
+  <plurals name="num_days_ago">
+    <item quantity="one">"hier"</item>
+    <item quantity="other">"Il y a <xliff:g id="COUNT">%d</xliff:g> jours"</item>
+  </plurals>
+  <plurals name="in_num_seconds">
+    <item quantity="one">"dans 1 seconde"</item>
+    <item quantity="other">"dans <xliff:g id="COUNT">%d</xliff:g> secondes"</item>
+  </plurals>
+  <plurals name="in_num_minutes">
+    <item quantity="one">"dans 1 minute"</item>
+    <item quantity="other">"dans <xliff:g id="COUNT">%d</xliff:g> minutes"</item>
+  </plurals>
+  <plurals name="in_num_hours">
+    <item quantity="one">"dans 1 heure"</item>
+    <item quantity="other">"dans <xliff:g id="COUNT">%d</xliff:g> heures"</item>
+  </plurals>
+  <plurals name="in_num_days">
+    <item quantity="one">"demain"</item>
+    <item quantity="other">"dans <xliff:g id="COUNT">%d</xliff:g> jours"</item>
+  </plurals>
+    <!-- no translation found for abbrev_num_seconds_ago:one (1849036840200069118) -->
+    <!-- no translation found for abbrev_num_seconds_ago:other (3699169366650930415) -->
+    <!-- no translation found for abbrev_num_minutes_ago:one (6361490147113871545) -->
+    <!-- no translation found for abbrev_num_minutes_ago:other (851164968597150710) -->
+    <!-- no translation found for abbrev_num_hours_ago:one (4796212039724722116) -->
+    <!-- no translation found for abbrev_num_hours_ago:other (6889970745748538901) -->
+    <!-- no translation found for abbrev_num_days_ago:one (8463161711492680309) -->
+    <!-- no translation found for abbrev_num_days_ago:other (3453342639616481191) -->
+    <!-- no translation found for abbrev_in_num_seconds:one (5842225370795066299) -->
+    <!-- no translation found for abbrev_in_num_seconds:other (5495880108825805108) -->
+    <!-- no translation found for abbrev_in_num_minutes:one (562786149928284878) -->
+    <!-- no translation found for abbrev_in_num_minutes:other (4216113292706568726) -->
+    <!-- no translation found for abbrev_in_num_hours:one (3274708118124045246) -->
+    <!-- no translation found for abbrev_in_num_hours:other (3705373766798013406) -->
+    <!-- no translation found for abbrev_in_num_days:one (2178576254385739855) -->
+    <!-- no translation found for abbrev_in_num_days:other (2973062968038355991) -->
+    <string name="preposition_for_date">"%s"</string>
+    <string name="preposition_for_time">"à %s"</string>
+    <string name="preposition_for_year">"en %s"</string>
+    <string name="day">" jour"</string>
+    <string name="days">" jours"</string>
+    <string name="hour">"heure"</string>
+    <string name="hours">"heures"</string>
+    <string name="minute">"mn"</string>
+    <string name="minutes">"mn"</string>
+    <string name="second">"s"</string>
+    <string name="seconds">"s"</string>
+    <string name="week">"semaine"</string>
+    <string name="weeks">"semaines"</string>
+    <string name="year">"année"</string>
+    <string name="years">"années"</string>
+    <string name="sunday">"dimanche"</string>
+    <string name="monday">"lundi"</string>
+    <string name="tuesday">"mardi"</string>
+    <string name="wednesday">"mercredi"</string>
+    <string name="thursday">"jeudi"</string>
+    <string name="friday">"vendredi"</string>
+    <string name="saturday">"samedi"</string>
+    <string name="every_weekday">"Tous les jours ouvrés (lun.-vend.)"</string>
+    <string name="daily">"Tous les jours"</string>
+    <string name="weekly">"Toutes les semaines le <xliff:g id="DAY">%s</xliff:g>"</string>
+    <string name="monthly">"Tous les mois"</string>
+    <string name="yearly">"Tous les ans"</string>
+    <string name="VideoView_error_title">"Échec de la lecture de la vidéo"</string>
+    <string name="VideoView_error_text_unknown">"Désolé, impossible de lire cette vidéo."</string>
+    <string name="VideoView_error_button">"OK"</string>
+    <string name="am">"AM"</string>
+    <string name="pm">"PM"</string>
+    <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string>
+    <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string>
+    <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string>
+    <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <!-- no translation found for date_time (6104442718633642836) -->
+    <skip />
+    <!-- no translation found for relative_time (1818557177829411417) -->
+    <skip />
+    <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string>
+    <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string>
+    <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string>
+    <string name="noon">"midi"</string>
+    <string name="Noon">"Midi"</string>
+    <string name="midnight">"minuit"</string>
+    <string name="Midnight">"Minuit"</string>
+    <!-- no translation found for month_day (5565829181417740906) -->
+    <skip />
+    <!-- no translation found for month (7026169712234774086) -->
+    <skip />
+    <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <!-- no translation found for month_year (9219019380312413367) -->
+    <skip />
+    <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string>
+    <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string>
+    <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string>
+    <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <!-- no translation found for abbrev_month_year (3856424847226891943) -->
+    <skip />
+    <!-- no translation found for abbrev_month_day (5028815883653985933) -->
+    <skip />
+    <!-- no translation found for abbrev_month (3131032032850777433) -->
+    <skip />
+    <string name="day_of_week_long_sunday">"dimanche"</string>
+    <string name="day_of_week_long_monday">"lundi"</string>
+    <string name="day_of_week_long_tuesday">"mardi"</string>
+    <string name="day_of_week_long_wednesday">"mercredi"</string>
+    <string name="day_of_week_long_thursday">"jeudi"</string>
+    <string name="day_of_week_long_friday">"vendredi"</string>
+    <string name="day_of_week_long_saturday">"samedi"</string>
+    <string name="day_of_week_medium_sunday">"dim."</string>
+    <string name="day_of_week_medium_monday">"lun."</string>
+    <string name="day_of_week_medium_tuesday">"mar."</string>
+    <string name="day_of_week_medium_wednesday">"mer."</string>
+    <string name="day_of_week_medium_thursday">"jeu."</string>
+    <string name="day_of_week_medium_friday">"ven."</string>
+    <string name="day_of_week_medium_saturday">"sam."</string>
+    <string name="day_of_week_short_sunday">"dim."</string>
+    <string name="day_of_week_short_monday">"lun."</string>
+    <string name="day_of_week_short_tuesday">"mar."</string>
+    <string name="day_of_week_short_wednesday">"mer."</string>
+    <string name="day_of_week_short_thursday">"jeu."</string>
+    <string name="day_of_week_short_friday">"vend."</string>
+    <string name="day_of_week_short_saturday">"sam."</string>
+    <string name="day_of_week_shorter_sunday">"dim"</string>
+    <string name="day_of_week_shorter_monday">"lun"</string>
+    <string name="day_of_week_shorter_tuesday">"mar."</string>
+    <string name="day_of_week_shorter_wednesday">"mer."</string>
+    <string name="day_of_week_shorter_thursday">"jeu."</string>
+    <string name="day_of_week_shorter_friday">"ven."</string>
+    <string name="day_of_week_shorter_saturday">"sam"</string>
+    <string name="day_of_week_shortest_sunday">"dim."</string>
+    <string name="day_of_week_shortest_monday">"lun."</string>
+    <string name="day_of_week_shortest_tuesday">"mar."</string>
+    <string name="day_of_week_shortest_wednesday">"merc."</string>
+    <string name="day_of_week_shortest_thursday">"jeu."</string>
+    <string name="day_of_week_shortest_friday">"ven."</string>
+    <string name="day_of_week_shortest_saturday">"sam"</string>
+    <string name="month_long_january">"janvier"</string>
+    <string name="month_long_february">"février"</string>
+    <string name="month_long_march">"mars"</string>
+    <string name="month_long_april">"avril"</string>
+    <string name="month_long_may">"mai"</string>
+    <string name="month_long_june">"juin"</string>
+    <string name="month_long_july">"juillet"</string>
+    <string name="month_long_august">"août"</string>
+    <string name="month_long_september">"septembre"</string>
+    <string name="month_long_october">"octobre"</string>
+    <string name="month_long_november">"novembre"</string>
+    <string name="month_long_december">"décembre"</string>
+    <string name="month_medium_january">"janv."</string>
+    <string name="month_medium_february">"févr."</string>
+    <string name="month_medium_march">"mars"</string>
+    <string name="month_medium_april">"avr."</string>
+    <string name="month_medium_may">"mai"</string>
+    <string name="month_medium_june">"juin"</string>
+    <string name="month_medium_july">"juil."</string>
+    <string name="month_medium_august">"août"</string>
+    <string name="month_medium_september">"sept."</string>
+    <string name="month_medium_october">"oct."</string>
+    <string name="month_medium_november">"nov."</string>
+    <string name="month_medium_december">"déc."</string>
+    <string name="month_shortest_january">"jan."</string>
+    <string name="month_shortest_february">"fév."</string>
+    <string name="month_shortest_march">"mar."</string>
+    <string name="month_shortest_april">"avr."</string>
+    <string name="month_shortest_may">"mai"</string>
+    <string name="month_shortest_june">"juin"</string>
+    <string name="month_shortest_july">"jui."</string>
+    <string name="month_shortest_august">"août"</string>
+    <string name="month_shortest_september">"sept."</string>
+    <string name="month_shortest_october">"oct."</string>
+    <string name="month_shortest_november">"nov."</string>
+    <string name="month_shortest_december">"déc."</string>
+    <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
+    <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
+    <string name="selectAll">"Sélectionner tout"</string>
+    <!-- no translation found for selectText (3889149123626888637) -->
+    <skip />
+    <!-- no translation found for stopSelectingText (4157931463872320996) -->
+    <skip />
+    <string name="cut">"Couper"</string>
+    <string name="cutAll">"Couper tout"</string>
+    <string name="copy">"Copier"</string>
+    <string name="copyAll">"Copier tout"</string>
+    <string name="paste">"Coller"</string>
+    <string name="copyUrl">"Copier l\'URL"</string>
+    <!-- no translation found for inputMethod (7673923508389094672) -->
+    <skip />
+    <!-- no translation found for editTextMenuTitle (1672989176958581452) -->
+    <skip />
+    <string name="low_internal_storage_view_title">"Espace disponible faible"</string>
+    <string name="low_internal_storage_view_text">"La mémoire du téléphone commence à être pleine."</string>
+    <string name="ok">"OK"</string>
+    <string name="cancel">"Annuler"</string>
+    <string name="yes">"OK"</string>
+    <string name="no">"Annuler"</string>
+    <string name="capital_on">"ON"</string>
+    <string name="capital_off">"OFF"</string>
+    <string name="whichApplication">"Terminer l\'action avec"</string>
+    <string name="alwaysUse">"Utiliser par défaut pour cette action."</string>
+    <string name="clearDefaultHintMsg">"Effacer par défaut dans les Paramètres de l\'accueil &gt; Applications &gt; Gérer les applications."</string>
+    <string name="chooseActivity">"Sélectionner une action"</string>
+    <string name="noApplications">"Aucune application ne peut effectuer cette action."</string>
+    <string name="aerr_title">"Désolé !"</string>
+    <string name="aerr_application">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (du processus <xliff:g id="PROCESS">%2$s</xliff:g>) s\'est interrompue inopinément. Merci de réessayer."</string>
+    <string name="aerr_process">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> s\'est interrompu de façon inopinée. Merci de réessayer."</string>
+    <string name="anr_title">"Désolé !"</string>
+    <string name="anr_activity_application">"L\'activité <xliff:g id="ACTIVITY">%1$s</xliff:g> (de l\'application <xliff:g id="APPLICATION">%2$s</xliff:g>) ne répond pas."</string>
+    <string name="anr_activity_process">"L\'activité <xliff:g id="ACTIVITY">%1$s</xliff:g> (du processus <xliff:g id="PROCESS">%2$s</xliff:g>) ne répond pas."</string>
+    <string name="anr_application_process">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (du processus <xliff:g id="PROCESS">%2$s</xliff:g>) ne répond pas."</string>
+    <string name="anr_process">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> ne répond pas."</string>
+    <string name="force_close">"Forcer la fermeture"</string>
+    <string name="wait">"Attendre"</string>
+    <string name="debug">"Débogage"</string>
+    <string name="sendText">"Sélectionner une action pour le texte"</string>
+    <string name="volume_ringtone">"Volume de la sonnerie"</string>
+    <!-- no translation found for volume_music (5421651157138628171) -->
+    <skip />
+    <!-- no translation found for volume_music_hint_playing_through_bluetooth (9165984379394601533) -->
+    <skip />
+    <string name="volume_call">"Volume des appels entrants"</string>
+    <!-- no translation found for volume_call_hint_playing_through_bluetooth (7750873841563910404) -->
+    <skip />
+    <string name="volume_alarm">"Volume de l\'alarme"</string>
+    <!-- no translation found for volume_notification (2422265656744276715) -->
+    <skip />
+    <string name="volume_unknown">"Volume"</string>
+    <string name="ringtone_default">"Sonnerie par défaut"</string>
+    <string name="ringtone_default_with_actual">"Sonnerie par défaut (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+    <string name="ringtone_silent">"Silencieux"</string>
+    <!-- no translation found for ringtone_picker_title (3515143939175119094) -->
+    <skip />
+    <string name="ringtone_unknown">"Sonnerie inconnue"</string>
+  <plurals name="wifi_available">
+    <item quantity="one">"Réseau Wi-Fi disponible"</item>
+    <item quantity="other">"Réseaux Wi-Fi disponibles"</item>
+  </plurals>
+  <plurals name="wifi_available_detailed">
+    <item quantity="one">"Ouvrir le réseau Wi-Fi disponible"</item>
+    <item quantity="other">"Ouvrir les réseaux Wi-Fi disponibles"</item>
+  </plurals>
+    <!-- no translation found for select_character (3365550120617701745) -->
+    <skip />
+    <string name="sms_control_default_app_name">"Application inconnue"</string>
+    <string name="sms_control_title">"Envoi des messages SMS"</string>
+    <string name="sms_control_message">"Vous allez envoyer un grand nombre de messages SMS. Sélectionnez OK pour continuer ou Annuler pour interrompre l\'envoi."</string>
+    <string name="sms_control_yes">"OK"</string>
+    <string name="sms_control_no">"Annuler"</string>
+    <string name="date_time_set">"Régler"</string>
+    <string name="default_permission_group">"Par défaut"</string>
+    <string name="no_permissions">"Aucune autorisation requise"</string>
+    <string name="perms_hide"><b>"Masquer"</b></string>
+    <string name="perms_show_all"><b>"Afficher tout"</b></string>
+    <string name="googlewebcontenthelper_loading">"Chargement..."</string>
+    <string name="usb_storage_title">"Connecté avec un câble USB"</string>
+    <string name="usb_storage_message">"Vous avez connecté votre téléphone à votre ordinateur avec un câble USB. Sélectionnez Monter pour copier des fichiers depuis votre ordinateur vers votre carte SD ou inversement."</string>
+    <string name="usb_storage_button_mount">"Monter"</string>
+    <string name="usb_storage_button_unmount">"Ne pas monter"</string>
+    <string name="usb_storage_error_message">"Un problème est survenu lors de l\'utilisation de votre carte SD en tant que périphérique de stockage USB."</string>
+    <string name="usb_storage_notification_title">"Connecté avec un câble USB"</string>
+    <string name="usb_storage_notification_message">"Sélectionner cette option pour copier des fichiers vers/à partir de votre ordinateur."</string>
+    <!-- no translation found for select_input_method (2086499663193509436) -->
+    <skip />
+    <!-- no translation found for fast_scroll_alphabet (5433275485499039199) -->
+    <skip />
+    <!-- no translation found for fast_scroll_numeric_alphabet (4030170524595123610) -->
+    <skip />
+    <!-- no translation found for candidates_style (5248064431114273041) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-it/arrays.xml b/core/res/res/values-it/arrays.xml
new file mode 100644 (file)
index 0000000..f9c904b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
new file mode 100644 (file)
index 0000000..771eabd
--- /dev/null
@@ -0,0 +1,723 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="byteShort">"B"</string>
+    <string name="kilobyteShort">"KB"</string>
+    <string name="megabyteShort">"MB"</string>
+    <string name="gigabyteShort">"GB"</string>
+    <string name="terabyteShort">"TB"</string>
+    <string name="petabyteShort">"PB"</string>
+    <string name="untitled">"&lt;senza nome&gt;"</string>
+    <string name="ellipsis">"…"</string>
+    <string name="emptyPhoneNumber">"(Nessun numero di telefono)"</string>
+    <string name="unknownName">"(Sconosciuto)"</string>
+    <string name="defaultVoiceMailAlphaTag">"Segreteria"</string>
+    <string name="defaultMsisdnAlphaTag">"MSISDN1"</string>
+    <string name="mmiError">"Problema di connessione o codice MMI non valido."</string>
+    <string name="serviceEnabled">"Il servizio è stato attivato."</string>
+    <string name="serviceEnabledFor">"Il servizio è stato attivato per:"</string>
+    <string name="serviceDisabled">"Il servizio è stato disattivato."</string>
+    <string name="serviceRegistered">"Registrazione effettuata."</string>
+    <string name="serviceErased">"Eliminazione effettuata."</string>
+    <string name="passwordIncorrect">"Password errata."</string>
+    <string name="mmiComplete">"MMI completo."</string>
+    <string name="badPin">"Il PIN attuale digitato è errato."</string>
+    <string name="badPuk">"Il PUK digitato è errato."</string>
+    <string name="mismatchPin">"I PIN inseriti non corrispondono."</string>
+    <string name="invalidPin">"Il PIN deve essere di 4-8 numeri."</string>
+    <!-- no translation found for needPuk (919668385956251611) -->
+    <skip />
+    <string name="needPuk2">"Digita il PUK2 per sbloccare la SIM."</string>
+    <string name="ClipMmi">"ID chiamante in entrata"</string>
+    <string name="ClirMmi">"ID chiamante in uscita"</string>
+    <string name="CfMmi">"Deviazione chiamate"</string>
+    <string name="CwMmi">"Avviso di chiamata"</string>
+    <string name="BaMmi">"Blocco chiamate"</string>
+    <string name="PwdMmi">"Modifica password"</string>
+    <string name="PinMmi">"Modifica PIN"</string>
+    <string name="CLIRDefaultOnNextCallOn">"ID chiamante generalmente limitato. Prossima chiamata: limitato"</string>
+    <string name="CLIRDefaultOnNextCallOff">"ID chiamante generalmente limitato. Prossima chiamata: non limitato"</string>
+    <string name="CLIRDefaultOffNextCallOn">"ID chiamante generalmente non limitato. Prossima chiamata: limitato"</string>
+    <string name="CLIRDefaultOffNextCallOff">"ID chiamante generalmente non limitato. Prossima chiamata: non limitato"</string>
+    <string name="serviceNotProvisioned">"Servizio non fornito."</string>
+    <string name="CLIRPermanent">"Impossibile modificare l\'impostazione dell\'ID del chiamante."</string>
+    <string name="serviceClassVoice">"Voce"</string>
+    <string name="serviceClassData">"Dati"</string>
+    <string name="serviceClassFAX">"FAX"</string>
+    <string name="serviceClassSMS">"SMS"</string>
+    <string name="serviceClassDataAsync">"Asinc"</string>
+    <string name="serviceClassDataSync">"Sinc"</string>
+    <string name="serviceClassPacket">"Pacchetto"</string>
+    <string name="serviceClassPAD">"PAD"</string>
+    <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
+    <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
+    <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string>
+    <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
+    <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
+    <string name="httpErrorOk">"OK"</string>
+    <string name="httpError">"La pagina web contiene un errore."</string>
+    <string name="httpErrorLookup">"Impossibile trovare l\'URL."</string>
+    <string name="httpErrorUnsupportedAuthScheme">"Schema di autenticazione del sito non supportato."</string>
+    <string name="httpErrorAuth">"Autenticazione non riuscita."</string>
+    <string name="httpErrorProxyAuth">"Autenticazione tramite il server proxy non riuscita."</string>
+    <string name="httpErrorConnect">"Connessione al server non riuscita."</string>
+    <string name="httpErrorIO">"Impossibile comunicare con il server. Riprova più tardi."</string>
+    <string name="httpErrorTimeout">"Tempo esaurito per la connessione al server."</string>
+    <string name="httpErrorRedirectLoop">"La pagina contiene troppi reindirizzamenti sul server."</string>
+    <string name="httpErrorUnsupportedScheme">"Protocollo non supportato."</string>
+    <string name="httpErrorFailedSslHandshake">"Impossibile stabilire una connessione protetta."</string>
+    <string name="httpErrorBadUrl">"Impossibile aprire la pagina. URL non valido."</string>
+    <string name="httpErrorFile">"Impossibile accedere al file."</string>
+    <string name="httpErrorFileNotFound">"Impossibile trovare il file richiesto."</string>
+    <string name="httpErrorTooManyRequests">"Troppe richieste in fase di elaborazione. Riprova più tardi."</string>
+    <string name="contentServiceSync">"Sincronizzazione"</string>
+    <string name="contentServiceSyncNotificationTitle">"Sincronizzazione"</string>
+    <string name="contentServiceTooManyDeletesNotificationDesc">"Troppe eliminazioni di <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
+    <string name="low_memory">"Spazio di archiviazione del telefono esaurito. Elimina alcuni file per liberare spazio."</string>
+    <string name="me">"Io"</string>
+    <string name="power_dialog">"Opzioni telefono"</string>
+    <string name="silent_mode">"Modalità silenziosa"</string>
+    <string name="turn_on_radio">"Attiva wireless"</string>
+    <string name="turn_off_radio">"Disattiva wireless"</string>
+    <string name="screen_lock">"Blocco schermo"</string>
+    <string name="power_off">"Spegni"</string>
+    <string name="shutdown_progress">"Spegnimento..."</string>
+    <string name="shutdown_confirm">"Il telefono verrà spento."</string>
+    <string name="no_recent_tasks">"Nessuna applicazione recente."</string>
+    <string name="global_actions">"Opzioni telefono"</string>
+    <string name="global_action_lock">"Blocco schermo"</string>
+    <string name="global_action_power_off">"Spegni"</string>
+    <string name="global_action_toggle_silent_mode">"Modalità silenziosa"</string>
+    <string name="global_action_silent_mode_on_status">"Audio non attivo"</string>
+    <string name="global_action_silent_mode_off_status">"Audio attivo"</string>
+    <string name="safeMode">"Modalità provvisoria"</string>
+    <string name="permgrouplab_costMoney">"Servizi che prevedono un costo"</string>
+    <string name="permgroupdesc_costMoney">"Consentono alle applicazioni di svolgere operazioni che possono comportare un costo."</string>
+    <string name="permgrouplab_messages">"I tuoi messaggi"</string>
+    <string name="permgroupdesc_messages">"Leggere e scrivere SMS, email e altri messaggi."</string>
+    <string name="permgrouplab_personalInfo">"Informazioni personali"</string>
+    <string name="permgroupdesc_personalInfo">"Accedere direttamente ai contatti e al calendario memorizzati sul telefono."</string>
+    <string name="permgrouplab_location">"La tua posizione"</string>
+    <string name="permgroupdesc_location">"Monitorare la posizione fisica dell\'utente"</string>
+    <string name="permgrouplab_network">"Comunicazione di rete"</string>
+    <string name="permgroupdesc_network">"Consentono l\'accesso delle applicazioni a varie funzionalità di rete."</string>
+    <string name="permgrouplab_accounts">"I tuoi account Google"</string>
+    <string name="permgroupdesc_accounts">"Accedere agli account Google disponibili."</string>
+    <string name="permgrouplab_hardwareControls">"Controlli hardware"</string>
+    <string name="permgroupdesc_hardwareControls">"Accedere direttamente all\'hardware del ricevitore."</string>
+    <string name="permgrouplab_phoneCalls">"Telefonate"</string>
+    <string name="permgroupdesc_phoneCalls">"Monitorare, registrare ed elaborare le telefonate."</string>
+    <string name="permgrouplab_systemTools">"Strumenti di sistema"</string>
+    <string name="permgroupdesc_systemTools">"Accesso al sistema e controllo di livello inferiore."</string>
+    <string name="permgrouplab_developmentTools">"Strumenti di sviluppo"</string>
+    <string name="permgroupdesc_developmentTools">"Funzionalità necessarie soltanto agli sviluppatori di applicazioni."</string>
+    <string name="permlab_statusBar">"disattivare o modificare la barra di stato"</string>
+    <string name="permdesc_statusBar">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
+    <string name="permlab_expandStatusBar">"espandere/comprimere la barra di stato"</string>
+    <string name="permdesc_expandStatusBar">"Consente all\'applicazione di espandere o comprimere la barra di stato."</string>
+    <string name="permlab_processOutgoingCalls">"intercettare chiamate in uscita"</string>
+    <string name="permdesc_processOutgoingCalls">"Consente all\'applicazione di elaborare le chiamate in uscita e di modificare il numero da comporre. Le applicazioni dannose potrebbero monitorare, deviare o impedire le chiamate in uscita."</string>
+    <string name="permlab_receiveSms">"ricevere SMS"</string>
+    <string name="permdesc_receiveSms">"Consente il ricevimento e l\'elaborazione di SMS da parte dell\'applicazione. Le applicazioni dannose potrebbero monitorare i messaggi o eliminarli senza visualizzarli."</string>
+    <string name="permlab_receiveMms">"ricevere MMS"</string>
+    <string name="permdesc_receiveMms">"Consente il ricevimento e l\'elaborazione di MMS da parte dell\'applicazione. Le applicazioni dannose potrebbero monitorare i messaggi o eliminarli senza visualizzarli."</string>
+    <string name="permlab_sendSms">"inviare SMS"</string>
+    <string name="permdesc_sendSms">"Consente all\'applicazione di inviare messaggi SMS. Le applicazioni dannose potrebbero inviare messaggi a tua insaputa facendoti sostenere dei costi."</string>
+    <string name="permlab_readSms">"leggere SMS o MMS"</string>
+    <string name="permdesc_readSms">"Consente all\'applicazione di leggere SMS memorizzati sul telefono o sulla SIM. Le applicazioni dannose potrebbero leggere messaggi riservati."</string>
+    <string name="permlab_writeSms">"modificare SMS o MMS"</string>
+    <string name="permdesc_writeSms">"Consente all\'applicazione di rispondere a SMS memorizzati sul telefono o sulla SIM. Le applicazioni dannose potrebbero eliminare i messaggi."</string>
+    <string name="permlab_receiveWapPush">"ricevere WAP"</string>
+    <string name="permdesc_receiveWapPush">"Consente il ricevimento e l\'elaborazione di messaggi WAP da parte dell\'applicazione. Le applicazioni dannose potrebbero monitorare i messaggi o eliminarli senza visualizzarli."</string>
+    <string name="permlab_getTasks">"recuperare applicazioni in esecuzione"</string>
+    <string name="permdesc_getTasks">"Consente all\'applicazione di recuperare informazioni sulle attività in esecuzione ed eseguite di recente. Le applicazioni dannose potrebbero essere in grado di scoprire informazioni riservate su altre applicazioni."</string>
+    <string name="permlab_reorderTasks">"ridisporre applicazioni in esecuzione"</string>
+    <string name="permdesc_reorderTasks">"Consente a un\'applicazione di spostare attività in primo e secondo piano. Le applicazioni dannose possono imporsi ponendosi automaticamente in primo piano."</string>
+    <string name="permlab_setDebugApp">"attivare il debug delle applicazioni"</string>
+    <string name="permdesc_setDebugApp">"Consente a un\'applicazione di attivare il debug per un\'altra applicazione. Le applicazioni dannose possono sfruttare questa possibilità per interrompere altre applicazioni."</string>
+    <string name="permlab_changeConfiguration">"cambiare le impostazioni della UI"</string>
+    <string name="permdesc_changeConfiguration">"Consente a un\'applicazione di modificare la configurazione corrente, come le dimensioni dei caratteri locali o complessive."</string>
+    <string name="permlab_restartPackages">"riavviare altre applicazioni"</string>
+    <string name="permdesc_restartPackages">"Consente a un\'applicazione di riavviare forzatamente altre applicazioni."</string>
+    <string name="permlab_setProcessForeground">"impedire l\'interruzione"</string>
+    <string name="permdesc_setProcessForeground">"Consente a un\'applicazione di eseguire i processi in primo piano in modo che non possano essere interrotti. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <string name="permlab_forceBack">"forzare la chiusura delle applicazioni"</string>
+    <string name="permdesc_forceBack">"Consente a un\'applicazione di forzare la chiusura di attività in primo piano. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <string name="permlab_dump">"recuperare lo stato interno del sistema"</string>
+    <string name="permdesc_dump">"Consente all\'applicazione di recuperare lo stato interno del sistema. Le applicazioni dannose potrebbero recuperare molte informazioni riservate e protette di cui non dovrebbero avere mai bisogno."</string>
+    <string name="permlab_addSystemService">"pubblicare servizi di basso livello"</string>
+    <string name="permdesc_addSystemService">"Consente a un\'applicazione di pubblicare i suoi servizi di sistema di basso livello. Le applicazioni dannose potrebbero assumere il controllo del sistema e impossessarsi di dati o danneggiarli."</string>
+    <string name="permlab_runSetActivityWatcher">"monitorare e controllare l\'avvio di tutte le applicazioni"</string>
+    <string name="permdesc_runSetActivityWatcher">"Consente a un\'applicazione di monitorare e controllare la modalità di avvio delle attività nel sistema. Le applicazioni dannose potrebbero compromettere totalmente il sistema. Questa autorizzazione è necessaria soltanto per lo sviluppo, mai per il normale utilizzo del telefono."</string>
+    <string name="permlab_broadcastPackageRemoved">"inviare broadcast rimossi dal pacchetto"</string>
+    <string name="permdesc_broadcastPackageRemoved">"Consente a un\'applicazione di trasmettere una notifica di rimozione del pacchetto di un\'applicazione. Le applicazioni dannose potrebbero sfruttare questa possibilità per interrompere ogni altra applicazione in esecuzione."</string>
+    <string name="permlab_broadcastSmsReceived">"inviare broadcast ricevuti tramite SMS"</string>
+    <string name="permdesc_broadcastSmsReceived">"Consente a un\'applicazione di trasmettere una notifica di ricevimento di un SMS. Le applicazioni dannose potrebbero sfruttare questa possibilità per far credere che siano stati ricevuti SMS."</string>
+    <string name="permlab_broadcastWapPush">"inviare broadcast ricevuti tramite WAP-PUSH"</string>
+    <string name="permdesc_broadcastWapPush">"Consente a un\'applicazione di trasmettere una notifica di ricevimento di un messaggio WAP PUSH. Le applicazioni dannose potrebbero sfruttare questa possibilità per far credere che sia stato ricevuto un MMS o per sostituire automaticamente il contenuto di pagine web con varianti dannose."</string>
+    <string name="permlab_setProcessLimit">"numero limite di processi in esecuzione"</string>
+    <string name="permdesc_setProcessLimit">"Consente a un\'applicazione di stabilire il numero massimo di processi in esecuzione. Mai necessario per le normali applicazioni."</string>
+    <string name="permlab_setAlwaysFinish">"chiudere tutte le applicazioni in background"</string>
+    <string name="permdesc_setAlwaysFinish">"Consente a un\'applicazione di controllare se le attività sono sempre completate quando vengono messe in secondo piano. Mai necessario per le normali applicazioni."</string>
+    <string name="permlab_fotaUpdate">"installare automaticamente aggiornamenti di sistema"</string>
+    <string name="permdesc_fotaUpdate">"Consente a un\'applicazione di ricevere notifiche sugli aggiornamenti del sistema in sospeso e di attivarne l\'installazione. Le applicazioni dannose possono sfruttare questa possibilità per danneggiare il sistema con aggiornamenti non autorizzati, o interferire con il processo di aggiornamento."</string>
+    <string name="permlab_batteryStats">"modificare le statistiche della batteria"</string>
+    <string name="permdesc_batteryStats">"Consente la modifica delle statistiche sulla batteria raccolte. Da non usare per normali applicazioni."</string>
+    <string name="permlab_internalSystemWindow">"visualizzare finestre non autorizzate"</string>
+    <string name="permdesc_internalSystemWindow">"Consente la creazione di finestre destinate all\'uso nell\'interfaccia utente di sistema interna. Da non usare per normali applicazioni."</string>
+    <string name="permlab_systemAlertWindow">"visualizzare avvisi a livello di sistema"</string>
+    <string name="permdesc_systemAlertWindow">"Consente a un\'applicazione di visualizzare finestre di avviso del sistema. Le applicazioni dannose possono sfruttare questa opzione per riempire lo schermo del telefono di messaggi."</string>
+    <string name="permlab_setAnimationScale">"modificare velocità di animazione globali"</string>
+    <string name="permdesc_setAnimationScale">"Consente a un\'applicazione di modificare la velocità di animazione globale (animazioni più veloci o più lente) in qualsiasi momento."</string>
+    <string name="permlab_manageAppTokens">"gestire i token delle applicazioni"</string>
+    <string name="permdesc_manageAppTokens">"Consente alle applicazioni di creare e gestire i propri token, ignorando il normale ordinamento Z. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <string name="permlab_injectEvents">"premere tasti e pulsanti di controllo"</string>
+    <string name="permdesc_injectEvents">"Consente a un\'applicazione di offrire i suoi eventi di input (pressioni di tasti etc.) ad altre applicazioni. Le applicazioni dannose possono sfruttare questa possibilità per assumere il controllo del telefono."</string>
+    <string name="permlab_readInputState">"registrare il testo digitato e le azioni effettuate"</string>
+    <string name="permdesc_readInputState">"Consente il rilevamento da parte delle applicazioni dei tasti premuti anche durante l\'interazione con un\'altra applicazione (come nel caso di inserimento di una password). Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <!-- no translation found for permlab_bindInputMethod (3360064620230515776) -->
+    <skip />
+    <!-- no translation found for permdesc_bindInputMethod (3734838321027317228) -->
+    <skip />
+    <string name="permlab_setOrientation">"cambiare l\'orientamento dello schermo"</string>
+    <string name="permdesc_setOrientation">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <string name="permlab_signalPersistentProcesses">"inviare segnali Linux alle applicazioni"</string>
+    <string name="permdesc_signalPersistentProcesses">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
+    <string name="permlab_persistentActivity">"lasciare sempre in esecuzione le applicazioni"</string>
+    <string name="permdesc_persistentActivity">"Consente a un\'applicazione di rendere delle sue parti costanti in modo che il sistema non possa usarla per altre applicazioni."</string>
+    <string name="permlab_deletePackages">"eliminare applicazioni"</string>
+    <string name="permdesc_deletePackages">"Consente a un\'applicazione di eliminare pacchetti Android. Le applicazioni dannose possono sfruttare questa possibilità per eliminare importanti applicazioni."</string>
+    <string name="permlab_clearAppUserData">"eliminare dati di altre applicazioni"</string>
+    <string name="permdesc_clearAppUserData">"Consente a un\'applicazione di cancellare dati dell\'utente."</string>
+    <string name="permlab_deleteCacheFiles">"eliminare le cache di altre applicazioni"</string>
+    <string name="permdesc_deleteCacheFiles">"Consente a un\'applicazione di eliminare file della cache."</string>
+    <string name="permlab_getPackageSize">"stabilire lo spazio di archiviazione delle applicazioni"</string>
+    <string name="permdesc_getPackageSize">"Consente a un\'applicazione di recuperare i suoi codici, dati e dimensioni della cache"</string>
+    <string name="permlab_installPackages">"installare direttamente applicazioni"</string>
+    <string name="permdesc_installPackages">"Consente a un\'applicazione di installare nuovi pacchetti Android o aggiornamenti. Le applicazioni dannose possono sfruttare questa possibilità per aggiungere nuove applicazioni con potenti autorizzazioni arbitrarie."</string>
+    <string name="permlab_clearAppCache">"eliminare tutti i dati della cache delle applicazioni"</string>
+    <string name="permdesc_clearAppCache">"Consente a un\'applicazione di liberare spazio sul telefono eliminando file nella directory della cache dell\'applicazione. L\'accesso è generalmente limitato a processi di sistema."</string>
+    <string name="permlab_readLogs">"leggere file di registro di sistema"</string>
+    <string name="permdesc_readLogs">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
+    <string name="permlab_diagnostic">"leggere/scrivere a risorse di proprietà di diag"</string>
+    <string name="permdesc_diagnostic">"Consente a un\'applicazione di leggere le risorse del gruppo diag e scrivere a esse, per esempio i file in /dev. Questa capacità potrebbe influire sulla stabilità e sicurezza del sistema. Dovrebbe essere utilizzata SOLTANTO per diagnostiche specifiche dell\'hardware effettuate dal produttore o dall\'operatore."</string>
+    <string name="permlab_changeComponentState">"attivare o disattivare componenti delle applicazioni"</string>
+    <!-- no translation found for permdesc_changeComponentState (4569107043246700630) -->
+    <skip />
+    <string name="permlab_setPreferredApplications">"impostare le applicazioni preferite"</string>
+    <string name="permdesc_setPreferredApplications">"Consente la modifica da parte di un\'applicazione delle applicazioni preferite. Le applicazioni dannose potrebbero essere in grado di modificare automaticamente le applicazioni in esecuzione, effettuando lo spoofing delle applicazioni esistenti per raccogliere dati riservati."</string>
+    <string name="permlab_writeSettings">"modificare le impostazioni di sistema globali"</string>
+    <string name="permdesc_writeSettings">"Consente la modifica in un\'applicazione dei dati delle impostazioni del sistema. Le applicazioni dannose possono danneggiare la configurazione del sistema."</string>
+    <!-- no translation found for permlab_writeSecureSettings (204676251876718288) -->
+    <skip />
+    <!-- no translation found for permdesc_writeSecureSettings (4116616249170428132) -->
+    <skip />
+    <string name="permlab_writeGservices">"modificare la mappa dei servizi Google"</string>
+    <string name="permdesc_writeGservices">"Consente a un\'applicazione di modificare la mappa dei servizi Google. Da non usare per normali applicazioni."</string>
+    <string name="permlab_receiveBootCompleted">"aprire automaticamente all\'avvio"</string>
+    <string name="permdesc_receiveBootCompleted">"Consente a un\'applicazione di aprirsi automaticamente al termine dell\'avvio del sistema. Potrebbe essere necessario più tempo per l\'avvio del telefono e l\'applicazione potrebbe rallentare tutte le funzioni del telefono rimanendo sempre in esecuzione."</string>
+    <string name="permlab_broadcastSticky">"inviare broadcast permanenti"</string>
+    <string name="permdesc_broadcastSticky">"Consente a un\'applicazione di inviare broadcast permanenti, che permangono anche al termine del broadcast. Le applicazioni dannose possono rendere il telefono lento o instabile tramite un uso eccessivo della memoria."</string>
+    <string name="permlab_readContacts">"leggere dati di contatto"</string>
+    <string name="permdesc_readContacts">"Consente la lettura da parte di un\'applicazione di tutti i dati (gli indirizzi) di contatto memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per inviare i dati ad altre persone."</string>
+    <string name="permlab_writeContacts">"scrivere dati di contatto"</string>
+    <string name="permdesc_writeContacts">"Consente a un\'applicazione di modificare i dati (gli indirizzi) di contatto memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare i dati di contatto."</string>
+    <string name="permlab_writeOwnerData">"scrivere dati del proprietario"</string>
+    <string name="permdesc_writeOwnerData">"Consente a un\'applicazione di modificare i dati del proprietario del telefono memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare tali dati."</string>
+    <string name="permlab_readOwnerData">"leggere dati del proprietario"</string>
+    <string name="permdesc_readOwnerData">"Consente a un\'applicazione di leggere i dati del proprietario del telefono memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per leggere tali dati."</string>
+    <string name="permlab_readCalendar">"leggere dati di calendario"</string>
+    <string name="permdesc_readCalendar">"Consente la lettura da parte di un\'applicazione di tutti gli eventi di calendario memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per inviare i tuoi eventi di calendario ad altre persone."</string>
+    <string name="permlab_writeCalendar">"scrivere dati di calendario"</string>
+    <string name="permdesc_writeCalendar">"Consente a un\'applicazione di modificare gli eventi di calendario memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare i dati del calendario."</string>
+    <string name="permlab_accessMockLocation">"fonti di localizzazione fittizie per test"</string>
+    <string name="permdesc_accessMockLocation">"Creare fonti di localizzazione fittizie per test. Le applicazioni dannose possono sfruttare questa possibilità per sostituire la posizione e/o lo stato restituito da reali fonti di localizzazione come GPS o provider di rete."</string>
+    <string name="permlab_accessLocationExtraCommands">"accedere a comandi aggiuntivi del provider di localizzazione"</string>
+    <string name="permdesc_accessLocationExtraCommands">"Accedere a comandi aggiuntivi del provider di localizzazione. Le applicazioni dannose possono sfruttare questa possibilità per interferire con il funzionamento del GPS o di altre fonti di localizzazione."</string>
+    <string name="permlab_accessFineLocation">"localizzazione precisa (GPS)"</string>
+    <string name="permdesc_accessFineLocation">"Consente l\'accesso a fonti di localizzazione precisa, come il sistema GPS del telefono, se disponibile. Le applicazioni dannose possono sfruttare questa possibilità per determinare la tua posizione e, nel farlo, far esaurire più in fretta la batteria."</string>
+    <string name="permlab_accessCoarseLocation">"localizzazione approssimativa (basata sulla rete)"</string>
+    <string name="permdesc_accessCoarseLocation">"Consente l\'accesso a fonti di localizzazione geografica non puntuale (come il database della rete cellulare) per determinare una posizione approssimativa del telefono, quando possibile. Le applicazioni dannose possono sfruttare questa possibilità per determinare approssimativamente dove ti trovi."</string>
+    <string name="permlab_accessSurfaceFlinger">"accedere a SurfaceFlinger"</string>
+    <string name="permdesc_accessSurfaceFlinger">"Consente l\'utilizzo dell\'applicazione di funzioni di basso livello SurfaceFlinger."</string>
+    <string name="permlab_readFrameBuffer">"leggere il buffer di frame"</string>
+    <string name="permdesc_readFrameBuffer">"Consente la lettura da parte dell\'applicazione dei contenuti del buffer di frame."</string>
+    <string name="permlab_modifyAudioSettings">"cambiare le impostazioni audio"</string>
+    <string name="permdesc_modifyAudioSettings">"Consente all\'applicazione di modificare impostazioni audio globali come volume e routing."</string>
+    <string name="permlab_recordAudio">"registrare audio"</string>
+    <string name="permdesc_recordAudio">"Consente l\'accesso dell\'applicazione al percorso di registrazione dell\'audio."</string>
+    <string name="permlab_camera">"scattare foto"</string>
+    <string name="permdesc_camera">"Consente di scattare foto nell\'applicazione con la fotocamera. L\'applicazione può acquisire in qualsiasi momento le immagini rilevate dalla fotocamera."</string>
+    <string name="permlab_brick">"disattivare definitivamente il telefono"</string>
+    <string name="permdesc_brick">"Consente all\'applicazione di disattivare l\'intero telefono in modo definitivo. Questa autorizzazione è molto pericolosa."</string>
+    <string name="permlab_reboot">"imporre il riavvio del telefono"</string>
+    <string name="permdesc_reboot">"Consente all\'applicazione di imporre il riavvio del telefono."</string>
+    <string name="permlab_mount_unmount_filesystems">"montare e smontare filesystem"</string>
+    <string name="permdesc_mount_unmount_filesystems">"Consente montaggio e smontaggio da parte dell\'applicazione dei filesystem degli archivi rimovibili."</string>
+    <string name="permlab_vibrate">"controllare la vibrazione"</string>
+    <string name="permdesc_vibrate">"Consente all\'applicazione di controllare la vibrazione."</string>
+    <string name="permlab_flashlight">"controllare il flash"</string>
+    <string name="permdesc_flashlight">"Consente all\'applicazione di controllare il flash."</string>
+    <string name="permlab_hardware_test">"testare l\'hardware"</string>
+    <string name="permdesc_hardware_test">"Consente all\'applicazione di controllare varie periferiche per il test dell\'hardware."</string>
+    <string name="permlab_callPhone">"chiamare direttamente i numeri di telefono"</string>
+    <string name="permdesc_callPhone">"Consente all\'applicazione di chiamare numeri automaticamente. Le applicazioni dannose potrebbero far risultare chiamate impreviste sulla bolletta telefonica. Questa autorizzazione non consente all\'applicazione di chiamare numeri di emergenza."</string>
+    <string name="permlab_callPrivileged">"chiamare direttamente tutti i numeri di telefono"</string>
+    <string name="permdesc_callPrivileged">"Consente all\'applicazione di chiamare qualsiasi numero, compresi quelli di emergenza, automaticamente. Le applicazioni dannose potrebbero effettuare chiamate non necessarie e illegali a servizi di emergenza."</string>
+    <string name="permlab_locationUpdates">"controllare le notifiche di aggiornamento della posizione"</string>
+    <string name="permdesc_locationUpdates">"Consente l\'attivazione/disattivazione delle notifiche di aggiornamento della posizione. Da non usare per normali applicazioni."</string>
+    <string name="permlab_checkinProperties">"accedere a proprietà di archiviazione"</string>
+    <string name="permdesc_checkinProperties">"Consente l\'accesso di lettura/scrittura alle proprietà caricate dal servizio di archiviazione. Da non usare per normali applicazioni."</string>
+    <string name="permlab_modifyPhoneState">"modificare lo stato del telefono"</string>
+    <string name="permdesc_modifyPhoneState">"Consente all\'applicazione di controllare le funzioni telefoniche del dispositivo. Un\'applicazione con questa autorizzazione può cambiare rete, accendere e spegnere la radio del telefono e così via, il tutto automaticamente."</string>
+    <string name="permlab_readPhoneState">"leggere lo stato del telefono"</string>
+    <string name="permdesc_readPhoneState">"Consente l\'accesso dell\'applicazione alle funzioni telefoniche del dispositivo. Un\'applicazione con questa autorizzazione può determinare il numero del telefono in uso, se una chiamata è attiva o meno, il numero a cui è collegata la chiamata e simili."</string>
+    <string name="permlab_wakeLock">"impedire la sospensione del telefono"</string>
+    <string name="permdesc_wakeLock">"Consente a un\'applicazione di impedire la sospensione del telefono."</string>
+    <string name="permlab_devicePower">"accendere o spegnere il telefono"</string>
+    <string name="permdesc_devicePower">"Consente all\'applicazione di accendere o spegnere il telefono."</string>
+    <string name="permlab_factoryTest">"eseguire in modalità test di fabbrica"</string>
+    <string name="permdesc_factoryTest">"In esecuzione come test del produttore di basso livello, consentendo l\'accesso completo all\'hardware del telefono. Disponibile soltanto quando il telefono è in esecuzione in modalità test del produttore."</string>
+    <string name="permlab_setWallpaper">"impostare lo sfondo"</string>
+    <string name="permdesc_setWallpaper">"Consente all\'applicazione di impostare lo sfondo del sistema."</string>
+    <string name="permlab_setWallpaperHints">"impostare suggerimenti per le dimensioni dello sfondo"</string>
+    <string name="permdesc_setWallpaperHints">"Consente all\'applicazione di impostare i suggerimenti per le dimensioni dello sfondo del sistema."</string>
+    <string name="permlab_masterClear">"ripristinare impostazioni predefinite di fabbrica"</string>
+    <string name="permdesc_masterClear">"Consente a un\'applicazione di ripristinare le impostazioni di fabbrica del sistema, eliminando tutti i dati, le configurazioni e le applicazioni installate."</string>
+    <string name="permlab_setTimeZone">"impostare il fuso orario"</string>
+    <string name="permdesc_setTimeZone">"Consente a un\'applicazione di modificare il fuso orario del telefono."</string>
+    <string name="permlab_getAccounts">"trovare account noti"</string>
+    <string name="permdesc_getAccounts">"Consente a un\'applicazione di recuperare l\'elenco di account memorizzati sul telefono."</string>
+    <string name="permlab_accessNetworkState">"visualizzare lo stato della rete"</string>
+    <string name="permdesc_accessNetworkState">"Consente a un\'applicazione di visualizzare lo stato di tutte le reti."</string>
+    <string name="permlab_createNetworkSockets">"accesso completo a Internet"</string>
+    <string name="permdesc_createNetworkSockets">"Consente a un\'applicazione di creare socket di rete."</string>
+    <string name="permlab_writeApnSettings">"scrivere impostazioni di nomi di punti di accesso"</string>
+    <string name="permdesc_writeApnSettings">"Consente a un\'applicazione di modificare le impostazioni APN, come proxy e porta di qualsiasi APN."</string>
+    <string name="permlab_changeNetworkState">"cambiare connettività di rete"</string>
+    <string name="permdesc_changeNetworkState">"Consente a un\'applicazione di modificare lo stato di connettività di rete."</string>
+    <string name="permlab_accessWifiState">"visualizzare lo stato Wi-Fi"</string>
+    <string name="permdesc_accessWifiState">"Consente a un\'applicazione di visualizzare le informazioni relative allo stato della connessione Wi-Fi."</string>
+    <string name="permlab_changeWifiState">"cambiare stato Wi-Fi"</string>
+    <string name="permdesc_changeWifiState">"Consente a un\'applicazione di connettersi/disconnettersi da punti di accesso Wi-Fi e di apportare modifiche alle reti Wi-Fi configurate."</string>
+    <string name="permlab_bluetoothAdmin">"gestione Bluetooth"</string>
+    <string name="permdesc_bluetoothAdmin">"Consente a un\'applicazione di configurare il telefono Bluetooth locale e di rilevare e abbinare dispositivi remoti."</string>
+    <string name="permlab_bluetooth">"creare connessioni Bluetooth"</string>
+    <string name="permdesc_bluetooth">"Consente a un\'applicazione di visualizzare la configurazione del telefono Bluetooth locale e di stabilire e accettare connessioni con dispositivi associati."</string>
+    <string name="permlab_disableKeyguard">"disattivare blocco tastiera"</string>
+    <string name="permdesc_disableKeyguard">"Consente la disattivazione da parte di un\'applicazione del blocco tastiera e di eventuali protezioni tramite password associate. Un valido esempio è la disattivazione da parte del telefono del blocco tastiera quando riceve una telefonata in entrata, e la successiva riattivazione del blocco al termine della chiamata."</string>
+    <string name="permlab_readSyncSettings">"leggere impostazioni di sincronizzazione"</string>
+    <string name="permdesc_readSyncSettings">"Consente a un\'applicazione di leggere le impostazioni di sincronizzazione, come l\'attivazione o meno della sincronizzazione per Contatti."</string>
+    <string name="permlab_writeSyncSettings">"scrivere impostazioni di sincronizzazione"</string>
+    <string name="permdesc_writeSyncSettings">"Consente a un\'applicazione di modificare le impostazioni di sincronizzazione, come l\'attivazione o meno della sincronizzazione per Contatti."</string>
+    <string name="permlab_readSyncStats">"leggere statistiche di sincronizzazione"</string>
+    <string name="permdesc_readSyncStats">"Consente a un\'applicazione di leggere le statistiche di sincronizzazione, per esempio la cronologia delle sincronizzazioni effettuate."</string>
+    <string name="permlab_subscribedFeedsRead">"leggere feed sottoscritti"</string>
+    <string name="permdesc_subscribedFeedsRead">"Consente a un\'applicazione di ottenere dettagli sui feed attualmente sincronizzati."</string>
+    <string name="permlab_subscribedFeedsWrite">"scrivere feed sottoscritti"</string>
+    <string name="permdesc_subscribedFeedsWrite">"Consente la modifica da parte di un\'applicazione dei feed attualmente sincronizzati. Le applicazioni dannose potrebbero essere in grado di modificare i feed sincronizzati."</string>
+    <!-- no translation found for phoneTypes:7 (9192514806975898961) -->
+    <!-- no translation found for emailAddressTypes:3 (2374913952870110618) -->
+    <!-- no translation found for postalAddressTypes:3 (4932682847595299369) -->
+    <!-- no translation found for imAddressTypes:3 (3145118944639869809) -->
+    <!-- no translation found for organizationTypes:2 (3455047468583965104) -->
+  <string-array name="imProtocols">
+    <item>"AIM"</item>
+    <item>"Windows Live"</item>
+    <item>"Yahoo"</item>
+    <item>"Skype"</item>
+    <item>"QQ"</item>
+    <item>"Google Talk"</item>
+    <item>"ICQ"</item>
+    <item>"Jabber"</item>
+  </string-array>
+    <!-- no translation found for keyguard_password_enter_pin_code (3731488827218876115) -->
+    <skip />
+    <string name="keyguard_password_wrong_pin_code">"Codice PIN errato."</string>
+    <string name="keyguard_label_text">"Per sbloccare, premere Menu, poi 0."</string>
+    <string name="emergency_call_dialog_number_for_display">"Numero di emergenza"</string>
+    <string name="lockscreen_carrier_default">"(Nessun servizio)"</string>
+    <!-- no translation found for lockscreen_screen_locked (7288443074806832904) -->
+    <skip />
+    <string name="lockscreen_instructions_when_pattern_enabled">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
+    <string name="lockscreen_instructions_when_pattern_disabled">"Premi Menu per sbloccare."</string>
+    <!-- no translation found for lockscreen_pattern_instructions (7478703254964810302) -->
+    <skip />
+    <string name="lockscreen_emergency_call">"Chiamata di emergenza"</string>
+    <string name="lockscreen_pattern_correct">"Corretta."</string>
+    <!-- no translation found for lockscreen_pattern_wrong (4817583279053112312) -->
+    <skip />
+    <string name="lockscreen_plugged_in">"In carica (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string>
+    <string name="lockscreen_low_battery">"Collega il caricabatterie."</string>
+    <string name="lockscreen_missing_sim_message_short">"Nessuna SIM presente."</string>
+    <string name="lockscreen_missing_sim_message">"Nessuna SIM presente nel telefono."</string>
+    <string name="lockscreen_missing_sim_instructions">"Inserisci una SIM."</string>
+    <string name="lockscreen_network_locked_message">"Rete bloccata"</string>
+    <string name="lockscreen_sim_puk_locked_message">"La SIM è bloccata tramite PUK."</string>
+    <string name="lockscreen_sim_puk_locked_instructions">"Contatta il servizio clienti."</string>
+    <string name="lockscreen_sim_locked_message">"La SIM è bloccata."</string>
+    <string name="lockscreen_sim_unlock_progress_dialog_message">"Sblocco SIM..."</string>
+    <string name="lockscreen_too_many_failed_attempts_dialog_message">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi di inserimento della sequenza di sblocco. "\n\n"Riprova fra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono tramite i dati di accesso di Google."\n\n"Riprova fra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+    <string name="lockscreen_too_many_failed_attempts_countdown">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+    <string name="lockscreen_forgot_pattern_button_text">"Hai dimenticato la sequenza?"</string>
+    <string name="lockscreen_glogin_too_many_attempts">"Troppi tentativi di inserimento della sequenza."</string>
+    <!-- no translation found for lockscreen_glogin_instructions (7400120254204758548) -->
+    <skip />
+    <string name="lockscreen_glogin_username_hint">"Nome utente (email)"</string>
+    <string name="lockscreen_glogin_password_hint">"Password"</string>
+    <string name="lockscreen_glogin_submit_button">"Accedi"</string>
+    <string name="lockscreen_glogin_invalid_input">"Password o nome utente non valido."</string>
+    <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string>
+    <!-- no translation found for hour_minute_ampm (7044207493989843593) -->
+    <skip />
+    <!-- no translation found for hour_minute_cap_ampm (5778825208801303756) -->
+    <skip />
+    <!-- no translation found for hour_ampm (7618670480400517084) -->
+    <skip />
+    <!-- no translation found for hour_cap_ampm (5117798389811605468) -->
+    <skip />
+    <string name="status_bar_clear_all_button">"Cancella notifiche"</string>
+    <string name="status_bar_no_notifications_title">"Nessuna notifica"</string>
+    <string name="status_bar_ongoing_events_title">"In corso"</string>
+    <string name="status_bar_latest_events_title">"Notifiche"</string>
+    <!-- no translation found for battery_status_text_percent_format (8818848472818880005) -->
+    <skip />
+    <string name="battery_status_charging">"In carica..."</string>
+    <string name="battery_low_title">"Collega il caricabatterie"</string>
+    <string name="battery_low_subtitle">"Batteria quasi scarica:"</string>
+    <string name="battery_low_percent_format">"meno di <xliff:g id="NUMBER">%d%%</xliff:g> rimanenti."</string>
+    <string name="factorytest_failed">"Test di fabbrica non riuscito"</string>
+    <string name="factorytest_not_system">"L\'azione FACTORY_TEST è supportata soltanto per i pacchetti installati in /system/app."</string>
+    <string name="factorytest_no_action">"Nessun pacchetto trovato che fornisca l\'azione FACTORY_TEST."</string>
+    <string name="factorytest_reboot">"Riavvia"</string>
+    <string name="save_password_label">"Conferma"</string>
+    <string name="save_password_message">"Memorizzare la password nel browser?"</string>
+    <string name="save_password_notnow">"Non ora"</string>
+    <string name="save_password_remember">"Memorizza"</string>
+    <string name="save_password_never">"Mai"</string>
+    <string name="open_permission_deny">"L\'utente non è autorizzato ad aprire questa pagina."</string>
+    <string name="text_copied">"Testo copiato negli appunti."</string>
+    <string name="more_item_label">"Altro"</string>
+    <string name="prepend_shortcut_label">"Menu+"</string>
+    <string name="menu_space_shortcut_label">"spazio"</string>
+    <string name="menu_enter_shortcut_label">"Invio"</string>
+    <string name="menu_delete_shortcut_label">"Canc"</string>
+    <string name="search_go">"Cerca"</string>
+    <string name="today">"Oggi"</string>
+    <string name="yesterday">"Ieri"</string>
+    <string name="tomorrow">"Domani"</string>
+    <string name="oneMonthDurationPast">"1 mese fa"</string>
+    <string name="beforeOneMonthDurationPast">"Oltre 1 mese fa"</string>
+  <plurals name="num_seconds_ago">
+    <item quantity="one">"1 secondo fa"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> secondi fa"</item>
+  </plurals>
+  <plurals name="num_minutes_ago">
+    <item quantity="one">"1 minuto fa"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> minuti fa"</item>
+  </plurals>
+  <plurals name="num_hours_ago">
+    <item quantity="one">"1 ora fa"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> ore fa"</item>
+  </plurals>
+  <plurals name="num_days_ago">
+    <item quantity="one">"ieri"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> giorni fa"</item>
+  </plurals>
+  <plurals name="in_num_seconds">
+    <item quantity="one">"tra 1 secondo"</item>
+    <item quantity="other">"tra <xliff:g id="COUNT">%d</xliff:g> secondi"</item>
+  </plurals>
+  <plurals name="in_num_minutes">
+    <item quantity="one">"tra 1 minuto"</item>
+    <item quantity="other">"tra <xliff:g id="COUNT">%d</xliff:g> minuti"</item>
+  </plurals>
+  <plurals name="in_num_hours">
+    <item quantity="one">"tra 1 ora"</item>
+    <item quantity="other">"tra <xliff:g id="COUNT">%d</xliff:g> ore"</item>
+  </plurals>
+  <plurals name="in_num_days">
+    <item quantity="one">"domani"</item>
+    <item quantity="other">"tra <xliff:g id="COUNT">%d</xliff:g> giorni"</item>
+  </plurals>
+    <!-- no translation found for abbrev_num_seconds_ago:one (1849036840200069118) -->
+    <!-- no translation found for abbrev_num_seconds_ago:other (3699169366650930415) -->
+    <!-- no translation found for abbrev_num_minutes_ago:one (6361490147113871545) -->
+    <!-- no translation found for abbrev_num_minutes_ago:other (851164968597150710) -->
+    <!-- no translation found for abbrev_num_hours_ago:one (4796212039724722116) -->
+    <!-- no translation found for abbrev_num_hours_ago:other (6889970745748538901) -->
+    <!-- no translation found for abbrev_num_days_ago:one (8463161711492680309) -->
+    <!-- no translation found for abbrev_num_days_ago:other (3453342639616481191) -->
+    <!-- no translation found for abbrev_in_num_seconds:one (5842225370795066299) -->
+    <!-- no translation found for abbrev_in_num_seconds:other (5495880108825805108) -->
+    <!-- no translation found for abbrev_in_num_minutes:one (562786149928284878) -->
+    <!-- no translation found for abbrev_in_num_minutes:other (4216113292706568726) -->
+    <!-- no translation found for abbrev_in_num_hours:one (3274708118124045246) -->
+    <!-- no translation found for abbrev_in_num_hours:other (3705373766798013406) -->
+    <!-- no translation found for abbrev_in_num_days:one (2178576254385739855) -->
+    <!-- no translation found for abbrev_in_num_days:other (2973062968038355991) -->
+    <string name="preposition_for_date">"il %s"</string>
+    <string name="preposition_for_time">"alle %s"</string>
+    <string name="preposition_for_year">"nel %s"</string>
+    <string name="day">"giorno"</string>
+    <string name="days">"giorni"</string>
+    <string name="hour">"ora"</string>
+    <string name="hours">"ore"</string>
+    <string name="minute">"min"</string>
+    <string name="minutes">"min"</string>
+    <string name="second">"sec"</string>
+    <string name="seconds">"sec"</string>
+    <string name="week">"settimana"</string>
+    <string name="weeks">"settimane"</string>
+    <string name="year">"anno"</string>
+    <string name="years">"anni"</string>
+    <string name="sunday">"Domenica"</string>
+    <string name="monday">"Lunedì"</string>
+    <string name="tuesday">"Martedì"</string>
+    <string name="wednesday">"Mercoledì"</string>
+    <string name="thursday">"Giovedì"</string>
+    <string name="friday">"Venerdì"</string>
+    <string name="saturday">"Sabato"</string>
+    <string name="every_weekday">"Ogni giorno feriale (lun-ven)"</string>
+    <string name="daily">"Quotidianamente"</string>
+    <string name="weekly">"Ogni settimana il <xliff:g id="DAY">%s</xliff:g>"</string>
+    <string name="monthly">"Mensilmente"</string>
+    <string name="yearly">"Annualmente"</string>
+    <string name="VideoView_error_title">"Impossibile riprodurre il video"</string>
+    <string name="VideoView_error_text_unknown">"Spiacenti. Impossibile riprodurre il video."</string>
+    <string name="VideoView_error_button">"OK"</string>
+    <string name="am">"AM"</string>
+    <string name="pm">"PM"</string>
+    <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string>
+    <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string>
+    <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string>
+    <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string>
+    <!-- no translation found for date_time (6104442718633642836) -->
+    <skip />
+    <!-- no translation found for relative_time (1818557177829411417) -->
+    <skip />
+    <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string>
+    <string name="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="medium_date_month_first">"<xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="DAY">dd</xliff:g>, <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string>
+    <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string>
+    <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string>
+    <string name="noon">"mezzogiorno"</string>
+    <string name="Noon">"Mezzogiorno"</string>
+    <string name="midnight">"mezzanotte"</string>
+    <string name="Midnight">"Mezzanotte"</string>
+    <!-- no translation found for month_day (5565829181417740906) -->
+    <skip />
+    <!-- no translation found for month (7026169712234774086) -->
+    <skip />
+    <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <!-- no translation found for month_year (9219019380312413367) -->
+    <skip />
+    <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string>
+    <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string>
+    <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string>
+    <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string>
+    <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string>
+    <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string>
+    <!-- no translation found for abbrev_month_year (3856424847226891943) -->
+    <skip />
+    <!-- no translation found for abbrev_month_day (5028815883653985933) -->
+    <skip />
+    <!-- no translation found for abbrev_month (3131032032850777433) -->
+    <skip />
+    <string name="day_of_week_long_sunday">"Domenica"</string>
+    <string name="day_of_week_long_monday">"Lunedì"</string>
+    <string name="day_of_week_long_tuesday">"Martedì"</string>
+    <string name="day_of_week_long_wednesday">"Mercoledì"</string>
+    <string name="day_of_week_long_thursday">"Giovedì"</string>
+    <string name="day_of_week_long_friday">"Venerdì"</string>
+    <string name="day_of_week_long_saturday">"Sabato"</string>
+    <string name="day_of_week_medium_sunday">"Dom"</string>
+    <string name="day_of_week_medium_monday">"Lun"</string>
+    <string name="day_of_week_medium_tuesday">"Mar"</string>
+    <string name="day_of_week_medium_wednesday">"Mer"</string>
+    <string name="day_of_week_medium_thursday">"Gio"</string>
+    <string name="day_of_week_medium_friday">"Ven"</string>
+    <string name="day_of_week_medium_saturday">"Sab"</string>
+    <string name="day_of_week_short_sunday">"Do"</string>
+    <string name="day_of_week_short_monday">"Lu"</string>
+    <string name="day_of_week_short_tuesday">"Ma"</string>
+    <string name="day_of_week_short_wednesday">"Me"</string>
+    <string name="day_of_week_short_thursday">"Gi"</string>
+    <string name="day_of_week_short_friday">"Ve"</string>
+    <string name="day_of_week_short_saturday">"Sa"</string>
+    <string name="day_of_week_shorter_sunday">"Do"</string>
+    <string name="day_of_week_shorter_monday">"Lu"</string>
+    <string name="day_of_week_shorter_tuesday">"Ma"</string>
+    <string name="day_of_week_shorter_wednesday">"Me"</string>
+    <string name="day_of_week_shorter_thursday">"Gi"</string>
+    <string name="day_of_week_shorter_friday">"V"</string>
+    <string name="day_of_week_shorter_saturday">"Sa"</string>
+    <string name="day_of_week_shortest_sunday">"D"</string>
+    <string name="day_of_week_shortest_monday">"Lun"</string>
+    <string name="day_of_week_shortest_tuesday">"M"</string>
+    <string name="day_of_week_shortest_wednesday">"Mer"</string>
+    <string name="day_of_week_shortest_thursday">"G"</string>
+    <string name="day_of_week_shortest_friday">"V"</string>
+    <string name="day_of_week_shortest_saturday">"Sa"</string>
+    <string name="month_long_january">"Gennaio"</string>
+    <string name="month_long_february">"Febbraio"</string>
+    <string name="month_long_march">"Marzo"</string>
+    <string name="month_long_april">"Aprile"</string>
+    <string name="month_long_may">"Maggio"</string>
+    <string name="month_long_june">"Giugno"</string>
+    <string name="month_long_july">"Luglio"</string>
+    <string name="month_long_august">"Agosto"</string>
+    <string name="month_long_september">"Settembre"</string>
+    <string name="month_long_october">"Ottobre"</string>
+    <string name="month_long_november">"Novembre"</string>
+    <string name="month_long_december">"Dicembre"</string>
+    <string name="month_medium_january">"Gen"</string>
+    <string name="month_medium_february">"Feb"</string>
+    <string name="month_medium_march">"Mar"</string>
+    <string name="month_medium_april">"Apr"</string>
+    <string name="month_medium_may">"Mag"</string>
+    <string name="month_medium_june">"Giu"</string>
+    <string name="month_medium_july">"Lug"</string>
+    <string name="month_medium_august">"Ago"</string>
+    <string name="month_medium_september">"Set"</string>
+    <string name="month_medium_october">"Ott"</string>
+    <string name="month_medium_november">"Nov"</string>
+    <string name="month_medium_december">"Dic"</string>
+    <string name="month_shortest_january">"G"</string>
+    <string name="month_shortest_february">"F"</string>
+    <string name="month_shortest_march">"Mar"</string>
+    <string name="month_shortest_april">"Ap"</string>
+    <string name="month_shortest_may">"Mag"</string>
+    <string name="month_shortest_june">"Gi"</string>
+    <string name="month_shortest_july">"Lug"</string>
+    <string name="month_shortest_august">"Ago"</string>
+    <string name="month_shortest_september">"Set"</string>
+    <string name="month_shortest_october">"O"</string>
+    <string name="month_shortest_november">"N"</string>
+    <string name="month_shortest_december">"Di"</string>
+    <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
+    <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
+    <string name="selectAll">"Seleziona tutto"</string>
+    <!-- no translation found for selectText (3889149123626888637) -->
+    <skip />
+    <!-- no translation found for stopSelectingText (4157931463872320996) -->
+    <skip />
+    <string name="cut">"Taglia"</string>
+    <string name="cutAll">"Taglia tutto"</string>
+    <string name="copy">"Copia"</string>
+    <string name="copyAll">"Copia tutto"</string>
+    <string name="paste">"Incolla"</string>
+    <string name="copyUrl">"Copia URL"</string>
+    <!-- no translation found for inputMethod (7673923508389094672) -->
+    <skip />
+    <!-- no translation found for editTextMenuTitle (1672989176958581452) -->
+    <skip />
+    <string name="low_internal_storage_view_title">"Spazio in esaurimento"</string>
+    <string name="low_internal_storage_view_text">"Spazio di archiviazione del telefono in esaurimento."</string>
+    <string name="ok">"OK"</string>
+    <string name="cancel">"Annulla"</string>
+    <string name="yes">"OK"</string>
+    <string name="no">"Annulla"</string>
+    <string name="capital_on">"ON"</string>
+    <string name="capital_off">"OFF"</string>
+    <string name="whichApplication">"Completa l\'azione con"</string>
+    <string name="alwaysUse">"Usa come predefinita per questa azione."</string>
+    <string name="clearDefaultHintMsg">"Cancella predefinita in Impostazioni home &gt; Applicazioni &gt; Gestisci applicazioni."</string>
+    <string name="chooseActivity">"Seleziona un\'azione"</string>
+    <string name="noApplications">"Nessuna applicazione è in grado di svolgere questa azione."</string>
+    <string name="aerr_title">"Spiacenti."</string>
+    <string name="aerr_application">"Interruzione imprevista dell\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> (processo<xliff:g id="PROCESS">%2$s</xliff:g>). Riprova."</string>
+    <string name="aerr_process">"Interruzione imprevista del processo <xliff:g id="PROCESS">%1$s</xliff:g>. Riprova."</string>
+    <string name="anr_title">"Spiacenti."</string>
+    <string name="anr_activity_application">"L\'attività <xliff:g id="ACTIVITY">%1$s</xliff:g> (nell\'applicazione <xliff:g id="APPLICATION">%2$s</xliff:g>) non risponde."</string>
+    <string name="anr_activity_process">"L\'attività <xliff:g id="ACTIVITY">%1$s</xliff:g> (nel processo <xliff:g id="PROCESS">%2$s</xliff:g>) non risponde."</string>
+    <string name="anr_application_process">"L\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> (nel processo <xliff:g id="PROCESS">%2$s</xliff:g>) non risponde."</string>
+    <string name="anr_process">"Il processo <xliff:g id="PROCESS">%1$s</xliff:g> non risponde."</string>
+    <string name="force_close">"Forza chiusura"</string>
+    <string name="wait">"Attendi"</string>
+    <string name="debug">"Debug"</string>
+    <string name="sendText">"Seleziona un\'azione per il testo"</string>
+    <string name="volume_ringtone">"Volume suoneria"</string>
+    <!-- no translation found for volume_music (5421651157138628171) -->
+    <skip />
+    <!-- no translation found for volume_music_hint_playing_through_bluetooth (9165984379394601533) -->
+    <skip />
+    <string name="volume_call">"Volume chiamate"</string>
+    <!-- no translation found for volume_call_hint_playing_through_bluetooth (7750873841563910404) -->
+    <skip />
+    <string name="volume_alarm">"Volume allarme"</string>
+    <!-- no translation found for volume_notification (2422265656744276715) -->
+    <skip />
+    <string name="volume_unknown">"Volume"</string>
+    <string name="ringtone_default">"Suoneria predefinita"</string>
+    <string name="ringtone_default_with_actual">"Suoneria predefinita (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+    <string name="ringtone_silent">"Silenzioso"</string>
+    <!-- no translation found for ringtone_picker_title (3515143939175119094) -->
+    <skip />
+    <string name="ringtone_unknown">"Suoneria sconosciuta"</string>
+  <plurals name="wifi_available">
+    <item quantity="one">"Rete Wi-Fi disponibile"</item>
+    <item quantity="other">"Reti Wi-Fi disponibili"</item>
+  </plurals>
+  <plurals name="wifi_available_detailed">
+    <item quantity="one">"Rete Wi-Fi aperta disponibile"</item>
+    <item quantity="other">"Reti Wi-Fi aperte disponibili"</item>
+  </plurals>
+    <!-- no translation found for select_character (3365550120617701745) -->
+    <skip />
+    <string name="sms_control_default_app_name">"Applicazione sconosciuta"</string>
+    <string name="sms_control_title">"Invio SMS"</string>
+    <string name="sms_control_message">"È in corso l\'invio di numerosi SMS. Seleziona \"OK\" per continuare, oppure \"Annulla\" per interrompere l\'invio."</string>
+    <string name="sms_control_yes">"OK"</string>
+    <string name="sms_control_no">"Annulla"</string>
+    <string name="date_time_set">"Imposta"</string>
+    <string name="default_permission_group">"Predefinito"</string>
+    <string name="no_permissions">"Nessuna autorizzazione richiesta"</string>
+    <string name="perms_hide"><b>"Nascondi"</b></string>
+    <string name="perms_show_all"><b>"Mostra tutto"</b></string>
+    <string name="googlewebcontenthelper_loading">"Caricamento..."</string>
+    <string name="usb_storage_title">"USB collegata"</string>
+    <string name="usb_storage_message">"Il telefono è stato collegato al computer tramite USB. Seleziona \"Collega\" se desideri copiare file tra il computer e la scheda SD del telefono."</string>
+    <string name="usb_storage_button_mount">"Collega"</string>
+    <string name="usb_storage_button_unmount">"Non collegare"</string>
+    <string name="usb_storage_error_message">"Problema di utilizzo della scheda SD per l\'archiviazione USB."</string>
+    <string name="usb_storage_notification_title">"USB collegata"</string>
+    <string name="usb_storage_notification_message">"Seleziona per copiare file sul/dal computer in uso."</string>
+    <!-- no translation found for select_input_method (2086499663193509436) -->
+    <skip />
+    <!-- no translation found for fast_scroll_alphabet (5433275485499039199) -->
+    <skip />
+    <!-- no translation found for fast_scroll_numeric_alphabet (4030170524595123610) -->
+    <skip />
+    <!-- no translation found for candidates_style (5248064431114273041) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc204-fr/strings.xml b/core/res/res/values-mcc204-fr/strings.xml
new file mode 100644 (file)
index 0000000..1b2f85b
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (2972154133076909542) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc204-it/strings.xml b/core/res/res/values-mcc204-it/strings.xml
new file mode 100644 (file)
index 0000000..1b2f85b
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (2972154133076909542) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc204-zh-rCN/strings.xml b/core/res/res/values-mcc204-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..1b2f85b
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (2972154133076909542) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc230-fr/strings.xml b/core/res/res/values-mcc230-fr/strings.xml
new file mode 100644 (file)
index 0000000..46bdbda
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (9011721823833053909) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc230-it/strings.xml b/core/res/res/values-mcc230-it/strings.xml
new file mode 100644 (file)
index 0000000..46bdbda
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (9011721823833053909) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc230-zh-rCN/strings.xml b/core/res/res/values-mcc230-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..46bdbda
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (9011721823833053909) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc232-fr/strings.xml b/core/res/res/values-mcc232-fr/strings.xml
new file mode 100644 (file)
index 0000000..0a20691
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (166900303893651370) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc232-it/strings.xml b/core/res/res/values-mcc232-it/strings.xml
new file mode 100644 (file)
index 0000000..0a20691
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (166900303893651370) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc232-zh-rCN/strings.xml b/core/res/res/values-mcc232-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..0a20691
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (166900303893651370) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc234-fr/strings.xml b/core/res/res/values-mcc234-fr/strings.xml
new file mode 100644 (file)
index 0000000..44a7f03
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (233769627858930762) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc234-it/strings.xml b/core/res/res/values-mcc234-it/strings.xml
new file mode 100644 (file)
index 0000000..44a7f03
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (233769627858930762) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc234-zh-rCN/strings.xml b/core/res/res/values-mcc234-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..44a7f03
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (233769627858930762) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc260-fr/strings.xml b/core/res/res/values-mcc260-fr/strings.xml
new file mode 100644 (file)
index 0000000..ddf5b9f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (6306793451973344945) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc260-it/strings.xml b/core/res/res/values-mcc260-it/strings.xml
new file mode 100644 (file)
index 0000000..ddf5b9f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (6306793451973344945) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc260-zh-rCN/strings.xml b/core/res/res/values-mcc260-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..ddf5b9f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (6306793451973344945) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc262-fr/strings.xml b/core/res/res/values-mcc262-fr/strings.xml
new file mode 100644 (file)
index 0000000..fad4872
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (273407696421660814) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc262-it/strings.xml b/core/res/res/values-mcc262-it/strings.xml
new file mode 100644 (file)
index 0000000..fad4872
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (273407696421660814) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-mcc262-zh-rCN/strings.xml b/core/res/res/values-mcc262-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..fad4872
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for locale_replacement (273407696421660814) -->
+    <skip />
+</resources>
diff --git a/core/res/res/values-zh-rCN/arrays.xml b/core/res/res/values-zh-rCN/arrays.xml
new file mode 100644 (file)
index 0000000..f9c904b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
new file mode 100644 (file)
index 0000000..e30e31b
--- /dev/null
@@ -0,0 +1,723 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="byteShort">"B"</string>
+    <string name="kilobyteShort">"KB"</string>
+    <string name="megabyteShort">"MB"</string>
+    <string name="gigabyteShort">"GB"</string>
+    <string name="terabyteShort">"TB"</string>
+    <string name="petabyteShort">"PB"</string>
+    <string name="untitled">"&lt;无标题&gt;"</string>
+    <string name="ellipsis">"..."</string>
+    <string name="emptyPhoneNumber">"(无电话号码)"</string>
+    <string name="unknownName">"(未知)"</string>
+    <string name="defaultVoiceMailAlphaTag">"语音信箱"</string>
+    <string name="defaultMsisdnAlphaTag">"MSISDN1"</string>
+    <string name="mmiError">"出现连接问题或 MMI 码无效。"</string>
+    <string name="serviceEnabled">"服务已启用。"</string>
+    <string name="serviceEnabledFor">"已针对以下内容启用了服务:"</string>
+    <string name="serviceDisabled">"服务已被禁用。"</string>
+    <string name="serviceRegistered">"注册成功。"</string>
+    <string name="serviceErased">"清除成功"</string>
+    <string name="passwordIncorrect">"密码不正确。"</string>
+    <string name="mmiComplete">"MMI 已完成。"</string>
+    <string name="badPin">"您键入的旧 PIN 不正确。"</string>
+    <string name="badPuk">"您键入的 PUK 不正确。"</string>
+    <string name="mismatchPin">"您输入的 PIN 不匹配。"</string>
+    <string name="invalidPin">"键入一个 4 到 8 位数的 PIN。"</string>
+    <!-- no translation found for needPuk (919668385956251611) -->
+    <skip />
+    <string name="needPuk2">"键入 PUK2 以解锁 SIM 卡。"</string>
+    <string name="ClipMmi">"来电者 ID"</string>
+    <string name="ClirMmi">"去电者 ID"</string>
+    <string name="CfMmi">"呼叫转移"</string>
+    <string name="CwMmi">"呼叫等待"</string>
+    <string name="BaMmi">"呼叫限制"</string>
+    <string name="PwdMmi">"密码更改"</string>
+    <string name="PinMmi">"PIN 更改"</string>
+    <string name="CLIRDefaultOnNextCallOn">"呼叫者 ID 默认情况下受限制。下一个呼叫:受限制"</string>
+    <string name="CLIRDefaultOnNextCallOff">"呼叫者 ID 默认情况下受限制。下一个呼叫:不受限制"</string>
+    <string name="CLIRDefaultOffNextCallOn">"呼叫者 ID 默认情况下不受限制。下一个呼叫:受限制"</string>
+    <string name="CLIRDefaultOffNextCallOff">"呼叫者 ID 默认情况下不受限制。下一个呼叫:不受限制"</string>
+    <string name="serviceNotProvisioned">"未提供服务。"</string>
+    <string name="CLIRPermanent">"不能更改呼叫者 ID 设置。"</string>
+    <string name="serviceClassVoice">"语音"</string>
+    <string name="serviceClassData">"数据"</string>
+    <string name="serviceClassFAX">"传真"</string>
+    <string name="serviceClassSMS">"短信"</string>
+    <string name="serviceClassDataAsync">"异步"</string>
+    <string name="serviceClassDataSync">"同步"</string>
+    <string name="serviceClassPacket">"包"</string>
+    <string name="serviceClassPAD">"PAD"</string>
+    <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未转移呼叫"</string>
+    <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
+    <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒后转移呼叫 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
+    <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未转移呼叫"</string>
+    <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未转移呼叫"</string>
+    <string name="httpErrorOk">"确定"</string>
+    <string name="httpError">"此网页包含错误。"</string>
+    <string name="httpErrorLookup">"找不到该网址。"</string>
+    <string name="httpErrorUnsupportedAuthScheme">"不支持此站点验证方案。"</string>
+    <string name="httpErrorAuth">"验证失败。"</string>
+    <string name="httpErrorProxyAuth">"通过代理服务器进行验证失败。"</string>
+    <string name="httpErrorConnect">"与服务器的连接失败。"</string>
+    <string name="httpErrorIO">"服务器无法通讯。请稍后重试。"</string>
+    <string name="httpErrorTimeout">"与服务器的连接超时。"</string>
+    <string name="httpErrorRedirectLoop">"该页面包含太多服务器重定向。"</string>
+    <string name="httpErrorUnsupportedScheme">"不支持该协议。"</string>
+    <string name="httpErrorFailedSslHandshake">"不能建立安全连接。"</string>
+    <string name="httpErrorBadUrl">"网址无效,此页面无法打开。"</string>
+    <string name="httpErrorFile">"此文件无法访问。"</string>
+    <string name="httpErrorFileNotFound">"找不到请求的文件。"</string>
+    <string name="httpErrorTooManyRequests">"正在处理的请求太多。请稍后重试。"</string>
+    <string name="contentServiceSync">"同步"</string>
+    <string name="contentServiceSyncNotificationTitle">"同步"</string>
+    <string name="contentServiceTooManyDeletesNotificationDesc">"太多<xliff:g id="CONTENT_TYPE">%s</xliff:g>删除项。"</string>
+    <string name="low_memory">"手机存储空间已满!请删除一些文件来腾出空间。"</string>
+    <string name="me">"我"</string>
+    <string name="power_dialog">"手机选项"</string>
+    <string name="silent_mode">"静音模式"</string>
+    <string name="turn_on_radio">"打开收音机"</string>
+    <string name="turn_off_radio">"关闭收音机"</string>
+    <string name="screen_lock">"屏幕锁定"</string>
+    <string name="power_off">"关机"</string>
+    <string name="shutdown_progress">"正在关机..."</string>
+    <string name="shutdown_confirm">"您的手机会关闭。"</string>
+    <string name="no_recent_tasks">"没有最近的应用程序。"</string>
+    <string name="global_actions">"手机选项"</string>
+    <string name="global_action_lock">"屏幕锁定"</string>
+    <string name="global_action_power_off">"关机"</string>
+    <string name="global_action_toggle_silent_mode">"静音模式"</string>
+    <string name="global_action_silent_mode_on_status">"声音已关闭"</string>
+    <string name="global_action_silent_mode_off_status">"声音已“开启”"</string>
+    <string name="safeMode">"安全模式"</string>
+    <string name="permgrouplab_costMoney">"需要您支付费用的服务"</string>
+    <string name="permgroupdesc_costMoney">"允许应用程序执行可能需要您支付费用的操作。"</string>
+    <string name="permgrouplab_messages">"您的消息"</string>
+    <string name="permgroupdesc_messages">"阅读并编写您的短信、电子邮件和其他消息。"</string>
+    <string name="permgrouplab_personalInfo">"您的个人信息"</string>
+    <string name="permgroupdesc_personalInfo">"直接访问手机中存储的联系人和日历。"</string>
+    <string name="permgrouplab_location">"您所处的位置"</string>
+    <string name="permgroupdesc_location">"监视您的物理位置"</string>
+    <string name="permgrouplab_network">"网络通讯"</string>
+    <string name="permgroupdesc_network">"允许应用程序访问各种网络功能。"</string>
+    <string name="permgrouplab_accounts">"您的 Google 帐户"</string>
+    <string name="permgroupdesc_accounts">"访问可用的 Google 帐户。"</string>
+    <string name="permgrouplab_hardwareControls">"硬件控件"</string>
+    <string name="permgroupdesc_hardwareControls">"直接在手机上访问硬件。"</string>
+    <string name="permgrouplab_phoneCalls">"手机通话"</string>
+    <string name="permgroupdesc_phoneCalls">"监视、记录和处理手机呼叫。"</string>
+    <string name="permgrouplab_systemTools">"系统工具"</string>
+    <string name="permgroupdesc_systemTools">"对系统进行低级访问和控制。"</string>
+    <string name="permgrouplab_developmentTools">"开发工具"</string>
+    <string name="permgroupdesc_developmentTools">"只有应用程序开发人员才需要的功能。"</string>
+    <string name="permlab_statusBar">"禁用或修改状态栏"</string>
+    <string name="permdesc_statusBar">"允许应用程序禁用状态栏或者添加和删除系统图标。"</string>
+    <string name="permlab_expandStatusBar">"展开/收拢状态栏"</string>
+    <string name="permdesc_expandStatusBar">"允许应用程序展开或收拢状态栏。"</string>
+    <string name="permlab_processOutgoingCalls">"拦截去电"</string>
+    <string name="permdesc_processOutgoingCalls">"允许应用程序处理去电和更改要拨打的号码。恶意应用程序可能会监视、重定向或阻止去电。"</string>
+    <string name="permlab_receiveSms">"接收短信"</string>
+    <string name="permdesc_receiveSms">"允许应用程序接收和处理短信。恶意应用程序可能监视您的消息,或将消息删除,而不向您显示。"</string>
+    <string name="permlab_receiveMms">"接收彩信"</string>
+    <string name="permdesc_receiveMms">"允许应用程序接收和处理彩信。恶意应用程序可能会借此监视您的信息,或在您尚未看到的情况下就将其删除。"</string>
+    <string name="permlab_sendSms">"发送短信"</string>
+    <string name="permdesc_sendSms">"允许应用程序发送短信。恶意应用程序可能会在未经您确认的情况下发送消息从而让您支付费用。"</string>
+    <string name="permlab_readSms">"读取短信或彩信"</string>
+    <string name="permdesc_readSms">"允许应用程序读取您的手机或 SIM 卡中存储的短信。恶意应用程序可能会读取您的机密消息。"</string>
+    <string name="permlab_writeSms">"编辑短信或彩信"</string>
+    <string name="permdesc_writeSms">"允许应用程序写入手机或 SIM 卡中存储的短信。恶意应用程序可能会借此删除您的消息。"</string>
+    <string name="permlab_receiveWapPush">"接收 WAP"</string>
+    <string name="permdesc_receiveWapPush">"允许应用程序接收和处理 WAP 消息。恶意应用程序可能监视您的消息,或将消息删除,而不向您显示。"</string>
+    <string name="permlab_getTasks">"检索运行的应用程序"</string>
+    <string name="permdesc_getTasks">"允许应用程序检索有关当前和最近运行的任务的信息。恶意应用程序可能会利用此权限发现有关其他应用程序的私有信息。"</string>
+    <string name="permlab_reorderTasks">"对正在运行的应用程序重新排序"</string>
+    <string name="permdesc_reorderTasks">"允许应用程序将任务移至前端和后台。恶意应用程序可能会借此强行进到前台,而不受您的控制。"</string>
+    <string name="permlab_setDebugApp">"启用应用程序调试"</string>
+    <string name="permdesc_setDebugApp">"允许应用程序启动对其他应用程序的调试。恶意应用程序可能利用此权限终止其他应用程序。"</string>
+    <string name="permlab_changeConfiguration">"更改 UI 设置"</string>
+    <string name="permdesc_changeConfiguration">"允许应用程序更改当前配置,例如语言设置或整体的字体大小。"</string>
+    <string name="permlab_restartPackages">"重新启动其他应用程序"</string>
+    <string name="permdesc_restartPackages">"允许应用程序强制重新启动其他应用程序。"</string>
+    <string name="permlab_setProcessForeground">"防止停止"</string>
+    <string name="permdesc_setProcessForeground">"允许应用程序在前台运行任何进程,因此该进程不能被终止。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_forceBack">"强制应用程序关闭"</string>
+    <string name="permdesc_forceBack">"允许应用程序强制前台的任何活动关闭和重新开始。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_dump">"检索系统内部状态"</string>
+    <string name="permdesc_dump">"允许应用程序检索系统的内部状态。恶意应用程序可能会检索通常它们本不需要的各种私有和安全信息。"</string>
+    <string name="permlab_addSystemService">"发布低级服务"</string>
+    <string name="permdesc_addSystemService">"允许应用程序发布自己的低级系统服务。恶意应用程序可能会攻击系统,以及盗取或破坏系统中的任何数据。"</string>
+    <string name="permlab_runSetActivityWatcher">"监视和控制所有应用程序启动"</string>
+    <string name="permdesc_runSetActivityWatcher">"允许应用程序监视和控制系统启动活动的方式。恶意应用程序可能会彻底损坏系统。此权限仅在开发时才需要,普通的手机应用不需要。"</string>
+    <string name="permlab_broadcastPackageRemoved">"发送包删除的广播"</string>
+    <string name="permdesc_broadcastPackageRemoved">"允许应用程序广播已删除某应用程序包的通知。恶意应用程序可能会利用此权限终止任何其他正在运行的应用程序。"</string>
+    <string name="permlab_broadcastSmsReceived">"发送短信收到的广播"</string>
+    <string name="permdesc_broadcastSmsReceived">"允许应用程序广播已收到短信的通知。恶意应用程序可能会利用此权限伪造收到的短信。"</string>
+    <string name="permlab_broadcastWapPush">"发送 WAP-PUSH 收到的广播"</string>
+    <string name="permdesc_broadcastWapPush">"允许应用程序播报收到 WAP PUSH 消息的通知。恶意应用程序可能会利用此权限乱发彩信或暗中用恶意内容替换任意网页中的内容。"</string>
+    <string name="permlab_setProcessLimit">"限制运行进程的数量"</string>
+    <string name="permdesc_setProcessLimit">"允许应用程序控制运行的最大进程数。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_setAlwaysFinish">"关闭所有后台应用程序"</string>
+    <string name="permdesc_setAlwaysFinish">"允许应用程序控制活动是否始终是一转至后台就完成。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_fotaUpdate">"自动安装系统更新"</string>
+    <string name="permdesc_fotaUpdate">"允许应用程序接收有关未决系统更新的通知并安装这些更新。恶意应用程序可能会利用此权限通过未经授权的更新破坏系统,通常情况下,它们会干扰更新过程。"</string>
+    <string name="permlab_batteryStats">"修改电池统计信息"</string>
+    <string name="permdesc_batteryStats">"允许修改收集的电池统计信息。普通应用程序不能使用此权限。"</string>
+    <string name="permlab_internalSystemWindow">"显示未授权的窗口"</string>
+    <string name="permdesc_internalSystemWindow">"允许创建专用于内部系统用户界面的窗口。普通应用程序不能使用此权限。"</string>
+    <string name="permlab_systemAlertWindow">"显示系统级警报"</string>
+    <string name="permdesc_systemAlertWindow">"允许应用程序显示系统警报窗口。恶意应用程序可能借此掌控整个手机屏幕。"</string>
+    <string name="permlab_setAnimationScale">"修改全局动画速度"</string>
+    <string name="permdesc_setAnimationScale">"允许应用程序随时更改全局动画速度(加快或减慢动画)。"</string>
+    <string name="permlab_manageAppTokens">"管理应用程序令牌"</string>
+    <string name="permdesc_manageAppTokens">"允许应用程序创建和管理自己的令牌,从而绕开其常规的 Z 方向。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_injectEvents">"按键和控制按钮"</string>
+    <string name="permdesc_injectEvents">"允许应用程序将其自己的输入活动(按键等)提供给其他应用程序。恶意应用程序可能会利用此权限掌控手机。"</string>
+    <string name="permlab_readInputState">"记录您键入的内容和执行的操作"</string>
+    <string name="permdesc_readInputState">"即使在与其他应用程序交互时(例如输入密码),也允许应用程序查看您按的键。普通应用程序从不需要使用此权限。"</string>
+    <!-- no translation found for permlab_bindInputMethod (3360064620230515776) -->
+    <skip />
+    <!-- no translation found for permdesc_bindInputMethod (3734838321027317228) -->
+    <skip />
+    <string name="permlab_setOrientation">"更改屏幕方向"</string>
+    <string name="permdesc_setOrientation">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string>
+    <string name="permlab_signalPersistentProcesses">"向应用程序发送 Linux 信号"</string>
+    <string name="permdesc_signalPersistentProcesses">"允许应用程序请求将提供的信号发送给所有持久进程。"</string>
+    <string name="permlab_persistentActivity">"让应用程序始终运行"</string>
+    <string name="permdesc_persistentActivity">"允许应用程序部分持续运行,这样系统便不能将其用于其他应用程序。"</string>
+    <string name="permlab_deletePackages">"删除应用程序"</string>
+    <string name="permdesc_deletePackages">"允许应用程序删除 Android 包。恶意应用程序可能利用此权限删除重要的应用程序。"</string>
+    <string name="permlab_clearAppUserData">"删除其他应用程序的数据"</string>
+    <string name="permdesc_clearAppUserData">"允许应用程序清除用户数据。"</string>
+    <string name="permlab_deleteCacheFiles">"删除其他应用程序的缓存"</string>
+    <string name="permdesc_deleteCacheFiles">"允许应用程序删除缓存文件。"</string>
+    <string name="permlab_getPackageSize">"计算应用程序存储空间"</string>
+    <string name="permdesc_getPackageSize">"允许应用程序检索其代码、数据和缓存大小"</string>
+    <string name="permlab_installPackages">"直接安装应用程序"</string>
+    <string name="permdesc_installPackages">"允许应用程序安装新的或更新的 Android 程序包。恶意应用程序可能会利用此权限添加其具有任意权限的新应用程序。"</string>
+    <string name="permlab_clearAppCache">"删除所有应用程序缓存数据"</string>
+    <string name="permdesc_clearAppCache">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。通常只限于访问系统进程。"</string>
+    <string name="permlab_readLogs">"读取系统日志文件"</string>
+    <string name="permdesc_readLogs">"允许应用程序从系统的各日志文件中进行读取。这样应用程序可以发现有关您正在通过手机执行的操作的常规信息,但这些信息不应包含任何个人或私有信息。"</string>
+    <string name="permlab_diagnostic">"读取/写入诊断拥有的资源"</string>
+    <string name="permdesc_diagnostic">"允许应用程序读取和写入诊断组拥有的任何资源;例如 /dev 中的文件。这可能会潜在地影响系统稳定性和安全性。此权限只应用于由制造商或操作员执行的硬件特定的诊断。"</string>
+    <string name="permlab_changeComponentState">"启用或禁用应用程序组件"</string>
+    <!-- no translation found for permdesc_changeComponentState (4569107043246700630) -->
+    <skip />
+    <string name="permlab_setPreferredApplications">"设置首选的应用程序"</string>
+    <string name="permdesc_setPreferredApplications">"允许应用程序修改首选的应用程序。恶意应用程序可能会利用此权限暗中更改运行的应用程序,从而骗过您的现有应用程序来收集您的私密数据。"</string>
+    <string name="permlab_writeSettings">"修改全局系统设置"</string>
+    <string name="permdesc_writeSettings">"允许应用程序修改系统的设置数据。恶意应用程序可能借此破坏您的系统配置。"</string>
+    <!-- no translation found for permlab_writeSecureSettings (204676251876718288) -->
+    <skip />
+    <!-- no translation found for permdesc_writeSecureSettings (4116616249170428132) -->
+    <skip />
+    <string name="permlab_writeGservices">"修改 Google 服务地图"</string>
+    <string name="permdesc_writeGservices">"允许应用程序修改 Google 服务地图。普通应用程序不能使用此权限。"</string>
+    <string name="permlab_receiveBootCompleted">"引导时自动启动"</string>
+    <string name="permdesc_receiveBootCompleted">"允许应用程序在系统完成引导后即自行启动。这样会加长启动手机所需的时间,而且如果应用程序一直运行,会降低手机的整体速度。"</string>
+    <string name="permlab_broadcastSticky">"发送顽固广播"</string>
+    <string name="permdesc_broadcastSticky">"允许应用程序发送顽固广播,这些广播在结束后仍会保留。恶意应用程序可能会使手机使用太多内存,从而降低其速度和稳定性。"</string>
+    <string name="permlab_readContacts">"读取联系数据"</string>
+    <string name="permdesc_readContacts">"允许应用程序读取您手机中存储的所有联系(地址)数据。恶意应用程序可能利用此权限将您的数据发送给其他人。"</string>
+    <string name="permlab_writeContacts">"写入联系数据"</string>
+    <string name="permdesc_writeContacts">"允许应用程序修改您手机中存储的联系(地址)数据。恶意应用程序可能会利用此权限清除或修改您的联系数据。"</string>
+    <string name="permlab_writeOwnerData">"写入所有者数据"</string>
+    <string name="permdesc_writeOwnerData">"允许应用程序修改您手机中存储的手机所有者数据。恶意应用程序可能会利用此权限清除或修改所有者数据。"</string>
+    <string name="permlab_readOwnerData">"读取所有者数据"</string>
+    <string name="permdesc_readOwnerData">"允许应用程序读取您手机中存储的手机所有者数据。恶意应用程序可能会利用此权限读取手机所有者数据。"</string>
+    <string name="permlab_readCalendar">"读取日历数据"</string>
+    <string name="permdesc_readCalendar">"允许应用程序读取您手机中存储的所有日历活动。恶意应用程序可能利用此权限将您的日历活动发送给其他人。"</string>
+    <string name="permlab_writeCalendar">"写入日历数据"</string>
+    <string name="permdesc_writeCalendar">"允许应用程序修改您手机中存储的日历活动。恶意应用程序可能会利用此权限清除或修改您的日历数据。"</string>
+    <string name="permlab_accessMockLocation">"用于测试的模仿位置源"</string>
+    <string name="permdesc_accessMockLocation">"创建用于测试的模仿位置源。恶意应用程序可能会利用此权限替代真正的位置源(例如 GPS 或网络提供商)返回的位置和/或状态。"</string>
+    <string name="permlab_accessLocationExtraCommands">"访问额外的位置提供程序命令"</string>
+    <string name="permdesc_accessLocationExtraCommands">"访问额外的位置提供程序命令。恶意应用程序可能会利用此权限干扰 GPS 或其他位置源的操作。"</string>
+    <string name="permlab_accessFineLocation">"精准 (GPS) 位置"</string>
+    <string name="permdesc_accessFineLocation">"访问精准的位置源,例如手机上的全球定位系统(如果有)。恶意应用程序可能会利用此权限确定您所处的位置,并可能消耗额外的电池电量。"</string>
+    <string name="permlab_accessCoarseLocation">"粗略(基于网络的)位置"</string>
+    <string name="permdesc_accessCoarseLocation">"访问粗略的位置源(例如蜂窝网络数据库)以确定手机的大体位置(如果适用)。恶意应用程序可能利用此权限来确定您所处的大体位置。"</string>
+    <string name="permlab_accessSurfaceFlinger">"访问 SurfaceFlinger"</string>
+    <string name="permdesc_accessSurfaceFlinger">"允许应用程序使用 SurfaceFlinger 低级功能。"</string>
+    <string name="permlab_readFrameBuffer">"读取帧缓冲区"</string>
+    <string name="permdesc_readFrameBuffer">"允许应用程序使用(读取)帧缓冲区中的内容。"</string>
+    <string name="permlab_modifyAudioSettings">"更改您的音频设置"</string>
+    <string name="permdesc_modifyAudioSettings">"允许应用程序修改全局音频设置,例如音量和路由。"</string>
+    <string name="permlab_recordAudio">"录音"</string>
+    <string name="permdesc_recordAudio">"允许应用程序访问录音路径。"</string>
+    <string name="permlab_camera">"拍照"</string>
+    <string name="permdesc_camera">"允许应用程序通过相机拍照。此权限使应用程序可随时收集相机正在拍摄的图片。"</string>
+    <string name="permlab_brick">"永久禁用手机"</string>
+    <string name="permdesc_brick">"允许应用程序永久禁用整个手机,这是很危险的。"</string>
+    <string name="permlab_reboot">"强制手机重新引导"</string>
+    <string name="permdesc_reboot">"允许应用程序强制手机重新引导。"</string>
+    <string name="permlab_mount_unmount_filesystems">"装载和卸载文件系统"</string>
+    <string name="permdesc_mount_unmount_filesystems">"允许应用程序装载和卸载文件系统以进行可移动存储。"</string>
+    <string name="permlab_vibrate">"控制振动器"</string>
+    <string name="permdesc_vibrate">"允许应用程序控制振动器。"</string>
+    <string name="permlab_flashlight">"控制闪光灯"</string>
+    <string name="permdesc_flashlight">"允许应用程序控制闪光灯。"</string>
+    <string name="permlab_hardware_test">"测试硬件"</string>
+    <string name="permdesc_hardware_test">"允许应用程序控制各外围设备以进行硬件测试。"</string>
+    <string name="permlab_callPhone">"直接呼叫电话号码"</string>
+    <string name="permdesc_callPhone">"允许应用程序在没有您干预的情况下呼叫电话号码。恶意应用程序可能在您的电话帐单上产生意外呼叫。请注意,此权限不允许应用程序呼叫紧急电话号码。"</string>
+    <string name="permlab_callPrivileged">"直接呼叫任何电话号码"</string>
+    <string name="permdesc_callPrivileged">"允许应用程序在没有您干预的情况下呼叫任何电话号码(包括紧急电话号码)。恶意应用程序可能对紧急服务拨打骚扰电话和非法电话。"</string>
+    <string name="permlab_locationUpdates">"控制位置更新通知"</string>
+    <string name="permdesc_locationUpdates">"允许启用/禁用来自收音机的位置更新通知。普通应用程序不能使用此权限。"</string>
+    <string name="permlab_checkinProperties">"访问检入属性"</string>
+    <string name="permdesc_checkinProperties">"允许对检入服务上传的属性进行读/写访问。普通应用程序不能使用此权限。"</string>
+    <string name="permlab_modifyPhoneState">"修改手机状态"</string>
+    <string name="permdesc_modifyPhoneState">"允许应用程序控制设备的手机功能。具有此权限的应用程序可能会切换网络,打开和关闭手机收音机以及类似操作,而不会通知您。"</string>
+    <string name="permlab_readPhoneState">"读取手机状态"</string>
+    <string name="permdesc_readPhoneState">"允许应用程序访问设备的电话功能。具有这种权限的应用程序可确定此电话的电话号码、是否在进行通话以及通话对方的号码等。"</string>
+    <string name="permlab_wakeLock">"防止手机休眠"</string>
+    <string name="permdesc_wakeLock">"允许应用程序防止手机进入休眠状态。"</string>
+    <string name="permlab_devicePower">"开机或关机"</string>
+    <string name="permdesc_devicePower">"允许应用程序打开或关闭手机。"</string>
+    <string name="permlab_factoryTest">"在出厂测试模式下运行"</string>
+    <string name="permdesc_factoryTest">"作为一项低级制造商测试来运行,从而允许对手机硬件进行完全访问。此权限仅当手机在制造商测试模式下运行时才可用。"</string>
+    <string name="permlab_setWallpaper">"设置壁纸"</string>
+    <string name="permdesc_setWallpaper">"允许应用程序设置系统壁纸。"</string>
+    <string name="permlab_setWallpaperHints">"精确设置壁纸大小"</string>
+    <string name="permdesc_setWallpaperHints">"允许应用程序精确设置系统壁纸大小。"</string>
+    <string name="permlab_masterClear">"将系统重设为出厂默认值"</string>
+    <string name="permdesc_masterClear">"允许应用程序将系统完全重设为其出厂设置,即清除所有数据、配置和安装的应用程序。"</string>
+    <string name="permlab_setTimeZone">"设置时区"</string>
+    <string name="permdesc_setTimeZone">"允许应用程序更改手机的时区。"</string>
+    <string name="permlab_getAccounts">"发现已知帐户"</string>
+    <string name="permdesc_getAccounts">"允许应用程序获取手机已知的帐户列表。"</string>
+    <string name="permlab_accessNetworkState">"查看网络状态"</string>
+    <string name="permdesc_accessNetworkState">"允许应用程序查看所有网络的状态。"</string>
+    <string name="permlab_createNetworkSockets">"完全的互联网访问"</string>
+    <string name="permdesc_createNetworkSockets">"允许应用程序创建网络套接字。"</string>
+    <string name="permlab_writeApnSettings">"写入“接入点名称”设置"</string>
+    <string name="permdesc_writeApnSettings">"允许应用程序修改 APN 设置,例如任何 APN 的代理和端口。"</string>
+    <string name="permlab_changeNetworkState">"更改网络连接性"</string>
+    <string name="permdesc_changeNetworkState">"允许应用程序更改状态网络连接性。"</string>
+    <string name="permlab_accessWifiState">"查看 Wi-Fi 状态"</string>
+    <string name="permdesc_accessWifiState">"允许应用程序查看有关 Wi-Fi 状态的信息。"</string>
+    <string name="permlab_changeWifiState">"更改 Wi-Fi 状态"</string>
+    <string name="permdesc_changeWifiState">"允许应用程序连接至 Wi-Fi 接入点以及与 Wi-Fi 接入点断开连接,并允许应用程序对配置的 Wi-Fi 网络进行更改。"</string>
+    <string name="permlab_bluetoothAdmin">"蓝牙管理"</string>
+    <string name="permdesc_bluetoothAdmin">"允许应用程序配置本地蓝牙手机以及发现远程设备并与其配对。"</string>
+    <string name="permlab_bluetooth">"创建蓝牙连接"</string>
+    <string name="permdesc_bluetooth">"允许应用程序查看本地蓝牙手机的配置以及建立和接受与配对设备的连接。"</string>
+    <string name="permlab_disableKeyguard">"禁用键锁"</string>
+    <string name="permdesc_disableKeyguard">"允许应用程序禁用键锁和任何关联的密码安全措施。这种情况的一个恰当示例就是这样一个手机:当收到来电时禁用键锁,当通话结束后再重新启用键锁。"</string>
+    <string name="permlab_readSyncSettings">"读取同步设置"</string>
+    <string name="permdesc_readSyncSettings">"允许应用程序读取同步设置,例如是否针对“联系人”启用同步。"</string>
+    <string name="permlab_writeSyncSettings">"写入同步设置"</string>
+    <string name="permdesc_writeSyncSettings">"允许应用程序修改同步设置,例如是否针对“联系人”启用同步。"</string>
+    <string name="permlab_readSyncStats">"读取同步统计信息"</string>
+    <string name="permdesc_readSyncStats">"允许应用程序读取同步统计信息;例如已发生的同步的历史记录。"</string>
+    <string name="permlab_subscribedFeedsRead">"读取订阅的供稿"</string>
+    <string name="permdesc_subscribedFeedsRead">"允许应用程序获取有关当前同步的供稿的详情。"</string>
+    <string name="permlab_subscribedFeedsWrite">"写入订阅的供稿"</string>
+    <string name="permdesc_subscribedFeedsWrite">"允许应用程序修改您当前同步的供稿。这样恶意程序可以更改您同步的供稿。"</string>
+    <!-- no translation found for phoneTypes:7 (9192514806975898961) -->
+    <!-- no translation found for emailAddressTypes:3 (2374913952870110618) -->
+    <!-- no translation found for postalAddressTypes:3 (4932682847595299369) -->
+    <!-- no translation found for imAddressTypes:3 (3145118944639869809) -->
+    <!-- no translation found for organizationTypes:2 (3455047468583965104) -->
+  <string-array name="imProtocols">
+    <item>"AIM"</item>
+    <item>"Windows Live"</item>
+    <item>"中国雅虎"</item>
+    <item>"Skype"</item>
+    <item>"QQ"</item>
+    <item>"Google Talk"</item>
+    <item>"ICQ"</item>
+    <item>"Jabber"</item>
+  </string-array>
+    <!-- no translation found for keyguard_password_enter_pin_code (3731488827218876115) -->
+    <skip />
+    <string name="keyguard_password_wrong_pin_code">"PIN 码不正确!"</string>
+    <string name="keyguard_label_text">"要解锁,请按“菜单”,然后按 0。"</string>
+    <string name="emergency_call_dialog_number_for_display">"紧急电话号码"</string>
+    <string name="lockscreen_carrier_default">"(无服务)"</string>
+    <!-- no translation found for lockscreen_screen_locked (7288443074806832904) -->
+    <skip />
+    <string name="lockscreen_instructions_when_pattern_enabled">"按“菜单”解锁或拨打紧急电话。"</string>
+    <string name="lockscreen_instructions_when_pattern_disabled">"按“菜单”解锁。"</string>
+    <!-- no translation found for lockscreen_pattern_instructions (7478703254964810302) -->
+    <skip />
+    <string name="lockscreen_emergency_call">"紧急电话"</string>
+    <string name="lockscreen_pattern_correct">"正确!"</string>
+    <!-- no translation found for lockscreen_pattern_wrong (4817583279053112312) -->
+    <skip />
+    <string name="lockscreen_plugged_in">"正在充电 (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string>
+    <string name="lockscreen_low_battery">"连接您的充电器。"</string>
+    <string name="lockscreen_missing_sim_message_short">"没有 SIM 卡。"</string>
+    <string name="lockscreen_missing_sim_message">"手机中无 SIM 卡。"</string>
+    <string name="lockscreen_missing_sim_instructions">"请插入 SIM 卡。"</string>
+    <string name="lockscreen_network_locked_message">"网络已锁定"</string>
+    <string name="lockscreen_sim_puk_locked_message">"SIM 卡已被 PUK 锁定。"</string>
+    <string name="lockscreen_sim_puk_locked_instructions">"请联系客服部门。"</string>
+    <string name="lockscreen_sim_locked_message">"SIM 卡已被锁定。"</string>
+    <string name="lockscreen_sim_unlock_progress_dialog_message">"正在解锁 SIM 卡..."</string>
+    <string name="lockscreen_too_many_failed_attempts_dialog_message">"您 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了您的解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+    <string name="lockscreen_failed_attempts_almost_glogin">"您已错误地绘制了您的解锁图案 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统会要求您使用您的 Google 登录帐户解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+    <string name="lockscreen_too_many_failed_attempts_countdown">"<xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+    <string name="lockscreen_forgot_pattern_button_text">"忘记了图案?"</string>
+    <string name="lockscreen_glogin_too_many_attempts">"图案尝试次数太多!"</string>
+    <!-- no translation found for lockscreen_glogin_instructions (7400120254204758548) -->
+    <skip />
+    <string name="lockscreen_glogin_username_hint">"用户名(电子邮件)"</string>
+    <string name="lockscreen_glogin_password_hint">"密码"</string>
+    <string name="lockscreen_glogin_submit_button">"登录"</string>
+    <string name="lockscreen_glogin_invalid_input">"用户名或密码无效。"</string>
+    <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string>
+    <!-- no translation found for hour_minute_ampm (7044207493989843593) -->
+    <skip />
+    <!-- no translation found for hour_minute_cap_ampm (5778825208801303756) -->
+    <skip />
+    <!-- no translation found for hour_ampm (7618670480400517084) -->
+    <skip />
+    <!-- no translation found for hour_cap_ampm (5117798389811605468) -->
+    <skip />
+    <string name="status_bar_clear_all_button">"清除通知"</string>
+    <string name="status_bar_no_notifications_title">"无通知"</string>
+    <string name="status_bar_ongoing_events_title">"正在进行的"</string>
+    <string name="status_bar_latest_events_title">"通知"</string>
+    <!-- no translation found for battery_status_text_percent_format (8818848472818880005) -->
+    <skip />
+    <string name="battery_status_charging">"正在充电..."</string>
+    <string name="battery_low_title">"请连接充电器"</string>
+    <string name="battery_low_subtitle">"电量在减少:"</string>
+    <string name="battery_low_percent_format">"剩余电量不足 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+    <string name="factorytest_failed">"出厂测试失败"</string>
+    <string name="factorytest_not_system">"只有在 /system/app 中安装的包支持 FACTORY_TEST 操作。"</string>
+    <string name="factorytest_no_action">"未发现支持 FACTORY_TEST 操作的包。"</string>
+    <string name="factorytest_reboot">"重新引导"</string>
+    <string name="save_password_label">"确认"</string>
+    <string name="save_password_message">"是否希望浏览器记住此密码?"</string>
+    <string name="save_password_notnow">"暂不保存"</string>
+    <string name="save_password_remember">"记住"</string>
+    <string name="save_password_never">"从不"</string>
+    <string name="open_permission_deny">"您无权打开此页面。"</string>
+    <string name="text_copied">"文本已复制到剪贴板。"</string>
+    <string name="more_item_label">"更多"</string>
+    <string name="prepend_shortcut_label">"“菜单”+"</string>
+    <string name="menu_space_shortcut_label">"空格"</string>
+    <string name="menu_enter_shortcut_label">"Enter 键"</string>
+    <string name="menu_delete_shortcut_label">"删除"</string>
+    <string name="search_go">"搜索"</string>
+    <string name="today">"今天"</string>
+    <string name="yesterday">"昨天"</string>
+    <string name="tomorrow">"明天"</string>
+    <string name="oneMonthDurationPast">"1 个月前"</string>
+    <string name="beforeOneMonthDurationPast">"1 个月前"</string>
+  <plurals name="num_seconds_ago">
+    <item quantity="one">"1 秒前"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 秒前"</item>
+  </plurals>
+  <plurals name="num_minutes_ago">
+    <item quantity="one">"1 分钟前"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</item>
+  </plurals>
+  <plurals name="num_hours_ago">
+    <item quantity="one">"1 小时前"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</item>
+  </plurals>
+  <plurals name="num_days_ago">
+    <item quantity="one">"昨天"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
+  </plurals>
+  <plurals name="in_num_seconds">
+    <item quantity="one">"1 秒后"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 秒后"</item>
+  </plurals>
+  <plurals name="in_num_minutes">
+    <item quantity="one">"1 分钟后"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 分钟后"</item>
+  </plurals>
+  <plurals name="in_num_hours">
+    <item quantity="one">"1 小时后"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 小时后"</item>
+  </plurals>
+  <plurals name="in_num_days">
+    <item quantity="one">"明天"</item>
+    <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> 天后"</item>
+  </plurals>
+    <!-- no translation found for abbrev_num_seconds_ago:one (1849036840200069118) -->
+    <!-- no translation found for abbrev_num_seconds_ago:other (3699169366650930415) -->
+    <!-- no translation found for abbrev_num_minutes_ago:one (6361490147113871545) -->
+    <!-- no translation found for abbrev_num_minutes_ago:other (851164968597150710) -->
+    <!-- no translation found for abbrev_num_hours_ago:one (4796212039724722116) -->
+    <!-- no translation found for abbrev_num_hours_ago:other (6889970745748538901) -->
+    <!-- no translation found for abbrev_num_days_ago:one (8463161711492680309) -->
+    <!-- no translation found for abbrev_num_days_ago:other (3453342639616481191) -->
+    <!-- no translation found for abbrev_in_num_seconds:one (5842225370795066299) -->
+    <!-- no translation found for abbrev_in_num_seconds:other (5495880108825805108) -->
+    <!-- no translation found for abbrev_in_num_minutes:one (562786149928284878) -->
+    <!-- no translation found for abbrev_in_num_minutes:other (4216113292706568726) -->
+    <!-- no translation found for abbrev_in_num_hours:one (3274708118124045246) -->
+    <!-- no translation found for abbrev_in_num_hours:other (3705373766798013406) -->
+    <!-- no translation found for abbrev_in_num_days:one (2178576254385739855) -->
+    <!-- no translation found for abbrev_in_num_days:other (2973062968038355991) -->
+    <string name="preposition_for_date">"在 %s"</string>
+    <string name="preposition_for_time">"在 %s"</string>
+    <string name="preposition_for_year">"%s 年内"</string>
+    <string name="day">"天"</string>
+    <string name="days">"天"</string>
+    <string name="hour">"小时"</string>
+    <string name="hours">"小时"</string>
+    <string name="minute">"分钟"</string>
+    <string name="minutes">"分钟"</string>
+    <string name="second">"秒"</string>
+    <string name="seconds">"秒"</string>
+    <string name="week">"周"</string>
+    <string name="weeks">"周"</string>
+    <string name="year">"年"</string>
+    <string name="years">"年"</string>
+    <string name="sunday">"周日"</string>
+    <string name="monday">"周一"</string>
+    <string name="tuesday">"周二"</string>
+    <string name="wednesday">"周三"</string>
+    <string name="thursday">"周四"</string>
+    <string name="friday">"周五"</string>
+    <string name="saturday">"周六"</string>
+    <string name="every_weekday">"每个工作日(周一到周五)"</string>
+    <string name="daily">"每天"</string>
+    <string name="weekly">"每周的<xliff:g id="DAY">%s</xliff:g>"</string>
+    <string name="monthly">"每月"</string>
+    <string name="yearly">"每年"</string>
+    <string name="VideoView_error_title">"无法播放视频"</string>
+    <string name="VideoView_error_text_unknown">"很抱歉,此视频不能播放。"</string>
+    <string name="VideoView_error_button">"确定"</string>
+    <string name="am">"AM"</string>
+    <string name="pm">"PM"</string>
+    <string name="numeric_date">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%m</xliff:g> 月 <xliff:g id="DAY">%d</xliff:g> 日"</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> 至 <xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>至<xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string>
+    <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> 至 <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string>
+    <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g>至 <xliff:g id="DATE2">%5$s</xliff:g>"</string>
+    <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> 至 <xliff:g id="TIME2">%2$s</xliff:g>"</string>
+    <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string>
+    <string name="wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g>"</string>
+    <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string>
+    <!-- no translation found for date_time (6104442718633642836) -->
+    <skip />
+    <!-- no translation found for relative_time (1818557177829411417) -->
+    <skip />
+    <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string>
+    <string name="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string>
+    <string name="full_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string>
+    <string name="medium_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string>
+    <string name="medium_date_day_first">"<xliff:g id="YEAR">yyyy</xliff:g> 年 <xliff:g id="MONTH">MMM</xliff:g> 月 <xliff:g id="DAY">dd</xliff:g> 日"</string>
+    <string name="twelve_hour_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">a</xliff:g>"</string>
+    <string name="twenty_four_hour_time_format">"<xliff:g id="HOUR">H</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g>"</string>
+    <string name="noon">"中午"</string>
+    <string name="Noon">"中午"</string>
+    <string name="midnight">"午夜"</string>
+    <string name="Midnight">"午夜"</string>
+    <!-- no translation found for month_day (5565829181417740906) -->
+    <skip />
+    <!-- no translation found for month (7026169712234774086) -->
+    <skip />
+    <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string>
+    <!-- no translation found for month_year (9219019380312413367) -->
+    <skip />
+    <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string>
+    <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日 <xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string>
+    <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="same_year_mdy1_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">",<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至<xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="same_month_mdy1_mdy2">"<xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string>
+    <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string>
+    <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%b</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string>
+    <!-- no translation found for abbrev_month_year (3856424847226891943) -->
+    <skip />
+    <!-- no translation found for abbrev_month_day (5028815883653985933) -->
+    <skip />
+    <!-- no translation found for abbrev_month (3131032032850777433) -->
+    <skip />
+    <string name="day_of_week_long_sunday">"周日"</string>
+    <string name="day_of_week_long_monday">"周一"</string>
+    <string name="day_of_week_long_tuesday">"周二"</string>
+    <string name="day_of_week_long_wednesday">"周三"</string>
+    <string name="day_of_week_long_thursday">"周四"</string>
+    <string name="day_of_week_long_friday">"周五"</string>
+    <string name="day_of_week_long_saturday">"周六"</string>
+    <string name="day_of_week_medium_sunday">"周日"</string>
+    <string name="day_of_week_medium_monday">"周一"</string>
+    <string name="day_of_week_medium_tuesday">"周二"</string>
+    <string name="day_of_week_medium_wednesday">"周三"</string>
+    <string name="day_of_week_medium_thursday">"周四"</string>
+    <string name="day_of_week_medium_friday">"周五"</string>
+    <string name="day_of_week_medium_saturday">"周六"</string>
+    <string name="day_of_week_short_sunday">"周日"</string>
+    <string name="day_of_week_short_monday">"周一"</string>
+    <string name="day_of_week_short_tuesday">"周二"</string>
+    <string name="day_of_week_short_wednesday">"周三"</string>
+    <string name="day_of_week_short_thursday">"周四"</string>
+    <string name="day_of_week_short_friday">"周五"</string>
+    <string name="day_of_week_short_saturday">"周六"</string>
+    <string name="day_of_week_shorter_sunday">"周日"</string>
+    <string name="day_of_week_shorter_monday">"周一"</string>
+    <string name="day_of_week_shorter_tuesday">"周二"</string>
+    <string name="day_of_week_shorter_wednesday">"周三"</string>
+    <string name="day_of_week_shorter_thursday">"周四"</string>
+    <string name="day_of_week_shorter_friday">"周五"</string>
+    <string name="day_of_week_shorter_saturday">"周六"</string>
+    <string name="day_of_week_shortest_sunday">"周日"</string>
+    <string name="day_of_week_shortest_monday">"周一"</string>
+    <string name="day_of_week_shortest_tuesday">"周二"</string>
+    <string name="day_of_week_shortest_wednesday">"周三"</string>
+    <string name="day_of_week_shortest_thursday">"周四"</string>
+    <string name="day_of_week_shortest_friday">"周五"</string>
+    <string name="day_of_week_shortest_saturday">"周六"</string>
+    <string name="month_long_january">"1 月"</string>
+    <string name="month_long_february">"2 月"</string>
+    <string name="month_long_march">"3 月"</string>
+    <string name="month_long_april">"4 月"</string>
+    <string name="month_long_may">"5 月"</string>
+    <string name="month_long_june">"6 月"</string>
+    <string name="month_long_july">"7 月"</string>
+    <string name="month_long_august">"8 月"</string>
+    <string name="month_long_september">"9 月"</string>
+    <string name="month_long_october">"10 月"</string>
+    <string name="month_long_november">"11 月"</string>
+    <string name="month_long_december">"12 月"</string>
+    <string name="month_medium_january">"1 月"</string>
+    <string name="month_medium_february">"2 月"</string>
+    <string name="month_medium_march">"3 月"</string>
+    <string name="month_medium_april">"4 月"</string>
+    <string name="month_medium_may">"5 月"</string>
+    <string name="month_medium_june">"6 月"</string>
+    <string name="month_medium_july">"7 月"</string>
+    <string name="month_medium_august">"8 月"</string>
+    <string name="month_medium_september">"9 月"</string>
+    <string name="month_medium_october">"10 月"</string>
+    <string name="month_medium_november">"11 月"</string>
+    <string name="month_medium_december">"12 月"</string>
+    <string name="month_shortest_january">"1 月"</string>
+    <string name="month_shortest_february">"2 月"</string>
+    <string name="month_shortest_march">"3 月"</string>
+    <string name="month_shortest_april">"4 月"</string>
+    <string name="month_shortest_may">"5 月"</string>
+    <string name="month_shortest_june">"6 月"</string>
+    <string name="month_shortest_july">"7 月"</string>
+    <string name="month_shortest_august">"8 月"</string>
+    <string name="month_shortest_september">"9 月"</string>
+    <string name="month_shortest_october">"10 月"</string>
+    <string name="month_shortest_november">"11 月"</string>
+    <string name="month_shortest_december">"12 月"</string>
+    <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
+    <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
+    <string name="selectAll">"全选"</string>
+    <!-- no translation found for selectText (3889149123626888637) -->
+    <skip />
+    <!-- no translation found for stopSelectingText (4157931463872320996) -->
+    <skip />
+    <string name="cut">"剪切"</string>
+    <string name="cutAll">"全部剪切"</string>
+    <string name="copy">"复制"</string>
+    <string name="copyAll">"全部复制"</string>
+    <string name="paste">"粘贴"</string>
+    <string name="copyUrl">"复制网址"</string>
+    <!-- no translation found for inputMethod (7673923508389094672) -->
+    <skip />
+    <!-- no translation found for editTextMenuTitle (1672989176958581452) -->
+    <skip />
+    <string name="low_internal_storage_view_title">"存储空间不足"</string>
+    <string name="low_internal_storage_view_text">"手机存储空间在减少。"</string>
+    <string name="ok">"正常"</string>
+    <string name="cancel">"取消"</string>
+    <string name="yes">"正常"</string>
+    <string name="no">"取消"</string>
+    <string name="capital_on">"开启"</string>
+    <string name="capital_off">"关闭"</string>
+    <string name="whichApplication">"使用以下内容完成操作"</string>
+    <string name="alwaysUse">"默认用于执行此操作。"</string>
+    <string name="clearDefaultHintMsg">"通过“主页设置”&gt;“应用程序”&gt;“管理应用程序”清除默认值。"</string>
+    <string name="chooseActivity">"选择操作"</string>
+    <string name="noApplications">"没有应用程序可执行此操作。"</string>
+    <string name="aerr_title">"很抱歉!"</string>
+    <string name="aerr_application">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(在进程 <xliff:g id="PROCESS">%2$s</xliff:g> 中)已意外停止。请重试。"</string>
+    <string name="aerr_process">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 已意外停止。请重试。"</string>
+    <string name="anr_title">"很抱歉!"</string>
+    <string name="anr_activity_application">"活动<xliff:g id="ACTIVITY">%1$s</xliff:g>(在应用程序 <xliff:g id="APPLICATION">%2$s</xliff:g> 中)无响应。"</string>
+    <string name="anr_activity_process">"活动<xliff:g id="ACTIVITY">%1$s</xliff:g>(在进程 <xliff:g id="PROCESS">%2$s</xliff:g> 中)无响应。"</string>
+    <string name="anr_application_process">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(在进程 <xliff:g id="PROCESS">%2$s</xliff:g> 中)无响应。"</string>
+    <string name="anr_process">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 无响应。"</string>
+    <string name="force_close">"强制关闭"</string>
+    <string name="wait">"等待"</string>
+    <string name="debug">"调试"</string>
+    <string name="sendText">"选择一个文本操作"</string>
+    <string name="volume_ringtone">"铃声音量"</string>
+    <!-- no translation found for volume_music (5421651157138628171) -->
+    <skip />
+    <!-- no translation found for volume_music_hint_playing_through_bluetooth (9165984379394601533) -->
+    <skip />
+    <string name="volume_call">"来电音量"</string>
+    <!-- no translation found for volume_call_hint_playing_through_bluetooth (7750873841563910404) -->
+    <skip />
+    <string name="volume_alarm">"警告音量"</string>
+    <!-- no translation found for volume_notification (2422265656744276715) -->
+    <skip />
+    <string name="volume_unknown">"音量"</string>
+    <string name="ringtone_default">"默认的手机铃声"</string>
+    <string name="ringtone_default_with_actual">"删除手机铃声(<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+    <string name="ringtone_silent">"静音"</string>
+    <!-- no translation found for ringtone_picker_title (3515143939175119094) -->
+    <skip />
+    <string name="ringtone_unknown">"未知的手机铃声"</string>
+  <plurals name="wifi_available">
+    <item quantity="one">"有可用的 Wi-Fi 网络"</item>
+    <item quantity="other">"有可用的 Wi-Fi 网络"</item>
+  </plurals>
+  <plurals name="wifi_available_detailed">
+    <item quantity="one">"打开可用的 Wi-Fi 网络"</item>
+    <item quantity="other">"打开可用的 Wi-Fi 网络"</item>
+  </plurals>
+    <!-- no translation found for select_character (3365550120617701745) -->
+    <skip />
+    <string name="sms_control_default_app_name">"未知的应用程序"</string>
+    <string name="sms_control_title">"正在发送短信"</string>
+    <string name="sms_control_message">"正在发送大量短信。选择“确定”继续,或选择“取消”停止发送。"</string>
+    <string name="sms_control_yes">"确定"</string>
+    <string name="sms_control_no">"取消"</string>
+    <string name="date_time_set">"设置"</string>
+    <string name="default_permission_group">"默认"</string>
+    <string name="no_permissions">"不需要任何权限"</string>
+    <string name="perms_hide"><b>"隐藏"</b></string>
+    <string name="perms_show_all"><b>"全部显示"</b></string>
+    <string name="googlewebcontenthelper_loading">"正在载入..."</string>
+    <string name="usb_storage_title">"USB 已连接"</string>
+    <string name="usb_storage_message">"您已通过 USB 将手机连接至计算机。如果要在计算机和手机的 SD 卡之间复制文件,请选择“装载”。"</string>
+    <string name="usb_storage_button_mount">"装载"</string>
+    <string name="usb_storage_button_unmount">"不装载"</string>
+    <string name="usb_storage_error_message">"使用 SD 卡进行 USB 存储时出现问题。"</string>
+    <string name="usb_storage_notification_title">"USB 已连接"</string>
+    <string name="usb_storage_notification_message">"选择以将文件复制到计算机或从计算机复制文件。"</string>
+    <!-- no translation found for select_input_method (2086499663193509436) -->
+    <skip />
+    <!-- no translation found for fast_scroll_alphabet (5433275485499039199) -->
+    <skip />
+    <!-- no translation found for fast_scroll_numeric_alphabet (4030170524595123610) -->
+    <skip />
+    <!-- no translation found for candidates_style (5248064431114273041) -->
+    <skip />
+</resources>
index a60845b..ee0db1d 100644 (file)
              {@link android.text.InputType#TYPE_TEXT_VARIATION_NORMAL}. -->
         <flag name="text" value="0x00000001" />
         <!-- Can be combined with <var>text</var> and its variations to
-             request captilization of all characters.  Corresponds to
+             request capitalization of all characters.  Corresponds to
              {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_CHARACTERS}. -->
         <flag name="textCapCharacters" value="0x00001001" />
         <!-- Can be combined with <var>text</var> and its variations to
-             request captilization of the first character of every word.  Corresponds to
+             request capitalization of the first character of every word.  Corresponds to
              {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_WORDS}. -->
         <flag name="textCapWords" value="0x00002001" />
         <!-- Can be combined with <var>text</var> and its variations to
-             request captilization of the first character of every sentence.  Corresponds to
+             request capitalization of the first character of every sentence.  Corresponds to
              {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_SENTENCES}. -->
         <flag name="textCapSentences" value="0x00004001" />
         <!-- Can be combined with <var>text</var> and its variations to
-             request auto-correction of texting being input.  Corresponds to
+             request auto-correction of text being input.  Corresponds to
              {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_CORRECT}. -->
         <flag name="textAutoCorrect" value="0x00008001" />
         <!-- Can be combined with <var>text</var> and its variations to
              allow multiple lines of text in the field.  Corresponds to
              {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. -->
         <flag name="textMultiLine" value="0x00020001" />
+        <!-- Can be combined with <var>text</var> and its variations to
+             indicate that a search string is being entered.  Corresponds to
+             {@link android.text.InputType#TYPE_TEXT_FLAG_SEARCH}. -->
+        <flag name="textSearch" value="0x00040001" />
         <!-- Text that will be used as a URI.  Corresponds to
              {@link android.text.InputType#TYPE_CLASS_TEXT} |
              {@link android.text.InputType#TYPE_TEXT_VARIATION_URI}. -->
              {@link android.text.InputType#TYPE_CLASS_TEXT} |
              {@link android.text.InputType#TYPE_TEXT_VARIATION_PASSWORD}. -->
         <flag name="textPassword" value="0x00000071" />
-        <!-- Text that will be used for a web search.  Corresponds to
+        <!-- Text that will be used for free form search strings.  Corresponds to
              {@link android.text.InputType#TYPE_CLASS_TEXT} |
-             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_SEARCH}. -->
-        <flag name="textWebSearch" value="0x00000081" />
+             {@link android.text.InputType#TYPE_TEXT_VARIATION_SEARCH_STRING} |
+             {@link android.text.InputType#TYPE_TEXT_FLAG_SEARCH}. -->
+        <flag name="textSearchString" value="0x00040081" />
         <!-- Text that is being supplied as text in a web form.  Corresponds to
              {@link android.text.InputType#TYPE_CLASS_TEXT} |
              {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. -->
             attribute.</i> -->
         <attr name="hint" />
         <!-- If supplied, this string will be displayed as the text of the "Search" button.
-          <i>Optional attribute.</i> -->
+          <i>Optional attribute.</i> 
+          {@deprecated This will create a non-standard UI appearance, because the search bar UI is
+                       changing to use only icons for its buttons.}-->
         <attr name="searchButtonText" format="string" />
+        <attr name="inputType" />
         
         <!-- Additional features are controlled by mode bits in this field.  Omitting
             this field, or setting to zero, provides default behavior.  <i>Optional attribute.</i> 
index e58cae4..c5dbfd8 100644 (file)
@@ -62,4 +62,6 @@
   <item type="id" name="inputMethod" />
   <item type="id" name="keyboardView" />
   <item type="id" name="button_close" />
+  <item type="id" name="selectText" />
+  <item type="id" name="stopSelectingText" />
 </resources>
index dadf3ce..ef678e7 100644 (file)
   
   <public type="drawable" name="ic_btn_search" id="0x0108009e" />
   <public type="drawable" name="ic_dialog_menu_generic" id="0x010800a0" />
+  <public type="drawable" name="ic_menu_login" id="0x010800a1" />
+  <public type="drawable" name="ic_menu_refresh" id="0x010800a2" />
+  <public type="drawable" name="ic_menu_notifications" id="0x010800a3" />
 </resources>
 
 
index d92fd5f..f9e2e0f 100644 (file)
         <item quantity="one">tomorrow</item>
         <item quantity="other">in <xliff:g id="count">%d</xliff:g> days</item>
     </plurals>
+    
+    <!-- This is used to express that something occurred some number of abbreviated seconds in the past (e.g., 5 secs ago). -->
+    <plurals name="abbrev_num_seconds_ago">
+        <item quantity="one">1 sec ago</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> secs ago</item>
+    </plurals>
+
+    <!-- This is used to express that something occurred some number of abbreviated minutes in the past (e.g., 5 mins ago). -->
+    <plurals name="abbrev_num_minutes_ago">
+        <item quantity="one">1 min ago</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> mins ago</item>
+    </plurals>
+
+    <!-- This is used to express that something occurred some number of abbreviated hours in the past (e.g., 5 hrs ago). -->
+    <plurals name="abbrev_num_hours_ago">
+        <item quantity="one">1 hour ago</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> hours ago</item>
+    </plurals>
+
+    <!-- This is used to express that something occurred some number of abbreviated days in the past (e.g., 5 days ago). -->
+    <plurals name="abbrev_num_days_ago">
+        <item quantity="one">yesterday</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> days ago</item>
+    </plurals>
+
+    <!-- This is used to express that something will occur some number of abbreviated seconds in the future (e.g., in 5 secs). -->
+    <plurals name="abbrev_in_num_seconds">
+        <item quantity="one">in 1 sec</item>
+        <item quantity="other">in <xliff:g id="count">%d</xliff:g> secs</item>
+    </plurals>
+
+    <!-- This is used to express that something will occur some number of abbreviated minutes in the future (e.g., in 5 mins). -->
+    <plurals name="abbrev_in_num_minutes">
+        <item quantity="one">in 1 min</item>
+        <item quantity="other">in <xliff:g id="count">%d</xliff:g> mins</item>
+    </plurals>
+
+    <!-- This is used to express that something will occur some number of abbreviated hours in the future (e.g., in 5 hrs). -->
+    <plurals name="abbrev_in_num_hours">
+        <item quantity="one">in 1 hour</item>
+        <item quantity="other">in <xliff:g id="count">%d</xliff:g> hours</item>
+    </plurals>
+
+    <!-- This is used to express that something will occur some number of abbreviated days in the future (e.g., in 5 days). -->
+    <plurals name="abbrev_in_num_days">
+        <item quantity="one">tomorrow</item>
+        <item quantity="other">in <xliff:g id="count">%d</xliff:g> days</item>
+    </plurals>
 
     <!-- String used to display the date. Preposition for date display ("on May 29") -->
     <string name="preposition_for_date">on %s</string>
          Example: "8:00 - 11:00 am, Dec 31, 2007" -->
     <string name="time_date">"<xliff:g id="time_range" example="8:00 - 11:00 am">%1$s</xliff:g>, <xliff:g id="date" example="Dec 31, 2007">%3$s</xliff:g>"</string>
 
+    <!-- Format indicating a specific date and time.
+         Example: "Dec 31, 2007, 11:00 am" -->
+    <string name="date_time">"<xliff:g id="date" example="Dec 31, 2007">%1$s</xliff:g>, <xliff:g id="time" example="11:00 am">%2$s</xliff:g>"</string>
+
+    <!-- Format indicating a relative expression and time.
+         Example: "4 hours ago, 11:00 am" -->
+    <string name="relative_time">"<xliff:g id="date" example="4 hours ago">%1$s</xliff:g>, <xliff:g id="time" example="11:00 am">%2$s</xliff:g>"</string>
+
     <!-- Format indicating a range of times on a particular day of the week.
          Example: "8:00 - 11:00 am, Mon" -->
     <string name="time_wday">"<xliff:g id="time_range" example="8:00 - 11:00 am">%1$s</xliff:g>, <xliff:g id="weekday" example="Mon">%2$s</xliff:g>"</string>
     <!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
     <string name="selectAll">Select all</string>
 
+    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+    <string name="selectText">Select text</string>
+
+    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+    <string name="stopSelectingText">Stop selecting text</string>
+
     <!-- Item on EditText context menu.  This action is used to cut selected the text into the clipboard.  -->
     <string name="cut">Cut</string>
 
index 19eb2c6..851a557 100644 (file)
         <item name="android:maxHeight">20dip</item>
         <item name="android:thumb">@android:drawable/seek_thumb</item>
         <item name="android:thumbOffset">8px</item>
+        <item name="android:focusable">true</item>
     </style>
 
     <style name="Widget.RatingBar">
diff --git a/core/res/res/xml-en/autotext.xml b/core/res/res/xml-en/autotext.xml
new file mode 100644 (file)
index 0000000..4c02a00
--- /dev/null
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<words>
+    <word src="abouta">about a</word>
+    <word src="aboutit">about it</word>
+    <word src="aboutthe">about the</word>
+    <word src="acheive">achieve</word>
+    <word src="acheived">achieved</word>
+    <word src="acheiving">achieving</word>
+    <word src="acomodate">accommodate</word>
+    <word src="accomodate">accommodate</word>
+    <word src="acn">can</word>
+    <word src="adn">and</word>
+    <word src="agian">again</word>
+    <word src="ahd">had</word>
+    <word src="ahve">have</word>
+    <word src="aint">ain't</word>
+    <word src="alot">a lot</word>
+    <word src="amde">made</word>
+    <word src="amke">make</word>
+    <word src="andone">and one</word>
+    <word src="andteh">and the</word>
+    <word src="anothe">another</word>
+    <word src="arent">aren't</word>
+    <word src="asthe">as the</word>
+    <word src="atthe">at the</word>
+    <word src="bakc">back</word>
+    <word src="beacuse">because</word>
+    <word src="becasue">because</word>
+    <word src="becaus">because</word>
+    <word src="becausea">because a</word>
+    <word src="becauseof">because of</word>
+    <word src="becausethe">because the</word>
+    <word src="becauseyou">because you</word>
+    <word src="becuase">because</word>
+    <word src="becuse">because</word>
+    <word src="beleive">believe</word>
+    <word src="butthe">but the</word>
+    <word src="cant">can't</word>
+    <word src="certian">certain</word>
+    <word src="changable">changeable</word>
+    <word src="chekc">check</word>
+    <word src="chnage">change</word>
+    <word src="couldnt">couldn't</word>
+    <word src="couldthe">could the</word>
+    <word src="couldve">could've</word>
+    <word src="cna">can</word>
+    <word src="committment">commitment</word>
+    <word src="committments">commitments</word>
+    <word src="companys">company's</word>
+    <word src="cxan">can</word>
+    <word src="didint">didn't</word>
+    <word src="didnot">did not</word>
+    <word src="didnt">didn't</word>
+    <word src="doesnt">doesn't</word>
+    <word src="dont">don't</word>
+    <word src="eyt">yet</word>
+    <word src="fidn">find</word>
+    <word src="fora">for a</word>
+    <word src="freind">friend</word>
+    <word src="friday">Friday</word>
+    <word src="hadbeen">had been</word>
+    <word src="hadnt">hadn't</word>
+    <word src="haev">have</word>
+    <word src="hasbeen">has been</word>
+    <word src="hasnt">hasn't</word>
+    <word src="havent">haven't</word>
+    <word src="hed">he'd</word>
+    <word src="hel">he'll</word>
+    <word src="heres">here's</word>
+    <word src="hes">he's</word>
+    <word src="hlep">help</word>
+    <word src="howd">how'd</word>
+    <word src="howll">how'll</word>
+    <word src="hows">how's</word>
+    <word src="howve">how've</word>
+    <word src="hte">the</word>
+    <word src="htis">this</word>
+    <word src="hvae">have</word>
+    <word src="i">I</word>
+    <word src="il">I'll</word>
+    <word src="im">I'm</word>
+    <word src="i'm">I'm</word>
+    <word src="i'll">I'll</word>
+    <word src="i've">I've</word>
+    <word src="inteh">in the</word>
+    <word src="isnt">isn't</word>
+    <word src="isthe">is the</word>
+    <word src="itd">it'd</word>
+    <word src="itis">it is</word>
+    <word src="itll">it'll</word>
+    <word src="itsa">it's a</word>
+    <word src="ive">I've</word>
+    <word src="lets">let's</word>
+    <word src="maam">ma'am</word>
+    <word src="mkae">make</word>
+    <word src="mkaes">makes</word>
+    <word src="monday">Monday</word>
+    <word src="mustnt">mustn't</word>
+    <word src="neednt">needn't</word>
+    <word src="oclock">o'clock</word>
+    <word src="ofits">of its</word>
+    <word src="ofthe">of the</word>
+    <word src="omre">more</word>
+    <word src="oneof">one of</word>
+    <word src="otehr">other</word>
+    <word src="outof">out of</word>
+    <word src="overthe">over the</word>
+    <word src="owrk">work</word>
+    <word src="percentof">percent of</word>
+    <word src="recieve">receive</word>
+    <word src="recieved">received</word>
+    <word src="recieving">receiving</word>
+    <word src="saidthat">said that</word>
+    <word src="saidthe">said the</word>
+    <word src="saturday">Saturday</word>
+    <word src="seh">she</word>
+    <word src="shant">shan't</word>
+    <word src="she'">she'll</word>
+    <word src="shel">she'll</word>
+    <word src="shes">she's</word>
+    <word src="shouldent">shouldn't</word>
+    <word src="shouldnt">shouldn't</word>
+    <word src="shouldve">should've</word>
+    <word src="sunday">Sunday</word>
+    <word src="tahn">than</word>
+    <word src="taht">that</word>
+    <word src="teh">the</word>
+    <word src="thatd">that'd</word>
+    <word src="thatll">that'll</word>
+    <word src="thats">that's</word>
+    <word src="thatthe">that the</word>
+    <word src="theres">there's</word>
+    <word src="theyd">they'd</word>
+    <word src="theyll">they'll</word>
+    <word src="theyre">they're</word>
+    <word src="theyve">they've</word>
+    <word src="thier">their</word>
+    <word src="thsi">this</word>
+    <word src="thursday">Thursday</word>
+    <word src="tothe">to the</word>
+    <word src="tuesday">Tuesday</word>
+    <word src="UnitedStates">United States</word>
+    <word src="unitedstates">United States</word>
+    <word src="visavis">vis-a-vis</word>
+    <word src="wasnt">wasn't</word>
+    <word src="wednesday">Wednesday</word>
+    <word src="wierd">weird</word>
+    <word src="wel">we'll</word>
+    <word src="wer">we're</word>
+    <word src="werent">weren't</word>
+    <word src="weve">we've</word>
+    <word src="whatd">what'd</word>
+    <word src="whatll">what'll</word>
+    <word src="whatm">what'm</word>
+    <word src="whatre">what're</word>
+    <word src="whats">what's</word>
+    <word src="whens">when's</word>
+    <word src="whered">where'd</word>
+    <word src="wherell">where'll</word>
+    <word src="wheres">where's</word>
+    <word src="whod">who'd</word>
+    <word src="wholl">who'll</word>
+    <word src="whos">who's</word>
+    <word src="whove">who've</word>
+    <word src="whyd">why'd</word>
+    <word src="whyll">why'll</word>
+    <word src="whys">why's</word>
+    <word src="whyve">why've</word>
+    <word src="witha">with a</word>
+    <word src="wont">won't</word>
+    <word src="wouldnt">wouldn't</word>
+    <word src="wouldve">would've</word>
+    <word src="yall">y'all</word>
+    <word src="youd">you'd</word>
+    <word src="youll">you'll</word>
+    <word src="youre">you're</word>
+    <word src="youve">you've</word>
+</words>
index 4c02a00..d964003 100644 (file)
 */
 -->
 <words>
-    <word src="abouta">about a</word>
-    <word src="aboutit">about it</word>
-    <word src="aboutthe">about the</word>
-    <word src="acheive">achieve</word>
-    <word src="acheived">achieved</word>
-    <word src="acheiving">achieving</word>
-    <word src="acomodate">accommodate</word>
-    <word src="accomodate">accommodate</word>
-    <word src="acn">can</word>
-    <word src="adn">and</word>
-    <word src="agian">again</word>
-    <word src="ahd">had</word>
-    <word src="ahve">have</word>
-    <word src="aint">ain't</word>
-    <word src="alot">a lot</word>
-    <word src="amde">made</word>
-    <word src="amke">make</word>
-    <word src="andone">and one</word>
-    <word src="andteh">and the</word>
-    <word src="anothe">another</word>
-    <word src="arent">aren't</word>
-    <word src="asthe">as the</word>
-    <word src="atthe">at the</word>
-    <word src="bakc">back</word>
-    <word src="beacuse">because</word>
-    <word src="becasue">because</word>
-    <word src="becaus">because</word>
-    <word src="becausea">because a</word>
-    <word src="becauseof">because of</word>
-    <word src="becausethe">because the</word>
-    <word src="becauseyou">because you</word>
-    <word src="becuase">because</word>
-    <word src="becuse">because</word>
-    <word src="beleive">believe</word>
-    <word src="butthe">but the</word>
-    <word src="cant">can't</word>
-    <word src="certian">certain</word>
-    <word src="changable">changeable</word>
-    <word src="chekc">check</word>
-    <word src="chnage">change</word>
-    <word src="couldnt">couldn't</word>
-    <word src="couldthe">could the</word>
-    <word src="couldve">could've</word>
-    <word src="cna">can</word>
-    <word src="committment">commitment</word>
-    <word src="committments">commitments</word>
-    <word src="companys">company's</word>
-    <word src="cxan">can</word>
-    <word src="didint">didn't</word>
-    <word src="didnot">did not</word>
-    <word src="didnt">didn't</word>
-    <word src="doesnt">doesn't</word>
-    <word src="dont">don't</word>
-    <word src="eyt">yet</word>
-    <word src="fidn">find</word>
-    <word src="fora">for a</word>
-    <word src="freind">friend</word>
-    <word src="friday">Friday</word>
-    <word src="hadbeen">had been</word>
-    <word src="hadnt">hadn't</word>
-    <word src="haev">have</word>
-    <word src="hasbeen">has been</word>
-    <word src="hasnt">hasn't</word>
-    <word src="havent">haven't</word>
-    <word src="hed">he'd</word>
-    <word src="hel">he'll</word>
-    <word src="heres">here's</word>
-    <word src="hes">he's</word>
-    <word src="hlep">help</word>
-    <word src="howd">how'd</word>
-    <word src="howll">how'll</word>
-    <word src="hows">how's</word>
-    <word src="howve">how've</word>
-    <word src="hte">the</word>
-    <word src="htis">this</word>
-    <word src="hvae">have</word>
-    <word src="i">I</word>
-    <word src="il">I'll</word>
-    <word src="im">I'm</word>
-    <word src="i'm">I'm</word>
-    <word src="i'll">I'll</word>
-    <word src="i've">I've</word>
-    <word src="inteh">in the</word>
-    <word src="isnt">isn't</word>
-    <word src="isthe">is the</word>
-    <word src="itd">it'd</word>
-    <word src="itis">it is</word>
-    <word src="itll">it'll</word>
-    <word src="itsa">it's a</word>
-    <word src="ive">I've</word>
-    <word src="lets">let's</word>
-    <word src="maam">ma'am</word>
-    <word src="mkae">make</word>
-    <word src="mkaes">makes</word>
-    <word src="monday">Monday</word>
-    <word src="mustnt">mustn't</word>
-    <word src="neednt">needn't</word>
-    <word src="oclock">o'clock</word>
-    <word src="ofits">of its</word>
-    <word src="ofthe">of the</word>
-    <word src="omre">more</word>
-    <word src="oneof">one of</word>
-    <word src="otehr">other</word>
-    <word src="outof">out of</word>
-    <word src="overthe">over the</word>
-    <word src="owrk">work</word>
-    <word src="percentof">percent of</word>
-    <word src="recieve">receive</word>
-    <word src="recieved">received</word>
-    <word src="recieving">receiving</word>
-    <word src="saidthat">said that</word>
-    <word src="saidthe">said the</word>
-    <word src="saturday">Saturday</word>
-    <word src="seh">she</word>
-    <word src="shant">shan't</word>
-    <word src="she'">she'll</word>
-    <word src="shel">she'll</word>
-    <word src="shes">she's</word>
-    <word src="shouldent">shouldn't</word>
-    <word src="shouldnt">shouldn't</word>
-    <word src="shouldve">should've</word>
-    <word src="sunday">Sunday</word>
-    <word src="tahn">than</word>
-    <word src="taht">that</word>
-    <word src="teh">the</word>
-    <word src="thatd">that'd</word>
-    <word src="thatll">that'll</word>
-    <word src="thats">that's</word>
-    <word src="thatthe">that the</word>
-    <word src="theres">there's</word>
-    <word src="theyd">they'd</word>
-    <word src="theyll">they'll</word>
-    <word src="theyre">they're</word>
-    <word src="theyve">they've</word>
-    <word src="thier">their</word>
-    <word src="thsi">this</word>
-    <word src="thursday">Thursday</word>
-    <word src="tothe">to the</word>
-    <word src="tuesday">Tuesday</word>
-    <word src="UnitedStates">United States</word>
-    <word src="unitedstates">United States</word>
-    <word src="visavis">vis-a-vis</word>
-    <word src="wasnt">wasn't</word>
-    <word src="wednesday">Wednesday</word>
-    <word src="wierd">weird</word>
-    <word src="wel">we'll</word>
-    <word src="wer">we're</word>
-    <word src="werent">weren't</word>
-    <word src="weve">we've</word>
-    <word src="whatd">what'd</word>
-    <word src="whatll">what'll</word>
-    <word src="whatm">what'm</word>
-    <word src="whatre">what're</word>
-    <word src="whats">what's</word>
-    <word src="whens">when's</word>
-    <word src="whered">where'd</word>
-    <word src="wherell">where'll</word>
-    <word src="wheres">where's</word>
-    <word src="whod">who'd</word>
-    <word src="wholl">who'll</word>
-    <word src="whos">who's</word>
-    <word src="whove">who've</word>
-    <word src="whyd">why'd</word>
-    <word src="whyll">why'll</word>
-    <word src="whys">why's</word>
-    <word src="whyve">why've</word>
-    <word src="witha">with a</word>
-    <word src="wont">won't</word>
-    <word src="wouldnt">wouldn't</word>
-    <word src="wouldve">would've</word>
-    <word src="yall">y'all</word>
-    <word src="youd">you'd</word>
-    <word src="youll">you'll</word>
-    <word src="youre">you're</word>
-    <word src="youve">you've</word>
 </words>
index fe4a222..4b5464f 100644 (file)
@@ -19,14 +19,16 @@ LOCAL_PATH := $(my-dir)
 ########################
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := permissions.xml
+LOCAL_MODULE := platform.xml
 
 LOCAL_MODULE_TAGS := user development
 
-# This will install the file in /system/etc
-#
 LOCAL_MODULE_CLASS := ETC
 
+# This will install the file in /system/etc/permissions
+#
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 
 include $(BUILD_PREBUILT)
similarity index 96%
rename from data/etc/permissions.xml
rename to data/etc/platform.xml
index bdaefff..21e452e 100644 (file)
@@ -87,6 +87,7 @@
     <assign-permission name="android.permission.SET_ANIMATION_SCALE" uid="shell" />
     <assign-permission name="android.permission.SET_PREFERRED_APPLICATIONS" uid="shell" />
     <assign-permission name="android.permission.WRITE_SETTINGS" uid="shell" />
+    <assign-permission name="android.permission.WRITE_SECURE_SETTINGS" uid="shell" />
     <assign-permission name="android.permission.BROADCAST_STICKY" uid="shell" />
     <!-- Development tool permissions granted to the shell. -->
     <assign-permission name="android.permission.SET_DEBUG_APP" uid="shell" />
     <!-- This is a list of all the libraries available for application
          code to link against. -->
 
-    <library name="com.google.android.maps"
-            file="/system/framework/com.google.android.maps.jar" />
-    <library name="com.google.android.gtalkservice"
-            file="/system/framework/com.google.android.gtalkservice.jar" />
     <library name="android.awt"
             file="/system/framework/android.awt.jar" />
     <library name="android.test.runner"
             file="/system/framework/android.test.runner.jar" />
     <library name="com.android.im.plugin"
-            file="/system/framework/com.android.im.plugin.jar"/>
+            file="/system/framework/com.android.im.plugin.jar"/>          
 
 </permissions>
index 37020cf..0048e80 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-locale=de
+locale=it
 
 extract()
 {
@@ -28,6 +28,8 @@ extract()
                 $file = $ARGV[0];
                 $locale = $ARGV[1];
 
+                $locale =~ s/([a-z][a-z])-([A-Z][A-Z])/$1-r$2/;
+
                 $file =~ /^(.*)\/values([^\/]*)\/(.*)/;
                 $prefix = $1;
                 $values = $2;
index e59cb3b..284afe3 100644 (file)
@@ -2,7 +2,7 @@ community=true
 page.title=Community
 @jd:body
 
-       <div id="mainBodyFixed">
+       <div id="mainBodyFluid">
                        <h1>Community</h1>
                        <p>Welcome to the Android community! We're glad you're here and invite you to participate in these discussions. Before posting, please red the <a href="#">Groups Charter</a> that covers the community guidelines.</p>
                        <p>To get the most out of this group, please do the following before you post:</p>
index cbb7ef4..a1feb5c 100644 (file)
Binary files a/docs/html/images/linearlayout.png and b/docs/html/images/linearlayout.png differ
index cd78043..c6b772e 100644 (file)
@@ -28,17 +28,18 @@ import android.util.AttributeSet;
 
 /**
  * 
- * An object used to define frame-by-frame animations that can be used as a View object's
- * background.
- * <p>Each frame in a frame-by-frame animation is a drawable 
- * <a href="{@docRoot}devel/resources-i18n.html">resource</a>.
+ * An object used to create frame-by-frame animations, defined by a series of Drawable objects,
+ * which can be used as a View object's background.
+ * <p>
  * The simplest way to create a frame-by-frame animation is to define the animation in an XML
- * file in the drawable/ folder, set it as the background to a View object, then call
- * AnimationDrawable.run() to start the animation, as shown here. More details about the
- * format of the animation XML file are given in
- * <a href="{@docRoot}reference/available-resources.html#animationdrawable">Frame by Frame
- * Animation</a>.
- * spin_animation.xml file in res/drawable/ folder:</p>
+ * file, placed in the res/drawable/ folder, and set it as the background to a View object. Then, call
+ * {@link #run()} to start the animation.
+ * <p>
+ * An AnimationDrawable defined in XML consists of a single <code>&lt;animation-list></code> element,
+ * and a series of nested <code>&lt;item></code> tags. Each item defines a frame of the animation.
+ * See the example below.
+ * </p>
+ * <p>spin_animation.xml file in res/drawable/ folder:</p>
  * <pre>&lt;!-- Animation frames are wheel0.png -- wheel5.png files inside the
  * res/drawable/ folder --&gt;
  * &lt;animation-list android:id=&quot;selected&quot; android:oneshot=&quot;false&quot;&gt;
@@ -51,7 +52,8 @@ import android.util.AttributeSet;
  * &lt;/animation-list&gt;</pre>
  *
  * <p>Here is the code to load and play this animation.</p>
- * <pre>// Load the ImageView that will host the animation and
+ * <pre>
+ * // Load the ImageView that will host the animation and
  * // set its background to our AnimationDrawable XML resource.
  * ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
  * img.setBackgroundResource(R.drawable.spin_animation);
@@ -62,6 +64,12 @@ import android.util.AttributeSet;
  * // Start the animation (looped playback by default).
  * frameAnimation.start()
  * </pre>
+ *
+ * @attr ref android.R.styleable#AnimationDrawable_visible
+ * @attr ref android.R.styleable#AnimationDrawable_variablePadding
+ * @attr ref android.R.styleable#AnimationDrawable_oneshot
+ * @attr ref android.R.styleable#AnimationDrawableItem_duration
+ * @attr ref android.R.styleable#AnimationDrawableItem_drawable
  */
 public class AnimationDrawable extends DrawableContainer implements Runnable {
     private final AnimationState mAnimationState;
index a838c5f..7f3df9a 100644 (file)
@@ -35,6 +35,24 @@ import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 
+/**
+ * A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
+ * BitmapDrawable from a file path, an input stream, through XML inflation, or from
+ * a {@link android.graphics.Bitmap} object.
+ * <p>It can be defined in an XML file with the <code>&lt;bitmap></code> element.</p>
+ * <p>
+ * Also see the {@link android.graphics.Bitmap} class, which handles the management and 
+ * transformation of raw bitmap graphics, and should be used when drawing to a 
+ * {@link android.graphics.Canvas}.
+ * </p>
+ *
+ * @attr ref android.R.styleable#BitmapDrawable_src
+ * @attr ref android.R.styleable#BitmapDrawable_antialias
+ * @attr ref android.R.styleable#BitmapDrawable_filter
+ * @attr ref android.R.styleable#BitmapDrawable_dither
+ * @attr ref android.R.styleable#BitmapDrawable_gravity
+ * @attr ref android.R.styleable#BitmapDrawable_tileMode
+ */
 public class BitmapDrawable extends Drawable {
 
     private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG;
index 86c0747..6029388 100644 (file)
@@ -29,11 +29,17 @@ import android.util.Log;
 import java.io.IOException;
 
 /**
- * A drawable that clips another drawable based on this drawable's current
- * level value.  You can control how much the child drawable gets clipped in width
+ * A Drawable that clips another Drawable based on this Drawable's current
+ * level value.  You can control how much the child Drawable gets clipped in width
  * and height based on the level, as well as a gravity to control where it is
  * placed in its overall container.  Most often used to implement things like
  * progress bars.
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;clip></code> element.</p>
+ *
+ * @attr ref android.R.styleable#ClipDrawable_clipOrientation
+ * @attr ref android.R.styleable#ClipDrawable_gravity
+ * @attr ref android.R.styleable#ClipDrawable_drawable
  */
 public class ClipDrawable extends Drawable implements Drawable.Callback {
     private ClipState mClipState;
index bebb461..226cc04 100644 (file)
@@ -26,10 +26,14 @@ import org.xmlpull.v1.XmlPullParserException;
 import java.io.IOException;
 
 /**
- * A ColorDrawable is a specialized drawable that fills the Canvas with a specified color,
- * and with respect to the clip region. Note that a ColorDrawable ignores the ColorFilter.
+ * A specialized Drawable that fills the Canvas with a specified color,
+ * with respect to the clip region. Note that a ColorDrawable ignores the ColorFilter.
  * It also ignores the Bounds, meaning it will draw everywhere in the current clip,
  * even if setBounds(...) was called with a smaller area.
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;color></code> element.</p>
+ *
+ * @attr ref android.R.styleable#ColorDrawable_color
  */
 public class ColorDrawable extends Drawable {
     private ColorState mState;
index ab3b23d..cdfca1b 100644 (file)
@@ -40,9 +40,35 @@ import org.xmlpull.v1.XmlPullParserException;
 import java.io.IOException;
 
 /**
- * A simple color gradient for buttons, backgrounds, etc. See
- * <a href="{@docRoot}reference/available-resources.html#gradientdrawable">Gradient</a>
- * in the Resources topic to learn how to specify this type as an XML resource.
+ * A Drawable with a color gradient for buttons, backgrounds, etc. 
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;shape></code> element.</p>
+ *
+ * @attr ref android.R.styleable#GradientDrawable_visible
+ * @attr ref android.R.styleable#GradientDrawable_shape
+ * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio
+ * @attr ref android.R.styleable#GradientDrawable_thicknessRatio
+ * @attr ref android.R.styleable#GradientDrawable_useLevel
+ * @attr ref android.R.styleable#GradientDrawableSize_width
+ * @attr ref android.R.styleable#GradientDrawableSize_height
+ * @attr ref android.R.styleable#GradientDrawableGradient_startColor
+ * @attr ref android.R.styleable#GradientDrawableGradient_centerColor
+ * @attr ref android.R.styleable#GradientDrawableGradient_endColor
+ * @attr ref android.R.styleable#GradientDrawableGradient_useLevel
+ * @attr ref android.R.styleable#GradientDrawableGradient_angle
+ * @attr ref android.R.styleable#GradientDrawableGradient_type
+ * @attr ref android.R.styleable#GradientDrawableGradient_centerX
+ * @attr ref android.R.styleable#GradientDrawableGradient_centerY
+ * @attr ref android.R.styleable#GradientDrawableGradient_gradientRadius
+ * @attr ref android.R.styleable#GradientDrawableSolid_color
+ * @attr ref android.R.styleable#GradientDrawableStroke_width
+ * @attr ref android.R.styleable#GradientDrawableStroke_color
+ * @attr ref android.R.styleable#GradientDrawableStroke_dashWidth
+ * @attr ref android.R.styleable#GradientDrawableStroke_dashGap
+ * @attr ref android.R.styleable#GradientDrawablePadding_left
+ * @attr ref android.R.styleable#GradientDrawablePadding_top
+ * @attr ref android.R.styleable#GradientDrawablePadding_right
+ * @attr ref android.R.styleable#GradientDrawablePadding_bottom
  */
 public class GradientDrawable extends Drawable {
     /**
index fe21692..80b8e96 100644 (file)
@@ -31,6 +31,15 @@ import java.io.IOException;
  * A Drawable that insets another Drawable by a specified distance.
  * This is used when a View needs a background that is smaller than
  * the View's actual bounds.
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;inset></code> element.</p>
+ *
+ * @attr ref android.R.styleable#InsetDrawable_visible
+ * @attr ref android.R.styleable#InsetDrawable_drawable
+ * @attr ref android.R.styleable#InsetDrawable_insetLeft
+ * @attr ref android.R.styleable#InsetDrawable_insetRight
+ * @attr ref android.R.styleable#InsetDrawable_insetTop
+ * @attr ref android.R.styleable#InsetDrawable_insetBottom
  */
 public class InsetDrawable extends Drawable implements Drawable.Callback
 {
index c389eb3..fa5ed0a 100644 (file)
@@ -27,8 +27,20 @@ import android.view.View;
 
 import java.io.IOException;
 
-/** Drawable that manages an array of other drawables. These are drawn in array
-    order, so the element with the largest index will be drawn on top.
+/** 
+ * A Drawable that manages an array of other Drawables. These are drawn in array
+ * order, so the element with the largest index will be drawn on top.
+ * <p>
+ * It can be defined in an XML file with the <code>&lt;layer-list></code> element.
+ * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
+ * </p>
+ *
+ * @attr ref android.R.styleable#LayerDrawableItem_left
+ * @attr ref android.R.styleable#LayerDrawableItem_top
+ * @attr ref android.R.styleable#LayerDrawableItem_right
+ * @attr ref android.R.styleable#LayerDrawableItem_bottom
+ * @attr ref android.R.styleable#LayerDrawableItem_drawable
+ * @attr ref android.R.styleable#LayerDrawableItem_id
 */
 public class LayerDrawable extends Drawable implements Drawable.Callback {
     LayerState mLayerState;
index 61f45b3..93dc32c 100644 (file)
@@ -27,13 +27,19 @@ import android.util.AttributeSet;
 
 /**
  * 
- * A resource that contains a number of alternate images, each assigned a maximum numerical value. 
+ * A resource that manages a number of alternate Drawables, each assigned a maximum numerical value. 
  * Setting the level value of the object with {@link #setLevel(int)} will load the image with the next 
- * greater or equal value assigned to its max attribute. See <a href="{@docRoot}reference/available-resources.html#levellistdrawable">
- * Level List</a> in the Resources topic to learn how to specify this type as an XML resource. A good example use of 
+ * greater or equal value assigned to its max attribute. 
+ * A good example use of 
  * a LevelListDrawable would be a battery level indicator icon, with different images to indicate the current
  * battery level.
- *
+ * <p>
+ * It can be defined in an XML file with the <code>&lt;level-list></code> element.
+ * Each Drawable level is defined in a nested <code>&lt;item></code>
+ * </p>
+ * @attr ref android.R.styleable#LevelListDrawableItem_minLevel
+ * @attr ref android.R.styleable#LevelListDrawableItem_maxLevel
+ * @attr ref android.R.styleable#LevelListDrawableItem_drawable
  */
 public class LevelListDrawable extends DrawableContainer {
     public LevelListDrawable()
index c2f05ec..3e03ed4 100644 (file)
@@ -31,9 +31,18 @@ import android.util.Log;
 import java.io.IOException;
 
 /**
- * <p>A drawable that can rotate another drawable based on the current level
+ * <p>A Drawable that can rotate another Drawable based on the current level
  * value. The start and end angles of rotation can be controlled to map any
  * circular arc to the level values range.</p>
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;rotate></code> element.</p>
+ *
+ * @attr ref android.R.styleable#RotateDrawable_visible
+ * @attr ref android.R.styleable#RotateDrawable_fromDegrees
+ * @attr ref android.R.styleable#RotateDrawable_toDegrees
+ * @attr ref android.R.styleable#RotateDrawable_pivotX
+ * @attr ref android.R.styleable#RotateDrawable_pivotY
+ * @attr ref android.R.styleable#RotateDrawable_drawable
  */
 public class RotateDrawable extends Drawable implements Drawable.Callback {
 
index c39f1fa..c40c8c0 100644 (file)
@@ -29,11 +29,18 @@ import android.util.Log;
 import java.io.IOException;
 
 /**
- * A drawable that changes the size of another drawable based on its current
- * level value.  You can control how much the child drawable changes in width
+ * A Drawable that changes the size of another Drawable based on its current
+ * level value.  You can control how much the child Drawable changes in width
  * and height based on the level, as well as a gravity to control where it is
  * placed in its overall container.  Most often used to implement things like
  * progress bars.
+ *
+ * <p>It can be defined in an XML file with the <code>&lt;scale></code> element.</p>
+ *
+ * @attr ref android.R.styleable#ScaleDrawable_scaleWidth
+ * @attr ref android.R.styleable#ScaleDrawable_scaleHeight
+ * @attr ref android.R.styleable#ScaleDrawable_scaleGravity
+ * @attr ref android.R.styleable#ScaleDrawable_drawable
  */
 public class ScaleDrawable extends Drawable implements Drawable.Callback {
     private ScaleState mScaleState;
index 17d5a2e..1dc1627 100644 (file)
@@ -31,6 +31,24 @@ import android.util.StateSet;
  * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string
  * ID value.
  *
+ * <p>It can be defined in an XML file with the <code>&lt;selector></code> element.
+ * Each state Drawable is defined in a nested <code>&lt;item></code> element.</p>
+ *
+ * @attr ref android.R.styleable#StateListDrawable_visible
+ * @attr ref android.R.styleable#StateListDrawable_variablePadding
+ * @attr ref android.R.styleable#StateListDrawable_constantSize
+ * @attr ref android.R.styleable#DrawableStates_state_focused
+ * @attr ref android.R.styleable#DrawableStates_state_window_focused
+ * @attr ref android.R.styleable#DrawableStates_state_enabled
+ * @attr ref android.R.styleable#DrawableStates_state_checkable
+ * @attr ref android.R.styleable#DrawableStates_state_checked
+ * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_active
+ * @attr ref android.R.styleable#DrawableStates_state_single
+ * @attr ref android.R.styleable#DrawableStates_state_first
+ * @attr ref android.R.styleable#DrawableStates_state_middle
+ * @attr ref android.R.styleable#DrawableStates_state_last
+ * @attr ref android.R.styleable#DrawableStates_state_pressed
  */
 public class StateListDrawable extends DrawableContainer {
     public StateListDrawable()
@@ -144,7 +162,45 @@ public class StateListDrawable extends DrawableContainer {
     StateListState getStateListState() {
         return mStateListState;
     }
+    
+    /**
+     * Gets the number of states contained in this drawable.
+     * 
+     * @return The number of states contained in this drawable.
+     * @see #getStateSet(int)
+     * @see #getStateDrawable(int)
+     * @hide pending API council
+     */
+    public int getStateCount() {
+        return mStateListState.getChildCount();        
+    }
 
+    /**
+     * Gets the state set at an index.
+     * 
+     * @param index The index of the state set.
+     * @return The state set at the index.
+     * @see #getStateCount()
+     * @see #getStateDrawable(int)
+     * @hide pending API council
+     */
+    public int[] getStateSet(int index) {
+        return mStateListState.mStateSets[index];
+    }
+    
+    /**
+     * Gets the drawable at an index.
+     * 
+     * @param index The index of the drawable.
+     * @return The drawable at the index.
+     * @see #getStateCount()
+     * @see #getStateSet(int)
+     * @hide pending API council
+     */
+    public Drawable getStateDrawable(int index) {
+        return mStateListState.getChildren()[index];
+    }
+    
     static final class StateListState extends DrawableContainerState
     {
         StateListState(StateListState orig, StateListDrawable owner)
index e12f4b5..a99d4e5 100644 (file)
@@ -20,9 +20,19 @@ import android.graphics.Canvas;
 import android.os.SystemClock;
 
 /**
- * Transition drawables are an extension of LayerDrawables and are intended to cross fade between
- * the first and second layers. To start the transition, call {@link #startTransition(int)}. To
- * display just the first layer, call {@link #resetTransition()}
+ * An extension of LayerDrawables that is intended to cross-fade between
+ * the first and second layer. To start the transition, call {@link #startTransition(int)}. To
+ * display just the first layer, call {@link #resetTransition()}.
+ * <p>
+ * It can be defined in an XML file with the <code>&lt;transition></code> element.
+ * Each Drawable in the transition is defined in a nested <code>&lt;item></code>.
+ * </p>
+ * @attr ref android.R.styleable#LayerDrawableItem_left
+ * @attr ref android.R.styleable#LayerDrawableItem_top
+ * @attr ref android.R.styleable#LayerDrawableItem_right
+ * @attr ref android.R.styleable#LayerDrawableItem_bottom
+ * @attr ref android.R.styleable#LayerDrawableItem_drawable
+ * @attr ref android.R.styleable#LayerDrawableItem_id
  *
  */
 public class TransitionDrawable extends LayerDrawable implements Drawable.Callback {
diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h
new file mode 100644 (file)
index 0000000..4268170
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 JETPLAYER_H_
+#define JETPLAYER_H_
+
+#include <utils/threads.h>
+#include <nativehelper/jni.h>
+
+#include <libsonivox/jet.h>
+#include <libsonivox/eas_types.h>
+#include "AudioTrack.h"
+
+
+namespace android {
+
+typedef void (*jetevent_callback)(int eventType, int val1, int val2, void *cookie);
+
+class JetPlayer {
+
+public:
+
+    static const int JET_USERID_UPDATE           = 1;
+    static const int JET_NUMQUEUEDSEGMENT_UPDATE = 2;
+    static const int JET_PAUSE_UPDATE            = 3;
+
+    JetPlayer(jobject javaJetPlayer, 
+            int maxTracks = 32, 
+            int trackBufferSize = 1200);
+    ~JetPlayer();
+    int init();
+    int release();
+    
+    int openFile(const char* url);
+    int closeFile();
+    int play();
+    int pause();
+    int queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
+            EAS_U32 muteFlags, EAS_U8 userID);
+    int setMuteFlags(EAS_U32 muteFlags, bool sync);
+    int setMuteFlag(int trackNum, bool muteFlag, bool sync);
+    int triggerClip(int clipId);
+
+    void setEventCallback(jetevent_callback callback);
+    
+    int getMaxTracks() { return mMaxTracks; };
+
+
+private:
+    static  int         renderThread(void*);
+    int                 render();
+    void                fireEventOnStatusChange();
+
+    JetPlayer() {} // no default constructor
+    void dump();
+    void dumpJetStatus(S_JET_STATUS* pJetStatus);
+
+    jetevent_callback   mEventCallback;
+
+    jobject             mJavaJetPlayerRef;
+    Mutex               mMutex; // mutex to sync the render and playback thread with the JET calls
+    pid_t               mTid;
+    Condition           mCondition;
+    volatile bool       mRender;
+    bool                mPaused;
+
+    EAS_STATE           mState;
+    int*                mMemFailedVar;
+
+    int                 mMaxTracks; // max number of MIDI tracks, usually 32
+    EAS_DATA_HANDLE     mEasData;
+    EAS_FILE_LOCATOR    mEasJetFileLoc;
+    EAS_PCM*            mAudioBuffer;// EAS renders the MIDI data into this buffer, 
+    AudioTrack*         mAudioTrack; // and we play it in this audio track
+    int                 mTrackBufferSize;
+    S_JET_STATUS        mJetStatus;
+    S_JET_STATUS        mPreviousJetStatus;
+
+    char                mJetFilePath[256];
+
+
+}; // end class JetPlayer
+
+} // end namespace android
+
+
+
+#endif /*JETPLAYER_H_*/
index da1489f..0ddfb8e 100644 (file)
@@ -134,6 +134,7 @@ private:
     Condition mWaitCbkCond; // condition enabling interface to wait for audio callback completion after a change is requested
     float mVolume;  // Volume applied to audio track
     int mStreamType; // Audio stream used for output
+    int mProcessSize;  // Size of audio blocks generated at a time by audioCallback() (in PCM frames).
 
     bool initAudioTrack();
     static void audioCallback(int event, void* user, void *info);
index 05cba30..f2719d3 100644 (file)
@@ -47,6 +47,11 @@ enum {
     METADATA_KEY_RATING          = 13,
     METADATA_KEY_COMMENT         = 14,
     METADATA_KEY_COPYRIGHT       = 15,
+    METADATA_KEY_BIT_RATE        = 16,
+    METADATA_KEY_FRAME_RATE      = 17,
+    METADATA_KEY_VIDEO_FORMAT    = 18,
+    METADATA_KEY_VIDEO_HEIGHT    = 19,
+    METADATA_KEY_VIDEO_WIDTH     = 20,
     // Add more here...
 };
 
index 124f07f..44acce5 100644 (file)
@@ -78,6 +78,8 @@ public:
 
             status_t    reconnect();
             void        disconnect();
+            status_t    lock();
+            status_t    unlock();
 
             status_t    getStatus() { return mStatus; }
 
@@ -91,6 +93,9 @@ public:
             // stop preview mode
             void        stopPreview();
 
+            // get preview state
+            bool        previewEnabled();
+
             // autoFocus - status returned from callback
             status_t    autoFocus();
 
index 14ac96e..2bd53dd 100644 (file)
@@ -96,6 +96,11 @@ public:
     virtual void        stopPreview() = 0;
 
     /**
+     * Returns true if preview is enabled.
+     */
+    virtual bool        previewEnabled() = 0;
+
+    /**
      * Start auto focus, the callback routine is called
      * once when focusing is complete. autoFocus() will
      * be called again if another auto focus is needed.
index 7c9ce99..c303cd8 100644 (file)
@@ -30,10 +30,10 @@ namespace android {
 // ---------------------------------------------------------------------------
 
 template <class TYPE>
-class EGLNativeSurface : public egl_native_window_t
+class EGLNativeSurface : public egl_native_window_t, public LightRefBase<TYPE>
 {
 public:
-    EGLNativeSurface() : mCount(0) 
+    EGLNativeSurface() { 
         memset(egl_native_window_t::reserved, 0, 
                 sizeof(egl_native_window_t::reserved));
         memset(egl_native_window_t::reserved_proc, 0, 
@@ -41,19 +41,10 @@ public:
         memset(egl_native_window_t::oem, 0, 
                 sizeof(egl_native_window_t::oem));
     }
-    inline void incStrong(void*) const {
-        android_atomic_inc(&mCount);
-    }
-    inline void decStrong(void*) const {
-        if (android_atomic_dec(&mCount) == 1) {
-             delete static_cast<const TYPE*>(this);
-         }
-    }
 protected:
     EGLNativeSurface& operator = (const EGLNativeSurface& rhs);
     EGLNativeSurface(const EGLNativeSurface& rhs);
     inline ~EGLNativeSurface() { };
-    mutable volatile int32_t mCount;
 };
 
 // ---------------------------------------------------------------------------
index 99c0d86..ea2fcee 100644 (file)
@@ -39,6 +39,12 @@ public:
     // connect new client with existing camera remote
     virtual status_t        connect(const sp<ICameraClient>& client) = 0;
 
+    // prevent other processes from using this ICamera interface
+    virtual status_t        lock() = 0;
+
+    // allow other processes to use this ICamera interface
+    virtual status_t        unlock() = 0;
+
     // pass the buffered ISurface to the camera service
     virtual status_t        setPreviewDisplay(const sp<ISurface>& surface) = 0;
 
@@ -52,6 +58,9 @@ public:
     // stop preview mode
     virtual void            stopPreview() = 0;
 
+    // get preview state
+    virtual bool            previewEnabled() = 0;
+
     // auto focus
     virtual status_t        autoFocus() = 0;
 
index 323ff07..699b1b0 100644 (file)
@@ -33,8 +33,6 @@ public:
     DECLARE_META_INTERFACE(Overlay);
 
     virtual void destroy() = 0; // one-way
-    
-    virtual ssize_t swapBuffers() = 0;
 };
 
 // ----------------------------------------------------------------------------
index ff031d5..9a7383c 100644 (file)
@@ -30,7 +30,7 @@ namespace android {
 typedef int32_t    SurfaceID;
 
 class IMemoryHeap;
-class Overlay;
+class OverlayRef;
 
 class ISurface : public IInterface
 {
@@ -44,7 +44,7 @@ public:
 
     virtual void unregisterBuffers() = 0;
     
-    virtual sp<Overlay> createOverlay(
+    virtual sp<OverlayRef> createOverlay(
             uint32_t w, uint32_t h, int32_t format) = 0;
 };
 
index f24780f..23cdee8 100644 (file)
 #include <utils/Errors.h>
 #include <utils/IInterface.h>
 #include <utils/RefBase.h>
+#include <utils/threads.h>
+
 #include <ui/PixelFormat.h>
+#include <ui/IOverlay.h>
 
 #include <hardware/overlay.h>
 
 namespace android {
 
-class IOverlay;
 class IMemory;
 class IMemoryHeap;
 
+// ----------------------------------------------------------------------------
+
+class OverlayRef : public LightRefBase<OverlayRef>
+{
+public:
+    OverlayRef(overlay_handle_t const*, const sp<IOverlay>&,
+            uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs);
+
+    static sp<OverlayRef> readFromParcel(const Parcel& data);
+    static status_t writeToParcel(Parcel* reply, const sp<OverlayRef>& o);    
+
+private:
+    friend class LightRefBase<OverlayRef>;
+    friend class Overlay;
+
+    OverlayRef();
+    virtual ~OverlayRef();
+
+    overlay_handle_t const *mOverlayHandle;
+    sp<IOverlay> mOverlayChanel;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    int32_t  mFormat;
+    int32_t  mWidthStride;
+    int32_t  mHeightStride;
+    bool mOwnHandle;
+};
+
+// ----------------------------------------------------------------------------
+
 class Overlay : public virtual RefBase
 {
 public:
-    Overlay(overlay_t* overlay, 
-            const sp<IOverlay>& o, const sp<IMemoryHeap>& heap);
+    Overlay(const sp<OverlayRef>& overlayRef);
 
     /* destroys this overlay */
     void destroy();
     
-    /* post/swaps buffers */
-    status_t swapBuffers();
-    
     /* get the HAL handle for this overlay */
     overlay_handle_t const* getHandleRef() const;
-    
-    /* returns the offset of the current buffer */
-    size_t getBufferOffset() const;
-    
-    /* returns a heap to this overlay. this may not be supported. */
-    sp<IMemoryHeap> getHeap() const;
-    
+
+    /* blocks until an overlay buffer is available and return that buffer. */
+    overlay_buffer_t dequeueBuffer();
+
+    /* release the overlay buffer and post it */
+    int queueBuffer(overlay_buffer_t buffer);
+
+    /* returns the address of a given buffer if supported, NULL otherwise. */
+    void* getBufferAddress(overlay_buffer_t buffer);
+
     /* get physical informations about the overlay */
     uint32_t getWidth() const;
     uint32_t getHeight() const;
     int32_t getFormat() const;
     int32_t getWidthStride() const;
     int32_t getHeightStride() const;
-
-    static sp<Overlay> readFromParcel(const Parcel& data);
-    static status_t writeToParcel(Parcel* reply, const sp<Overlay>& o);
-
+    status_t getStatus() const;
+    
 private:
-    Overlay(overlay_handle_t*, const sp<IOverlay>&, const sp<IMemoryHeap>&,  
-            uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs);
-
     virtual ~Overlay();
 
-    sp<IOverlay>        mOverlay;
-    sp<IMemoryHeap>     mHeap;
-    size_t              mCurrentBufferOffset;
-    overlay_handle_t const *mOverlayHandle;
-    uint32_t            mWidth;
-    uint32_t            mHeight;
-    int32_t             mFormat;
-    int32_t             mWidthStride;
-    int32_t             mHeightStride;
+    sp<OverlayRef> mOverlayRef;
+    overlay_data_device_t *mOverlayData;
+    status_t mStatus;
 };
 
 // ----------------------------------------------------------------------------
index c61df32..b65c959 100644 (file)
@@ -43,7 +43,7 @@ enum {
 
     // logical pixel formats used by the SurfaceFlinger -----------------------
     PIXEL_FORMAT_CUSTOM         = -4,
-        // Custom pixel-format described by a PixelFormatInfo sructure
+        // Custom pixel-format described by a PixelFormatInfo structure
 
     PIXEL_FORMAT_TRANSLUCENT    = -3,
         // System chooses a format that supports translucency (many alpha bits)
@@ -96,7 +96,7 @@ struct PixelFormatInfo
     uint32_t    reserved[2];
 };
 
-// considere caching the results of these functions are they're not
+// Consider caching the results of these functions are they're not
 // guaranteed to be fast.
 ssize_t     bytesPerPixel(PixelFormat format);
 ssize_t     bitsPerPixel(PixelFormat format);
index a0c4608..7689673 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <hardware/copybit.h>
 
-#include <corecg/SkRegion.h>
+#include <core/SkRegion.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
index ff89738..574acf4 100644 (file)
@@ -32,7 +32,10 @@ class MemoryHeapBase : public virtual BnMemoryHeap
 public:
     enum {
         READ_ONLY = IMemoryHeap::READ_ONLY,
-        MAP_ONCE = IMemoryHeap::MAP_ONCE
+        MAP_ONCE = IMemoryHeap::MAP_ONCE,
+        // memory won't be mapped locally, but will be mapped in the remote
+        // process.
+        DONT_MAP_LOCALLY = 0x00000100
     };
 
     /* 
index e37b56f..cbda0fd 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef ANDROID_REF_BASE_H
 #define ANDROID_REF_BASE_H
 
+#include <cutils/atomic.h>
 #include <utils/TextOutput.h>
 
 #include <stdint.h>
@@ -142,6 +143,29 @@ private:
 
 // ---------------------------------------------------------------------------
 
+template <class T>
+class LightRefBase
+{
+public:
+    inline LightRefBase() : mCount(0) { }
+    inline void incStrong(const void* id) const {
+        android_atomic_inc(&mCount);
+    }
+    inline void decStrong(const void* id) const {
+        if (android_atomic_dec(&mCount) == 1) {
+            delete static_cast<const T*>(this);
+        }
+    }
+    
+protected:
+    inline ~LightRefBase() { }
+    
+private:
+    mutable volatile int32_t mCount;
+};
+
+// ---------------------------------------------------------------------------
+
 template <typename T>
 class sp
 {
index d54795c..b2a8e96 100644 (file)
@@ -79,11 +79,6 @@ AudioStreamIn* A2dpAudioInterface::openInputStream(
     return NULL;
 }
 
-status_t A2dpAudioInterface::standby()
-{
-    return 0;
-}
-
 status_t A2dpAudioInterface::setMicMute(bool state)
 {
     return 0;
@@ -123,8 +118,8 @@ status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
 // ----------------------------------------------------------------------------
 
 A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
-    mFd(-1), mStartCount(0), mRetryCount(0), mData(NULL),
-    mInitialized(false), mBufferRemaining(0)
+    mFd(-1), mStandby(false), mStartCount(0), mRetryCount(0), mData(NULL),
+    mInitialized(false)
 {
 }
 
@@ -155,26 +150,50 @@ A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
 
 ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
 {    
+    status_t status = NO_INIT;
+    size_t remaining = bytes;
+
     if (!mInitialized) {
-        int ret = a2dp_init("00:00:00:00:00:00", 44100, 2, &mData);
-        if (ret)
-            return ret;
+        status = a2dp_init("00:00:00:00:00:00", 44100, 2, &mData);
+        if (status < 0) {
+            LOGE("a2dp_init failed err: %d\n", status);
+            goto Error;
+        }
         mInitialized = true;
     }
     
-    size_t remaining = bytes;
     while (remaining > 0) {
-        int written = a2dp_write(mData, buffer, remaining);        
-        remaining -= written;
-        buffer = ((char *)buffer) + written;
+        status = a2dp_write(mData, buffer, remaining);
+        if (status <= 0) {
+            LOGE("a2dp_write failed err: %d\n", status);
+            goto Error;
+        }
+        remaining -= status;
+        buffer = ((char *)buffer) + status;
     }
+
+    mStandby = false;
     
     return bytes;
+
+Error:   
+    // Simulate audio output timing in case of error
+    usleep(bytes * 1000000 / frameSize() / sampleRate());
+
+    return status;
 }
 
 status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
 {
-    return 0;
+    int result = 0;
+
+    if (!mStandby) {
+        result = a2dp_stop(mData);
+        if (result == 0)
+            mStandby = true;
+    }
+
+    return result;
 }
 
 status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
index 03bf933..b8119a1 100644 (file)
@@ -35,7 +35,6 @@ public:
                         A2dpAudioInterface();
     virtual             ~A2dpAudioInterface();
     virtual status_t    initCheck();
-    virtual status_t    standby();
 
     virtual status_t    setVoiceVolume(float volume);
     virtual status_t    setMasterVolume(float volume);
@@ -74,11 +73,11 @@ private:
                                 int channelCount,
                                 uint32_t sampleRate);
         virtual uint32_t    sampleRate() const { return 44100; }
-        // must be 32-bit aligned - driver only seems to like 4800
-        virtual size_t      bufferSize() const { return 5120; }
+        // SBC codec wants a multiple of 512
+        virtual size_t      bufferSize() const { return 512 * 30; }
         virtual int         channelCount() const { return 2; }
         virtual int         format() const { return AudioSystem::PCM_16_BIT; }
-        virtual uint32_t    latency() const { return 0; }
+        virtual uint32_t    latency() const { return ((1000*channelCount()*bufferSize())/frameSize())/sampleRate() + 200; }
         virtual status_t    setVolume(float volume) { return INVALID_OPERATION; }
         virtual ssize_t     write(const void* buffer, size_t bytes);
                 status_t    standby();
@@ -86,14 +85,11 @@ private:
 
     private:
                 int         mFd;
+                bool        mStandby;
                 int         mStartCount;
                 int         mRetryCount;
                 void*       mData;
                 bool        mInitialized;
-
-#define kBufferSize 50000
-                char                    mBuffer[kBufferSize];
-                int                     mBufferRemaining;
     };
 
     Mutex                   mLock;
index 8eee9cc..b4940cb 100644 (file)
@@ -49,14 +49,6 @@ AudioDumpInterface::~AudioDumpInterface()
 }
 
 
-status_t AudioDumpInterface::standby()
-{
-    if(mStreamOut)  mStreamOut->Close();
-    gFirst = true;
-    return mFinalInterface->standby();
-}
-
-
 AudioStreamOut* AudioDumpInterface::openOutputStream(
         int format, int channelCount, uint32_t sampleRate, status_t *status)
 {
@@ -106,6 +98,14 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
     return ret;
 }
 
+status_t AudioStreamOutDump::standby()
+{
+    Close();
+    gFirst = true;
+    return mFinalStream->standby();
+}
+
+
 void AudioStreamOutDump::Close(void)
 {
     if(mOutFile) {
index a65e56a..82b5250 100644 (file)
@@ -40,6 +40,7 @@ public:
     virtual uint32_t    latency() const { return mFinalStream->latency(); }
     virtual status_t    setVolume(float volume)
                             { return mFinalStream->setVolume(volume); }
+    virtual status_t    standby();
     virtual status_t    dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); }
     void                Close(void);
 
@@ -54,7 +55,6 @@ class AudioDumpInterface : public AudioHardwareBase
 
 public:
                         AudioDumpInterface(AudioHardwareInterface* hw);
-    virtual status_t    standby();
     virtual AudioStreamOut* openOutputStream(
                                 int format=0,
                                 int channelCount=0,
index 53b18ad..d4692ad 100644 (file)
@@ -100,11 +100,11 @@ static bool settingsAllowed() {
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(), Thread(false),
         mMasterVolume(0), mMasterMute(true), mHardwareAudioMixer(0), mA2dpAudioMixer(0),
-        mAudioMixer(0), mAudioHardware(0), mA2dpAudioInterface(0),
-        mHardwareOutput(0), mA2dpOutput(0), mOutput(0), mAudioRecordThread(0),
-        mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0),
-        mMixBuffer(0), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0),
-        mStandby(false), mInWrite(false)
+        mAudioMixer(0), mAudioHardware(0), mA2dpAudioInterface(0), mHardwareOutput(0),
+        mA2dpOutput(0), mOutput(0), mRequestedOutput(0), mAudioRecordThread(0),
+        mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0),
+        mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false),
+        mInWrite(false)
 {
     mHardwareStatus = AUDIO_HW_IDLE;
     mAudioHardware = AudioHardwareInterface::create();
@@ -116,9 +116,9 @@ AudioFlinger::AudioFlinger()
         mHardwareOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);
         mHardwareStatus = AUDIO_HW_IDLE;
         if (mHardwareOutput) {
-            mSampleRate = mHardwareOutput->sampleRate();
-            mHardwareAudioMixer = new AudioMixer(getOutputFrameCount(mHardwareOutput), mSampleRate);
-            setOutput(mHardwareOutput);
+            mHardwareAudioMixer = new AudioMixer(getOutputFrameCount(mHardwareOutput), mHardwareOutput->sampleRate());
+            mRequestedOutput = mHardwareOutput;
+            doSetOutput(mHardwareOutput);
 
             // FIXME - this should come from settings
             setMasterVolume(1.0f);
@@ -159,7 +159,6 @@ AudioFlinger::AudioFlinger()
     }
 
     char value[PROPERTY_VALUE_MAX];
-    // FIXME: What property should this be???
     property_get("ro.audio.silent", value, "0");
     if (atoi(value)) {
         LOGD("Silence is golden");
@@ -173,9 +172,8 @@ AudioFlinger::~AudioFlinger()
         mAudioRecordThread->exit();
         mAudioRecordThread.clear();        
     }
-    delete mOutput;
-    delete mA2dpOutput;
     delete mAudioHardware;
+    // deleting mA2dpAudioInterface also deletes mA2dpOutput;
     delete mA2dpAudioInterface;
     delete [] mMixBuffer;
     delete mHardwareAudioMixer;
@@ -184,26 +182,22 @@ AudioFlinger::~AudioFlinger()
  
 void AudioFlinger::setOutput(AudioStreamOut* output)
 {
-    // lock on mOutputLock to prevent threadLoop() from starving us
-    Mutex::Autolock _l2(mOutputLock);
-    
-    // to synchronize with threadLoop()
-    Mutex::Autolock _l(mLock);
+    mRequestedOutput = output;
+}
 
-    if (mOutput != output) {
-        mSampleRate = output->sampleRate();
-        mChannelCount = output->channelCount();
-    
-        // FIXME - Current mixer implementation only supports stereo output
-        if (mChannelCount == 1) {
-            LOGE("Invalid audio hardware channel count");
-        }
-        mFormat = output->format();
-        mFrameCount = getOutputFrameCount(output);
-                
-        mAudioMixer = (output == mA2dpOutput ? mA2dpAudioMixer : mHardwareAudioMixer);
-        mOutput = output;
+void AudioFlinger::doSetOutput(AudioStreamOut* output)
+{
+    mSampleRate = output->sampleRate();
+    mChannelCount = output->channelCount();
+
+    // FIXME - Current mixer implementation only supports stereo output
+    if (mChannelCount == 1) {
+        LOGE("Invalid audio hardware channel count");
     }
+    mFormat = output->format();
+    mFrameCount = getOutputFrameCount(output);
+    mAudioMixer = (output == mA2dpOutput ? mA2dpAudioMixer : mHardwareAudioMixer);
+    mOutput = output;
 }
 
 size_t AudioFlinger::getOutputFrameCount(AudioStreamOut* output) 
@@ -330,21 +324,11 @@ bool AudioFlinger::threadLoop()
     Vector< sp<Track> > tracksToRemove;
     size_t enabledTracks = 0;
     nsecs_t standbyTime = systemTime();
-    AudioMixer* mixer = 0;
-    size_t frameCount = 0;
-    int channelCount = 0;
-    uint32_t sampleRate = 0;
-    AudioStreamOut* output = 0;
 
     do {
         enabledTracks = 0;
         { // scope for the mLock
         
-            // locking briefly on the secondary mOutputLock is necessary to avoid
-            // having this thread starve the thread that called setOutput()
-            mOutputLock.lock();
-            mOutputLock.unlock();
-
             Mutex::Autolock _l(mLock);
             const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
 
@@ -354,7 +338,7 @@ bool AudioFlinger::threadLoop()
                 LOGV("Audio hardware entering standby\n");
                 mHardwareStatus = AUDIO_HW_STANDBY;
                 if (!mStandby) {
-                    mAudioHardware->standby();
+                    mOutput->standby();
                     mStandby = true;
                 }
                 mHardwareStatus = AUDIO_HW_IDLE;
@@ -366,15 +350,16 @@ bool AudioFlinger::threadLoop()
                 continue;
             }
 
-            // get active mixer and output parameter while the lock is held and keep them
-            // consistent till the next loop.
-            
-            mixer = audioMixer();
-            frameCount = mFrameCount;
-            channelCount = mChannelCount;
-            sampleRate = mSampleRate;
-            output = mOutput;
-            
+            // check for change in output
+            if (mRequestedOutput != mOutput) {
+
+                // put current output into standby mode
+                if (mOutput) mOutput->standby();
+
+                // change output
+                doSetOutput(mRequestedOutput);
+            }
+
             // find out which tracks need to be processed
             size_t count = activeTracks.size();
             for (size_t i=0 ; i<count ; i++) {
@@ -386,7 +371,7 @@ bool AudioFlinger::threadLoop()
 
                 // The first time a track is added we wait
                 // for all its buffers to be filled before processing it
-                mixer->setActiveTrack(track->name());
+                mAudioMixer->setActiveTrack(track->name());
                 if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
                         !track->isPaused())
                 {
@@ -412,8 +397,8 @@ bool AudioFlinger::threadLoop()
                     }
 
                     // XXX: these things DON'T need to be done each time
-                    mixer->setBufferProvider(track);
-                    mixer->enable(AudioMixer::MIXING);
+                    mAudioMixer->setBufferProvider(track);
+                    mAudioMixer->enable(AudioMixer::MIXING);
 
                     int param;
                     if ( track->mFillingUpStatus == Track::FS_FILLED) {
@@ -428,15 +413,15 @@ bool AudioFlinger::threadLoop()
                     } else {
                         param = AudioMixer::RAMP_VOLUME;
                     }
-                    mixer->setParameter(param, AudioMixer::VOLUME0, left);
-                    mixer->setParameter(param, AudioMixer::VOLUME1, right);
-                    mixer->setParameter(
+                    mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
+                    mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+                    mAudioMixer->setParameter(
                         AudioMixer::TRACK,
                         AudioMixer::FORMAT, track->format());
-                    mixer->setParameter(
+                    mAudioMixer->setParameter(
                         AudioMixer::TRACK,
                         AudioMixer::CHANNEL_COUNT, track->channelCount());
-                    mixer->setParameter(
+                    mAudioMixer->setParameter(
                         AudioMixer::RESAMPLE,
                         AudioMixer::SAMPLE_RATE,
                         int(cblk->sampleRate));
@@ -463,7 +448,7 @@ bool AudioFlinger::threadLoop()
                         }
                     }
                     // LOGV("disable(%d)", track->name());
-                    mixer->disable(AudioMixer::MIXING);
+                    mAudioMixer->disable(AudioMixer::MIXING);
                 }
             }
 
@@ -475,27 +460,27 @@ bool AudioFlinger::threadLoop()
                     mActiveTracks.remove(track);
                     if (track->isTerminated()) {
                         mTracks.remove(track);
-                        mixer->deleteTrackName(track->mName);
+                        mAudioMixer->deleteTrackName(track->mName);
                     }
                 }
             }  
        }
         if (LIKELY(enabledTracks)) {
             // mix buffers...
-            mixer->process(curBuf);
+            mAudioMixer->process(curBuf);
 
             // output audio to hardware
             mLastWriteTime = systemTime();
             mInWrite = true;
-            size_t mixBufferSize = frameCount*channelCount*sizeof(int16_t);
-            output->write(curBuf, mixBufferSize);
+            size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t);
+            mOutput->write(curBuf, mixBufferSize);
             mNumWrites++;
             mInWrite = false;
             mStandby = false;
             nsecs_t temp = systemTime();
             standbyTime = temp + kStandbyTimeInNsecs;
             nsecs_t delta = temp - mLastWriteTime;
-            nsecs_t maxPeriod = seconds(frameCount) / sampleRate * 2;
+            nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2;
             if (delta > maxPeriod) {
                 LOGW("write blocked for %llu msecs", ns2ms(delta));
                 mNumDelayedWrites++;
@@ -653,6 +638,8 @@ status_t AudioFlinger::setMasterVolume(float value)
 
 status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask)
 {
+    status_t err = NO_ERROR;
+
     // check calling permissions
     if (!settingsAllowed()) {
         return PERMISSION_DENIED;
@@ -677,16 +664,20 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask)
     }
 #endif
 
-    AutoMutex lock(mHardwareLock);
-    mHardwareStatus = AUDIO_HW_GET_ROUTING;
-    uint32_t r;
-    uint32_t err = mAudioHardware->getRouting(mode, &r);
-    if (err == NO_ERROR) {
-        r = (r & ~mask) | (routes & mask);
-        mHardwareStatus = AUDIO_HW_SET_ROUTING;
-        err = mAudioHardware->setRouting(mode, r);
+    // do nothing if only A2DP routing is affected
+    mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP;
+    if (mask) {
+        AutoMutex lock(mHardwareLock);
+        mHardwareStatus = AUDIO_HW_GET_ROUTING;
+        uint32_t r;
+        err = mAudioHardware->getRouting(mode, &r);
+        if (err == NO_ERROR) {
+            r = (r & ~mask) | (routes & mask);
+            mHardwareStatus = AUDIO_HW_SET_ROUTING;
+            err = mAudioHardware->setRouting(mode, r);
+        }
+        mHardwareStatus = AUDIO_HW_IDLE;
     }
-    mHardwareStatus = AUDIO_HW_IDLE;
     return err;
 }
 
index d9f7b49..7c84e62 100644 (file)
@@ -150,6 +150,7 @@ private:
     virtual                 ~AudioFlinger();
     
     void                    setOutput(AudioStreamOut* output);
+    void                    doSetOutput(AudioStreamOut* output);
     size_t                  getOutputFrameCount(AudioStreamOut* output);
 
     // Internal dump utilites.
@@ -450,7 +451,6 @@ private:
 
     mutable     Mutex                                       mHardwareLock;
     mutable     Mutex                                       mLock;
-    mutable     Mutex                                       mOutputLock;
     mutable     Condition                                   mWaitWorkCV;
                 DefaultKeyedVector< pid_t, wp<Client> >     mClients;
                 SortedVector< wp<Track> >                   mActiveTracks;
@@ -468,6 +468,7 @@ private:
                 AudioStreamOut*                     mHardwareOutput;
                 AudioStreamOut*                     mA2dpOutput;
                 AudioStreamOut*                     mOutput;
+                AudioStreamOut*                     mRequestedOutput;
                 sp<AudioRecordThread>               mAudioRecordThread;
                 uint32_t                            mSampleRate;
                 size_t                              mFrameCount;
index e6a163b..e455186 100644 (file)
@@ -61,12 +61,6 @@ status_t AudioHardwareGeneric::initCheck()
     return NO_INIT;
 }
 
-status_t AudioHardwareGeneric::standby()
-{
-    // Implement: audio hardware to standby mode
-    return NO_ERROR;
-}
-
 AudioStreamOut* AudioHardwareGeneric::openOutputStream(
         int format, int channelCount, uint32_t sampleRate, status_t *status)
 {
@@ -215,6 +209,12 @@ ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
     return ssize_t(::write(mFd, buffer, bytes));
 }
 
+status_t AudioStreamOutGeneric::standby()
+{
+    // Implement: audio hardware to standby mode
+    return NO_ERROR;
+}
+
 status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
index a2342cd..bc006b8 100644 (file)
@@ -50,6 +50,7 @@ public:
     virtual uint32_t    latency() const { return 0; }
     virtual status_t    setVolume(float volume) { return INVALID_OPERATION; }
     virtual ssize_t     write(const void* buffer, size_t bytes);
+    virtual status_t    standby();
     virtual status_t    dump(int fd, const Vector<String16>& args);
 
 private:
@@ -92,7 +93,6 @@ public:
                         AudioHardwareGeneric();
     virtual             ~AudioHardwareGeneric();
     virtual status_t    initCheck();
-    virtual status_t    standby();
     virtual status_t    setVoiceVolume(float volume);
     virtual status_t    setMasterVolume(float volume);
 
index d309902..e9f3d69 100644 (file)
@@ -41,11 +41,6 @@ status_t AudioHardwareStub::initCheck()
     return NO_ERROR;
 }
 
-status_t AudioHardwareStub::standby()
-{
-    return NO_ERROR;
-}
-
 AudioStreamOut* AudioHardwareStub::openOutputStream(
         int format, int channelCount, uint32_t sampleRate, status_t *status)
 {
@@ -125,6 +120,11 @@ ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes)
     return bytes;
 }
 
+status_t AudioStreamOutStub::standby()
+{
+    return NO_ERROR;
+}
+
 status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
index 5316d60..7ec5b95 100644 (file)
@@ -37,6 +37,7 @@ public:
     virtual uint32_t    latency() const { return 0; }
     virtual status_t    setVolume(float volume) { return NO_ERROR; }
     virtual ssize_t     write(const void* buffer, size_t bytes);
+    virtual status_t    standby();
     virtual status_t    dump(int fd, const Vector<String16>& args);
 };
 
@@ -59,7 +60,6 @@ public:
                         AudioHardwareStub();
     virtual             ~AudioHardwareStub();
     virtual status_t    initCheck();
-    virtual status_t    standby();
     virtual status_t    setVoiceVolume(float volume);
     virtual status_t    setMasterVolume(float volume);
 
index e9e34c3..d18f59a 100644 (file)
@@ -36,8 +36,8 @@
 #include <ui/ISurfaceFlingerClient.h>
 #include <ui/EGLNativeWindowSurface.h>
 
-#include <graphics/SkBitmap.h>
-#include <graphics/SkImageDecoder.h>
+#include <core/SkBitmap.h>
+#include <images/SkImageDecoder.h>
 
 #include <GLES/egl.h>
 
index cd72179..19e32ec 100644 (file)
@@ -250,7 +250,7 @@ void DisplayHardware::init(uint32_t dpy)
 
     mOverlayEngine = NULL;
     if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
-        overlay_open(module, &mOverlayEngine);
+        overlay_control_open(module, &mOverlayEngine);
     }
 }
 
@@ -266,7 +266,7 @@ void DisplayHardware::fini()
     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mDisplay);
     copybit_close(mBlitEngine);
-    overlay_close(mOverlayEngine);
+    overlay_control_close(mOverlayEngine);
 }
 
 void DisplayHardware::releaseScreen() const
index de4a2cc..df97b60 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "DisplayHardware/DisplayHardwareBase.h"
 
-struct overlay_device_t;
+struct overlay_control_device_t;
 struct copybit_device_t;
 struct copybit_image_t;
 struct copybit_t;
@@ -78,7 +78,7 @@ public:
     void getDisplaySurface(GGLSurface* fb) const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     copybit_device_t* getBlitEngine() const { return mBlitEngine; }
-    overlay_device_t* getOverlayEngine() const { return mOverlayEngine; }
+    overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
     
     Rect bounds() const {
         return Rect(mWidth, mHeight);
@@ -103,7 +103,7 @@ private:
     mutable Region  mDirty;
     sp<EGLDisplaySurface> mDisplaySurface;
     copybit_device_t*     mBlitEngine;
-    overlay_device_t*     mOverlayEngine;
+    overlay_control_device_t* mOverlayEngine;
 };
 
 }; // namespace android
index 8ba0851..f65d669 100644 (file)
@@ -316,7 +316,7 @@ uint32_t Layer::doTransaction(uint32_t flags)
             if (err == NO_ERROR) {
                 const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0;
                 android_atomic_and(~mask, &(lcblk->swapState));
-                // since a buffer became availlable, we can let the client go...
+                // since a buffer became available, we can let the client go...
                 mFlinger->scheduleBroadcast(client);
                 mResizeTransactionDone = true;
 
@@ -511,7 +511,7 @@ Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions)
             }
             mResizeTransactionDone = false;
             recomputeVisibleRegions = true;
-            invalidate = true;
+            this->contentDirty = true;
         }
     }
 
index af353e2..bdefba3 100644 (file)
@@ -53,14 +53,15 @@ Vector<GLuint> LayerBase::deletedTextures;
 int32_t LayerBase::sIdentity = 0;
 
 LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
-    : dpy(display), invalidate(false),
+    : dpy(display), contentDirty(false),
       mFlinger(flinger),
       mTransformed(false),
       mOrientation(0),
       mCanUseCopyBit(false),
       mTransactionFlags(0),
       mPremultipliedAlpha(true),
-      mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
+      mIdentity(uint32_t(android_atomic_inc(&sIdentity))),
+      mInvalidate(0)
 {
     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
     mFlags = hw.getFlags();
@@ -205,7 +206,7 @@ uint32_t LayerBase::doTransaction(uint32_t flags)
     if (temp.sequence != front.sequence) {
         // invalidate and recompute the visible regions if needed
         flags |= eVisibleRegion;
-        this->invalidate = true;
+        this->contentDirty = true;
     }
     
     // Commit the transaction
@@ -299,12 +300,22 @@ void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
 void LayerBase::unlockPageFlip(
         const Transform& planeTransform, Region& outDirtyRegion)
 {
+    if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
+        outDirtyRegion.orSelf(visibleRegionScreen);
+    }
 }
 
 void LayerBase::finishPageFlip()
 {
 }
 
+void LayerBase::invalidate()
+{
+    if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
+        mFlinger->signalEvent();
+    }
+}
+
 void LayerBase::drawRegion(const Region& reg) const
 {
     Region::iterator iterator(reg);
index b3f3771..5e14dc8 100644 (file)
@@ -75,7 +75,7 @@ public:
     virtual ~LayerBase();
     
     DisplayID           dpy;
-    mutable bool        invalidate;
+    mutable bool        contentDirty;
             Region      visibleRegionScreen;
             Region      transparentRegionScreen;
             Region      coveredRegionScreen;
@@ -112,18 +112,87 @@ public:
             Rect visibleBounds() const;
             void drawRegion(const Region& reg) const;
 
+            void invalidate();
+            
+    /**
+     * draw - performs some global clipping optimizations
+     * and calls onDraw().
+     * Typically this method is not overridden, instead implement onDraw()
+     * to perform the actual drawing.  
+     */
     virtual void draw(const Region& clip) const;
+    
+    /**
+     * onDraw - draws the surface.
+     */
     virtual void onDraw(const Region& clip) const = 0;
+    
+    /**
+     * initStates - called just after construction
+     */
     virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
+    
+    /**
+     * setSizeChanged - called when the *current* state's size is changed.
+     */
     virtual void setSizeChanged(uint32_t w, uint32_t h);
+    
+    /**
+     * doTransaction - process the transaction. This is a good place to figure
+     * out which attributes of the surface have changed.
+     */
     virtual uint32_t doTransaction(uint32_t transactionFlags);
+    
+    /**
+     * setVisibleRegion - called to set the new visible region. This gives
+     * a chance to update the new visible region or record the fact it changed.
+     */
     virtual void setVisibleRegion(const Region& visibleRegion);
+    
+    /**
+     * setCoveredRegion - called when the covered region changes. The covered
+     * region correspond to any area of the surface that is covered 
+     * (transparently or not) by another surface.
+     */
     virtual void setCoveredRegion(const Region& coveredRegion);
+    
+    /**
+     * getPhysicalSize - returns the physical size of the drawing state of
+     * the surface. If the surface is backed by a bitmap, this is the size of
+     * the bitmap (as opposed to the size of the drawing state).
+     */
     virtual Point getPhysicalSize() const;
+    
+    /**
+     * lockPageFlip - called each time the screen is redrawn and returns whether
+     * the visible regions need to be recomputed (this is a fairly heavy
+     * operation, so this should be set only if needed). Typically this is used
+     * to figure out if the content or size of a surface has changed.
+     */
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
+    
+    /**
+     * unlockPageFlip - called each time the screen is redrawn. updates the
+     * final dirty region wrt the planeTransform.
+     * At this point, all visible regions, surface position and size, etc... are
+     * correct.
+     */
     virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+    
+    /**
+     * finishPageFlip - called after all surfaces have drawn.
+     */
     virtual void finishPageFlip();
+    
+    /**
+     * needsBlending - true if this surface needs blending
+     */
     virtual bool needsBlending() const  { return false; }
+    
+    /**
+     * isSecure - true if this surface is secure, that is if it prevents a
+     * screenshot to be taken,
+     */
     virtual bool isSecure() const       { return false; }
 
             enum { // flags for doTransaction()
@@ -162,7 +231,6 @@ protected:
 
           bool canUseCopybit() const;
           
-          
                 SurfaceFlinger* mFlinger;
                 uint32_t        mFlags;
 
@@ -184,7 +252,10 @@ protected:
                 bool            mPremultipliedAlpha;
 
                 // only read
-     const      uint32_t        mIdentity;
+    const       uint32_t        mIdentity;
+     
+                // atomic
+    volatile    int32_t         mInvalidate;
                 
 
 private:
@@ -254,7 +325,7 @@ public:
                 { return INVALID_OPERATION; }
         virtual void postBuffer(ssize_t offset) { }
         virtual void unregisterBuffers() { };
-        virtual sp<Overlay> createOverlay(
+        virtual sp<OverlayRef> createOverlay(
                 uint32_t w, uint32_t h, int32_t format) {
             return NULL;
         };
index e3ae7fb..efadbcf 100644 (file)
@@ -71,7 +71,7 @@ uint32_t LayerBlur::doTransaction(uint32_t flags)
         mRefreshCache = true;
         mCacheDirty = true;
         flags |= eVisibleRegion;
-        this->invalidate = true;
+        this->contentDirty = true;
     }
     return LayerBase::doTransaction(flags);    
 }
index 3861e68..700e4f5 100644 (file)
@@ -46,7 +46,7 @@ const char* const LayerBuffer::typeID = "LayerBuffer";
 LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
         Client* client, int32_t i)
     : LayerBaseClient(flinger, display, client, i),
-    mBuffer(0), mTextureName(-1U), mInvalidate(false), mNeedsBlending(false)
+      mNeedsBlending(false)
 {
 }
 
@@ -57,43 +57,333 @@ LayerBuffer::~LayerBuffer()
         s->disown();
         mClientSurface.clear();
     }
+}
 
-    // this should always be called from the OpenGL thread
-    if (mTextureName != -1U) {
-        //glDeleteTextures(1, &mTextureName);
-        deletedTextures.add(mTextureName);
-    }
-    // to help debugging we set those to zero
-    mWidth = mHeight = 0;
+sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const
+{
+    Mutex::Autolock _l(mLock);
+    return mClientSurface.promote();
 }
 
-bool LayerBuffer::needsBlending() const
+sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const
 {
+    sp<SurfaceBuffer> s;
     Mutex::Autolock _l(mLock);
+    s = mClientSurface.promote();
+    if (s == 0) {
+        s = new SurfaceBuffer(clientIndex(),
+                const_cast<LayerBuffer *>(this));
+        mClientSurface = s;
+    }
+    return s;
+}
+
+bool LayerBuffer::needsBlending() const {
     return mNeedsBlending;
 }
 
+void LayerBuffer::setNeedsBlending(bool blending) {
+    mNeedsBlending = blending;
+}
+
+void LayerBuffer::postBuffer(ssize_t offset)
+{
+    sp<Source> source(getSource());
+    if (source != 0)
+        source->postBuffer(offset);
+}
+
+void LayerBuffer::unregisterBuffers()
+{
+    sp<Source> source(getSource());
+    if (source != 0)
+        source->unregisterBuffers();
+    // XXX: clear mSource
+}
+
+uint32_t LayerBuffer::doTransaction(uint32_t flags)
+{
+    sp<Source> source(getSource());
+    if (source != 0)
+        source->onTransaction(flags);
+    return LayerBase::doTransaction(flags);    
+}
+
+void LayerBuffer::unlockPageFlip(const Transform& planeTransform,
+        Region& outDirtyRegion)
+{
+    // this code-path must be as tight as possible, it's called each time
+    // the screen is composited.
+    sp<Source> source(getSource());
+    if (source != 0)
+        source->onVisibilityResolved(planeTransform);
+    LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);    
+}
+
 void LayerBuffer::onDraw(const Region& clip) const
 {
+    sp<Source> source(getSource());
+    if (LIKELY(source != 0)) {
+        source->onDraw(clip);
+    } else {
+        clearWithOpenGL(clip);
+    }
+}
+
+/**
+ * This creates a "buffer" source for this surface
+ */
+status_t LayerBuffer::registerBuffers(int w, int h, int hstride, int vstride,
+        PixelFormat format, const sp<IMemoryHeap>& memoryHeap)
+{
+    Mutex::Autolock _l(mLock);
+    if (mSource != 0)
+        return INVALID_OPERATION;
+
+    sp<BufferSource> source = new BufferSource(*this, w, h,
+            hstride, vstride, format, memoryHeap);
+
+    status_t result = source->getStatus();
+    if (result == NO_ERROR) {
+        mSource = source;
+    }
+    return result;
+}    
+
+/**
+ * This creates an "overlay" source for this surface
+ */
+sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f)
+{
+    sp<OverlayRef> result;
+    Mutex::Autolock _l(mLock);
+    if (mSource != 0)
+        return result;
+
+    sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f);
+    if (result != 0) {
+        mSource = source;
+    }
+    return result;
+}
+
+sp<LayerBuffer::Source> LayerBuffer::getSource() const {
+    Mutex::Autolock _l(mLock);
+    return mSource;
+}
+
+// ============================================================================
+// LayerBuffer::SurfaceBuffer
+// ============================================================================
+
+LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner)
+: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner)
+{
+}
+
+LayerBuffer::SurfaceBuffer::~SurfaceBuffer()
+{
+    unregisterBuffers();
+    mOwner = 0;
+}
+
+status_t LayerBuffer::SurfaceBuffer::registerBuffers(
+        int w, int h, int hs, int vs,
+        PixelFormat format, const sp<IMemoryHeap>& heap)
+{
+    LayerBuffer* owner(getOwner());
+    if (owner)
+        return owner->registerBuffers(w, h, hs, vs, format, heap);
+    return NO_INIT;
+}
+
+void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset)
+{
+    LayerBuffer* owner(getOwner());
+    if (owner)
+        owner->postBuffer(offset);
+}
+
+void LayerBuffer::SurfaceBuffer::unregisterBuffers()
+{
+    LayerBuffer* owner(getOwner());
+    if (owner)
+        owner->unregisterBuffers();
+}
+
+sp<OverlayRef> LayerBuffer::SurfaceBuffer::createOverlay(
+        uint32_t w, uint32_t h, int32_t format) {
+    sp<OverlayRef> result;
+    LayerBuffer* owner(getOwner());
+    if (owner)
+        result = owner->createOverlay(w, h, format);
+    return result;
+}
+
+void LayerBuffer::SurfaceBuffer::disown()
+{
+    Mutex::Autolock _l(mLock);
+    mOwner = 0;
+}
+
+// ============================================================================
+// LayerBuffer::Buffer
+// ============================================================================
+
+LayerBuffer::Buffer::Buffer(const sp<IMemoryHeap>& heap, ssize_t offset,
+        int w, int h, int hs, int vs, int f)
+: mHeap(heap)
+{
+    NativeBuffer& src(mNativeBuffer);
+    src.crop.l = 0;
+    src.crop.t = 0;
+    src.crop.r = w;
+    src.crop.b = h;
+    src.img.w = hs ?: w;
+    src.img.h = vs ?: h;
+    src.img.format = f;
+    src.img.offset = offset;
+    src.img.base   = heap->base();
+    src.img.fd     = heap->heapID();
+    // FIXME: make sure this buffer lies within the heap, in which case, set
+    // mHeap to null
+}
+
+LayerBuffer::Buffer::~Buffer()
+{
+}
+
+// ============================================================================
+// LayerBuffer::Source
+// LayerBuffer::BufferSource
+// LayerBuffer::OverlaySource
+// ============================================================================
+
+LayerBuffer::Source::Source(LayerBuffer& layer)
+    : mLayer(layer)
+{    
+}
+LayerBuffer::Source::~Source() {    
+}
+void LayerBuffer::Source::onDraw(const Region& clip) const {
+}
+void LayerBuffer::Source::onTransaction(uint32_t flags) {
+}
+void LayerBuffer::Source::onVisibilityResolved(
+        const Transform& planeTransform) {
+}
+void LayerBuffer::Source::postBuffer(ssize_t offset) {
+}
+void LayerBuffer::Source::unregisterBuffers() {
+}
+
+// ---------------------------------------------------------------------------
+
+LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
+        int w, int h, int hstride, int vstride,
+        PixelFormat format, const sp<IMemoryHeap>& memoryHeap)
+    : Source(layer), mStatus(NO_ERROR), mTextureName(-1U)
+{
+    if (memoryHeap == NULL) {
+        // this is allowed, but in this case, it is illegal to receive
+        // postBuffer(). The surface just erases the framebuffer with
+        // fully transparent pixels.
+        mHeap.clear();
+        mWidth = w;
+        mHeight = h;
+        mLayer.setNeedsBlending(false);
+        return;
+    }
+
+    status_t err = (memoryHeap->heapID() >= 0) ? NO_ERROR : NO_INIT;
+    if (err != NO_ERROR) {
+        mStatus = err;
+        return;
+    }
+
+    // TODO: validate format/parameters
+    mHeap = memoryHeap;
+    mWidth = w;
+    mHeight = h;
+    mHStride = hstride;
+    mVStride = vstride;
+    mFormat = format;
+    PixelFormatInfo info;
+    getPixelFormatInfo(format, &info);
+    mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0);
+}
+
+LayerBuffer::BufferSource::~BufferSource()
+{    
+    if (mTextureName != -1U) {
+        LayerBase::deletedTextures.add(mTextureName);
+    }
+}
+
+void LayerBuffer::BufferSource::postBuffer(ssize_t offset)
+{    
+    sp<IMemoryHeap> heap;
+    int w, h, hs, vs, f;
+    { // scope for the lock
+        Mutex::Autolock _l(mLock);
+        w = mWidth;
+        h = mHeight;
+        hs= mHStride;
+        vs= mVStride;
+        f = mFormat;
+        heap = mHeap;
+    }
+
+    sp<Buffer> buffer;
+    if (heap != 0) {
+        buffer = new LayerBuffer::Buffer(heap, offset, w, h, hs, vs, f);
+        if (buffer->getStatus() != NO_ERROR)
+            buffer.clear();
+        setBuffer(buffer);
+        mLayer.invalidate();
+    }
+}
+
+void LayerBuffer::BufferSource::unregisterBuffers()
+{
+    Mutex::Autolock _l(mLock);
+    mHeap.clear();
+    mBuffer.clear();
+    mLayer.invalidate();
+}
+
+sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const
+{
+    Mutex::Autolock _l(mLock);
+    return mBuffer;
+}
+
+void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
+{
+    Mutex::Autolock _l(mLock);
+    mBuffer = buffer;
+}
+
+void LayerBuffer::BufferSource::onDraw(const Region& clip) const 
+{
     sp<Buffer> buffer(getBuffer());
     if (UNLIKELY(buffer == 0))  {
         // nothing to do, we don't have a buffer
-        clearWithOpenGL(clip);
+        mLayer.clearWithOpenGL(clip);
         return;
     }
 
     status_t err = NO_ERROR;
     NativeBuffer src(buffer->getBuffer());
-    const int can_use_copybit = canUseCopybit();
+    const Rect& transformedBounds = mLayer.getTransformedBounds();
+    const int can_use_copybit = mLayer.canUseCopybit();
 
     if (can_use_copybit)  {
-        //StopWatch watch("MDP");
-
         const int src_width  = src.crop.r - src.crop.l;
         const int src_height = src.crop.b - src.crop.t;
-        int W = mTransformedBounds.width();
-        int H = mTransformedBounds.height();
-        if (getOrientation() & Transform::ROT_90) {
+        int W = transformedBounds.width();
+        int H = transformedBounds.height();
+        if (mLayer.getOrientation() & Transform::ROT_90) {
             int t(W); W=H; H=t;
         }
 
@@ -104,7 +394,7 @@ void LayerBuffer::onDraw(const Region& clip) const
          * the requested scale factor, in which case we perform the scaling
          * in several passes. */
 
-        copybit_device_t* copybit = mFlinger->getBlitEngine();
+        copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine();
         const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT);
         const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT);
 
@@ -117,13 +407,10 @@ void LayerBuffer::onDraw(const Region& clip) const
         else if (src_height*mag < H)    yscale = mag;
 
         if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) {
-            //LOGD("MDP scaling hack w=%d, h=%d, ww=%d, wh=%d, xs=%f, ys=%f",
-            //        src_width, src_height, W, H, xscale, yscale);
-
             if (UNLIKELY(mTemporaryDealer == 0)) {
                 // allocate a memory-dealer for this the first time
-                mTemporaryDealer = mFlinger->getSurfaceHeapManager()
-                        ->createHeap(ISurfaceComposer::eHardware);
+                mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager()
+                ->createHeap(ISurfaceComposer::eHardware);
                 mTempBitmap.init(mTemporaryDealer);
             }
 
@@ -149,14 +436,14 @@ void LayerBuffer::onDraw(const Region& clip) const
             }
         }
 
-        const DisplayHardware& hw(graphicPlane(0).displayHardware());
+        const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware());
         copybit_image_t dst;
         hw.getDisplaySurface(&dst);
         const copybit_rect_t& drect
-                = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds);
-        const State& s(drawingState());
+        = reinterpret_cast<const copybit_rect_t&>(transformedBounds);
+        const State& s(mLayer.drawingState());
         region_iterator it(clip);
-        copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation());
+        copybit->set_parameter(copybit, COPYBIT_TRANSFORM, mLayer.getOrientation());
         copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha);
         copybit->set_parameter(copybit, COPYBIT_DITHER,
                 s.flags & ISurfaceComposer::eLayerDither ?
@@ -167,245 +454,115 @@ void LayerBuffer::onDraw(const Region& clip) const
 
     if (!can_use_copybit || err) {
         if (UNLIKELY(mTextureName == -1LU)) {
-            mTextureName = createTexture();
+            mTextureName = mLayer.createTexture();
         }
         GLuint w = 0;
         GLuint h = 0;
         GGLSurface t;
-            t.version = sizeof(GGLSurface);
-            t.width  = src.crop.r;
-            t.height = src.crop.b;
-            t.stride = src.img.w;
-            t.vstride= src.img.h;
-            t.format = src.img.format;
-            t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset);
+        t.version = sizeof(GGLSurface);
+        t.width  = src.crop.r;
+        t.height = src.crop.b;
+        t.stride = src.img.w;
+        t.vstride= src.img.h;
+        t.format = src.img.format;
+        t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset);
         const Region dirty(Rect(t.width, t.height));
-        loadTexture(dirty, mTextureName, t, w, h);
-        drawWithOpenGL(clip, mTextureName, t);
+        mLayer.loadTexture(dirty, mTextureName, t, w, h);
+        mLayer.drawWithOpenGL(clip, mTextureName, t);
     }
 }
 
-void LayerBuffer::invalidateLocked()
-{
-    mInvalidate = true;
-    mFlinger->signalEvent();
-}
-
-void LayerBuffer::invalidate()
-{
-    Mutex::Autolock _l(mLock);
-    invalidateLocked();
-}
-
-void LayerBuffer::unlockPageFlip(const Transform& planeTransform,
-        Region& outDirtyRegion)
-{
-    Mutex::Autolock _l(mLock);
-    if (mInvalidate) {
-        mInvalidate = false;
-        outDirtyRegion.orSelf(visibleRegionScreen);
-    }
-}
-
-sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const
-{
-    Mutex::Autolock _l(mLock);
-    return mClientSurface.promote();
-}
-
-sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const
-{
-    sp<SurfaceBuffer> s;
-    Mutex::Autolock _l(mLock);
-    s = mClientSurface.promote();
-    if (s == 0) {
-        s = new SurfaceBuffer(clientIndex(),
-                const_cast<LayerBuffer *>(this));
-        mClientSurface = s;
-    }
-    return s;
-}
-
-
-status_t LayerBuffer::registerBuffers(int w, int h, int hstride, int vstride,
-            PixelFormat format, const sp<IMemoryHeap>& memoryHeap)
-{
-    if (memoryHeap == NULL) {
-        // this is allowed, but in this case, it is illegal to receive
-        // postBuffer(). The surface just erases the framebuffer with
-        // fully transparent pixels.
-        mHeap.clear();
-        mWidth = w;
-        mHeight = h;
-        mNeedsBlending = false;
-        return NO_ERROR;
-    }
-    
-    status_t err = (memoryHeap->heapID() >= 0) ? NO_ERROR : NO_INIT;
-    if (err != NO_ERROR)
-        return err;
-
-    // TODO: validate format/parameters
-
-    Mutex::Autolock _l(mLock);
-    mHeap = memoryHeap;
-    mWidth = w;
-    mHeight = h;
-    mHStride = hstride;
-    mVStride = vstride;
-    mFormat = format;
-    PixelFormatInfo info;
-    getPixelFormatInfo(format, &info);
-    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
-    return NO_ERROR;
-}
-
-void LayerBuffer::postBuffer(ssize_t offset)
-{
-    sp<IMemoryHeap> heap;
-    int w, h, hs, vs, f;
-    { // scope for the lock
-        Mutex::Autolock _l(mLock);
-        w = mWidth;
-        h = mHeight;
-        hs= mHStride;
-        vs= mVStride;
-        f = mFormat;
-        heap = mHeap;
-    }
-
-    sp<Buffer> buffer;
-    if (heap != 0) {
-        buffer = new Buffer(heap, offset, w, h, hs, vs, f);
-        if (buffer->getStatus() != NO_ERROR)
-            buffer.clear();
-        setBuffer(buffer);
-        invalidate();
-    }
-}
+// ---------------------------------------------------------------------------
 
-void LayerBuffer::unregisterBuffers()
+LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
+        sp<OverlayRef>* overlayRef, 
+        uint32_t w, uint32_t h, int32_t format)
+    : Source(layer), mVisibilityChanged(false), mOverlay(0), mOverlayHandle(0)
 {
-    Mutex::Autolock _l(mLock);
-    mHeap.clear();
-    mBuffer.clear();
-    invalidateLocked();
-}
+    overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
 
-sp<Overlay> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t format)
-{
-    sp<Overlay> result;
-    Mutex::Autolock _l(mLock);
-    if (mHeap != 0 || mBuffer != 0) {
-        // we're a push surface. error.
-        return result;
-    }
-    
-    overlay_device_t* overlay_dev = mFlinger->getOverlayEngine();
     if (overlay_dev == NULL) {
         // overlays not supported
-        return result;
+        return;
     }
 
     overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format);
     if (overlay == NULL) {
         // couldn't create the overlay (no memory? no more overlays?)
-        return result;
+        return;
     }
-    
-    /* TODO: implement the real stuff here */
-    
-    return result;
-}
-
-sp<LayerBuffer::Buffer> LayerBuffer::getBuffer() const
-{
-    Mutex::Autolock _l(mLock);
-    return mBuffer;
-}
 
-void LayerBuffer::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
-{
-    Mutex::Autolock _l(mLock);
-    mBuffer = buffer;
-}
+    // enable dithering...
+    overlay_dev->setParameter(overlay_dev, overlay, 
+            OVERLAY_DITHER, OVERLAY_ENABLE);
 
-// ---------------------------------------------------------------------------
+    mOverlay = overlay;
+    mWidth = overlay->w;
+    mHeight = overlay->h;
+    mFormat = overlay->format; 
+    mWidthStride = overlay->w_stride;
+    mHeightStride = overlay->h_stride;
 
-LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner)
-    : LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner)
-{
-}
+    mOverlayHandle = overlay->getHandleRef(overlay);
+    
+    // NOTE: here it's okay to acquire a reference to "this"m as long as
+    // the reference is not released before we leave the ctor.
+    sp<OverlayChanel> chanel = new OverlayChanel(this);
 
-LayerBuffer::SurfaceBuffer::~SurfaceBuffer()
-{
-    unregisterBuffers();
-    mOwner = 0;
+    *overlayRef = new OverlayRef(mOverlayHandle, chanel,
+            mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
 }
 
-status_t LayerBuffer::SurfaceBuffer::registerBuffers(
-        int w, int h, int hs, int vs,
-        PixelFormat format, const sp<IMemoryHeap>& heap)
-{
-    LayerBuffer* owner(getOwner());
-    if (owner)
-        return owner->registerBuffers(w, h, hs, vs, format, heap);
-    return NO_INIT;
+LayerBuffer::OverlaySource::~OverlaySource()
+{    
 }
 
-void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset)
+void LayerBuffer::OverlaySource::onTransaction(uint32_t flags)
 {
-    LayerBuffer* owner(getOwner());
-    if (owner)
-        owner->postBuffer(offset);
+    const Layer::State& front(mLayer.drawingState());
+    const Layer::State& temp(mLayer.currentState());
+    if (temp.sequence != front.sequence) {
+        mVisibilityChanged = true;
+    }
 }
 
-void LayerBuffer::SurfaceBuffer::unregisterBuffers()
+void LayerBuffer::OverlaySource::onVisibilityResolved(
+        const Transform& planeTransform)
 {
-    LayerBuffer* owner(getOwner());
-    if (owner)
-        owner->unregisterBuffers();
-}
-
-sp<Overlay> LayerBuffer::SurfaceBuffer::createOverlay(
-        uint32_t w, uint32_t h, int32_t format) {
-    sp<Overlay> result;
-    LayerBuffer* owner(getOwner());
-    if (owner)
-        result = owner->createOverlay(w, h, format);
-    return result;
+    // this code-path must be as tight as possible, it's called each time
+    // the screen is composited.
+    if (UNLIKELY(mOverlay != 0)) {
+        if (mVisibilityChanged) {
+            mVisibilityChanged = false;
+            const Rect& bounds = mLayer.getTransformedBounds();
+            int x = bounds.left;
+            int y = bounds.top;
+            int w = bounds.width();
+            int h = bounds.height();
+            
+            // we need a lock here to protect "destroy"
+            Mutex::Autolock _l(mLock);
+            if (mOverlay) {
+                overlay_control_device_t* overlay_dev = 
+                    mLayer.mFlinger->getOverlayEngine();
+                overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h);
+                overlay_dev->setParameter(overlay_dev, mOverlay, 
+                        OVERLAY_TRANSFORM, mLayer.getOrientation());
+            }
+        }
+    }
 }
 
-void LayerBuffer::SurfaceBuffer::disown()
+void LayerBuffer::OverlaySource::serverDestroy() 
 {
+    // we need a lock here to protect "onVisibilityResolved"
     Mutex::Autolock _l(mLock);
-    mOwner = 0;
-}
-
-
-// ---------------------------------------------------------------------------
-
-LayerBuffer::Buffer::Buffer(const sp<IMemoryHeap>& heap, ssize_t offset,
-        int w, int h, int hs, int vs, int f)
-    : mCount(0), mHeap(heap)
-{
-    NativeBuffer& src(mNativeBuffer);
-    src.crop.l = 0;
-    src.crop.t = 0;
-    src.crop.r = w;
-    src.crop.b = h;
-    src.img.w = hs ?: w;
-    src.img.h = vs ?: h;
-    src.img.format = f;
-    src.img.offset = offset;
-    src.img.base   = heap->base();
-    src.img.fd     = heap->heapID();
-    // FIXME: make sure this buffer lies within the heap, in which case, set
-    // mHeap to null
-}
-
-LayerBuffer::Buffer::~Buffer()
-{
+    if (mOverlay) {
+        overlay_control_device_t* overlay_dev = 
+                mLayer.mFlinger->getOverlayEngine();
+        overlay_dev->destroyOverlay(overlay_dev, mOverlay);
+        mOverlay = 0;
+    }
 }
 
 // ---------------------------------------------------------------------------
index 3e616f2..63ec2cf 100644 (file)
@@ -33,10 +33,24 @@ namespace android {
 
 class MemoryDealer;
 class Region;
-class Overlay;
+class OverlayRef;
 
 class LayerBuffer : public LayerBaseClient
 {
+    class Source : public LightRefBase<Source> {
+    public:
+        Source(LayerBuffer& layer);
+        virtual ~Source();
+        virtual void onDraw(const Region& clip) const;
+        virtual void onTransaction(uint32_t flags);
+        virtual void onVisibilityResolved(const Transform& planeTransform);
+        virtual void postBuffer(ssize_t offset);
+        virtual void unregisterBuffers();
+    protected:
+        LayerBuffer& mLayer;
+    };
+
+
 public:
     static const uint32_t typeInfo;
     static const char* const typeID;
@@ -51,39 +65,31 @@ public:
 
     virtual sp<LayerBaseClient::Surface> getSurface() const;
     virtual void onDraw(const Region& clip) const;
+    virtual uint32_t doTransaction(uint32_t flags);
     virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
 
     status_t registerBuffers(int w, int h, int hstride, int vstride,
             PixelFormat format, const sp<IMemoryHeap>& heap);
     void postBuffer(ssize_t offset);
     void unregisterBuffers();
-    sp<Overlay> createOverlay(uint32_t w, uint32_t h, int32_t format);
-    void invalidate();
-    void invalidateLocked();
+    sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format);
+    
+    sp<Source> getSource() const;
+    void setNeedsBlending(bool blending);
+    const Rect& getTransformedBounds() const {
+        return mTransformedBounds;
+    }
 
 private:
-
-    struct NativeBuffer
-    {
+    struct NativeBuffer {
         copybit_image_t   img;
         copybit_rect_t    crop;
     };
 
-    class Buffer
-    {
+    class Buffer : public LightRefBase<Buffer> {
     public:
         Buffer(const sp<IMemoryHeap>& heap, ssize_t offset,
                 int w, int h, int hs, int vs, int f);
-        inline void incStrong(void*) const {
-            android_atomic_inc(&mCount);
-        }
-        inline void decStrong(void*) const {
-            int32_t c = android_atomic_dec(&mCount);
-            //LOGE_IF(c<1, "Buffer::decStrong() called too many times");
-            if (c == 1) {
-                 delete this;
-             }
-        }
         inline status_t getStatus() const {
             return mHeap!=0 ? NO_ERROR : NO_INIT;
         }
@@ -91,15 +97,87 @@ private:
             return mNativeBuffer;
         }
     protected:
+        friend class LightRefBase<Buffer>;
         Buffer& operator = (const Buffer& rhs);
         Buffer(const Buffer& rhs);
         ~Buffer();
-        mutable volatile int32_t mCount;
     private:
         sp<IMemoryHeap>    mHeap;
         NativeBuffer       mNativeBuffer;
     };
 
+    class BufferSource : public Source {
+    public:
+        BufferSource(LayerBuffer& layer,
+                int w, int h, int hstride, int vstride,
+                PixelFormat format, const sp<IMemoryHeap>& heap);
+        virtual ~BufferSource();
+
+        status_t getStatus() const { return mStatus; }
+        sp<Buffer> getBuffer() const;
+        void setBuffer(const sp<Buffer>& buffer);
+
+        virtual void onDraw(const Region& clip) const;
+        virtual void postBuffer(ssize_t offset);
+        virtual void unregisterBuffers();
+    private:
+        mutable Mutex   mLock;
+        sp<IMemoryHeap> mHeap;
+        sp<Buffer>      mBuffer;
+        status_t        mStatus;
+        int             mWidth;
+        int             mHeight;
+        int             mHStride;
+        int             mVStride;
+        int             mFormat;
+        mutable sp<MemoryDealer> mTemporaryDealer;
+        mutable LayerBitmap mTempBitmap;
+        mutable GLuint  mTextureName;
+    };
+    
+    class OverlaySource : public Source {
+    public:
+        OverlaySource(LayerBuffer& layer,
+                sp<OverlayRef>* overlayRef, 
+                uint32_t w, uint32_t h, int32_t format);
+        virtual ~OverlaySource();
+        virtual void onTransaction(uint32_t flags);
+        virtual void onVisibilityResolved(const Transform& planeTransform);
+    private:
+        void serverDestroy(); 
+        class OverlayChanel : public BnOverlay {
+            mutable Mutex mLock;
+            sp<OverlaySource> mSource;
+            virtual void destroy() {
+                sp<OverlaySource> source;
+                { // scope for the lock;
+                    Mutex::Autolock _l(mLock);
+                    source = mSource;
+                    mSource.clear();
+                }
+                if (source != 0) {
+                    source->serverDestroy();
+                }
+            }
+        public:
+            OverlayChanel(const sp<OverlaySource>& source)
+                : mSource(source) {
+            }
+        };
+        friend class OverlayChanel;
+        bool mVisibilityChanged;
+
+        overlay_t* mOverlay;        
+        overlay_handle_t const *mOverlayHandle;
+        uint32_t mWidth;
+        uint32_t mHeight;
+        int32_t mFormat;
+        int32_t mWidthStride;
+        int32_t mHeightStride;
+        mutable Mutex mLock;
+    };
+
+
     class SurfaceBuffer : public LayerBaseClient::Surface
     {
     public:
@@ -109,7 +187,7 @@ private:
                 PixelFormat format, const sp<IMemoryHeap>& heap);
         virtual void postBuffer(ssize_t offset);
         virtual void unregisterBuffers();
-        virtual sp<Overlay> createOverlay(
+        virtual sp<OverlayRef> createOverlay(
                 uint32_t w, uint32_t h, int32_t format);
        void disown();
     private:
@@ -122,24 +200,14 @@ private:
     };
 
     friend class SurfaceFlinger;
-    sp<Buffer> getBuffer() const;
-    void       setBuffer(const sp<Buffer>& buffer);
     sp<SurfaceBuffer>   getClientSurface() const;
 
     mutable Mutex   mLock;
-    sp<IMemoryHeap> mHeap;
-    sp<Buffer>      mBuffer;
-    int             mWidth;
-    int             mHeight;
-    int             mHStride;
-    int             mVStride;
-    int             mFormat;
-    mutable GLuint  mTextureName;
+    sp<Source>      mSource;
+
     bool            mInvalidate;
     bool            mNeedsBlending;
     mutable wp<SurfaceBuffer> mClientSurface;
-    mutable sp<MemoryDealer> mTemporaryDealer;
-    mutable LayerBitmap mTempBitmap;
 };
 
 // ---------------------------------------------------------------------------
index 9b82bad..3e7132b 100644 (file)
@@ -23,7 +23,7 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
-#include <graphics/SkBitmap.h>
+#include <core/SkBitmap.h>
 
 #include <ui/EGLDisplaySurface.h>
 
index e8de21a..4c719e8 100644 (file)
@@ -231,7 +231,7 @@ copybit_device_t* SurfaceFlinger::getBlitEngine() const
     return graphicPlane(0).displayHardware().getBlitEngine();
 }
 
-overlay_device_t* SurfaceFlinger::getOverlayEngine() const
+overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const
 {
     return graphicPlane(0).displayHardware().getOverlayEngine();
 }
@@ -773,12 +773,12 @@ void SurfaceFlinger::computeVisibleRegions(
         coveredRegion.andSelf(aboveCoveredLayers);
 
         // compute this layer's dirty region
-        if (layer->invalidate) {
+        if (layer->contentDirty) {
             // we need to invalidate the whole region
             dirty = visibleRegion;
             // as well, as the old visible region
             dirty.orSelf(layer->visibleRegionScreen);
-            layer->invalidate = false;
+            layer->contentDirty = false;
         } else {
             // compute the exposed region
             // dirty = what's visible now - what's wasn't covered before
@@ -1456,7 +1456,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
                     "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
                     layer->getTypeID(), layer,
                     s.z, layer->tx(), layer->ty(), s.w, s.h,
-                    layer->needsBlending(), layer->invalidate,
+                    layer->needsBlending(), layer->contentDirty,
                     s.alpha, s.flags,
                     s.transform[0], s.transform[1],
                     s.transform[2], s.transform[3]);
@@ -1474,22 +1474,6 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
             }
             result.append(buffer);
             buffer[0] = 0;
-            /*** LayerBuffer ***/
-            LayerBuffer* const lbuf =
-                LayerBase::dynamicCast<LayerBuffer*>((LayerBase*)layer);
-            if (lbuf) {
-                sp<LayerBuffer::Buffer> lbb(lbuf->getBuffer());
-                if (lbb != 0) {
-                    const LayerBuffer::NativeBuffer& nbuf(lbb->getBuffer());
-                    snprintf(buffer, SIZE,
-                            "      "
-                            "mBuffer={w=%u, h=%u, f=%d, offset=%u, base=%p, fd=%d }\n",
-                            nbuf.img.w, nbuf.img.h, nbuf.img.format, nbuf.img.offset,
-                            nbuf.img.base, nbuf.img.fd);
-                }
-            }
-            result.append(buffer);
-            buffer[0] = 0;
             /*** Layer ***/
             Layer* const l = LayerBase::dynamicCast<Layer*>((LayerBase*)layer);
             if (l) {
index 92021d0..a242f1a 100644 (file)
@@ -180,7 +180,7 @@ public:
             }
 
             copybit_device_t* getBlitEngine() const;
-            overlay_device_t* getOverlayEngine() const;
+            overlay_control_device_t* getOverlayEngine() const;
             
 private:
     friend class BClient;
@@ -382,24 +382,16 @@ private:
 
 // ---------------------------------------------------------------------------
 
-class FreezeLock {
+class FreezeLock : public LightRefBase<FreezeLock> {
     SurfaceFlinger* mFlinger;
-    mutable volatile int32_t mCount;
 public:
     FreezeLock(SurfaceFlinger* flinger)
-        : mFlinger(flinger), mCount(0) {
+        : mFlinger(flinger) {
         mFlinger->incFreezeCount();
     }
     ~FreezeLock() {
         mFlinger->decFreezeCount();
     }
-    inline void incStrong(void*) const {
-        android_atomic_inc(&mCount);
-    }
-    inline void decStrong(void*) const {
-        if (android_atomic_dec(&mCount) == 1)
-             delete this;
-    }
 };
 
 // ---------------------------------------------------------------------------
index 2f617c4..0b4835e 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <GLES/gl.h>
 
-#include <corecg/SkMatrix.h>
+#include <core/SkMatrix.h>
 
 namespace android {
 
index 9527009..4a325ac 100644 (file)
@@ -136,6 +136,18 @@ sp<ICamera> Camera::remote()
     return mCamera;
 }
 
+status_t Camera::lock()
+{
+    if (mCamera != 0) return mCamera->lock();
+    return NO_INIT;
+}
+
+status_t Camera::unlock()
+{
+    if (mCamera != 0) return mCamera->unlock();
+    return NO_INIT;
+}
+
 // pass the buffered ISurface to the camera service
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
@@ -172,6 +184,13 @@ void Camera::stopPreview()
     mCamera->stopPreview();
 }
 
+// get preview state
+bool Camera::previewEnabled()
+{
+    LOGV("previewEnabled");
+    return mCamera->previewEnabled();
+}
+
 status_t Camera::autoFocus()
 {
     LOGV("autoFocus");
index 6a2dc6b..7b0922e 100644 (file)
@@ -35,7 +35,10 @@ enum {
     TAKE_PICTURE,
     SET_PARAMETERS,
     GET_PARAMETERS,
-    CONNECT
+    CONNECT,
+    LOCK,
+    UNLOCK,
+    PREVIEW_ENABLED
 };
 
 class BpCamera: public BpInterface<ICamera>
@@ -96,6 +99,16 @@ public:
         remote()->transact(STOP_PREVIEW, data, &reply);
     }
 
+    // check preview state
+    bool previewEnabled()
+    {
+        LOGV("previewEnabled");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        remote()->transact(PREVIEW_ENABLED, data, &reply);
+        return reply.readInt32();
+    }
+
     // auto focus
     status_t autoFocus()
     {
@@ -146,6 +159,20 @@ public:
         remote()->transact(CONNECT, data, &reply);
         return reply.readInt32();
     }
+    virtual status_t lock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        remote()->transact(LOCK, data, &reply);
+        return reply.readInt32();
+    }
+    virtual status_t unlock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        remote()->transact(UNLOCK, data, &reply);
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera");
@@ -194,6 +221,12 @@ status_t BnCamera::onTransact(
             stopPreview();
             return NO_ERROR;
         } break;
+        case PREVIEW_ENABLED: {
+            LOGV("PREVIEW_ENABLED");
+            CHECK_INTERFACE(ICamera, data, reply);
+            reply->writeInt32(previewEnabled());
+            return NO_ERROR;
+        } break;
         case AUTO_FOCUS: {
             LOGV("AUTO_FOCUS");
             CHECK_INTERFACE(ICamera, data, reply);
@@ -225,6 +258,16 @@ status_t BnCamera::onTransact(
             reply->writeInt32(connect(cameraClient));
             return NO_ERROR;
         } break;
+        case LOCK: {
+            CHECK_INTERFACE(ICamera, data, reply);
+            reply->writeInt32(lock());
+            return NO_ERROR;
+        } break;
+        case UNLOCK: {
+            CHECK_INTERFACE(ICamera, data, reply);
+            reply->writeInt32(unlock());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
index 59d1ea0..fed47c2 100644 (file)
@@ -27,7 +27,6 @@ namespace android {
 
 enum {
     DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction
-    SWAP_BUFFERS,
 };
 
 class BpOverlay : public BpInterface<IOverlay>
@@ -44,14 +43,6 @@ public:
         data.writeInterfaceToken(IOverlay::getInterfaceDescriptor());
         remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY);
     }
-
-    virtual ssize_t swapBuffers()
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOverlay::getInterfaceDescriptor());
-        remote()->transact(SWAP_BUFFERS, data, &reply);
-        return reply.readInt32();
-    }
 };
 
 IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay");
@@ -73,12 +64,6 @@ status_t BnOverlay::onTransact(
             destroy();
             return NO_ERROR;
         } break;
-        case SWAP_BUFFERS: {
-            CHECK_INTERFACE(IOverlay, data, reply);
-            ssize_t offset = swapBuffers();
-            reply->writeInt32(offset);
-            return NO_ERROR;
-        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
index c1c9596..54f78fe 100644 (file)
@@ -73,7 +73,7 @@ public:
         remote()->transact(UNREGISTER_BUFFERS, data, &reply);
     }
 
-    virtual sp<Overlay> createOverlay(
+    virtual sp<OverlayRef> createOverlay(
              uint32_t w, uint32_t h, int32_t format)
     {
         Parcel data, reply;
@@ -82,7 +82,7 @@ public:
         data.writeInt32(h);
         data.writeInt32(format);
         remote()->transact(CREATE_OVERLAY, data, &reply);
-        return Overlay::readFromParcel(reply);
+        return OverlayRef::readFromParcel(reply);
     }
 };
 
@@ -128,8 +128,8 @@ status_t BnSurface::onTransact(
             int w = data.readInt32();
             int h = data.readInt32();
             int f = data.readInt32();
-            sp<Overlay> o = createOverlay(w, h, w);
-            return Overlay::writeToParcel(reply, o);
+            sp<OverlayRef> o = createOverlay(w, h, w);
+            return OverlayRef::writeToParcel(reply, o);
         } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
index 2267c3e..a79950c 100644 (file)
 
 #include <utils/IMemory.h>
 #include <utils/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/MemoryHeapBase.h>
 
 #include <ui/IOverlay.h>
 #include <ui/Overlay.h>
 
+#include <hardware/overlay.h>
+
 namespace android {
 
-Overlay::Overlay(overlay_handle_t* handle, 
-        const sp<IOverlay>& o, const sp<IMemoryHeap>& heap, 
-        uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs)
-    : mOverlay(o), mHeap(heap), mCurrentBufferOffset(0), mOverlayHandle(handle),
-      mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs)
+Overlay::Overlay(const sp<OverlayRef>& overlayRef)
+    : mOverlayRef(overlayRef), mOverlayData(0), mStatus(NO_INIT)
 {
+    mOverlayData = NULL;
+    hw_module_t const* module;
+    if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
+        if (overlay_data_open(module, &mOverlayData) == NO_ERROR) {
+            mStatus = mOverlayData->initialize(mOverlayData,
+                    overlayRef->mOverlayHandle);
+        }
+    }
 }
 
-Overlay::Overlay(overlay_t* overlay, 
-        const sp<IOverlay>& o, const sp<IMemoryHeap>& heap)
-    : mOverlay(o), mHeap(heap) 
-{
-    mCurrentBufferOffset = 0; 
-    mOverlayHandle = overlay->getHandleRef(overlay);
-    mWidth = overlay->w;
-    mHeight = overlay->h;
-    mFormat = overlay->format; 
-    mWidthStride = overlay->w_stride;
-    mHeightStride = overlay->h_stride;
+Overlay::~Overlay() {
+    if (mOverlayData) {
+        overlay_data_close(mOverlayData);
+    }
 }
 
-
-Overlay::~Overlay() {
+overlay_buffer_t Overlay::dequeueBuffer()
+{
+    return mOverlayData->dequeueBuffer(mOverlayData);
 }
 
-void Overlay::destroy() {  
-    mOverlay->destroy();
+int Overlay::queueBuffer(overlay_buffer_t buffer)
+{
+    return mOverlayData->queueBuffer(mOverlayData, buffer);
 }
 
-status_t Overlay::swapBuffers() {
-    ssize_t result = mOverlay->swapBuffers();
-    if (result < 0)
-        return status_t(result);
-    mCurrentBufferOffset = result;
-    return NO_ERROR;
+void* Overlay::getBufferAddress(overlay_buffer_t buffer)
+{
+    return mOverlayData->getBufferAddress(mOverlayData, buffer);
 }
 
-overlay_handle_t const* Overlay::getHandleRef() const {
-    return mOverlayHandle;
+void Overlay::destroy() {  
+    mOverlayRef->mOverlayChanel->destroy();
 }
 
-size_t Overlay::getBufferOffset() const {
-    return mCurrentBufferOffset;
+status_t Overlay::getStatus() const {
+    return mStatus;
 }
 
-sp<IMemoryHeap> Overlay::getHeap() const {
-    return mHeap;
+overlay_handle_t const* Overlay::getHandleRef() const {
+    return mOverlayRef->mOverlayHandle;
 }
 
 uint32_t Overlay::getWidth() const {
-    return mWidth;
+    return mOverlayRef->mWidth;
 }
 
 uint32_t Overlay::getHeight() const {
-    return mHeight;
+    return mOverlayRef->mHeight;
 }
 
 int32_t Overlay::getFormat() const {
-    return mFormat;
+    return mOverlayRef->mFormat;
 }
 
 int32_t Overlay::getWidthStride() const {
-    return mWidthStride;
+    return mOverlayRef->mWidthStride;
 }
 
 int32_t Overlay::getHeightStride() const {
-    return mHeightStride;
+    return mOverlayRef->mHeightStride;
+}
+// ----------------------------------------------------------------------------
+
+OverlayRef::OverlayRef() 
+ : mOverlayHandle(0),
+    mWidth(0), mHeight(0), mFormat(0), mWidthStride(0), mHeightStride(0),
+    mOwnHandle(true)
+{    
+}
+
+OverlayRef::OverlayRef(overlay_handle_t const* handle, const sp<IOverlay>& chanel,
+         uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs)
+    : mOverlayHandle(handle), mOverlayChanel(chanel),
+    mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs),
+    mOwnHandle(false)
+{
+}
+
+OverlayRef::~OverlayRef()
+{
+    if (mOwnHandle) {
+        /* FIXME: handles should be promoted to "real" API and be handled by 
+         * the framework */
+        for (int i=0 ; i<mOverlayHandle->numFds ; i++) {
+            close(mOverlayHandle->fds[i]);
+        }
+        free((void*)mOverlayHandle);
+    }
 }
 
-sp<Overlay> Overlay::readFromParcel(const Parcel& data) {
-    sp<Overlay> result;
+sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) {
+    sp<OverlayRef> result;
     sp<IOverlay> overlay = IOverlay::asInterface(data.readStrongBinder());
     if (overlay != NULL) {
-        sp<IMemoryHeap> heap = IMemoryHeap::asInterface(data.readStrongBinder());
         uint32_t w = data.readInt32();
         uint32_t h = data.readInt32();
         uint32_t f = data.readInt32();
@@ -111,15 +139,21 @@ sp<Overlay> Overlay::readFromParcel(const Parcel& data) {
             handle->fds[i] = data.readFileDescriptor();
         for (int i=0 ; i<numint ; i++)
             handle->data[i] = data.readInt32();
-        result = new Overlay(handle, overlay, heap, w, h, f, ws, hs);
+        result = new OverlayRef();
+        result->mOverlayHandle = handle;
+        result->mOverlayChanel = overlay;
+        result->mWidth = w;
+        result->mHeight = h;
+        result->mFormat = f;
+        result->mWidthStride = ws;
+        result->mHeightStride = hs;
     }
     return result;
 }
 
-status_t Overlay::writeToParcel(Parcel* reply, const sp<Overlay>& o) {
+status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) {
     if (o != NULL) {
-        reply->writeStrongBinder(o->mOverlay->asBinder());
-        reply->writeStrongBinder(o->mHeap->asBinder());
+        reply->writeStrongBinder(o->mOverlayChanel->asBinder());
         reply->writeInt32(o->mWidth);
         reply->writeInt32(o->mHeight);
         reply->writeInt32(o->mFormat);
index 3e07f2b..26e694a 100644 (file)
@@ -21,8 +21,6 @@
 #include <utils/Debug.h>
 #include <utils/String8.h>
 #include <ui/Region.h>
-#include <corecg/SkRegion.h>
-#include <corecg/SkRect.h>
 
 namespace android {
 
index 59963c9..8251728 100644 (file)
@@ -119,19 +119,24 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size)
         // if it didn't work, let mmap() fail.
     }
 
-    void* base = (uint8_t*)mmap(0, size,
-            PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
-    if (base == MAP_FAILED) {
-        LOGE("mmap(fd=%d, size=%u) failed (%s)",
-                fd, uint32_t(size), strerror(errno));
-        close(fd);
-        return -errno;
+    if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+        void* base = (uint8_t*)mmap(0, size,
+                PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+        if (base == MAP_FAILED) {
+            LOGE("mmap(fd=%d, size=%u) failed (%s)",
+                    fd, uint32_t(size), strerror(errno));
+            close(fd);
+            return -errno;
+        }
+        //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
+        mBase = base;
+        mNeedUnmap = true;
+    } else  {
+        mBase = 0; // not MAP_FAILED
+        mNeedUnmap = false;
     }
-    //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
     mFD = fd;
-    mBase = base;
     mSize = size;
-    mNeedUnmap = true;
     return NO_ERROR;
 }
 
index 7c47081..3d4d4a2 100644 (file)
@@ -147,6 +147,7 @@ public class GpsLocationProvider extends LocationProviderImpl {
 
     // properties loaded from PROPERTIES_FILE
     private Properties mProperties;
+    private String mNtpServer;
 
     private Context mContext;
     private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
@@ -179,6 +180,7 @@ public class GpsLocationProvider extends LocationProviderImpl {
             FileInputStream stream = new FileInputStream(file);
             mProperties.load(stream);
             stream.close();
+            mNtpServer = mProperties.getProperty("NTP_SERVER", null);
         } catch (IOException e) {
             Log.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE, e);
         }
@@ -192,7 +194,7 @@ public class GpsLocationProvider extends LocationProviderImpl {
     public boolean requiresNetwork() {
         // We want updateNetworkState() to get called when the network state changes
         // for XTRA and NTP time injection support.
-        return true;
+        return (mNtpServer != null || native_supports_xtra());
     }
 
     public void updateNetworkState(int state) {
@@ -308,12 +310,14 @@ public class GpsLocationProvider extends LocationProviderImpl {
             mEventThread = new GpsEventThread();
             mEventThread.start();
 
-            // run network thread for NTP and XTRA support
-            if (mNetworkThread == null) {
-                mNetworkThread = new GpsNetworkThread();
-                mNetworkThread.start();
-            } else {
-                mNetworkThread.signal();
+            if (requiresNetwork()) {
+                // run network thread for NTP and XTRA support
+                if (mNetworkThread == null) {
+                    mNetworkThread = new GpsNetworkThread();
+                    mNetworkThread.start();
+                } else {
+                    mNetworkThread.signal();
+                }
             }
         } else {
             Log.w(TAG, "Failed to enable location provider");
@@ -560,7 +564,7 @@ public class GpsLocationProvider extends LocationProviderImpl {
 
         mLastFixTime = System.currentTimeMillis();
         // report time to first fix
-        if (mTTFF == 0) {
+        if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
             mTTFF = (int)(mLastFixTime - mFixRequestTime);
             if (Config.LOGD) Log.d(TAG, "TTFF: " + mTTFF);
 
@@ -763,25 +767,34 @@ public class GpsLocationProvider extends LocationProviderImpl {
                     synchronized (this) {
                         try {
                             if (!mNetworkAvailable) {
-                                if (Config.LOGD) Log.d(TAG, "NetworkThread wait for network");
+                                if (Config.LOGD) Log.d(TAG, 
+                                        "NetworkThread wait for network");
                                 wait();
                             } else if (waitTime > 0) {
-                                if (Config.LOGD) Log.d(TAG, "NetworkThread wait for " + waitTime + "ms");
+                                if (Config.LOGD) {
+                                    Log.d(TAG, "NetworkThread wait for " +
+                                            waitTime + "ms");
+                                }
                                 wait(waitTime);
                             }
                         } catch (InterruptedException e) {
-                            if (Config.LOGD) Log.d(TAG, "InterruptedException in GpsNetworkThread");
+                            if (Config.LOGD) {
+                                Log.d(TAG, "InterruptedException in GpsNetworkThread");
+                            }
                         }
                     }
                     waitTime = getWaitTime();
-                } while (mEnabled && ((!mXtraDownloadRequested && waitTime > 0) || !mNetworkAvailable));
+                } while (mEnabled && ((!mXtraDownloadRequested && waitTime > 0)
+                        || !mNetworkAvailable));
                 if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop");
                 
                 if (mEnabled) {
-                    if (mNextNtpTime <= System.currentTimeMillis()) {
-                        String ntpServer = mProperties.getProperty("NTP_SERVER", "pool.ntp.org"); 
-                        if (Config.LOGD) Log.d(TAG, "Requesting time from NTP server " + ntpServer);
-                        if (client.requestTime(ntpServer, 10000)) {
+                    if (mNtpServer != null && 
+                            mNextNtpTime <= System.currentTimeMillis()) {
+                        if (Config.LOGD) {
+                            Log.d(TAG, "Requesting time from NTP server " + mNtpServer);
+                        }
+                        if (client.requestTime(mNtpServer, 10000)) {
                             long time = client.getNtpTime();
                             long timeReference = client.getNtpTimeReference();
                             int certainty = (int)(client.getRoundTripTime()/2);
@@ -799,11 +812,13 @@ public class GpsLocationProvider extends LocationProviderImpl {
                     }
 
                     if ((mXtraDownloadRequested || 
-                            (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) && 
-                            xtraDownloader != null) {
+                            (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis()))
+                            && xtraDownloader != null) {
                         byte[] data = xtraDownloader.downloadXtraData();
                         if (data != null) {
-                            if (Config.LOGD) Log.d(TAG, "calling native_inject_xtra_data");
+                            if (Config.LOGD) {
+                                Log.d(TAG, "calling native_inject_xtra_data");
+                            }
                             native_inject_xtra_data(data, data.length);
                             mNextXtraTime = 0;
                             mXtraDownloadRequested = false;
@@ -827,7 +842,10 @@ public class GpsLocationProvider extends LocationProviderImpl {
 
         private long getWaitTime() {
             long now = System.currentTimeMillis();
-            long waitTime = mNextNtpTime - now;
+            long waitTime = Long.MAX_VALUE;
+            if (mNtpServer != null) {
+                waitTime = mNextNtpTime - now;
+            }
             if (mNextXtraTime != 0) {
                 long xtraWaitTime = mNextXtraTime - now;
                 if (xtraWaitTime < waitTime) {
index 6efbbf6..b8545a6 100644 (file)
@@ -28,10 +28,12 @@ import org.apache.http.conn.params.ConnRouteParams;
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.util.Properties;
+import java.util.Random;
 
 import android.content.Context;
 import android.net.Proxy;
 import android.net.http.AndroidHttpClient;
+import android.util.Config;
 import android.util.Log;
 
 /**
@@ -46,7 +48,7 @@ public class GpsXtraDownloader {
     private Context mContext;
     private String[] mXtraServers;
     // to load balance our server requests
-    private int mNextServerIndex = 0;
+    private int mNextServerIndex;
 
     GpsXtraDownloader(Context context, Properties properties) {
         mContext = context;
@@ -69,6 +71,10 @@ public class GpsXtraDownloader {
             if (server2 != null) mXtraServers[count++] = server2;
             if (server3 != null) mXtraServers[count++] = server3;
         }
+        
+        // randomize first server
+        Random random = new Random();
+        mNextServerIndex = random.nextInt(count);
     }
 
     byte[] downloadXtraData() {
@@ -100,6 +106,8 @@ public class GpsXtraDownloader {
 
     protected static byte[] doDownload(String url, boolean isProxySet, 
             String proxyHost, int proxyPort) {
+        if (Config.LOGD) Log.d(TAG, "Downloading XTRA data from " + url);
+
         AndroidHttpClient client = null;
         try {
             client = AndroidHttpClient.newInstance("Android");
@@ -121,7 +129,7 @@ public class GpsXtraDownloader {
             HttpResponse response = client.execute(req);
             StatusLine status = response.getStatusLine();
             if (status.getStatusCode() != 200) { // HTTP 200 is success.
-                Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+                if (Config.LOGD) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
                 return null;
             }
 
@@ -150,7 +158,7 @@ public class GpsXtraDownloader {
             }
             return body;
         } catch (Exception e) {
-            Log.d(TAG, "error " + e);
+            if (Config.LOGD) Log.d(TAG, "error " + e);
         } finally {
             if (client != null) {
                 client.close();
index 545d8c9..079c9c7 100644 (file)
@@ -103,6 +103,12 @@ public class LocationCache {
                 cellState.getLac(), cellState.getCid());
             Record record = mCellCache.lookup(primaryCellKey);
 
+            // Relax MCC/MNC condition
+            if (record == null) {
+                primaryCellKey = getCellCacheKey(-1, -1, cellState.getLac(), cellState.getCid());
+                record = mCellCache.lookup(primaryCellKey);
+            }
+
             if (record == null) {
                 // Make a server request if primary cell doesn't exist in DB
                 return false;
@@ -121,6 +127,14 @@ public class LocationCache {
                     String historicalCellKey = getCellCacheKey(historicalCell.getMcc(),
                         historicalCell.getMnc(), historicalCell.getLac(), historicalCell.getCid());
                     Record record = mCellCache.lookup(historicalCellKey);
+
+                    // Relax MCC/MNC condition
+                    if (record == null) {
+                        historicalCellKey = getCellCacheKey(-1, -1, historicalCell.getLac(),
+                            historicalCell.getCid());
+                        record = mCellCache.lookup(historicalCellKey);
+                    }
+
                     if (record != null && record.isValid()) {
                         mCellCentroid.addLocation(record.getLat(), record.getLng(),
                             record.getAccuracy(), record.getConfidence());
@@ -561,7 +575,6 @@ public class LocationCache {
             double cLng = getCentroidLng();
 
             int meanDistanceSum = 0;
-            int meanRadiiSum = 0;
             int smallestCircle = MAX_ACCURACY_ALLOWED;
             int smallestCircleDistance = MAX_ACCURACY_ALLOWED;
             float[] distance = new float[1];
@@ -577,11 +590,10 @@ public class LocationCache {
                     smallestCircle = mRadii[i];
                     smallestCircleDistance = (int)distance[0];
                 }
-                meanRadiiSum += mRadii[i];
             }
 
             if (outlierExists) {
-                return (meanDistanceSum + meanRadiiSum)/mNumber;
+                return meanDistanceSum/mNumber;
             } else {
                 return Math.max(smallestCircle, smallestCircleDistance);
             }
index 4efcdd5..179c630 100644 (file)
@@ -1167,6 +1167,10 @@ public class LocationMasfClient {
 
     private void addNeighborsToCellProfile(CellState cellState, ProtoBuf cellularProfile) {
         List<CellState.NeighborCell> neighbors = cellState.getNeighbors();
+
+        int mPrimaryMcc = cellState.getMcc();
+        int mPrimaryMnc = cellState.getMnc();
+
         if (neighbors != null) {
             for (CellState.NeighborCell neighbor : neighbors) {
                 ProtoBuf nCell = new ProtoBuf(GcellularMessageTypes.GCELL);
@@ -1176,6 +1180,12 @@ public class LocationMasfClient {
                 if (neighbor.getPsc() != -1) {
                     nCell.setInt(GCell.PRIMARY_SCRAMBLING_CODE, neighbor.getPsc());
                 }
+                if (mPrimaryMcc != -1) {
+                    nCell.setInt(GCell.MCC, mPrimaryMcc);
+                }
+                if (mPrimaryMnc != -1) {
+                    nCell.setInt(GCell.MNC, mPrimaryMnc);
+                }
                 cellularProfile.addProtoBuf(GCellularProfile.NEIGHBORS, nCell);
             }
         }
index fb325f9..52cf69f 100644 (file)
@@ -670,6 +670,35 @@ public class AudioManager {
     }
 
     /**
+     * Sets audio routing to the wired headset on or off.
+     *
+     * @param on set <var>true</var> to route audio to/from wired 
+     *           headset; <var>false</var> disable wired headset audio
+     * @hide
+     */
+    public void setWiredHeadsetOn(boolean on){
+        // A2DP has higher priority than wired headset, so headset connect/disconnect events
+        // should not affect A2DP routing
+        setRouting(MODE_NORMAL,
+                on ? ROUTE_HEADSET : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
+        setRouting(MODE_RINGTONE,
+                on ? ROUTE_HEADSET | ROUTE_SPEAKER : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
+        setRouting(MODE_IN_CALL,
+                on ? ROUTE_HEADSET : ROUTE_EARPIECE, ROUTE_ALL);
+    }
+
+    /**
+     * Checks whether audio routing to the wired headset is on or off.
+     *
+     * @return true if audio is being routed to/from wired headset;
+     *         false if otherwise
+     * @hide
+     */
+    public boolean isWiredHeadsetOn() {
+        return (getRouting(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true;
+    }
+
+    /**
      * Sets the microphone mute on or off.
      *
      * @param on set <var>true</var> to mute the microphone; 
index c246905..2b7656f 100644 (file)
@@ -269,7 +269,7 @@ public class AudioTrack
         // stream type
         if( (streamType != AudioManager.STREAM_ALARM) && (streamType != AudioManager.STREAM_MUSIC)
            && (streamType != AudioManager.STREAM_RING) && (streamType != AudioManager.STREAM_SYSTEM)
-           && (streamType != AudioManager.STREAM_VOICE_CALL) ) {
+           && (streamType != AudioManager.STREAM_VOICE_CALL) && (streamType != AudioManager.STREAM_NOTIFICATION) ) {
             throw (new IllegalArgumentException("Invalid stream type."));
         } else {
             mStreamType = streamType;
diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java
new file mode 100644 (file)
index 0000000..b9268d5
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * 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.media;
+
+
+import java.lang.ref.WeakReference;
+import java.lang.CloneNotSupportedException;
+
+import android.os.Looper;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * JetPlayer provides access to JET content playback and control.
+ * <p>
+ * Use <code>JetPlayer.getJetPlayer()</code> to get an instance of this class.
+ * There can only be one instance of this class at any one time.
+ * 
+ * @hide
+ */
+public class JetPlayer
+{    
+    //--------------------------------------------
+    // Constants
+    //------------------------
+    /**
+     * The maximum number of simultaneous tracks. Use {@link #getMaxTracks()} to
+     * access this value.
+     */
+    protected static int MAXTRACKS = 32;
+        
+    // These constants are to be kept in sync with the ones in include/media/JetPlayer.h
+    protected static final int JET_USERID_UPDATE           = 1;
+    protected static final int JET_NUMQUEUEDSEGMENT_UPDATE = 2;
+    protected static final int JET_PAUSE_UPDATE            = 3;
+
+    
+    //--------------------------------------------
+    // Member variables
+    //------------------------
+    private EventHandler            mNativeEventHandler = null;
+    
+    /**
+     * Lock to protect event listener updates against event notifications
+     */
+    protected final Object mStatusListenerLock = new Object();
+    
+    protected JetStatusUpdateListener mJetStatusUpdateListener = null;
+    
+    protected static JetPlayer singletonRef;
+    
+    
+    //--------------------------------
+    // Used exclusively by native code
+    //--------------------
+    /** 
+     * Accessed by native methods: provides access to C++ JetPlayer object 
+     */
+    @SuppressWarnings("unused")
+    private int mNativePlayerInJavaObj;
+
+    
+    //--------------------------------------------
+    // Constructor, finalize
+    //------------------------
+    public static JetPlayer getJetPlayer() {
+        if (singletonRef == null)
+            singletonRef = new JetPlayer();
+        return singletonRef;
+    }
+    
+    
+    public Object clone() throws CloneNotSupportedException {
+        // JetPlayer is a singleton class,
+        // so you can't clone a JetPlayer instance
+        throw new CloneNotSupportedException();    
+    }
+    
+
+    private JetPlayer() {
+                
+        native_setup(new WeakReference<JetPlayer>(this),
+                JetPlayer.getMaxTracks(), 
+                1200); //TODO parametrize this (?)
+    }
+    
+    
+    protected void finalize() { 
+        native_finalize(); 
+    }
+    
+    
+    public void release() {
+        native_release();
+    }
+    
+    
+    private void createNativeEventHandler() {
+        Looper looper;
+        if ((looper = Looper.myLooper()) != null) {
+            mNativeEventHandler = new EventHandler(this, looper);
+        } else if ((looper = Looper.getMainLooper()) != null) {
+            mNativeEventHandler = new EventHandler(this, looper);
+        } else {
+            mNativeEventHandler = null;
+        }
+    }
+    
+    
+    //--------------------------------------------
+    // Getters
+    //------------------------
+    /**
+     * Returns the maximum number of simultaneous MIDI tracks supported by the Jet player
+     */
+    public static int getMaxTracks() {
+        return JetPlayer.MAXTRACKS;
+    }
+    
+    
+    //--------------------------------------------
+    // Jet functionality
+    //------------------------
+    public boolean openJetFile(String path) {
+        return native_openJetFile(path);
+    }
+    
+    
+    public boolean closeJetFile() {
+        return native_closeJetFile();
+    }
+    
+    
+    public boolean play() {
+        return native_playJet();
+    }
+    
+    
+    public boolean pause() {
+        return native_pauseJet();
+    }
+    
+    
+    public boolean queueJetSegment(int segmentNum, int libNum, int repeatCount,
+        int transpose, int muteFlags, byte userID) {
+        return native_queueJetSegment(segmentNum, libNum, repeatCount, 
+                transpose, muteFlags, userID);
+    }
+    
+    
+    public boolean queueJetSegmentMuteArray(int segmentNum, int libNum, int repeatCount,
+            int transpose, boolean[] muteArray, byte userID) {
+        if (muteArray.length != JetPlayer.getMaxTracks()) {
+            return false;
+        }
+        return native_queueJetSegmentMuteArray(segmentNum, libNum, repeatCount,
+                transpose, muteArray, userID);
+    }
+    
+    
+    public boolean setMuteFlags(int muteFlags, boolean sync) {
+        return native_setMuteFlags(muteFlags, sync);
+    }
+    
+    
+    public boolean setMuteArray(boolean[] muteArray, boolean sync) {
+        if(muteArray.length != JetPlayer.getMaxTracks())
+            return false;
+        return native_setMuteArray(muteArray, sync);
+    }
+    
+    
+    public boolean setMuteFlag(int trackId, boolean muteFlag, boolean sync) {
+        return native_setMuteFlag(trackId, muteFlag, sync);
+    }
+    
+    
+    public boolean triggerClip(int clipId) {
+        return native_triggerClip(clipId);
+    }
+    
+    
+     
+    //---------------------------------------------------------
+    // Internal class to handle events posted from native code
+    //------------------------
+    private class EventHandler extends Handler
+    {
+        private JetPlayer mJet;
+
+        public EventHandler(JetPlayer jet, Looper looper) {
+            super(looper);
+            mJet = jet;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+            case JET_USERID_UPDATE:
+                synchronized (mStatusListenerLock) {
+                    if (mJetStatusUpdateListener != null) {
+                        mJetStatusUpdateListener.onJetUserIdUpdate(msg.arg1, msg.arg2);
+                    }
+                }
+                return;
+            case JET_NUMQUEUEDSEGMENT_UPDATE:
+                synchronized (mStatusListenerLock) {
+                    if (mJetStatusUpdateListener != null) {
+                        mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(msg.arg1);
+                    }
+                }
+                return;
+            case JET_PAUSE_UPDATE:
+                synchronized (mStatusListenerLock) {
+                    if (mJetStatusUpdateListener != null)
+                        mJetStatusUpdateListener.onJetPauseUpdate(msg.arg1);
+                }
+                return;
+
+
+            default:
+                loge("Unknown message type " + msg.what);
+                return;
+            }
+        }
+    }
+    
+    
+    //--------------------------------------------
+    // Jet event listener
+    //------------------------
+    public void setStatusUpdateListener(JetStatusUpdateListener listener) {
+        synchronized(mStatusListenerLock) {
+            mJetStatusUpdateListener = listener;
+        }
+        
+        if ((listener != null) && (mNativeEventHandler == null)) {
+            createNativeEventHandler();
+        }
+    }
+    
+    /**
+     * Handles the notification when the JET segment userID is updated.
+     */
+    public interface JetStatusUpdateListener {
+        /**
+         * Callback for when JET's currently playing segment userID is updated.
+         * 
+         * @param userId the ID of the currently playing segment
+         * @param repeatCount the repetition count for the segment (0 means it plays once)
+         */
+        void onJetUserIdUpdate(int userId, int repeatCount);
+        
+        /**
+         * Callback for when JET's number of queued segments is updated.
+         * 
+         * @param nbSegments the number of segments in the JET queue
+         */
+        void onJetNumQueuedSegmentUpdate(int nbSegments);
+        
+        /**
+         * Callback for when JET pause state is updated.
+         * 
+         * @param paused indicates whether JET is paused or not
+         */
+        void onJetPauseUpdate(int paused);
+    };
+    
+    
+    //--------------------------------------------
+    // Native methods
+    //------------------------
+    private native final boolean native_setup(Object Jet_this,
+                int maxTracks, int trackBufferSize);
+    private native final void    native_finalize();
+    private native final void    native_release();
+    private native final boolean native_openJetFile(String pathToJetFile);
+    private native final boolean native_closeJetFile();
+    private native final boolean native_playJet();
+    private native final boolean native_pauseJet();
+    private native final boolean native_queueJetSegment(int segmentNum, int libNum,
+            int repeatCount, int transpose, int muteFlags, byte userID);
+    private native final boolean native_queueJetSegmentMuteArray(int segmentNum, int libNum, 
+            int repeatCount, int transpose, boolean[] muteArray, byte userID);
+    private native final boolean native_setMuteFlags(int muteFlags, boolean sync);
+    private native final boolean native_setMuteArray(boolean[]muteArray, boolean sync);
+    private native final boolean native_setMuteFlag(int trackId, boolean muteFlag, boolean sync);
+    private native final boolean native_triggerClip(int clipId); 
+    
+    //---------------------------------------------------------
+    // Called exclusively by native code
+    //--------------------
+    @SuppressWarnings("unused")
+    private static void postEventFromNative(Object jetplayer_ref,
+            int what, int arg1, int arg2) {
+        
+        JetPlayer jet = (JetPlayer)((WeakReference)jetplayer_ref).get();
+
+        if( (jet!=null) && (jet.mNativeEventHandler!=null) ){
+            Message m = jet.mNativeEventHandler.obtainMessage(what, arg1, arg2, null);
+            jet.mNativeEventHandler.sendMessage(m);
+        }
+    }
+    
+    //---------------------------------------------------------
+    // Utils
+    //--------------------
+    private final static String TAG = "JetPlayer-J";
+    
+    private static void logd(String msg) {
+        Log.d(TAG, "[ android.media.JetPlayer ] " + msg);
+    }
+    
+    private static void loge(String msg) {
+        Log.e(TAG, "[ android.media.JetPlayer ] " + msg);
+    }
+}
index 9763bc7..c1a0c21 100644 (file)
@@ -233,10 +233,16 @@ public class MediaMetadataRetriever
     public static final int METADATA_KEY_TITLE           = 7;
     public static final int METADATA_KEY_YEAR            = 8;
     public static final int METADATA_KEY_DURATION        = 9;
-    public static final int METADATA_KEY_NUM_TRACKS     = 10;
-    public static final int METADATA_KEY_IS_DRM_CRIPPLED= 11;
-    public static final int METADATA_KEY_CODEC          = 12;
-    public static final int METADATA_KEY_RATING         = 13;
-    public static final int METADATA_KEY_COMMENT        = 14;
-    public static final int METADATA_KEY_COPYRIGHT      = 15;
+    public static final int METADATA_KEY_NUM_TRACKS      = 10;
+    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
+    public static final int METADATA_KEY_CODEC           = 12;
+    public static final int METADATA_KEY_RATING          = 13;
+    public static final int METADATA_KEY_COMMENT         = 14;
+    public static final int METADATA_KEY_COPYRIGHT       = 15;
+    public static final int METADATA_KEY_BIT_RATE        = 16;
+    public static final int METADATA_KEY_FRAME_RATE      = 17;
+    public static final int METADATA_KEY_VIDEO_FORMAT    = 18;
+    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
+    public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
+    // Add more here...
 }
diff --git a/media/java/android/media/ResampleInputStream.java b/media/java/android/media/ResampleInputStream.java
new file mode 100644 (file)
index 0000000..e26eae5
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.media;
+
+import android.util.Config;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * ResampleInputStream
+ * @hide
+ */
+public final class ResampleInputStream extends InputStream
+{    
+    static {
+        System.loadLibrary("media_jni");
+    }
+    
+    private final static String TAG = "ResampleInputStream";
+    
+    // pcm input stream
+    private InputStream mInputStream;
+
+    // sample rates, assumed to be normalized
+    private final int mRateIn;
+    private final int mRateOut;
+    
+    // input pcm data
+    private byte[] mBuf;
+    private int mBufCount;
+    
+    // length of 2:1 fir
+    private static final int mFirLength = 29;
+    
+    // helper for bytewise read()
+    private final byte[] mOneByte = new byte[1];
+    
+    /**
+     * Create a new ResampleInputStream, which converts the sample rate
+     * @param inputStream InputStream containing 16 bit PCM.
+     * @param rateIn the input sample rate.
+     * @param rateOut the output sample rate.
+     * This only handles rateIn == rateOut / 2 for the moment.
+     */
+    public ResampleInputStream(InputStream inputStream, int rateIn, int rateOut) {
+        // only support 2:1 at the moment
+        if (rateIn != 2 * rateOut) throw new IllegalArgumentException("only support 2:1 at the moment");
+        rateIn = 2;
+        rateOut = 1;
+
+        mInputStream = inputStream;
+        mRateIn = rateIn;
+        mRateOut = rateOut;
+    }
+
+    @Override
+    public int read() throws IOException {
+        int rtn = read(mOneByte, 0, 1);
+        return rtn == 1 ? (0xff & mOneByte[0]) : -1;
+    }
+    
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read(byte[] b, int offset, int length) throws IOException {
+        if (mInputStream == null) throw new IllegalStateException("not open");
+
+        // ensure that mBuf is big enough to cover requested 'length'
+        int nIn = ((length / 2) * mRateIn / mRateOut + mFirLength) * 2;
+        if (mBuf == null) {
+            mBuf = new byte[nIn];
+        } else if (nIn > mBuf.length) {
+            byte[] bf = new byte[nIn];
+            System.arraycopy(mBuf, 0, bf, 0, mBufCount);
+            mBuf = bf;
+        }
+        
+        // read until we have enough data for at least one output sample
+        while (true) {
+            int len = ((mBufCount / 2 - mFirLength) * mRateOut / mRateIn) * 2;
+            if (len > 0) {
+                length = len < length ? len : (length / 2) * 2;
+                break;
+            }
+            // TODO: should mBuf.length below be nIn instead?
+            int n = mInputStream.read(mBuf, mBufCount, mBuf.length - mBufCount);
+            if (n == -1) return -1;
+            mBufCount += n;
+        }
+
+        // resample input data
+        fir21(mBuf, 0, b, offset, length / 2);
+        
+        // move any unused bytes to front of mBuf
+        int nFwd = length * mRateIn / mRateOut;
+        mBufCount -= nFwd;
+        if (mBufCount > 0) System.arraycopy(mBuf, nFwd, mBuf, 0, mBufCount);
+        
+        return length;
+    }
+
+/*
+    @Override
+    public int available() throws IOException {
+        int nsamples = (mIn - mOut + mInputStream.available()) / 2;
+        return ((nsamples - mFirLength) * mRateOut / mRateIn) * 2;
+    }
+*/
+
+    @Override
+    public void close() throws IOException {
+        try {
+            if (mInputStream != null) mInputStream.close();
+        } finally {
+            mInputStream = null;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mInputStream != null) {
+            close();
+            throw new IllegalStateException("someone forgot to close ResampleInputStream");
+        }
+    }
+
+    //
+    // fir filter code JNI interface
+    //
+    private static native void fir21(byte[] in, int inOffset,
+            byte[] out, int outOffset, int npoints);
+
+}
index cb0fc83..3620494 100644 (file)
@@ -7,7 +7,8 @@ LOCAL_SRC_FILES:= \
        android_media_MediaRecorder.cpp \
        android_media_MediaScanner.cpp \
        android_media_MediaMetadataRetriever.cpp \
-       android_media_AmrInputStream.cpp
+       android_media_AmrInputStream.cpp \
+       android_media_ResampleInputStream.cpp
 
 LOCAL_SHARED_LIBRARIES := \
        libopencoreplayer \
index 5b0ecb8..4624a18 100644 (file)
@@ -21,7 +21,7 @@
 #include <assert.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
-#include <graphics/SkBitmap.h>
+#include <core/SkBitmap.h>
 #include <media/mediametadataretriever.h>
 #include <private/media/VideoFrame.h>
 
index 8083882..5562254 100644 (file)
@@ -519,6 +519,7 @@ extern int register_android_media_MediaRecorder(JNIEnv *env);
 extern int register_android_media_MediaScanner(JNIEnv *env);
 extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
 extern int register_android_media_AmrInputStream(JNIEnv *env);
+extern int register_android_media_ResampleInputStream(JNIEnv *env);
 
 jint JNI_OnLoad(JavaVM* vm, void* reserved)
 {
@@ -556,6 +557,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
         goto bail;
     }
 
+    if (register_android_media_ResampleInputStream(env) < 0) {
+        LOGE("ERROR: ResampleInputStream native registration failed\n");
+        goto bail;
+    }
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;
 
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
new file mode 100644 (file)
index 0000000..0247cdb
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "ResampleInputStream"
+#include "utils/Log.h"
+
+#include <media/mediarecorder.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utils/threads.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+
+//
+// helper function to throw an exception
+//
+static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
+    if (jclass cls = env->FindClass(ex)) {
+        char msg[1000];
+        sprintf(msg, fmt, data);
+        env->ThrowNew(cls, msg);
+        env->DeleteLocalRef(cls);
+    }
+}
+
+
+#define FIR_COEF(coef) (short)(0x10000 * coef)
+static const short fir21[] = {
+    FIR_COEF(-0.006965742326),
+    FIR_COEF(-0.008428945737),
+    FIR_COEF( 0.004241280174),
+    FIR_COEF( 0.022141096893),
+    FIR_COEF( 0.018765669437),
+    FIR_COEF(-0.009871891152),
+    FIR_COEF(-0.024842433247),
+    FIR_COEF( 0.006121772058),
+    FIR_COEF( 0.045890841611),
+    FIR_COEF( 0.021573503509),
+    FIR_COEF(-0.059681984668),
+    FIR_COEF(-0.076036275138),
+    FIR_COEF( 0.072405390275),
+    FIR_COEF( 0.308255674582),
+    FIR_COEF( 0.424321210495),
+    FIR_COEF( 0.308255674582),
+    FIR_COEF( 0.072405390275),
+    FIR_COEF(-0.076036275138),
+    FIR_COEF(-0.059681984668),
+    FIR_COEF( 0.021573503509),
+    FIR_COEF( 0.045890841611),
+    FIR_COEF( 0.006121772058),
+    FIR_COEF(-0.024842433247),
+    FIR_COEF(-0.009871891152),
+    FIR_COEF( 0.018765669437),
+    FIR_COEF( 0.022141096893),
+    FIR_COEF( 0.004241280174),
+    FIR_COEF(-0.008428945737),
+    FIR_COEF(-0.006965742326)
+};
+static const int nFir21 = sizeof(fir21) / sizeof(fir21[0]);
+
+static const int BUF_SIZE = 2048;
+
+
+static void android_media_ResampleInputStream_fir21(JNIEnv *env, jclass clazz,
+         jbyteArray jIn,  jint jInOffset,
+         jbyteArray jOut, jint jOutOffset,
+         jint jNpoints) {
+    
+    // safety first!
+    if (nFir21 + jNpoints > BUF_SIZE) {
+        throwException(env, "java/lang/IllegalArgumentException",
+                "FIR+data too long %d", nFir21 + jNpoints);
+        return;
+    }
+    
+    // get input data
+    short in[BUF_SIZE];
+    env->GetByteArrayRegion(jIn, jInOffset, (jNpoints * 2 + nFir21 - 1) * 2, (jbyte*)in);
+    
+    // compute filter
+    short out[BUF_SIZE];
+    for (int i = 0; i < jNpoints; i++) {
+        long sum = 0;
+        const short* firp = &fir21[0];
+        const short* inp = &in[i * 2];
+        for (int n = nFir21; --n >= 0; ) {
+            sum += ((long)*firp++) * ((long)*inp++);
+        }
+        out[i] = (short)(sum >> 16);
+    }
+
+    // save new values
+    env->SetByteArrayRegion(jOut, jOutOffset, jNpoints * 2, (jbyte*)out);
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"fir21", "([BI[BII)V", (void*)android_media_ResampleInputStream_fir21},
+};
+
+
+int register_android_media_ResampleInputStream(JNIEnv *env)
+{
+    const char* const kClassPathName = "android/media/ResampleInputStream";
+    jclass clazz;
+
+    clazz = env->FindClass(kClassPathName);
+    if (clazz == NULL) {
+        LOGE("Can't find %s", kClassPathName);
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+            kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
index 650684a..2a697b9 100644 (file)
@@ -16,10 +16,11 @@ LOCAL_SRC_FILES:= \
        mediarecorder.cpp \
        IMediaMetadataRetriever.cpp \
        mediametadataretriever.cpp \
-       ToneGenerator.cpp
+       ToneGenerator.cpp \
+       JetPlayer.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-       libui libcutils libutils
+       libui libcutils libutils libsonivox
 
 LOCAL_MODULE:= libmedia
 
index d4f2e5a..ce65312 100644 (file)
@@ -231,20 +231,22 @@ status_t AudioTrack::set(
     mAudioTrack = track;
     mCblkMemory = cblk;
     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
+    mCblk->out = 1;
+    // Update buffer size in case it has been limited by AudioFlinger during track creation
+    mFrameCount = mCblk->frameCount;
     if (sharedBuffer == 0) {
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
     } else {
         mCblk->buffers = sharedBuffer->pointer();
+         // Force buffer full condition as data is already present in shared memory
+        mCblk->stepUser(mFrameCount);
     }
-    mCblk->out = 1;
     mCblk->volume[0] = mCblk->volume[1] = 0x1000;
     mVolume[LEFT] = 1.0f;
     mVolume[RIGHT] = 1.0f;
     mSampleRate = sampleRate;
     mStreamType = streamType;
     mFormat = format;
-    // Update buffer size in case it has been limited by AudioFlinger during track creation
-    mFrameCount = mCblk->frameCount;
     mChannelCount = channelCount;
     mSharedBuffer = sharedBuffer;
     mMuted = false;
@@ -327,11 +329,6 @@ void AudioTrack::start()
      }
 
     if (android_atomic_or(1, &mActive) == 0) {
-        if (mSharedBuffer != 0) {
-             // Force buffer full condition as data is already present in shared memory
-            mCblk->user = mFrameCount;
-            mCblk->flowControlFlag = 0;
-        }
         mNewPosition = mCblk->server + mUpdatePeriod;
         if (t != 0) {
            t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
@@ -467,7 +464,6 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount
     }
 
     if (loopStart >= loopEnd ||
-        loopStart < cblk->user ||
         loopEnd - loopStart > mFrameCount) {
         LOGW("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user);
         return BAD_VALUE;
@@ -958,12 +954,8 @@ uint32_t audio_track_cblk_t::framesAvailable_l()
     uint32_t s = this->server;
 
     if (out) {
-        if (u < loopEnd) {
-            return s + frameCount - u;
-        } else {
-            uint32_t limit = (s < loopStart) ? s : loopStart;
-            return limit + frameCount - u;
-        }
+        uint32_t limit = (s < loopStart) ? s : loopStart;
+        return limit + frameCount - u;
     } else {
         return frameCount + u - s;
     }
index 615ae37..85b5944 100644 (file)
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/Parcel.h>
-#include <graphics/SkBitmap.h>
+#include <SkBitmap.h>
 #include <media/IMediaMetadataRetriever.h>
 
 namespace android {
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
new file mode 100644 (file)
index 0000000..f0edf88
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "JetPlayer-C"
+
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <media/JetPlayer.h>
+
+
+#ifdef HAVE_GETTID
+static pid_t myTid() { return gettid(); }
+#else
+static pid_t myTid() { return getpid(); }
+#endif
+
+
+namespace android
+{
+
+static const int MIX_NUM_BUFFERS = 4;
+static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) :
+        mEventCallback(NULL),
+        mJavaJetPlayerRef(javaJetPlayer),
+        mTid(-1),
+        mRender(false),
+        mPaused(false),
+        mMaxTracks(maxTracks),
+        mEasData(NULL),
+        mEasJetFileLoc(NULL),
+        mAudioTrack(NULL),
+        mTrackBufferSize(trackBufferSize)
+{
+    LOGV("JetPlayer constructor");
+    mPreviousJetStatus.currentUserID = -1;
+    mPreviousJetStatus.segmentRepeatCount = -1;
+    mPreviousJetStatus.numQueuedSegments = -1;
+    mPreviousJetStatus.paused = true;
+}
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::~JetPlayer()
+{
+    LOGV("~JetPlayer");
+    release();
+
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::init()
+{
+    //Mutex::Autolock lock(&mMutex);
+
+    EAS_RESULT result;
+
+    // retrieve the EAS library settings
+    if (pLibConfig == NULL)
+        pLibConfig = EAS_Config();
+    if (pLibConfig == NULL) {
+        LOGE("JetPlayer::init(): EAS library configuration could not be retrieved, aborting.");
+        return EAS_FAILURE;
+    }
+
+    // init the EAS library
+    result = EAS_Init(&mEasData);
+    if( result != EAS_SUCCESS) {
+        LOGE("JetPlayer::init(): Error initializing Sonivox EAS library, aborting.");
+        mState = EAS_STATE_ERROR;
+        return result;
+    }
+    // init the JET library
+    result = JET_Init(mEasData, NULL, 0);
+    if( result != EAS_SUCCESS) {
+        LOGE("JetPlayer::init(): Error initializing JET library, aborting.");
+        mState = EAS_STATE_ERROR;
+        return result;
+    }
+
+    // create the output AudioTrack
+    mAudioTrack = new AudioTrack();
+    mAudioTrack->set(AudioTrack::MUSIC,  //TODO parametrize this
+            pLibConfig->sampleRate,
+            1, // format = PCM 16bits per sample,
+            pLibConfig->numChannels,
+            mTrackBufferSize,
+            0);
+
+    // create render and playback thread
+    {
+        Mutex::Autolock l(mMutex);
+        LOGV("JetPlayer::init(): trying to start render thread");
+        createThreadEtc(renderThread, this, "jetRenderThread", ANDROID_PRIORITY_AUDIO);
+        mCondition.wait(mMutex);
+    }
+    if (mTid > 0) {
+        // render thread started, we're ready
+        LOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid);
+        mState = EAS_STATE_READY;
+    } else {
+        LOGE("JetPlayer::init(): failed to start render thread.");
+        mState = EAS_STATE_ERROR;
+        return EAS_FAILURE;
+    }
+
+    return EAS_SUCCESS;
+}
+
+void JetPlayer::setEventCallback(jetevent_callback eventCallback)
+{
+    Mutex::Autolock l(mMutex);
+    mEventCallback = eventCallback;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::release()
+{
+    LOGV("JetPlayer::release()");
+    Mutex::Autolock lock(mMutex);
+    mPaused = true;
+    mRender = false;
+    if (mEasData) {
+        JET_Pause(mEasData);
+        JET_CloseFile(mEasData);
+        JET_Shutdown(mEasData);
+        EAS_Shutdown(mEasData);
+    }
+    if (mEasJetFileLoc) {
+        free(mEasJetFileLoc);
+        mEasJetFileLoc = NULL;
+    }
+    if (mAudioTrack) {
+        mAudioTrack->stop();
+        mAudioTrack->flush();
+        delete mAudioTrack;
+        mAudioTrack = NULL;
+    }
+    if (mAudioBuffer) {
+        delete mAudioBuffer;
+        mAudioBuffer = NULL;
+    }
+    mEasData = NULL;
+    
+    return EAS_SUCCESS;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::renderThread(void* p) {
+
+    return ((JetPlayer*)p)->render();
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::render() {
+    EAS_RESULT result = EAS_FAILURE;
+    EAS_I32 count;
+    int temp;
+    bool audioStarted = false;
+
+    LOGV("JetPlayer::render(): entering");
+
+    // allocate render buffer
+    mAudioBuffer = 
+        new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS];
+    if (!mAudioBuffer) {
+        LOGE("JetPlayer::render(): mAudioBuffer allocate failed");
+        goto threadExit;
+    }
+
+    // signal main thread that we started
+    {
+        Mutex::Autolock l(mMutex);
+        mTid = myTid();
+        LOGV("JetPlayer::render(): render thread(%d) signal", mTid);
+        mCondition.signal();
+    }
+
+   while (1) {
+        mMutex.lock(); // [[[[[[[[ LOCK ---------------------------------------
+
+        // nothing to render, wait for client thread to wake us up
+        while (!mRender)
+        {
+            LOGV("JetPlayer::render(): signal wait");
+            mCondition.wait(mMutex);
+            LOGV("JetPlayer::render(): signal rx'd");
+        }
+        
+        // render midi data into the input buffer
+        int num_output = 0;
+        EAS_PCM* p = mAudioBuffer;
+        for (int i = 0; i < MIX_NUM_BUFFERS; i++) {
+            result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
+            if (result != EAS_SUCCESS) {
+                LOGE("JetPlayer::render(): EAS_Render returned error %ld", result);
+            }
+            p += count * pLibConfig->numChannels;
+            num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
+        }
+
+        // update playback state
+        //LOGV("JetPlayer::render(): updating state");
+        JET_Status(mEasData, &mJetStatus);
+        fireEventOnStatusChange();
+        mPaused = mJetStatus.paused;
+
+        mMutex.unlock(); // UNLOCK ]]]]]]]] -----------------------------------
+
+        // check audio output track
+        if (mAudioTrack == NULL) {
+            LOGE("JetPlayer::render(): output AudioTrack was not created");
+            goto threadExit;
+        }
+
+        // Write data to the audio hardware
+        //LOGV("JetPlayer::render(): writing to audio output");
+        if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) {
+            LOGE("JetPlayer::render(): Error in writing:%d",temp);
+            return temp;
+        }
+
+        // start audio output if necessary
+        if (!audioStarted) {
+            LOGV("JetPlayer::render(): starting audio playback");
+            mAudioTrack->start();
+            audioStarted = true;
+        }
+
+    }//while (1)
+
+threadExit:
+    mAudioTrack->flush();
+    if (mAudioBuffer) {
+        delete [] mAudioBuffer;
+        mAudioBuffer = NULL;
+    }
+    mMutex.lock();
+    mTid = -1;
+    mCondition.signal();
+    mMutex.unlock();
+    return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// fire up an event if any of the status fields has changed
+// precondition: mMutex locked
+void JetPlayer::fireEventOnStatusChange()
+{
+    if(  (mJetStatus.currentUserID      != mPreviousJetStatus.currentUserID)
+       ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) {
+        if(mEventCallback)  {
+            mEventCallback(
+                JetPlayer::JET_USERID_UPDATE,
+                mJetStatus.currentUserID,
+                mJetStatus.segmentRepeatCount,
+                mJavaJetPlayerRef);
+        }
+        mPreviousJetStatus.currentUserID      = mJetStatus.currentUserID;
+        mPreviousJetStatus.segmentRepeatCount = mJetStatus.segmentRepeatCount;
+    }
+
+    if(mJetStatus.numQueuedSegments != mPreviousJetStatus.numQueuedSegments) {
+        if(mEventCallback)  {
+            mEventCallback(
+                JetPlayer::JET_NUMQUEUEDSEGMENT_UPDATE,
+                mJetStatus.numQueuedSegments,
+                -1,
+                mJavaJetPlayerRef);
+        }
+        mPreviousJetStatus.numQueuedSegments  = mJetStatus.numQueuedSegments;
+    }
+
+    if(mJetStatus.paused != mPreviousJetStatus.paused) {
+        if(mEventCallback)  {
+            mEventCallback(JetPlayer::JET_PAUSE_UPDATE,
+                mJetStatus.paused,
+                -1,
+                mJavaJetPlayerRef);
+        }
+        mPreviousJetStatus.paused = mJetStatus.paused;
+    }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::openFile(const char* path)
+{
+    LOGV("JetPlayer::openFile(): path=%s", path);
+
+    Mutex::Autolock lock(mMutex);
+
+    mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE));
+    memset(mJetFilePath, 0, 256);
+    strncpy(mJetFilePath, path, strlen(path));
+    mEasJetFileLoc->path = mJetFilePath;
+
+    mEasJetFileLoc->fd = 0;
+    mEasJetFileLoc->length = 0;
+    mEasJetFileLoc->offset = 0;
+
+    EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc);
+    if(result != EAS_SUCCESS)
+        mState = EAS_STATE_ERROR;
+    else
+        mState = EAS_STATE_OPEN;
+    return( result );
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::closeFile()
+{
+    Mutex::Autolock lock(mMutex);
+    return JET_CloseFile(mEasData);
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::play()
+{
+    LOGV("JetPlayer::play(): entering");
+    Mutex::Autolock lock(mMutex);
+
+    EAS_RESULT result = JET_Play(mEasData);
+
+    mPaused = false;
+    mRender = true;
+
+    JET_Status(mEasData, &mJetStatus);
+    this->dumpJetStatus(&mJetStatus);
+    
+    fireEventOnStatusChange();
+
+    // wake up render thread
+    LOGV("JetPlayer::play(): wakeup render thread");
+    mCondition.signal();
+
+    return result;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::pause()
+{
+    Mutex::Autolock lock(mMutex);
+    mPaused = true;
+    EAS_RESULT result = JET_Pause(mEasData);
+
+    mRender = false;
+
+    JET_Status(mEasData, &mJetStatus);
+    this->dumpJetStatus(&mJetStatus);
+    fireEventOnStatusChange();
+
+
+    return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
+        EAS_U32 muteFlags, EAS_U8 userID)
+{
+    LOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d",
+        segmentNum, libNum, repeatCount, transpose);
+    Mutex::Autolock lock(mMutex);
+    return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync)
+{
+    Mutex::Autolock lock(mMutex);
+    return JET_SetMuteFlags(mEasData, muteFlags, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync)
+{
+    Mutex::Autolock lock(mMutex);
+    return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::triggerClip(int clipId)
+{
+    LOGV("JetPlayer::triggerClip clipId=%d", clipId);
+    Mutex::Autolock lock(mMutex);
+    return JET_TriggerClip(mEasData, clipId);
+}
+
+//-------------------------------------------------------------------------------------------------
+void JetPlayer::dump()
+{
+    LOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path);
+}
+
+void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus)
+{
+    if(pJetStatus!=NULL)
+        LOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d",
+                pJetStatus->currentUserID, pJetStatus->segmentRepeatCount,
+                pJetStatus->numQueuedSegments, pJetStatus->paused);
+    else
+        LOGE(">> JET player status is NULL");
+}
+
+
+} // end namespace android
+
index 0dee1f6..584d135 100644 (file)
@@ -56,11 +56,11 @@ const ToneGenerator::ToneDescriptor
         { { 950, 1400, 1800, 0 }, { 330, 1000, 0 },                  ToneGenerator::TONEGEN_INF },  // TONE_SUP_ERROR
         { { 425, 0 },             { 200, 600, 200, 3000, 0 },        ToneGenerator::TONEGEN_INF },  // TONE_SUP_CALL_WAITING
         { { 425, 0 },             { 1000, 4000, 0 },                 ToneGenerator::TONEGEN_INF },  // TONE_SUP_RINGTONE
-        { { 400, 1200, 0 },       { 35, 0 },                         0 },                           // TONE_PROP_BEEP
+        { { 400, 1200, 0 },       { 40, 0 },                         0 },                           // TONE_PROP_BEEP
         { { 1200, 0 },            { 100, 100, 0 },                   1 },                           // TONE_PROP_ACK
         { { 300, 400, 500, 0 },   { 400, 0 },                        0 },                           // TONE_PROP_NACK
         { { 400, 1200, 0 },       { 200, 0 },                        0 },                           // TONE_PROP_PROMPT
-        { { 400, 1200, 0 },       { 35, 200, 35, 0 },                0 }                            // TONE_PROP_BEEP2
+        { { 400, 1200, 0 },       { 40, 200, 40, 0 },                0 }                            // TONE_PROP_BEEP2
     };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -106,6 +106,8 @@ ToneGenerator::ToneGenerator(int streamType, float volume) {
     mpAudioTrack = 0;
     mpToneDesc = 0;
     mpNewToneDesc = 0;
+    // Generate tone by chunks of 20 ms to keep cadencing precision
+    mProcessSize = (mSamplingRate * 20) / 1000;
 
     if (initAudioTrack()) {
         LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000));
@@ -330,150 +332,162 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) {
     const AudioTrack::Buffer *buffer = static_cast<const AudioTrack::Buffer *>(info);
     ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user);
     short *lpOut = buffer->i16;
-    unsigned int lReqSmp = buffer->size/sizeof(short);
-    unsigned int lGenSmp;
-    unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT;
-    bool lSignal = false;
+    unsigned int lNumSmp = buffer->size/sizeof(short);
 
     if (buffer->size == 0) return;
 
-    lpToneGen->mLock.lock();
 
     // Clear output buffer: WaveGenerator accumulates into lpOut buffer
     memset(lpOut, 0, buffer->size);
 
-    // Update pcm frame count and end time (current time at the end of this process)
-    lpToneGen->mTotalSmp += lReqSmp;
-
-    // Update tone gen state machine and select wave gen command
-    switch (lpToneGen->mState) {
-    case TONE_PLAYING:
-        lWaveCmd = WaveGenerator::WAVEGEN_CONT;
-        break;
-    case TONE_STARTING:
-        LOGV("Starting Cbk");
-
-        lWaveCmd = WaveGenerator::WAVEGEN_START;
-        break;
-    case TONE_STOPPING:
-    case TONE_RESTARTING:
-        LOGV("Stop/restart Cbk");
-
-        lWaveCmd = WaveGenerator::WAVEGEN_STOP;
-        lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below
-        break;
-    default:
-        LOGV("Extra Cbk");
-        goto audioCallback_Exit;
-    }
-
-    // Exit if tone sequence is over
-    if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) {
-        if (lpToneGen->mState == TONE_PLAYING) {
-            lpToneGen->mState = TONE_STOPPING;            
-        }
-        goto audioCallback_Exit;
-    }
-
-    if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) {
-        // Time to go to next sequence segment
+    while (lNumSmp) {
+        unsigned int lReqSmp = lNumSmp < lpToneGen->mProcessSize*2 ? lNumSmp : lpToneGen->mProcessSize;
+        unsigned int lGenSmp;
+        unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT;
+        bool lSignal = false;
+        lpToneGen->mLock.lock();
 
-        LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000));
-
-        lGenSmp = lReqSmp;
-
-        if (lpToneGen->mCurSegment & 0x0001) {
-            // If odd segment,  OFF -> ON transition : reset wave generator
+        // Update pcm frame count and end time (current time at the end of this process)
+        lpToneGen->mTotalSmp += lReqSmp;
+    
+        // Update tone gen state machine and select wave gen command
+        switch (lpToneGen->mState) {
+        case TONE_PLAYING:
+            lWaveCmd = WaveGenerator::WAVEGEN_CONT;
+            break;
+        case TONE_STARTING:
+            LOGV("Starting Cbk");
+    
             lWaveCmd = WaveGenerator::WAVEGEN_START;
-
-            LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
-        } else {
-            // If even segment,  ON -> OFF transition : ramp volume down
+            break;
+        case TONE_STOPPING:
+        case TONE_RESTARTING:
+            LOGV("Stop/restart Cbk");
+    
             lWaveCmd = WaveGenerator::WAVEGEN_STOP;
-
-            LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
+            lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below
+            break;
+        default:
+            LOGV("Extra Cbk");
+            goto audioCallback_EndLoop;
         }
-
-        // Pre increment segment index and handle loop if last segment reached
-        if (lpToneGen->mpToneDesc->segments[++lpToneGen->mCurSegment] == 0) {
-            LOGV("Last Seg: %d\n", lpToneGen->mCurSegment);
-
-            // Pre increment loop count and restart if total count not reached. Stop sequence otherwise
-            if (++lpToneGen->mCurCount <= lpToneGen->mpToneDesc->repeatCnt) {
-                LOGV("Repeating Count: %d\n", lpToneGen->mCurCount);
-
-                lpToneGen->mCurSegment = 0;
-
-                LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
-                        (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
-
+        
+    
+        // Exit if tone sequence is over
+        if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) {
+            if (lpToneGen->mState == TONE_PLAYING) {
+                lpToneGen->mState = TONE_STOPPING;            
+            }
+            goto audioCallback_EndLoop;
+        }
+    
+        if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) {
+            // Time to go to next sequence segment
+    
+            LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000));
+    
+            lGenSmp = lReqSmp;
+    
+            if (lpToneGen->mCurSegment & 0x0001) {
+                // If odd segment,  OFF -> ON transition : reset wave generator
+                lWaveCmd = WaveGenerator::WAVEGEN_START;
+    
+                LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
             } else {
-                LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000));
-
-                // Cancel OFF->ON transition in case previous segment tone state was OFF
-                if (!(lpToneGen->mCurSegment & 0x0001)) {
-                    lGenSmp = 0;
+                // If even segment,  ON -> OFF transition : ramp volume down
+                lWaveCmd = WaveGenerator::WAVEGEN_STOP;
+    
+                LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
+            }
+    
+            // Pre increment segment index and handle loop if last segment reached
+            if (lpToneGen->mpToneDesc->segments[++lpToneGen->mCurSegment] == 0) {
+                LOGV("Last Seg: %d\n", lpToneGen->mCurSegment);
+    
+                // Pre increment loop count and restart if total count not reached. Stop sequence otherwise
+                if (++lpToneGen->mCurCount <= lpToneGen->mpToneDesc->repeatCnt) {
+                    LOGV("Repeating Count: %d\n", lpToneGen->mCurCount);
+    
+                    lpToneGen->mCurSegment = 0;
+    
+                    LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
+                            (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
+    
+                } else {
+                    LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000));
+    
+                    // Cancel OFF->ON transition in case previous segment tone state was OFF
+                    if (!(lpToneGen->mCurSegment & 0x0001)) {
+                        lGenSmp = 0;
+                    }
                 }
+            } else {
+                LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
+                        (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
             }
+    
+            // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more
+            lpToneGen->mNextSegSmp
+                    += (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] * lpToneGen->mSamplingRate) / 1000;
+    
         } else {
-            LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
-                    (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
-        }
-
-        // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more
-        lpToneGen->mNextSegSmp
-                += (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] * lpToneGen->mSamplingRate) / 1000;
-
-    } else {
-        // Inside a segment keep tone ON or OFF
-        if (lpToneGen->mCurSegment & 0x0001) {
-            lGenSmp = 0;  // If odd segment, tone is currently OFF
-        } else {
-            lGenSmp = lReqSmp;  // If event segment, tone is currently ON
+            // Inside a segment keep tone ON or OFF
+            if (lpToneGen->mCurSegment & 0x0001) {
+                lGenSmp = 0;  // If odd segment, tone is currently OFF
+            } else {
+                lGenSmp = lReqSmp;  // If event segment, tone is currently ON
+            }
         }
-    }
-
-    if (lGenSmp) {
-        // If samples must be generated, call all active wave generators and acumulate waves in lpOut
-        unsigned int lWaveIdx;
-
-        for (lWaveIdx = 0; lWaveIdx < (unsigned int)lpToneGen->mWaveGens.size(); lWaveIdx++) {
-            WaveGenerator *lpWaveGen = lpToneGen->mWaveGens[lWaveIdx];
-            lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd);
+    
+        if (lGenSmp) {
+            // If samples must be generated, call all active wave generators and acumulate waves in lpOut
+            unsigned int lWaveIdx;
+    
+            for (lWaveIdx = 0; lWaveIdx < (unsigned int)lpToneGen->mWaveGens.size(); lWaveIdx++) {
+                WaveGenerator *lpWaveGen = lpToneGen->mWaveGens[lWaveIdx];
+                lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd);
+            }
         }
-    }
-
-audioCallback_Exit:
-
-    switch (lpToneGen->mState) {
-    case TONE_RESTARTING:
-        LOGV("Cbk restarting track\n");
-        if (lpToneGen->prepareWave()) {
-            lpToneGen->mState = TONE_STARTING;
-        } else {
+        
+        lNumSmp -= lReqSmp;
+        lpOut += lReqSmp;
+    
+audioCallback_EndLoop:
+    
+        switch (lpToneGen->mState) {
+        case TONE_RESTARTING:
+            LOGV("Cbk restarting track\n");
+            if (lpToneGen->prepareWave()) {
+                lpToneGen->mState = TONE_STARTING;
+            } else {
+                lpToneGen->mState = TONE_INIT;
+                lpToneGen->mpAudioTrack->stop();
+            }
+            lSignal = true;
+            break;
+        case TONE_STOPPING:
             lpToneGen->mState = TONE_INIT;
+            LOGV("Cbk Stopping track\n");
+            lSignal = true;
             lpToneGen->mpAudioTrack->stop();
+            
+            // Force loop exit
+            lNumSmp = 0;
+            break;
+        case TONE_STARTING:
+            LOGV("Cbk starting track\n");
+            lpToneGen->mState = TONE_PLAYING;
+            lSignal = true;
+           break;
+        default:
+            break;
         }
-        lSignal = true;
-        break;
-    case TONE_STOPPING:
-        lpToneGen->mState = TONE_INIT;
-        LOGV("Cbk Stopping track\n");
-        lSignal = true;
-        lpToneGen->mpAudioTrack->stop();
-        break;
-    case TONE_STARTING:
-        LOGV("Cbk starting track\n");
-        lpToneGen->mState = TONE_PLAYING;
-        lSignal = true;
-       break;
-    default:
-        break;
-    }
 
-    if (lSignal)
-        lpToneGen->mWaitCbkCond.signal();
-    lpToneGen->mLock.unlock();
+        if (lSignal)
+            lpToneGen->mWaitCbkCond.signal();
+        lpToneGen->mLock.unlock();
+    }
 }
 
 
index 825e145..6ee4c0d 100644 (file)
@@ -493,8 +493,6 @@ MediaRecorder::MediaRecorder()
     if (service != NULL) {
         mMediaRecorder = service->createMediaRecorder(getpid());
     }
-
-    mMediaRecorder = service->createMediaRecorder(getpid());
     if (mMediaRecorder != NULL) {
         mCurrentState = MEDIA_RECORDER_IDLE;
     }
index 773cf0b..160ebef 100644 (file)
@@ -257,6 +257,7 @@ android.provider.MediaStore$Audio$Media
 android.provider.MediaStore$Images$Media
 android.provider.Settings$Gservices
 android.provider.Settings$NameValueCache
+android.provider.Settings$Secure
 android.provider.Settings$System
 android.provider.Sync$Settings$QueryMap
 android.provider.Telephony$MmsSms$PendingMessages
@@ -328,6 +329,7 @@ android.view.IWindowSession$Stub$Proxy
 android.view.KeyCharacterMap
 android.view.KeyEvent
 android.view.KeyEvent$1
+android.view.KeyEvent$Callback
 android.view.LayoutInflater
 android.view.MenuInflater
 android.view.MotionEvent
@@ -338,6 +340,7 @@ android.view.TouchDelegate
 android.view.VelocityTracker
 android.view.View
 android.view.View$AttachInfo
+android.view.View$AttachInfo$SoundEffectPlayer
 android.view.View$BaseSavedState$1
 android.view.View$MeasureSpec
 android.view.View$ScrollabilityCache
@@ -345,7 +348,6 @@ android.view.ViewGroup
 android.view.ViewGroup$MarginLayoutParams
 android.view.ViewRoot
 android.view.ViewRoot$1
-android.view.ViewRoot$RootHandler
 android.view.ViewRoot$TrackballAxis
 android.view.ViewRoot$W
 android.view.ViewStub
@@ -361,6 +363,36 @@ android.view.animation.Animation
 android.view.animation.AnimationSet
 android.view.animation.LinearInterpolator
 android.view.animation.Transformation
+android.view.inputmethod.BaseInputConnection
+android.view.inputmethod.CompletionInfo$1
+android.view.inputmethod.CompletionInfo
+android.view.inputmethod.DefaultInputMethod
+android.view.inputmethod.EditorInfo$1
+android.view.inputmethod.EditorInfo
+android.view.inputmethod.ExtractedText$1
+android.view.inputmethod.ExtractedText
+android.view.inputmethod.ExtractedTextRequest$1
+android.view.inputmethod.ExtractedTextRequest
+android.view.inputmethod.InputBinding$1
+android.view.inputmethod.InputBinding
+android.view.inputmethod.InputConnection
+android.view.inputmethod.InputConnectionWrapper
+android.view.inputmethod.InputMethod$SessionCallback
+android.view.inputmethod.InputMethod
+android.view.inputmethod.InputMethodInfo$1
+android.view.inputmethod.InputMethodInfo
+android.view.inputmethod.InputMethodManager$1
+android.view.inputmethod.InputMethodManager$2
+android.view.inputmethod.InputMethodManager$3
+android.view.inputmethod.InputMethodManager$NoOpInputConnection
+android.view.inputmethod.InputMethodManager
+android.view.inputmethod.InputMethodSession$EventCallback
+android.view.inputmethod.InputMethodSession
+android.view.inputmethod.MutableInputConnectionWrapper
+android.view.inputmethod.SimpleInputMethod$InputMethodSessionCallbackWrapper
+android.view.inputmethod.SimpleInputMethod$Session$InputMethodEventCallbackWrapper
+android.view.inputmethod.SimpleInputMethod$Session
+android.view.inputmethod.SimpleInputMethod
 android.webkit.BrowserFrame
 android.webkit.CacheManager
 android.webkit.CallbackProxy
@@ -456,7 +488,14 @@ android.widget.TextView$1
 android.widget.TextView$Blink
 android.widget.TextView$BufferType
 android.widget.TextView$ChangeWatcher
+android.widget.TextView$CharWrapper
+android.widget.TextView$Drawables
+android.widget.TextView$InputContentType
+android.widget.TextView$InputMethodState
+android.widget.TextView$Marquee
+android.widget.TextView$MenuHandler
 android.widget.TextView$SavedState$1
+android.widget.TextView$SavedState
 android.widget.ToggleButton
 android.widget.TwoLineListItem
 android.widget.VideoView
@@ -493,6 +532,34 @@ com.android.internal.telephony.gsm.PdpConnection$PdpFailCause
 com.android.internal.telephony.gsm.RIL
 com.android.internal.telephony.gsm.ServiceStateTracker
 com.android.internal.view.menu.MenuDialogHelper
+com.android.internal.view.IInputConnectionWrapper$MyHandler
+com.android.internal.view.IInputConnectionWrapper$SomeArgs
+com.android.internal.view.IInputConnectionWrapper
+com.android.internal.view.IInputContext$Stub$Proxy
+com.android.internal.view.IInputContext$Stub
+com.android.internal.view.IInputContext
+com.android.internal.view.IInputContextCallback$Stub$Proxy
+com.android.internal.view.IInputContextCallback$Stub
+com.android.internal.view.IInputContextCallback
+com.android.internal.view.IInputMethod$Stub$Proxy
+com.android.internal.view.IInputMethod$Stub
+com.android.internal.view.IInputMethod
+com.android.internal.view.IInputMethodCallback$Stub$Proxy
+com.android.internal.view.IInputMethodCallback$Stub
+com.android.internal.view.IInputMethodCallback
+com.android.internal.view.IInputMethodClient$Stub$Proxy
+com.android.internal.view.IInputMethodClient$Stub
+com.android.internal.view.IInputMethodClient
+com.android.internal.view.IInputMethodManager$Stub$Proxy
+com.android.internal.view.IInputMethodManager$Stub
+com.android.internal.view.IInputMethodManager
+com.android.internal.view.IInputMethodSession$Stub$Proxy
+com.android.internal.view.IInputMethodSession$Stub
+com.android.internal.view.IInputMethodSession
+com.android.internal.view.InputBindResult$1
+com.android.internal.view.InputBindResult
+com.android.internal.view.InputConnectionWrapper$InputContextCallback
+com.android.internal.view.InputConnectionWrapper
 com.android.internal.widget.LockPatternView
 com.google.android.gles_jni.EGLImpl
 com.google.android.gles_jni.GLImpl
index cb681e0..2bea731 100644 (file)
@@ -88,23 +88,8 @@ class HeadsetObserver extends UEventObserver {
             mHeadsetName = newName;
             mHeadsetState = newState;
             AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-    
-            if (mHeadsetState == 1) {
-                audioManager.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_HEADSET,
-                                       AudioManager.ROUTE_ALL);
-                audioManager.setRouting(AudioManager.MODE_RINGTONE,
-                                       AudioManager.ROUTE_HEADSET | AudioManager.ROUTE_SPEAKER,
-                                       AudioManager.ROUTE_ALL);
-                audioManager.setRouting(AudioManager.MODE_IN_CALL, AudioManager.ROUTE_HEADSET,
-                                       AudioManager.ROUTE_ALL);
-            } else {
-                audioManager.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER,
-                                       AudioManager.ROUTE_ALL);
-                audioManager.setRouting(AudioManager.MODE_RINGTONE, AudioManager.ROUTE_SPEAKER,
-                                       AudioManager.ROUTE_ALL);
-                audioManager.setRouting(AudioManager.MODE_IN_CALL, AudioManager.ROUTE_EARPIECE,
-                                       AudioManager.ROUTE_ALL);
-            }
+
+            audioManager.setWiredHeadsetOn(mHeadsetState == 1);
             sendIntent();
         }
     }
index c701ca1..63d7c94 100644 (file)
@@ -952,7 +952,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
 
     public void setInputMethod(IBinder token, String id) {
         synchronized (mMethodMap) {
-            if (mCurToken == null || mCurToken != token) {
+            if (mCurToken == null) {
+                if (mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException(
+                            "Using null token requires permission "
+                            + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+                }
+            } else if (mCurToken != token) {
                 Log.w(TAG, "Ignoring setInputMethod of token: " + token);
             }
 
@@ -1258,6 +1266,100 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
         }
     }
     
+    // ----------------------------------------------------------------------
+    
+    public boolean setInputMethodEnabled(String id, boolean enabled) {
+        synchronized (mMethodMap) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission "
+                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+            }
+            
+            // Make sure this is a valid input method.
+            InputMethodInfo imm = mMethodMap.get(id);
+            if (imm == null) {
+                if (imm == null) {
+                    throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+                }
+            }
+            
+            StringBuilder builder = new StringBuilder(256);
+            
+            boolean removed = false;
+            String firstId = null;
+            
+            // Look through the currently enabled input methods.
+            String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_INPUT_METHODS);
+            if (enabledStr != null) {
+                final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+                splitter.setString(enabledStr);
+                while (splitter.hasNext()) {
+                    String curId = splitter.next();
+                    if (curId.equals(id)) {
+                        if (enabled) {
+                            // We are enabling this input method, but it is
+                            // already enabled.  Nothing to do.  The previous
+                            // state was enabled.
+                            return true;
+                        }
+                        // We are disabling this input method, and it is
+                        // currently enabled.  Skip it to remove from the
+                        // new list.
+                        removed = true;
+                    } else if (!enabled) {
+                        // We are building a new list of input methods that
+                        // doesn't contain the given one.
+                        if (firstId == null) firstId = curId;
+                        if (builder.length() > 0) builder.append(':');
+                        builder.append(curId);
+                    }
+                }
+            }
+            
+            if (!enabled) {
+                if (!removed) {
+                    // We are disabling the input method but it is already
+                    // disabled.  Nothing to do.  The previous state was
+                    // disabled.
+                    return false;
+                }
+                // Update the setting with the new list of input methods.
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
+                // We the disabled input method is currently selected, switch
+                // to another one.
+                String selId = Settings.Secure.getString(mContext.getContentResolver(),
+                        Settings.Secure.DEFAULT_INPUT_METHOD);
+                if (id.equals(selId)) {
+                    Settings.Secure.putString(mContext.getContentResolver(),
+                            Settings.Secure.DEFAULT_INPUT_METHOD,
+                            firstId != null ? firstId : "");
+                }
+                // Previous state was enabled.
+                return true;
+            }
+            
+            // Add in the newly enabled input method.
+            if (enabledStr == null || enabledStr.length() == 0) {
+                enabledStr = id;
+            } else {
+                enabledStr = enabledStr + ':' + id;
+            }
+            
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
+            
+            // Previous state was disabled.
+            return false;
+        }
+    }
+    
+    // ----------------------------------------------------------------------
+    
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingPermission("android.permission.DUMP")
index 831d1d2..ed9ee79 100644 (file)
@@ -569,7 +569,7 @@ public class LocationManagerService extends ILocationManager.Stub {
 
     private WifiManager.WifiLock getWifiWakelock() {
         if (mWifiLock == null && mWifiManager != null) {
-            mWifiLock = mWifiManager.createWifiLock(WIFILOCK_KEY);
+            mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
             mWifiLock.setReferenceCounted(false);
         }
         return mWifiLock;
index 03480d1..002ebed 100644 (file)
@@ -137,7 +137,11 @@ class MountService extends IMountService.Stub {
      * Broadcasts the USB mass storage connected event to all clients.
      */
     void notifyUmsConnected() {
-        setUsbStorageNotificationVisibility(true);
+        String storageState = Environment.getExternalStorageState();
+        if (!storageState.equals(Environment.MEDIA_REMOVED) &&
+                !storageState.equals(Environment.MEDIA_BAD_REMOVAL)) {
+            setUsbStorageNotificationVisibility(true);
+        }
         Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
         mContext.sendBroadcast(intent);
     }
index e86ff02..f0d5eaf 100644 (file)
@@ -204,7 +204,7 @@ class PackageManagerService extends IPackageManager.Stub {
     boolean mRestoredSettings;
     boolean mReportedUidError;
 
-    // Group-ids that are given to all packages as read from etc/permissions.xml.
+    // Group-ids that are given to all packages as read from etc/permissions/*.xml.
     int[] mGlobalGids;
 
     // These are the built-in uid -> permission mappings that were read from the
@@ -534,8 +534,43 @@ class PackageManagerService extends IPackageManager.Stub {
     }
 
     void readPermissions() {
+        // Read permissions from .../etc/permission directory.
+        File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
+        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+            Log.w(TAG, "No directory " + libraryDir + ", skipping");
+            return;
+        }
+        if (!libraryDir.canRead()) {
+            Log.w(TAG, "Directory " + libraryDir + " cannot be read");
+            return;
+        }
+
+        // Iterate over the files in the directory and scan .xml files
+        for (File f : libraryDir.listFiles()) {
+            // We'll read platform.xml last
+            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
+                continue;
+            }
+            
+            if (!f.getPath().endsWith(".xml")) {
+                Log.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
+                continue;
+            }
+            if (!f.canRead()) {
+                Log.w(TAG, "Permissions library file " + f + " cannot be read");
+                continue;
+            }
+
+            readPermissionsFromXml(f);
+        }
+        
+        // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
         final File permFile = new File(Environment.getRootDirectory(),
-                "etc/permissions.xml");
+                "etc/permissions/platform.xml");
+        readPermissionsFromXml(permFile);
+    }
+    
+    private void readPermissionsFromXml(File permFile) {        
         FileReader permReader = null;
         try {
             permReader = new FileReader(permFile);
@@ -566,9 +601,9 @@ class PackageManagerService extends IPackageManager.Stub {
                         Log.w(TAG, "<group> without gid at "
                                 + parser.getPositionDescription());
                     }
+
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-                    
                 } else if ("permission".equals(name)) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
@@ -622,6 +657,7 @@ class PackageManagerService extends IPackageManager.Stub {
                         Log.w(TAG, "<library> without file at "
                                 + parser.getPositionDescription());
                     } else {
+                        Log.i(TAG, "Got library " + lname + " in " + lfile);
                         this.mSharedLibraries.put(lname, lfile);
                     }
                     XmlUtils.skipCurrentTag(parser);
index 3fa2087..ca0ad1a 100644 (file)
@@ -158,6 +158,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
     private UnsynchronizedWakeLock mBroadcastWakeLock;
     private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
     private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
+    private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private TimeoutTask mTimeoutTask = new TimeoutTask();
@@ -180,6 +181,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
     private HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
     private long mScreenOnTime;
     private long mScreenOnStartTime;
+    private boolean mPreventScreenOn;
 
     // Used when logging number and duration of touch-down cycles
     private long mTotalTouchDownTime;
@@ -383,6 +385,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
                                 PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false);
         mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock(
                                 PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
+        mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
+                                PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
 
         mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
         mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -790,6 +794,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
         pw.println("  mBroadcastWakeLock=" + mBroadcastWakeLock);
         pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
         pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
+        pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
         mScreenBrightness.dump(pw, "  mScreenBrightness: ");
         mKeyboardBrightness.dump(pw, "  mKeyboardBrightness: ");
         mButtonBrightness.dump(pw, "  mButtonBrightness: ");
@@ -1035,6 +1040,118 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
         }
     }
 
+    /**
+     * Prevents the screen from turning on even if it *should* turn on due
+     * to a subsequent full wake lock being acquired.
+     * <p>
+     * This is a temporary hack that allows an activity to "cover up" any
+     * display glitches that happen during the activity's startup
+     * sequence.  (Specifically, this API was added to work around a
+     * cosmetic bug in the "incoming call" sequence, where the lock screen
+     * would flicker briefly before the incoming call UI became visible.)
+     * TODO: There ought to be a more elegant way of doing this,
+     * probably by having the PowerManager and ActivityManager
+     * work together to let apps specify that the screen on/off
+     * state should be synchronized with the Activity lifecycle.
+     * <p>
+     * Note that calling preventScreenOn(true) will NOT turn the screen
+     * off if it's currently on.  (This API only affects *future*
+     * acquisitions of full wake locks.)
+     * But calling preventScreenOn(false) WILL turn the screen on if
+     * it's currently off because of a prior preventScreenOn(true) call.
+     * <p>
+     * Any call to preventScreenOn(true) MUST be followed promptly by a call
+     * to preventScreenOn(false).  In fact, if the preventScreenOn(false)
+     * call doesn't occur within 5 seconds, we'll turn the screen back on
+     * ourselves (and log a warning about it); this prevents a buggy app
+     * from disabling the screen forever.)
+     * <p>
+     * TODO: this feature should really be controlled by a new type of poke
+     * lock (rather than an IPowerManager call).
+     */
+    public void preventScreenOn(boolean prevent) {
+        // TODO: use a totally new permission (separate from DEVICE_POWER) for this?
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        synchronized (mLocks) {
+            if (prevent) {
+                // First of all, grab a partial wake lock to
+                // make sure the CPU stays on during the entire
+                // preventScreenOn(true) -> preventScreenOn(false) sequence.
+                mPreventScreenOnPartialLock.acquire();
+
+                // Post a forceReenableScreen() call (for 5 seconds in the
+                // future) to make sure the matching preventScreenOn(false) call
+                // has happened by then.
+                mHandler.removeCallbacks(mForceReenableScreenTask);
+                mHandler.postDelayed(mForceReenableScreenTask, 5000);
+
+                // Finally, set the flag that prevents the screen from turning on.
+                // (Below, in setPowerState(), we'll check mPreventScreenOn and
+                // we *won't* call Power.setScreenState(true) if it's set.)
+                mPreventScreenOn = true;
+            } else {
+                // (Re)enable the screen.
+                mPreventScreenOn = false;
+
+                // We're "undoing" a the prior preventScreenOn(true) call, so we
+                // no longer need the 5-second safeguard.
+                mHandler.removeCallbacks(mForceReenableScreenTask);
+
+                // Forcibly turn on the screen if it's supposed to be on.  (This
+                // handles the case where the screen is currently off because of
+                // a prior preventScreenOn(true) call.)
+                if ((mPowerState & SCREEN_ON_BIT) != 0) {
+                    if (mSpew) {
+                        Log.d(TAG,
+                              "preventScreenOn: turning on after a prior preventScreenOn(true)!");
+                    }
+                    int err = Power.setScreenState(true);
+                    if (err != 0) {
+                        Log.w(TAG, "preventScreenOn: error from Power.setScreenState(): " + err);
+                    }
+                }
+
+                // Release the partial wake lock that we held during the
+                // preventScreenOn(true) -> preventScreenOn(false) sequence.
+                mPreventScreenOnPartialLock.release();
+            }
+        }
+    }
+
+    /**
+     * Sanity-check that gets called 5 seconds after any call to
+     * preventScreenOn(true).  This ensures that the original call
+     * is followed promptly by a call to preventScreenOn(false).
+     */
+    private void forceReenableScreen() {
+        // We shouldn't get here at all if mPreventScreenOn is false, since
+        // we should have already removed any existing
+        // mForceReenableScreenTask messages...
+        if (!mPreventScreenOn) {
+            Log.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do");
+            return;
+        }
+
+        // Uh oh.  It's been 5 seconds since a call to
+        // preventScreenOn(true) and we haven't re-enabled the screen yet.
+        // This means the app that called preventScreenOn(true) is either
+        // slow (i.e. it took more than 5 seconds to call preventScreenOn(false)),
+        // or buggy (i.e. it forgot to call preventScreenOn(false), or
+        // crashed before doing so.)
+
+        // Log a warning, and forcibly turn the screen back on.
+        Log.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! "
+              + "Forcing the screen back on...");
+        preventScreenOn(false);
+    }
+
+    private Runnable mForceReenableScreenTask = new Runnable() {
+            public void run() {
+                forceReenableScreen();
+            }
+        };
+
     private void setPowerState(int state)
     {
         setPowerState(state, false, false);
@@ -1096,7 +1213,31 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
 
             if (oldScreenOn != newScreenOn) {
                 if (newScreenOn) {
-                    err = Power.setScreenState(true);
+                    // Turn on the screen UNLESS there was a prior
+                    // preventScreenOn(true) request.  (Note that the lifetime
+                    // of a single preventScreenOn() request is limited to 5
+                    // seconds to prevent a buggy app from disabling the
+                    // screen forever; see forceReenableScreen().)
+                    boolean reallyTurnScreenOn = true;
+                    if (mSpew) {
+                        Log.d(TAG, "- turning screen on...  mPreventScreenOn = "
+                              + mPreventScreenOn);
+                    }
+
+                    if (mPreventScreenOn) {
+                        if (mSpew) {
+                            Log.d(TAG, "- PREVENTING screen from really turning on!");
+                        }
+                        reallyTurnScreenOn = false;
+                    }
+                    if (reallyTurnScreenOn) {
+                        err = Power.setScreenState(true);
+                    } else {
+                        Power.setScreenState(false);
+                        // But continue as if we really did turn the screen on...
+                        err = 0;
+                    }
+
                     mScreenOnStartTime = SystemClock.elapsedRealtime();
                     mLastTouchDown = 0;
                     mTotalTouchDownTime = 0;
index 70c3110..fef3598 100644 (file)
@@ -36,6 +36,7 @@ import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Calendar;
 
@@ -43,13 +44,13 @@ import java.util.Calendar;
 public class Watchdog extends Thread {
     static final String TAG = "Watchdog";
     static final boolean localLOGV = false || Config.LOGV;
-    
+
     // Set this to true to use debug default values.
     static final boolean DB = false;
-    
+
     static final int MONITOR = 2718;
     static final int GLOBAL_PSS = 2719;
-    
+
     static final int TIME_TO_WAIT = DB ? 15*1000 : 60*1000;
     static final int EVENT_LOG_TAG = 2802;
     static final int EVENT_LOG_PROC_PSS_TAG = 2803;
@@ -61,29 +62,29 @@ public class Watchdog extends Thread {
     static final int EVENT_LOG_MEMINFO_TAG = 2809;
     static final int EVENT_LOG_VMSTAT_TAG = 2810;
     static final int EVENT_LOG_REQUESTED_REBOOT_TAG = 2811;
-    
+
     static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes
     static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60;      // 2 hours
     static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB
     static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB
     static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024;    // 8MB
     static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024;   // 12MB
-    
+
     static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60;           // 1:00am
     static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60;             // 5:00am
     static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60;   // 5 minutes
     static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60;        // 3 minutes
     static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
-    
+
     static final int REBOOT_DEFAULT_INTERVAL = DB ? 1 : 0;                 // never force reboot
     static final int REBOOT_DEFAULT_START_TIME = 3*60*60;                  // 3:00am
     static final int REBOOT_DEFAULT_WINDOW = 60*60;                        // within 1 hour
-    
+
     static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP";
     static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
-    
+
     static Watchdog sWatchdog;
-    
+
     /* This handler will be used to post message back onto the main thread */
     final Handler mHandler;
     final Runnable mGlobalPssCollected;
@@ -96,11 +97,11 @@ public class Watchdog extends Thread {
     boolean mCompleted;
     boolean mForceKillSystem;
     Monitor mCurrentMonitor;
-    
+
     PssRequestor mPhoneReq;
     int mPhonePid;
     int mPhonePss;
-    
+
     long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000);
     boolean mHavePss;
     long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000);
@@ -117,7 +118,7 @@ public class Watchdog extends Thread {
             MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD,
             Settings.Gservices.MEMCHECK_PHONE_HARD_THRESHOLD,
             MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD);
-    
+
     final Calendar mCalendar = Calendar.getInstance();
     long mMemcheckLastTime;
     long mMemcheckExecStartTime;
@@ -127,10 +128,10 @@ public class Watchdog extends Thread {
     boolean mNeedScheduledCheck;
     PendingIntent mCheckupIntent;
     PendingIntent mRebootIntent;
-    
+
     long mBootTime;
     int mRebootInterval;
-    
+
     boolean mReqRebootNoWait;     // should wait for one interval before reboot?
     int mReqRebootInterval = -1;  // >= 0 if a reboot has been requested
     int mReqRebootStartTime = -1; // >= 0 if a specific start time has been requested
@@ -138,7 +139,7 @@ public class Watchdog extends Thread {
     int mReqMinScreenOff = -1;    // >= 0 if a specific screen off time has been requested
     int mReqMinNextAlarm = -1;    // >= 0 if specific time to next alarm has been requested
     int mReqRecheckInterval= -1;  // >= 0 if a specific recheck interval has been requested
-    
+
     /**
      * This class monitors the memory in a particular process.
      */
@@ -147,17 +148,17 @@ public class Watchdog extends Thread {
         final String mEnabledSetting;
         final String mSoftSetting;
         final String mHardSetting;
-        
+
         int mSoftThreshold;
         int mHardThreshold;
         boolean mEnabled;
         long mLastPss;
-        
+
         static final int STATE_OK = 0;
         static final int STATE_SOFT = 1;
         static final int STATE_HARD = 2;
         int mState;
-        
+
         MemMonitor(String processName, String enabledSetting,
                 String softSetting, int defSoftThreshold,
                 String hardSetting, int defHardThreshold) {
@@ -168,7 +169,7 @@ public class Watchdog extends Thread {
             mSoftThreshold = defSoftThreshold;
             mHardThreshold = defHardThreshold;
         }
-        
+
         void retrieveSettings(ContentResolver resolver) {
             mSoftThreshold = Settings.Gservices.getInt(
                     resolver, mSoftSetting, mSoftThreshold);
@@ -177,7 +178,7 @@ public class Watchdog extends Thread {
             mEnabled = Settings.Gservices.getInt(
                     resolver, mEnabledSetting, 0) != 0;
         }
-        
+
         boolean checkLocked(long curTime, int pid, int pss) {
             mLastPss = pss;
             if (mLastPss < mSoftThreshold) {
@@ -188,19 +189,19 @@ public class Watchdog extends Thread {
                 mState = STATE_HARD;
             }
             EventLog.writeEvent(EVENT_LOG_PROC_PSS_TAG, mProcessName, pid, mLastPss);
-            
+
             if (mState == STATE_OK) {
                 // Memory is good, don't recover.
                 return false;
             }
-            
+
             if (mState == STATE_HARD) {
                 // Memory is really bad, kill right now.
                 EventLog.writeEvent(EVENT_LOG_HARD_RESET_TAG, mProcessName, pid,
                         mHardThreshold, mLastPss);
                 return mEnabled;
             }
-            
+
             // It is time to schedule a reset...
             // Check if we are currently within the time to kill processes due
             // to memory use.
@@ -219,13 +220,13 @@ public class Watchdog extends Thread {
             }
             return mEnabled;
         }
-        
+
         void clear() {
             mLastPss = 0;
             mState = STATE_OK;
         }
     }
-    
+
     /**
      * Used for scheduling monitor callbacks and checking memory usage.
      */
@@ -242,7 +243,7 @@ public class Watchdog extends Thread {
                         logGlobalMemory();
                     }
                 } break;
-                
+
                 case MONITOR: {
                     if (mHavePss) {
                         // During the last pass we collected pss information, so
@@ -251,7 +252,7 @@ public class Watchdog extends Thread {
                         if (localLOGV) Log.v(TAG, "Have pss, checking memory.");
                         checkMemory();
                     }
-                    
+
                     if (mHaveGlobalPss) {
                         // During the last pass we collected pss information, so
                         // now it is time to report it.
@@ -259,9 +260,9 @@ public class Watchdog extends Thread {
                         if (localLOGV) Log.v(TAG, "Have global pss, logging.");
                         logGlobalMemory();
                     }
-                    
+
                     long now = SystemClock.uptimeMillis();
-                    
+
                     // See if we should force a reboot.
                     int rebootInterval = mReqRebootInterval >= 0
                             ? mReqRebootInterval : Settings.Gservices.getInt(
@@ -273,7 +274,7 @@ public class Watchdog extends Thread {
                         // be considered...
                         checkReboot(false);
                     }
-                    
+
                     // See if we should check memory conditions.
                     long memCheckInterval = Settings.Gservices.getLong(
                             mResolver, Settings.Gservices.MEMCHECK_INTERVAL,
@@ -287,7 +288,7 @@ public class Watchdog extends Thread {
                         if (localLOGV) Log.v(TAG, "Collecting memory usage.");
                         collectMemory();
                         mHavePss = true;
-                        
+
                         long memCheckRealtimeInterval = Settings.Gservices.getLong(
                                 mResolver, Settings.Gservices.MEMCHECK_LOG_REALTIME_INTERVAL,
                                 MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000;
@@ -299,13 +300,13 @@ public class Watchdog extends Thread {
                             mHaveGlobalPss = true;
                         }
                     }
-                    
+
                     final int size = mMonitors.size();
                     for (int i = 0 ; i < size ; i++) {
                         mCurrentMonitor = mMonitors.get(i);
                         mCurrentMonitor.monitor();
                     }
-                    
+
                     synchronized (Watchdog.this) {
                         mCompleted = true;
                         mCurrentMonitor = null;
@@ -320,7 +321,7 @@ public class Watchdog extends Thread {
             mHandler.sendEmptyMessage(GLOBAL_PSS);
         }
     }
-    
+
     final class CheckupReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context c, Intent intent) {
@@ -328,7 +329,7 @@ public class Watchdog extends Thread {
             checkMemory();
         }
     }
-    
+
     final class RebootReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context c, Intent intent) {
@@ -336,7 +337,7 @@ public class Watchdog extends Thread {
             checkReboot(true);
         }
     }
-    
+
     final class RebootRequestReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context c, Intent intent) {
@@ -354,15 +355,15 @@ public class Watchdog extends Thread {
             checkReboot(true);
         }
     }
-    
+
     public interface Monitor {
         void monitor();
     }
-    
+
     public interface PssRequestor {
         void requestPss();
     }
-    
+
     public class PssStats {
         public int mEmptyPss;
         public int mEmptyCount;
@@ -374,20 +375,20 @@ public class Watchdog extends Thread {
         public int mVisibleCount;
         public int mForegroundPss;
         public int mForegroundCount;
-        
+
         public int mNoPssCount;
-        
+
         public int mProcDeaths[] = new int[10];
     }
-    
+
     public static Watchdog getInstance() {
         if (sWatchdog == null) {
             sWatchdog = new Watchdog();
         }
-        
+
         return sWatchdog;
     }
-    
+
     private Watchdog() {
         super("watchdog");
         mHandler = new HeartbeatHandler();
@@ -402,24 +403,24 @@ public class Watchdog extends Thread {
         mPower = power;
         mAlarm = alarm;
         mActivity = activity;
-        
+
         context.registerReceiver(new CheckupReceiver(),
                 new IntentFilter(CHECKUP_ACTION));
         mCheckupIntent = PendingIntent.getBroadcast(context,
                 0, new Intent(CHECKUP_ACTION), 0);
-        
+
         context.registerReceiver(new RebootReceiver(),
                 new IntentFilter(REBOOT_ACTION));
         mRebootIntent = PendingIntent.getBroadcast(context,
                 0, new Intent(REBOOT_ACTION), 0);
-        
+
         context.registerReceiver(new RebootRequestReceiver(),
                 new IntentFilter(Intent.ACTION_REBOOT),
                 android.Manifest.permission.REBOOT, null);
-        
+
         mBootTime = System.currentTimeMillis();
     }
-    
+
     public void processStarted(PssRequestor req, String name, int pid) {
         synchronized (this) {
             if ("com.android.phone".equals(name)) {
@@ -429,7 +430,7 @@ public class Watchdog extends Thread {
             }
         }
     }
-    
+
     public void reportPss(PssRequestor req, String name, int pss) {
         synchronized (this) {
             if (mPhoneReq == req) {
@@ -437,7 +438,7 @@ public class Watchdog extends Thread {
             }
         }
     }
-    
+
     public void addMonitor(Monitor monitor) {
         synchronized (this) {
             if (isAlive()) {
@@ -459,7 +460,7 @@ public class Watchdog extends Thread {
             }
         }
     }
-    
+
     /**
      * Retrieve memory usage over all application processes.  This is an
      * async operation, so must be done before doing memory checks.
@@ -467,7 +468,7 @@ public class Watchdog extends Thread {
     void collectGlobalMemory() {
         mActivity.requestPss(mGlobalPssCollected);
     }
-    
+
     /**
      * Check memory usage in the system, scheduling kills/reboots as needed.
      * This always runs on the mHandler thread.
@@ -476,11 +477,11 @@ public class Watchdog extends Thread {
         boolean needScheduledCheck;
         long curTime;
         long nextTime = 0;
-        
+
         long recheckInterval = Settings.Gservices.getLong(
                 mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
                 MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
-        
+
         mSystemMemMonitor.retrieveSettings(mResolver);
         mPhoneMemMonitor.retrieveSettings(mResolver);
         retrieveBrutalityAmount();
@@ -488,7 +489,7 @@ public class Watchdog extends Thread {
         synchronized (this) {
             curTime = System.currentTimeMillis();
             mNeedScheduledCheck = false;
-            
+
             // How is the system doing?
             if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
                     (int)Process.getPss(Process.myPid()))) {
@@ -497,7 +498,7 @@ public class Watchdog extends Thread {
                 notifyAll();
                 return;
             }
-            
+
             // How is the phone process doing?
             if (mPhoneReq != null) {
                 if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
@@ -508,7 +509,7 @@ public class Watchdog extends Thread {
             } else {
                 mPhoneMemMonitor.clear();
             }
-            
+
             needScheduledCheck = mNeedScheduledCheck;
             if (needScheduledCheck) {
                 // Something is going bad, but now is not a good time to
@@ -523,14 +524,14 @@ public class Watchdog extends Thread {
                     computeMemcheckTimesLocked(nextTime);
                     nextTime = mMemcheckExecStartTime;
                 }
-                
+
                 if (localLOGV) {
                     mCalendar.setTimeInMillis(nextTime);
                     Log.v(TAG, "Next Alarm Time: " + mCalendar);
                 }
             }
         }
-        
+
         if (needScheduledCheck) {
             if (localLOGV) Log.v(TAG, "Scheduling next memcheck alarm for "
                     + ((nextTime-curTime)/1000/60) + "m from now");
@@ -555,7 +556,7 @@ public class Watchdog extends Thread {
     final long[] mVMStatSizes = new long[mVMStatFields.length];
     final long[] mPrevVMStatSizes = new long[mVMStatFields.length];
     long mLastLogGlobalMemoryTime;
-    
+
     void logGlobalMemory() {
         PssStats stats = mPssStats;
         mActivity.collectPss(stats);
@@ -591,7 +592,7 @@ public class Watchdog extends Thread {
                 (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
                 (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
     }
-    
+
     void checkReboot(boolean fromAlarm) {
         int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
                 : Settings.Gservices.getInt(
@@ -604,7 +605,7 @@ public class Watchdog extends Thread {
             mAlarm.remove(mRebootIntent);
             return;
         }
-        
+
         long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime
                 : Settings.Gservices.getLong(
                 mResolver, Settings.Gservices.REBOOT_START_TIME,
@@ -617,17 +618,17 @@ public class Watchdog extends Thread {
                 : Settings.Gservices.getLong(
                 mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
                 MEMCHECK_DEFAULT_RECHECK_INTERVAL)) * 1000;
-        
+
         retrieveBrutalityAmount();
-        
+
         long realStartTime;
         long now;
-        
+
         synchronized (this) {
             now = System.currentTimeMillis();
             realStartTime = computeCalendarTime(mCalendar, now,
                     rebootStartTime);
-            
+
             long rebootIntervalMillis = rebootInterval*24*60*60*1000;
             if (DB || mReqRebootNoWait ||
                     (now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) {
@@ -639,7 +640,7 @@ public class Watchdog extends Thread {
                     rebootSystem("Checkin scheduled forced");
                     return;
                 }
-                
+
                 // Are we within the reboot window?
                 if (now < realStartTime) {
                     // Schedule alarm for next check interval.
@@ -654,7 +655,7 @@ public class Watchdog extends Thread {
                         rebootSystem("Checked scheduled range");
                         return;
                     }
-                    
+
                     // Schedule next alarm either within the window or in the
                     // next interval.
                     if ((now+recheckInterval) >= (realStartTime+rebootWindowMillis)) {
@@ -670,21 +671,25 @@ public class Watchdog extends Thread {
                 }
             }
         }
-        
+
         if (localLOGV) Log.v(TAG, "Scheduling next reboot alarm for "
                 + ((realStartTime-now)/1000/60) + "m from now");
         mAlarm.remove(mRebootIntent);
         mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent);
     }
-    
+
     /**
      * Perform a full reboot of the system.
      */
     void rebootSystem(String reason) {
         Log.i(TAG, "Rebooting system because: " + reason);
-        android.os.Power.reboot(reason);
+        try {
+            android.os.Power.reboot(reason);
+        } catch (IOException e) {
+            Log.e(TAG, "Reboot failed!", e);
+        }
     }
-    
+
     /**
      * Load the current Gservices settings for when
      * {@link #shouldWeBeBrutalLocked} will allow the brutality to happen.
@@ -700,12 +705,12 @@ public class Watchdog extends Thread {
                 mResolver, Settings.Gservices.MEMCHECK_MIN_ALARM,
                 MEMCHECK_DEFAULT_MIN_ALARM)) * 1000;
     }
-    
+
     /**
      * Determine whether it is a good time to kill, crash, or otherwise
      * plunder the current situation for the overall long-term benefit of
      * the world.
-     * 
+     *
      * @param curTime The current system time.
      * @return Returns null if this is a good time, else a String with the
      * text of why it is not a good time.
@@ -714,40 +719,40 @@ public class Watchdog extends Thread {
         if (mBattery == null || !mBattery.isPowered()) {
             return "battery";
         }
-        
+
         if (mMinScreenOff >= 0 && (mPower == null ||
                 mPower.timeSinceScreenOn() < mMinScreenOff)) {
             return "screen";
         }
-            
+
         if (mMinAlarm >= 0 && (mAlarm == null ||
                 mAlarm.timeToNextAlarm() < mMinAlarm)) {
             return "alarm";
         }
-            
+
         return null;
     }
-    
+
     /**
      * Compute the times during which we next would like to perform process
      * restarts.
-     * 
+     *
      * @param curTime The current system time.
      */
     void computeMemcheckTimesLocked(long curTime) {
         if (mMemcheckLastTime == curTime) {
             return;
         }
-        
+
         mMemcheckLastTime = curTime;
-        
+
         long memcheckExecStartTime = Settings.Gservices.getLong(
                 mResolver, Settings.Gservices.MEMCHECK_EXEC_START_TIME,
                 MEMCHECK_DEFAULT_EXEC_START_TIME);
         long memcheckExecEndTime = Settings.Gservices.getLong(
                 mResolver, Settings.Gservices.MEMCHECK_EXEC_END_TIME,
                 MEMCHECK_DEFAULT_EXEC_END_TIME);
-        
+
         mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
                 memcheckExecEndTime);
         if (mMemcheckExecEndTime < curTime) {
@@ -758,7 +763,7 @@ public class Watchdog extends Thread {
         }
         mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
                 memcheckExecStartTime);
-        
+
         if (localLOGV) {
             mCalendar.setTimeInMillis(curTime);
             Log.v(TAG, "Current Time: " + mCalendar);
@@ -768,7 +773,7 @@ public class Watchdog extends Thread {
             Log.v(TAG, "End Check Time: " + mCalendar);
         }
     }
-    
+
     static long computeCalendarTime(Calendar c, long curTime,
             long secondsSinceMidnight) {
 
@@ -782,7 +787,7 @@ public class Watchdog extends Thread {
         c.set(Calendar.MINUTE, val);
         c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60));
         c.set(Calendar.MILLISECOND, 0);
-        
+
         long newTime = c.getTimeInMillis();
         if (newTime < curTime) {
             // The given time (in seconds since midnight) has already passed for today, so advance
@@ -790,19 +795,19 @@ public class Watchdog extends Thread {
             c.add(Calendar.DAY_OF_MONTH, 1);
             newTime = c.getTimeInMillis();
         }
-            
+
         return newTime;
     }
-    
+
     @Override
     public void run() {
         while (true) {
             mCompleted = false;
             mHandler.sendEmptyMessage(MONITOR);
-            
+
             synchronized (this) {
                 long timeout = TIME_TO_WAIT;
-                
+
                 // NOTE: We use uptimeMillis() here because we do not want to increment the time we
                 // wait while asleep. If the device is asleep then the thing that we are waiting
                 // to timeout on is asleep as well and won't have a chance to run. Causing a false
@@ -827,22 +832,22 @@ public class Watchdog extends Thread {
                     continue;
                 }
             }
-        
-            // If we got here, that means that the system is most likely hung. 
-            // First send a SIGQUIT so that we can see where it was hung. Then 
+
+            // If we got here, that means that the system is most likely hung.
+            // First send a SIGQUIT so that we can see where it was hung. Then
             // kill this process so that the system will restart.
             String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null";
             EventLog.writeEvent(EVENT_LOG_TAG, name);
             Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
-            
+
             // Wait a bit longer before killing so we can make sure that the stacks are captured.
             try {
                 Thread.sleep(10*1000);
             } catch (InterruptedException e) {
             }
-            
+
             // Only kill the process if the debugger is not attached.
-            if (!Debug.isDebuggerConnected()) { 
+            if (!Debug.isDebuggerConnected()) {
                 Process.killProcess(Process.myPid());
             }
         }
index 7a0deff..f2483ff 100644 (file)
@@ -47,7 +47,6 @@ import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.BatteryManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.text.TextUtils;
@@ -67,7 +66,7 @@ import java.io.PrintWriter;
  * the IWifiManager interface. It also creates a WifiMonitor to listen
  * for Wifi-related events.
  *
- * {@hide}
+ * @hide
  */
 public class WifiService extends IWifiManager.Stub {
     private static final String TAG = "WifiService";
@@ -106,9 +105,9 @@ public class WifiService extends IWifiManager.Stub {
     private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
 
     // Wake lock used by driver-stop operation
-    private static PowerManager.WakeLock mDriverStopWakeLock;
+    private static PowerManager.WakeLock sDriverStopWakeLock;
     // Wake lock used by other operations
-    private static PowerManager.WakeLock mWakeLock;
+    private static PowerManager.WakeLock sWakeLock;
 
     private static final int MESSAGE_ENABLE_WIFI      = 0;
     private static final int MESSAGE_DISABLE_WIFI     = 1;
@@ -116,7 +115,7 @@ public class WifiService extends IWifiManager.Stub {
     private static final int MESSAGE_START_WIFI       = 3;
     private static final int MESSAGE_RELEASE_WAKELOCK = 4;
 
-    private WifiHandler mWifiHandler;
+    private final  WifiHandler mWifiHandler;
 
     /*
      * Map used to keep track of hidden networks presence, which
@@ -197,17 +196,17 @@ public class WifiService extends IWifiManager.Stub {
         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
 
         PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
-        mDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+        sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+        sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
         mWifiStateTracker.setReleaseWakeLockCallback(
                 new Runnable() {
                     public void run() {
                         mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
-                        synchronized (mDriverStopWakeLock) {
-                            if (mDriverStopWakeLock.isHeld()) {
+                        synchronized (sDriverStopWakeLock) {
+                            if (sDriverStopWakeLock.isHeld()) {
                                 if (DBG) Log.d(TAG, "Releasing driver-stop wakelock " +
-                                        mDriverStopWakeLock);
-                                mDriverStopWakeLock.release();
+                                        sDriverStopWakeLock);
+                                sDriverStopWakeLock.release();
                             }
                         }
                     }
@@ -435,7 +434,7 @@ public class WifiService extends IWifiManager.Stub {
          * before adding a new one
          */
         synchronized (mWifiHandler) {
-            mWakeLock.acquire();
+            sWakeLock.acquire();
             sendEnableMessage(enable, true);
         }
 
@@ -679,7 +678,7 @@ public class WifiService extends IWifiManager.Stub {
         if (!TextUtils.isEmpty(value)) {
             try {
                 config.priority = Integer.parseInt(value);
-            } catch (NumberFormatException e) {
+            } catch (NumberFormatException ignore) {
             }
         }
 
@@ -688,7 +687,7 @@ public class WifiService extends IWifiManager.Stub {
         if (!TextUtils.isEmpty(value)) {
             try {
                 config.hiddenSSID = Integer.parseInt(value) != 0;
-            } catch (NumberFormatException e) {
+            } catch (NumberFormatException ignore) {
             }
         }
 
@@ -697,7 +696,7 @@ public class WifiService extends IWifiManager.Stub {
         if (!TextUtils.isEmpty(value)) {
             try {
                 config.wepTxKeyIndex = Integer.parseInt(value);
-            } catch (NumberFormatException e) {
+            } catch (NumberFormatException ignore) {
             }
         }
 
@@ -823,7 +822,7 @@ public class WifiService extends IWifiManager.Stub {
             if (!TextUtils.isEmpty(priorityVal)) {
                 try {
                     currentPriority = Integer.parseInt(priorityVal);
-                } catch (NumberFormatException e) {
+                } catch (NumberFormatException ignore) {
                 }
             }
             doReconfig = currentPriority != config.priority;
@@ -1473,7 +1472,7 @@ public class WifiService extends IWifiManager.Stub {
                 /*
                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
                  * AND we are transitioning from a state in which the device was supposed
-                 * to stay awake and a state in which it is not supposed to stay awake.
+                 * to stay awake to a state in which it is not supposed to stay awake.
                  * If "stay awake" state is not changing, we do nothing, to avoid resetting
                  * the already-set timer.
                  */
@@ -1499,7 +1498,7 @@ public class WifiService extends IWifiManager.Stub {
          * of {@code 0} isn't really a plugged type, but rather an indication that the
          * device isn't plugged in at all, there is no bit value corresponding to a
          * {@code pluggedType} value of {@code 0}. That is why we shift by
-         * {@code pluggedType&nbsp;&mdash;&nbsp;1} instead of by {@code pluggedType}.
+         * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
          * @param stayAwakeConditions a bit string specifying which "plugged types" should
          * keep the device (and hence Wi-Fi) awake.
          * @param pluggedType the type of plug (USB, AC, or none) for which the check is
@@ -1519,33 +1518,44 @@ public class WifiService extends IWifiManager.Stub {
         msg.sendToTarget();
     }
 
+    private void sendStartMessage(boolean scanOnlyMode) {
+        if (DBG) Log.d(TAG, "sendStartMessage(" + scanOnlyMode + ")");
+        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+    }
+
     private void updateWifiState() {
         boolean wifiEnabled = getPersistedWifiEnabled();
         boolean airplaneMode = isAirplaneModeOn();
         boolean lockHeld = mLocks.hasLocks();
+        int strongestLockMode;
 
         boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
         boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
+        if (mDeviceIdle && lockHeld) {
+            strongestLockMode = mLocks.getStrongestLockMode();
+        } else {
+            strongestLockMode = WifiManager.WIFI_MODE_FULL;
+        }
 
         synchronized (mWifiHandler) {
             if (wifiShouldBeEnabled) {
                 if (wifiShouldBeStarted) {
-                    mWakeLock.acquire();
+                    sWakeLock.acquire();
                     sendEnableMessage(true, false);
-                    mWakeLock.acquire();
-                    mWifiHandler.sendEmptyMessage(MESSAGE_START_WIFI);
+                    sWakeLock.acquire();
+                    sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
                 } else {
                     int wakeLockTimeout =
                             Settings.Secure.getInt(
                                     mContext.getContentResolver(),
                                     Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                                     DEFAULT_WAKELOCK_TIMEOUT);
-                    mDriverStopWakeLock.acquire();
+                    sDriverStopWakeLock.acquire();
                     mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
                     mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
                 }
             } else {
-                mWakeLock.acquire();
+                sWakeLock.acquire();
                 sendEnableMessage(false, false);
             }
         }
@@ -1590,37 +1600,36 @@ public class WifiService extends IWifiManager.Stub {
         public void handleMessage(Message msg) {
             switch (msg.what) {
 
-                case MESSAGE_ENABLE_WIFI: {
+                case MESSAGE_ENABLE_WIFI:
                     setWifiEnabledBlocking(true, msg.arg1 == 1);
-                    /* fall through */
-                }
+                    sWakeLock.release();
+                    break;
 
-                case MESSAGE_START_WIFI: {
-                    mWifiStateTracker.startDriver();
-                    mWakeLock.release();
+                case MESSAGE_START_WIFI:
+                    mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+                    mWifiStateTracker.restart();
+                    sWakeLock.release();
                     break;
-                }
 
-                case MESSAGE_DISABLE_WIFI: {
+                case MESSAGE_DISABLE_WIFI:
+                    // a non-zero msg.arg1 value means the "enabled" setting
+                    // should be persisted
                     setWifiEnabledBlocking(false, msg.arg1 == 1);
-                    mWakeLock.release();
+                    sWakeLock.release();
                     break;
-                }
 
-                case MESSAGE_STOP_WIFI: {
-                    mWifiStateTracker.stopDriver();
+                case MESSAGE_STOP_WIFI:
+                    mWifiStateTracker.disconnectAndStop();
                     // don't release wakelock
                     break;
-                }
 
-                case MESSAGE_RELEASE_WAKELOCK: {
-                    synchronized (mDriverStopWakeLock) {
-                        if (mDriverStopWakeLock.isHeld()) {
-                            mDriverStopWakeLock.release();
+                case MESSAGE_RELEASE_WAKELOCK:
+                    synchronized (sDriverStopWakeLock) {
+                        if (sDriverStopWakeLock.isHeld()) {
+                            sDriverStopWakeLock.release();
                         }
                     }
                     break;
-                }
             }
         }
     }
@@ -1653,6 +1662,9 @@ public class WifiService extends IWifiManager.Stub {
                                          r.SSID == null ? "" : r.SSID);
             }
         }
+        pw.println();
+        pw.println("Locks held:");
+        mLocks.dump(pw);
     }
 
     private static String stateName(int wifiState) {
@@ -1674,11 +1686,13 @@ public class WifiService extends IWifiManager.Stub {
 
     private class WifiLock implements IBinder.DeathRecipient {
         String mTag;
+        int mLockMode;
         IBinder mBinder;
 
-        WifiLock(String tag, IBinder binder) {
+        WifiLock(int lockMode, String tag, IBinder binder) {
             super();
             mTag = tag;
+            mLockMode = lockMode;
             mBinder = binder;
             try {
                 mBinder.linkToDeath(this, 0);
@@ -1694,30 +1708,40 @@ public class WifiService extends IWifiManager.Stub {
         }
 
         public String toString() {
-            return "WifiLock{" + mTag + " binder=" + mBinder + "}";
+            return "WifiLock{" + mTag + " type=" + mLockMode + " binder=" + mBinder + "}";
         }
     }
 
     private class LockList {
-        private ArrayList<WifiLock> mList;
+        private List<WifiLock> mList;
 
-        public LockList() {
+        private LockList() {
             mList = new ArrayList<WifiLock>();
         }
 
-        public boolean hasLocks() {
+        private synchronized boolean hasLocks() {
             return !mList.isEmpty();
         }
 
-        public void addLock(WifiLock lock) {
-            int index = findLockByBinder(lock.mBinder);
-            if (index < 0) {
+        private synchronized int getStrongestLockMode() {
+            if (mList.isEmpty()) {
+                return WifiManager.WIFI_MODE_FULL;
+            }
+            for (WifiLock l : mList) {
+                if (l.mLockMode == WifiManager.WIFI_MODE_FULL) {
+                    return WifiManager.WIFI_MODE_FULL;
+                }
+            }
+            return WifiManager.WIFI_MODE_SCAN_ONLY;
+        }
+
+        private void addLock(WifiLock lock) {
+            if (findLockByBinder(lock.mBinder) < 0) {
                 mList.add(lock);
-            } else {
             }
         }
 
-        public WifiLock removeLock(IBinder binder) {
+        private WifiLock removeLock(IBinder binder) {
             int index = findLockByBinder(binder);
             if (index >= 0) {
                 return mList.remove(index);
@@ -1733,11 +1757,28 @@ public class WifiService extends IWifiManager.Stub {
                     return i;
             return -1;
         }
+
+        private synchronized void clear() {
+            if (!mList.isEmpty()) {
+                mList.clear();
+                updateWifiState();
+            }
+        }
+
+        private void dump(PrintWriter pw) {
+            for (WifiLock l : mList) {
+                pw.print("    ");
+                pw.println(l);
+            }
+        }
     }
 
-    public boolean acquireWifiLock(IBinder binder, String tag) {
+    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
-        WifiLock wifiLock = new WifiLock(tag, binder);
+        if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+            return false;
+        }
+        WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
         synchronized (mLocks) {
             return acquireWifiLockLocked(wifiLock);
         }
index 6c58997..d2149f1 100644 (file)
@@ -9659,7 +9659,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
         if (DEBUG_BROADCAST) Log.v(
             TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
             + " ordered=" + ordered);
-
+        if ((resultTo != null) && !ordered) {
+            Log.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
+        }
+        
         // Handle special intents: if this broadcast is from the package
         // manager about a package being removed, we need to remove all of
         // its activities from the history stack.
index 5de1e4b..b18aaf7 100644 (file)
@@ -208,10 +208,12 @@ class PendingIntentRecord extends IIntentSender.Stub {
                         break;
                     case IActivityManager.INTENT_SENDER_BROADCAST:
                         try {
+                            // If a completion callback has been requested, require
+                            // that the broadcast be delivered synchronously
                             owner.broadcastIntentInPackage(key.packageName, uid,
                                     finalIntent, resolvedType,
                                     finishedReceiver, code, null, null, null,
-                                    false, false);
+                                    (finishedReceiver != null), false);
                             sendFinish = false;
                         } catch (RuntimeException e) {
                             Log.w(ActivityManagerService.TAG,
index a4b8b47..d3a19c5 100644 (file)
@@ -921,7 +921,12 @@ final class ServiceStateTracker extends Handler
                         // need adjust time to reflect default timezone setting
                         long tzOffset;
                         tzOffset = zone.getOffset(System.currentTimeMillis());
-                        setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset);
+                        if (getAutoTime()) {
+                            setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset);
+                        } else {
+                            // Adjust the saved NITZ time to account for tzOffset.
+                            mSavedTime = mSavedTime - tzOffset;
+                        }
                     } else if (iso.equals("")){
                         // Country code not found.  This is likely a test network.
                         // Get a TimeZone based only on the NITZ parameters (best guess).
index 2d2f9ae..0d56fb7 100644 (file)
@@ -52,8 +52,6 @@ class CommandParamsFactory extends Handler {
     static final int REFRESH_NAA_INIT                       = 0x03;
     static final int REFRESH_UICC_RESET                     = 0x04;
 
-    private static final String TAG = "CmdParamsFactory";
-
     static synchronized CommandParamsFactory getInstance(Handler caller,
             SIMFileHandler fh) {
         if (sInstance != null) {
@@ -173,6 +171,7 @@ class CommandParamsFactory extends Handler {
         }
     }
 
+    @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
         case MSG_ID_LOAD_ICON_DONE:
@@ -751,7 +750,7 @@ class CommandParamsFactory extends Handler {
      private boolean processPlayTone(CommandDetails cmdDet,
             List<ComprehensionTlv> ctlvs) throws ResultException {
 
-        StkLog.d(TAG, "process PlayTone");
+        StkLog.d(this, "process PlayTone");
 
         Tone tone = null;
         TextMessage textMsg = new TextMessage();
@@ -816,7 +815,7 @@ class CommandParamsFactory extends Handler {
      */
      private boolean processSetupCall(CommandDetails cmdDet,
             List<ComprehensionTlv> ctlvs) throws ResultException {
-        StkLog.d(TAG, "process SetupCall");
+        StkLog.d(this, "process SetupCall");
 
         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
         ComprehensionTlv ctlv = null;
index 2219f58..a63d1ca 100644 (file)
@@ -67,7 +67,6 @@ class IconLoader extends Handler {
     // CLUT entry size, {Red, Green, Black}
     private static final int CLUT_ENTRY_SIZE = 3;
 
-    static private final String TAG = "STK IconLoader"; 
 
     private IconLoader(Looper looper , SIMFileHandler fh) {
         super(looper);
@@ -128,6 +127,7 @@ class IconLoader extends Handler {
         readId();
     }
 
+    @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
 
@@ -163,7 +163,7 @@ class IconLoader extends Handler {
                 break;
             }
         } catch (Exception e) {
-            Log.d(TAG, "Icon load failed");
+            StkLog.d(this, "Icon load failed");
             // post null icon back to the caller.
             postIcon();
         }
@@ -251,10 +251,10 @@ class IconLoader extends Handler {
                 bitIndex = 7;
             }
             pixels[pixelIndex++] = bitToBnW((currentByte >> bitIndex-- ) & 0x01);
-        };
+        }
 
         if (pixelIndex != numOfPixels) {
-            Log.e(TAG, "parse end and size error");
+            StkLog.d("IconLoader", "parseToBnW; size error");
         }
         return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
     }
index e1a20f6..7120a37 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony.gsm.stk;
 
-import android.util.Log;
-
 /**
  * {@hide}
  */
@@ -37,8 +35,6 @@ public class ImageDescriptor {
 
     public static final int ID_LENGTH = 9;
 
-    private static final String TAG = "ImageDescriptor";
-
     ImageDescriptor() {
         width = 0;
         height = 0;
@@ -72,7 +68,7 @@ public class ImageDescriptor {
 
             d.length = ((rawData[valueIndex++] & 0xff) << 8 | (rawData[valueIndex++] & 0xff));
         } catch (IndexOutOfBoundsException e) {
-            Log.d(TAG, "failed parsing image descriptor");
+            StkLog.d("ImageDescripter", "parse; failed parsing image descriptor");
             d = null;
         }
         return d;
index 746b1f1..c890d1c 100644 (file)
@@ -20,126 +20,124 @@ import com.android.internal.telephony.gsm.SIMFileHandler;
 import com.android.internal.telephony.gsm.SimUtils;
 
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
+import android.os.HandlerState;
+import android.os.HandlerStateMachine;
 import android.os.Message;
 
-import java.util.concurrent.BlockingQueue;
-
 /**
- * Class used for queuing raw ril messages, decoding them into CommanParams 
- * objects and sending the result back to the STK Service. 
- *
+ * Class used for queuing raw ril messages, decoding them into CommanParams
+ * objects and sending the result back to the STK Service.
  */
-class RilMessageDecoder extends Handler {
+class RilMessageDecoder extends HandlerStateMachine {
 
     // members
-    private final BlockingQueue<RilMessage> mInQueue;
     private static RilMessageDecoder sInstance = null;
     private CommandParamsFactory mCmdParamsFactory = null;
     private RilMessage mCurrentRilMessage = null;
     private Handler mCaller = null;
 
+    // States
+    private StateStart mStateStart = new StateStart();
+    private StateCmdParamsReady mStateCmdParamsReady = new StateCmdParamsReady();
+
     // constants
     static final int START = 1;
     static final int CMD_PARAMS_READY = 2;
 
-    static RilMessageDecoder getInstance(BlockingQueue<RilMessage> inQ,
-            Handler caller, SIMFileHandler fh) {
-        if (sInstance != null) {
-            return sInstance;
+    static synchronized RilMessageDecoder getInstance(Handler caller, SIMFileHandler fh) {
+        if (sInstance == null) {
+            sInstance = new RilMessageDecoder(caller, fh);
         }
-        if (inQ != null) {
-            HandlerThread thread = new HandlerThread("Stk RIL Messages decoder");
-            thread.start();
-            return new RilMessageDecoder(thread.getLooper(), inQ, caller, fh);
-        }
-        return null;
+        return sInstance;
     }
 
-    private RilMessageDecoder(Looper looper, BlockingQueue<RilMessage> inQ,
-            Handler caller, SIMFileHandler fh) {
-        super(looper);
-        mInQueue = inQ;
+    private RilMessageDecoder(Handler caller, SIMFileHandler fh) {
+        super("RilMessageDecoder");
+        setDbg(false);
+        setInitialState(mStateStart);
+
         mCaller = caller;
-        mCmdParamsFactory = CommandParamsFactory.getInstance(this, fh);
+        mCmdParamsFactory = CommandParamsFactory.getInstance(this.getHandler(), fh);
     }
 
-    public void handleMessage(Message msg) {
-        switch(msg.what) {
-        case START:
-            start();
-            break;
-        case CMD_PARAMS_READY:
-            mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1);
-            mCurrentRilMessage.mData = msg.obj;
-            sendCmdForExecution();
-            break;
+    class StateStart extends HandlerState {
+        @Override public void processMessage(Message msg) {
+            if (msg.what == START) {
+                if (decodeMessageParams((RilMessage)msg.obj)) {
+                    transitionTo(mStateCmdParamsReady);
+                }
+            } else {
+                StkLog.d(this, "StateStart unexpected expecting START=" +
+                         START + " got " + msg.what);
+            }
         }
     }
 
-    private void start() {
-        boolean interrupted = false;
-        try {
-            while (true) {
-                try {
-                    mCurrentRilMessage = mInQueue.take();
-                    StkLog.d(this, "Decoding new message");
-                    break;
-                } catch (InterruptedException e) {
-                    interrupted = true;
-                    // fall through and retry
-                }
-            }
-        } finally {
-            if (interrupted) {
-                Thread.currentThread().interrupt();
-            }
-            if (mCurrentRilMessage != null) {
-                decodeMessage(mCurrentRilMessage);
+    class StateCmdParamsReady extends HandlerState {
+        @Override public void processMessage(Message msg) {
+            if (msg.what == CMD_PARAMS_READY) {
+                mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1);
+                mCurrentRilMessage.mData = msg.obj;
+                sendCmdForExecution(mCurrentRilMessage);
+                transitionTo(mStateStart);
+            } else {
+                StkLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY="
+                         + CMD_PARAMS_READY + " got " + msg.what);
+                deferMessage(msg);
             }
         }
     }
 
-    private void decodeMessage(RilMessage msg) {
-        switch(msg.mId) {
+    public void startDecodingMessageParams(RilMessage rilMsg) {
+        Message msg = obtainMessage(START);
+        msg.obj = rilMsg;
+        sendMessage(msg);
+    }
+
+    private boolean decodeMessageParams(RilMessage rilMsg) {
+        boolean decodingStarted;
+
+        mCurrentRilMessage = rilMsg;
+        switch(rilMsg.mId) {
         case Service.MSG_ID_SESSION_END:
         case Service.MSG_ID_CALL_SETUP:
             mCurrentRilMessage.mResCode = ResultCode.OK;
-            sendCmdForExecution();
+            sendCmdForExecution(mCurrentRilMessage);
+            decodingStarted = false;
             break;
         case Service.MSG_ID_PROACTIVE_COMMAND:
         case Service.MSG_ID_EVENT_NOTIFY:
         case Service.MSG_ID_REFRESH:
             byte[] rawData = null;
             try {
-                rawData = SimUtils.hexStringToBytes((String) msg.mData);
+                rawData = SimUtils.hexStringToBytes((String) rilMsg.mData);
             } catch (Exception e) {
                 // zombie messages are dropped
-                getNextMessage();
-                return;
+                StkLog.d(this, "decodeMessageParams dropping zombie messages");
+                decodingStarted = false;
+                break;
             }
             try {
-                // Start asynch parsing of the command parameters. 
+                // Start asynch parsing of the command parameters.
                 mCmdParamsFactory.make(BerTlv.decode(rawData));
+                decodingStarted = true;
             } catch (ResultException e) {
                 // send to Service for proper RIL communication.
                 mCurrentRilMessage.mResCode = e.result();
-                sendCmdForExecution();
+                sendCmdForExecution(mCurrentRilMessage);
+                decodingStarted = false;
             }
             break;
+        default:
+            decodingStarted = false;
+            break;
         }
+        return decodingStarted;
     }
 
-    private void sendCmdForExecution() {
+    private void sendCmdForExecution(RilMessage rilMsg) {
         Message msg = mCaller.obtainMessage(Service.MSG_ID_RIL_MSG_DECODED,
-                new RilMessage(mCurrentRilMessage));
+                new RilMessage(rilMsg));
         msg.sendToTarget();
-        getNextMessage();
-    }
-
-    private void getNextMessage() {
-        Message nextMsg = this.obtainMessage(START);
-        nextMsg.sendToTarget();
     }
-}
\ No newline at end of file
+}
index aae9b30..95602d1 100644 (file)
@@ -123,11 +123,8 @@ public class Service extends Handler implements AppInterface {
     private StkCmdMessage mCurrntCmd = null;
     private StkCmdMessage mMenuCmd = null;
 
-    private BlockingQueue<RilMessage> mRilMessagesQ = null;
     private RilMessageDecoder mMsgDecoder = null;
 
-    public static final String TAG = "STK";
-
     // Service constants.
     static final int MSG_ID_SESSION_END              = 1;
     static final int MSG_ID_PROACTIVE_COMMAND        = 2;
@@ -159,12 +156,8 @@ public class Service extends Handler implements AppInterface {
         mCmdIf = ci;
         mContext = context;
 
-        // Initialize a blocking queue to be used for ril messages, and a 
-        // RilMessagesDecoder for decoding the messages into a CommandParams. 
-        // Each CommandParams is put into a SynchronousQueue and pulled by the 
-        // Service take() when its ready to handle the next command.
-        mRilMessagesQ = new LinkedBlockingQueue<RilMessage>();
-        mMsgDecoder = RilMessageDecoder.getInstance(mRilMessagesQ, this, fh);
+        // Get the RilMessagesDecoder for decoding the messages.
+        mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
 
         // Register ril events handling.
         mCmdIf.setOnStkSessionEnd(this, MSG_ID_SESSION_END, null);
@@ -177,8 +170,6 @@ public class Service extends Handler implements AppInterface {
 
         // Register for SIM ready event.
         mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null);
-        // start decoding ril messages.
-        mMsgDecoder.obtainMessage(RilMessageDecoder.START).sendToTarget();
     }
 
     private void handleRilMsg(RilMessage rilMsg) {
@@ -470,9 +461,7 @@ public class Service extends Handler implements AppInterface {
         return getInstance(null, null, null, null, null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
     public void handleMessage(Message msg) {
 
         switch (msg.what) {
@@ -492,10 +481,10 @@ public class Service extends Handler implements AppInterface {
                     }
                 }
             }
-            mRilMessagesQ.add(new RilMessage(msg.what, data));
+            mMsgDecoder.startDecodingMessageParams(new RilMessage(msg.what, data));
             break;
         case MSG_ID_CALL_SETUP:
-            mRilMessagesQ.add(new RilMessage(msg.what, null));
+            mMsgDecoder.startDecodingMessageParams(new RilMessage(msg.what, null));
             break;
         case MSG_ID_SIM_LOADED:
             break;
@@ -550,6 +539,7 @@ public class Service extends Handler implements AppInterface {
         switch (resMsg.resCode) {
         case HELP_INFO_REQUIRED:
             helpRequired = true;
+            // fall through
         case OK:
         case PRFRMD_WITH_PARTIAL_COMPREHENSION:
         case PRFRMD_WITH_MISSING_INFO:
index cd02a8b..2cf87ba 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony.gsm.stk;
 
-import android.util.Log;
-
 import com.android.internal.telephony.gsm.GsmAlphabet;
 import com.android.internal.telephony.gsm.SimUtils;
 import com.android.internal.telephony.gsm.stk.Duration.TimeUnit;
@@ -27,8 +25,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 abstract class ValueParser {
-    
-    private static final String TAG = "ValueParser";
 
     /**
      * Search for a Command Details object from a list.
@@ -186,7 +182,7 @@ abstract class ValueParser {
      */
     static ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv)
             throws ResultException {
-        Log.d(TAG, "retrieveIconIdList:");
+        StkLog.d("ValueParser", "retrieveItemsIconId:");
         ItemsIconId id = new ItemsIconId();
 
         byte[] rawValue = ctlv.getRawValue();
index 9174fb8..c4bca41 100644 (file)
 
 package android.test;
 
+import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.SystemClock;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -26,18 +28,33 @@ import android.view.ViewGroup;
 
 /**
  * Reusable methods for generating touch events. These methods can be used with
- * InstrumentationTestCase or ActivityTestCases to simulate user interaction with
+ * InstrumentationTestCase or ActivityInstrumentationTestCase2 to simulate user interaction with
  * the application through a touch screen.
  */
 public class TouchUtils {
     
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way down
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void dragQuarterScreenDown(ActivityInstrumentationTestCase test) {
-        int screenHeight = test.getActivity().getWindowManager().getDefaultDisplay().getHeight();
-        int screenWidth = test.getActivity().getWindowManager().getDefaultDisplay().getWidth();
+        dragQuarterScreenDown(test, test.getActivity());
+    }
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way down
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     */
+    public static void dragQuarterScreenDown(InstrumentationTestCase test, Activity activity) {
+        Display display = activity.getWindowManager().getDefaultDisplay();
+        int screenHeight = display.getHeight();
+        int screenWidth = display.getWidth();
         
         final float x = screenWidth / 2.0f;
         final float fromY = screenHeight * 0.5f;
@@ -48,11 +65,26 @@ public class TouchUtils {
     
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way up
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void dragQuarterScreenUp(ActivityInstrumentationTestCase test) {
-        int screenHeight = test.getActivity().getWindowManager().getDefaultDisplay().getHeight();
-        int screenWidth = test.getActivity().getWindowManager().getDefaultDisplay().getWidth();
+        dragQuarterScreenDown(test, test.getActivity());
+    }
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way up
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     */
+    public static void dragQuarterScreenUp(InstrumentationTestCase test, Activity activity) {
+        Display display = activity.getWindowManager().getDefaultDisplay();
+        int screenHeight = display.getHeight();
+        int screenWidth = display.getWidth();
         
         final float x = screenWidth / 2.0f;
         final float fromY = screenHeight * 0.5f;
@@ -62,13 +94,31 @@ public class TouchUtils {
     }
     
     /**
-     * Scroll a VirewGroup to the bottom by repeatedly calling
-     * {@link #dragQuarterScreenUp(ActivityInstrumentationTestCase)}
+     * Scroll a ViewGroup to the bottom by repeatedly calling
+     * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The ViewGroup that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void scrollToBottom(ActivityInstrumentationTestCase test, ViewGroup v) {
+        scrollToBottom(test, test.getActivity(), v);
+    }
+    
+    /**
+     * Scroll a ViewGroup to the bottom by repeatedly calling
+     * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The ViewGroup that should be dragged
+     */
+    public static void scrollToBottom(InstrumentationTestCase test, Activity activity,
+            ViewGroup v) {
         View firstChild;
         int firstId = Integer.MIN_VALUE;
         int firstTop = Integer.MIN_VALUE; 
@@ -77,7 +127,7 @@ public class TouchUtils {
         do {
             prevId = firstId;
             prevTop = firstTop;
-            TouchUtils.dragQuarterScreenUp(test);
+            TouchUtils.dragQuarterScreenUp(test, activity);
             firstChild = v.getChildAt(0);
             firstId = firstChild.getId();
             firstTop = firstChild.getTop(); 
@@ -86,12 +136,29 @@ public class TouchUtils {
 
     /**
      * Scroll a ViewGroup to the top by repeatedly calling
-     * {@link #dragQuarterScreenDown(ActivityInstrumentationTestCase)}
+     * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The ViewGroup that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void scrollToTop(ActivityInstrumentationTestCase test, ViewGroup v) {
+        scrollToTop(test, test.getActivity(), v);
+    }
+    
+    /**
+     * Scroll a ViewGroup to the top by repeatedly calling
+     * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The ViewGroup that should be dragged
+     */
+    public static void scrollToTop(InstrumentationTestCase test, Activity activity, ViewGroup v) {
         View firstChild;
         int firstId = Integer.MIN_VALUE;
         int firstTop = Integer.MIN_VALUE; 
@@ -100,7 +167,7 @@ public class TouchUtils {
         do {
             prevId = firstId;
             prevTop = firstTop;
-            TouchUtils.dragQuarterScreenDown(test);
+            TouchUtils.dragQuarterScreenDown(test, activity);
             firstChild = v.getChildAt(0);
             firstId = firstChild.getId();
             firstTop = firstChild.getTop(); 
@@ -110,22 +177,57 @@ public class TouchUtils {
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v) {
-        dragViewToBottom(test, v, 4);
+        dragViewToBottom(test, test.getActivity(), v, 4);
     }
     
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The view that should be dragged
+     */
+    public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v) {
+        dragViewToBottom(test, activity, v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
-    public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v, int stepCount) {
-        int screenHeight = test.getActivity().getWindowManager().getDefaultDisplay().getHeight();
+    @Deprecated
+    public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v,
+            int stepCount) {
+        dragViewToBottom(test, test.getActivity(), v, stepCount);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v,
+            int stepCount) {
+        int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
 
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
@@ -139,15 +241,14 @@ public class TouchUtils {
         
         drag(test, x, x, fromY, toY, stepCount);
     }
-    
+
     /**
      * Simulate touching the center of a view and releasing quickly (before the tap timeout).
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void tapView(InstrumentationTestCase test, View v) {
-
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
         
@@ -181,10 +282,10 @@ public class TouchUtils {
     }
 
     /**
-     * Simulate touching the center of a view and cancelling (so no on click should
+     * Simulate touching the center of a view and cancelling (so no onClick should
      * fire, etc).
      *
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void touchAndCancelView(InstrumentationTestCase test, View v) {
@@ -215,15 +316,14 @@ public class TouchUtils {
         inst.waitForIdleSync();
 
     }
-    
+
     /**
      * Simulate touching the center of a view and releasing.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void clickView(InstrumentationTestCase test, View v) {
-
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
         
@@ -266,10 +366,25 @@ public class TouchUtils {
     /**
      * Simulate touching the center of a view, holding until it is a long press, and then releasing.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be clicked
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void longClickView(ActivityInstrumentationTestCase test, View v) {
+        longClickView((InstrumentationTestCase) test, v);
+    }
+
+    /**
+     * Simulate touching the center of a view, holding until it is a long press, and then releasing.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     */
+    public static void longClickView(InstrumentationTestCase test, View v) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
         
@@ -284,7 +399,8 @@ public class TouchUtils {
         long downTime = SystemClock.uptimeMillis();
         long eventTime = SystemClock.uptimeMillis();
 
-        MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
 
@@ -306,25 +422,56 @@ public class TouchUtils {
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v) {
-        dragViewToTop(test, v, 4);
+        dragViewToTop((InstrumentationTestCase) test, v, 4);
     }
     
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
+    @Deprecated
     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v, int stepCount) {
+        dragViewToTop((InstrumentationTestCase) test, v, stepCount);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     */
+    public static void dragViewToTop(InstrumentationTestCase test, View v) {
+        dragViewToTop(test, v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void dragViewToTop(InstrumentationTestCase test, View v, int stepCount) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
         
@@ -379,11 +526,33 @@ public class TouchUtils {
             // Same as left -- do nothing
         }
     }
+
+    /**
+     * Simulate touching a view and dragging it by the specified amount.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param deltaX Amount to drag horizontally in pixels
+     * @param deltaY Amount to drag vertically in pixels
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewBy(ActivityInstrumentationTestCase test, View v, int gravity,
+            int deltaX, int deltaY) {
+        return dragViewBy((InstrumentationTestCase) test, v, gravity, deltaX, deltaY);
+    }
     
     /**
      * Simulate touching a view and dragging it by the specified amount.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
@@ -391,8 +560,12 @@ public class TouchUtils {
      * @param deltaY Amount to drag vertically in pixels
      * 
      * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
-    public static int dragViewBy(ActivityInstrumentationTestCase test, View v, int gravity, int deltaX,
+    public static int dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX,
             int deltaY) {
         int[] xy = new int[2];
         
@@ -411,7 +584,7 @@ public class TouchUtils {
     /**
      * Simulate touching a view and dragging it to a specified location.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
@@ -419,8 +592,31 @@ public class TouchUtils {
      * @param toY Final location of the view after dragging
      * 
      * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
-    public static int dragViewTo(ActivityInstrumentationTestCase test, View v, int gravity, int toX, int toY) {
+    @Deprecated
+    public static int dragViewTo(ActivityInstrumentationTestCase test, View v, int gravity, int toX,
+            int toY) {
+        return dragViewTo((InstrumentationTestCase) test, v, gravity, toX, toY);
+    }
+
+    /**
+     * Simulate touching a view and dragging it to a specified location.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     */
+    public static int dragViewTo(InstrumentationTestCase test, View v, int gravity, int toX,
+            int toY) {
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
@@ -440,7 +636,28 @@ public class TouchUtils {
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewToX(ActivityInstrumentationTestCase test, View v, int gravity,
+            int toX) {
+        return dragViewToX((InstrumentationTestCase) test, v, gravity, toX);
+    }
+
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
+     * 
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
@@ -448,7 +665,7 @@ public class TouchUtils {
      * 
      * @return distance in pixels covered by the drag
      */
-    public static int dragViewToX(ActivityInstrumentationTestCase test, View v, int gravity, int toX) {
+    public static int dragViewToX(InstrumentationTestCase test, View v, int gravity, int toX) {
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
@@ -466,7 +683,28 @@ public class TouchUtils {
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves vertically.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewToY(ActivityInstrumentationTestCase test, View v, int gravity,
+            int toY) {
+        return dragViewToY((InstrumentationTestCase) test, v, gravity, toY);
+    }
+    
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves vertically.
+     * 
+     * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
@@ -474,7 +712,7 @@ public class TouchUtils {
      * 
      * @return distance in pixels covered by the drag
      */
-    public static int dragViewToY(ActivityInstrumentationTestCase test, View v, int gravity, int toY) {
+    public static int dragViewToY(InstrumentationTestCase test, View v, int gravity, int toY) {
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
@@ -489,18 +727,39 @@ public class TouchUtils {
         return deltaY;
     }
     
+
     /**
      * Simulate touching a specific location and dragging to a new location.
      * 
-     * @param test The test cast that is being run
+     * @param test The test case that is being run
      * @param fromX X coordinate of the initial touch, in screen coordinates
      * @param toX Xcoordinate of the drag destination, in screen coordinates
      * @param fromY X coordinate of the initial touch, in screen coordinates
      * @param toY Y coordinate of the drag destination, in screen coordinates
      * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
      */
-    public static void drag(ActivityInstrumentationTestCase test, float fromX, float toX, float fromY, float toY,
-            int stepCount) {
+    @Deprecated
+    public static void drag(ActivityInstrumentationTestCase test, float fromX, float toX,
+            float fromY, float toY, int stepCount) {
+        drag((InstrumentationTestCase) test, fromX, toX, fromY, toY, stepCount);
+    }
+    
+    /**
+     * Simulate touching a specific location and dragging to a new location.
+     * 
+     * @param test The test case that is being run
+     * @param fromX X coordinate of the initial touch, in screen coordinates
+     * @param toX Xcoordinate of the drag destination, in screen coordinates
+     * @param fromY X coordinate of the initial touch, in screen coordinates
+     * @param toY Y coordinate of the drag destination, in screen coordinates
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void drag(InstrumentationTestCase test, float fromX, float toX, float fromY,
+            float toY, int stepCount) {
         Instrumentation inst = test.getInstrumentation();
 
         long downTime = SystemClock.uptimeMillis();
@@ -531,5 +790,4 @@ public class TouchUtils {
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
     }
-
 }
index 9eb3e94..d970de3 100644 (file)
@@ -67,7 +67,8 @@ public class GoogleHttpClientTest extends AndroidTestCase {
     @LargeTest
     public void testThreadCheck() throws Exception {
         ContentResolver resolver = getContext().getContentResolver();
-        GoogleHttpClient client = new GoogleHttpClient(resolver, "Test");
+        GoogleHttpClient client = new GoogleHttpClient(resolver, "Test",
+                false /* no gzip */);
 
         try {
             // Note: we must test against a real server, because the connection
@@ -101,7 +102,8 @@ public class GoogleHttpClientTest extends AndroidTestCase {
 
         // TODO: Use a MockContentProvider/MockContentResolver instead.
         ContentResolver resolver = getContext().getContentResolver();
-        GoogleHttpClient client = new GoogleHttpClient(resolver, "Test");
+        GoogleHttpClient client = new GoogleHttpClient(resolver, "Test",
+                false /* not gzip capable */);
         Settings.Gservices.putString(resolver,
                 "url:test", "http://foo.bar/ rewrite " + mServerUrl + "new/");
 
diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/HandlerStateMachineTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/HandlerStateMachineTest.java
new file mode 100644 (file)
index 0000000..29045a3
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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 com.android.unit_tests.os;
+
+import junit.framework.TestCase;
+import java.util.Vector;
+
+import android.os.Handler;
+import android.os.HandlerState;
+import android.os.HandlerStateMachine;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.Message;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+public class HandlerStateMachineTest extends TestCase {
+    private static final int TEST_WHAT_1 = 1;
+    private static final int TEST_WHAT_2 = 2;
+
+    private static final boolean DBG = false;
+    private static final String TAG = "HandlerStateMachineTest";
+
+    private boolean mDidEnter = false;
+    private boolean mDidExit = false;
+    private Vector<Integer> mGotMessagesWhat = new Vector<Integer>();
+
+    /**
+     * This test statemachine has two states, it receives
+     * two messages in state mS1 deferring them until what == TEST_WHAT_2
+     * and then transitions to state mS2. State mS2 should then receive
+     * both of the deferred messages first TEST_WHAT_1 and then TEST_WHAT_2.
+     * When TEST_WHAT_2 is received it invokes notifyAll so the test can
+     * conclude.
+     */
+    class StateMachine1 extends HandlerStateMachine {
+        StateMachine1(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+            setInitialState(mS1);
+        }
+
+        class S1 extends HandlerState {
+            @Override public void enter(Message message) {
+                mDidEnter = true;
+            }
+
+            @Override public void processMessage(Message message) {
+                deferMessage(message);
+                if (message.what == TEST_WHAT_2) {
+                    transitionTo(mS2);
+                }
+            }
+
+            @Override public void exit(Message message) {
+                mDidExit = true;
+            }
+        }
+
+        class S2 extends HandlerState {
+            @Override public void processMessage(Message message) {
+                mGotMessagesWhat.add(message.what);
+                if (message.what == TEST_WHAT_2) {
+                    synchronized (mThisSm) {
+                        mThisSm.notifyAll();
+                    }
+                }
+            }
+        }
+
+        private StateMachine1 mThisSm;
+        private S1 mS1 = new S1();
+        private S2 mS2 = new S2();
+    }
+
+    @SmallTest
+    public void testStateMachine1() throws Exception {
+        StateMachine1 sm1 = new StateMachine1("sm1");
+        if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E");
+
+        synchronized (sm1) {
+            // Send two messages
+            sm1.sendMessage(sm1.obtainMessage(TEST_WHAT_1));
+            sm1.sendMessage(sm1.obtainMessage(TEST_WHAT_2));
+
+            try {
+                // wait for the messages to be handled
+                sm1.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertTrue(mDidEnter);
+        assertTrue(mDidExit);
+        assertTrue(mGotMessagesWhat.size() == 2);
+        assertTrue(mGotMessagesWhat.get(0) == TEST_WHAT_1);
+        assertTrue(mGotMessagesWhat.get(1) == TEST_WHAT_2);
+        if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X");
+    }
+}
index 8eb5ca0..8c642f4 100644 (file)
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     
     <application>
         <uses-library android:name="android.test.runner" />
index 6844de7..1c3d671 100644 (file)
@@ -180,5 +180,9 @@ public class CookieTest extends AndroidTestCase {
         assertTrue(cookie.equals("a=b; a=c"));
         cookie = mCookieManager.getCookie(url);
         assertTrue(cookie.equals("a=c"));
+
+        mCookieManager.setCookie(url, "a=d");
+        cookie = mCookieManager.getCookie(url + "/wee");
+        assertTrue(cookie.equals("a=b; a=c; a=d"));
     }
 }
index 9913bfc..8106a50 100644 (file)
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+        
+        <activity android:name="android.widget.AutoCompleteTextViewSimple" 
+                  android:label="AutoCompleteTextViewSimple">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
 
     </application>
 
diff --git a/tests/FrameworkTest/res/layout/autocompletetextview_simple.xml b/tests/FrameworkTest/res/layout/autocompletetextview_simple.xml
new file mode 100644 (file)
index 0000000..d408a86
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/samples/SampleCode/res/layout/baseline_1.xml
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layout"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <AutoCompleteTextView
+        android:id="@+id/autocompletetextview1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:inputType="text|textAutoComplete"
+        android:completionThreshold="1" />
+        />
+
+</LinearLayout>
diff --git a/tests/FrameworkTest/src/android/widget/AutoCompleteTextViewSimple.java b/tests/FrameworkTest/src/android/widget/AutoCompleteTextViewSimple.java
new file mode 100644 (file)
index 0000000..af16cf8
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.widget;
+
+import com.android.frameworktest.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+public class AutoCompleteTextViewSimple extends Activity 
+        implements OnItemClickListener, OnItemSelectedListener {
+
+    private final String LOG_TAG = "AutoCompleteTextViewSimple";
+    
+    private AutoCompleteTextView mTextView;
+    
+    /** These are cleared by resetItemListeners(), and set by the callback listeners */
+    public boolean mItemClickCalled;
+    public int mItemClickPosition;
+    public boolean mItemSelectedCalled;
+    public int mItemSelectedPosition;
+    public boolean mNothingSelectedCalled;
+
+    @Override
+    protected void onCreate(Bundle icicle)
+    {
+        // Be sure to call the super class.
+        super.onCreate(icicle);
+
+        // setup layout & views
+        setContentView(R.layout.autocompletetextview_simple);
+        mTextView = (AutoCompleteTextView) findViewById(R.id.autocompletetextview1);
+        
+        // configure callbacks used for monitoring
+        mTextView.setOnItemClickListener(this);
+        mTextView.setOnItemSelectedListener(this);
+        resetItemListeners();
+        
+        setStringAdapter(5, "a");
+    }
+
+    /**
+     * @return The AutoCompleteTextView used in this test activity.
+     */
+    public AutoCompleteTextView getTextView() {
+        return mTextView;
+    }
+
+    /**
+     * Set the autocomplete data to an adapter containing 0..n strings with a consistent prefix.
+     */
+    public void setStringAdapter(int numSuggestions, String prefix) {
+        // generate the string array
+        String[] strings = new String[numSuggestions];
+        for (int i = 0; i < numSuggestions; ++i) {
+            strings[i] = prefix + String.valueOf(i);
+        }
+        
+        // install it with an adapter
+        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_dropdown_item_1line, strings);
+        mTextView.setAdapter(adapter);
+    }
+    
+    /**
+     * For monitoring OnItemClickListener & OnItemSelectedListener
+     * 
+     * An alternative here would be to provide a set of pass-through callbacks
+     */
+    public void resetItemListeners() {
+        mItemClickCalled = false;
+        mItemClickPosition = -1;
+        mItemSelectedCalled = false;
+        mItemSelectedPosition = -1;
+        mNothingSelectedCalled = false;
+    }
+    
+    /**
+     * Implements OnItemClickListener
+     */
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        Log.d(LOG_TAG, "onItemClick() position " + position);
+        mItemClickCalled = true;
+        mItemClickPosition = position;
+    }
+
+    /** 
+     * Implements OnItemSelectedListener
+     */
+    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+        Log.d(LOG_TAG, "onItemSelected() position " + position);
+        mItemSelectedCalled = true;
+        mItemSelectedPosition = position;
+    }
+
+    /** 
+     * Implements OnItemSelectedListener
+     */
+    public void onNothingSelected(AdapterView<?> parent) {
+        Log.d(LOG_TAG, "onNothingSelected()");
+        mNothingSelectedCalled = true;
+    }
+
+}
diff --git a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java
new file mode 100644 (file)
index 0000000..93cb84a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.widget;
+
+import android.app.Instrumentation;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class AutoCompleteTextViewCallbacks 
+        extends ActivityInstrumentationTestCase2<AutoCompleteTextViewSimple> {
+
+    public AutoCompleteTextViewCallbacks() {
+        super("com.android.frameworktest", AutoCompleteTextViewSimple.class);
+    }
+
+    /** Test that the initial popup of the suggestions does not select anything */
+    @MediumTest
+    public void testPopupNoSelection() {
+        AutoCompleteTextViewSimple theActivity = getActivity();
+        AutoCompleteTextView textView = theActivity.getTextView();
+        final Instrumentation instrumentation = getInstrumentation();
+        
+        // focus and type
+        textView.requestFocus();
+        instrumentation.waitForIdleSync();
+        sendKeys("A");
+        
+        // now check for selection callbacks.  Nothing should be clicked or selected.
+        assertFalse("onItemClick should not be called", theActivity.mItemClickCalled);
+        assertFalse("onItemSelected should not be called", theActivity.mItemSelectedCalled);
+        
+        // arguably, this should be "false", because we aren't deselecting - we shouldn't
+        // really be calling it.  But it's not the end of the world, and we might wind up
+        // breaking something if we change this.
+        assertTrue("onNothingSelected should be called", theActivity.mNothingSelectedCalled);
+    }
+
+    /** Test that arrow-down into the popup calls the onSelected callback */
+    @MediumTest
+    public void testPopupEnterSelection() {
+        AutoCompleteTextViewSimple theActivity = getActivity();
+        AutoCompleteTextView textView = theActivity.getTextView();
+        final Instrumentation instrumentation = getInstrumentation();
+        
+        // focus and type
+        textView.requestFocus();
+        instrumentation.waitForIdleSync();
+        sendKeys("A");
+        
+        // prepare to move down into the popup
+        theActivity.resetItemListeners();
+        sendKeys("DPAD_DOWN");
+        
+        // now check for selection callbacks.
+        assertFalse("onItemClick should not be called", theActivity.mItemClickCalled);
+        assertTrue("onItemSelected should be called", theActivity.mItemSelectedCalled);
+        assertEquals("onItemSelected position", 0, theActivity.mItemSelectedPosition);
+        assertFalse("onNothingSelected should not be called", theActivity.mNothingSelectedCalled);
+        
+        // try one more time - should move from 0 to 1
+        theActivity.resetItemListeners();
+        sendKeys("DPAD_DOWN");
+        
+        // now check for selection callbacks.
+        assertFalse("onItemClick should not be called", theActivity.mItemClickCalled);
+        assertTrue("onItemSelected should be called", theActivity.mItemSelectedCalled);
+        assertEquals("onItemSelected position", 1, theActivity.mItemSelectedPosition);
+        assertFalse("onNothingSelected should not be called", theActivity.mNothingSelectedCalled);
+    }
+
+    /** Test that arrow-up out of the popup calls the onNothingSelected callback */
+    @MediumTest
+    public void testPopupLeaveSelection() {
+        AutoCompleteTextViewSimple theActivity = getActivity();
+        AutoCompleteTextView textView = theActivity.getTextView();
+        final Instrumentation instrumentation = getInstrumentation();
+        
+        // focus and type
+        textView.requestFocus();
+        instrumentation.waitForIdleSync();
+        sendKeys("A");
+        
+        // move down into the popup
+        sendKeys("DPAD_DOWN");
+        
+        // now move back up out of the popup
+        theActivity.resetItemListeners();
+        sendKeys("DPAD_UP");
+
+        // now check for selection callbacks.
+        assertFalse("onItemClick should not be called", theActivity.mItemClickCalled);
+        assertFalse("onItemSelected should not be called", theActivity.mItemSelectedCalled);
+        assertTrue("onNothingSelected should be called", theActivity.mNothingSelectedCalled);
+    }
+
+}
index 1aa37c5..58f4ccb 100644 (file)
 
 package android.widget;
 
-import java.util.ArrayList;
-import java.util.Random;
+import com.android.internal.database.ArrayListCursor;
+import com.google.android.collect.Lists;
 
 import android.content.Context;
 import android.database.Cursor;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.database.ArrayListCursor;
-import com.google.android.collect.Lists;
+import java.util.ArrayList;
+import java.util.Random;
 
 /**
  * This is a series of tests of basic API contracts for SimpleCursorAdapter.  It is
@@ -34,7 +34,7 @@ import com.google.android.collect.Lists;
  * NOTE:  This contract holds for underlying cursor types too and these should
  * be extracted into a set of tests that can be run on any descendant of CursorAdapter.
  */
-public class SimpleCursorAdapterTest extends InstrumentationTestCase {
+public class SimpleCursorAdapterTest extends AndroidTestCase {
     
     String[] mFrom;
     int[] mTo;
@@ -55,7 +55,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
         mFrom = new String[]{"Column1", "Column2"};
         mTo = new int[]{com.android.internal.R.id.text1, com.android.internal.R.id.text2};
         mLayout = com.android.internal.R.layout.simple_list_item_2;
-        mContext = getInstrumentation().getTargetContext();
+        mContext = getContext();
 
         // raw data for building a basic test cursor
         mData2x2 = createTestList(2, 2);
@@ -65,7 +65,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     /**
      * Borrowed from CursorWindowTest.java
      */
-    private static ArrayList<ArrayList> createTestList(int rows, int cols) {
+    private ArrayList<ArrayList> createTestList(int rows, int cols) {
         ArrayList<ArrayList> list = Lists.newArrayList();
         Random generator = new Random();
 
@@ -84,7 +84,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     /**
      * Test creating with a live cursor
      */
-    @MediumTest
+    @SmallTest
     public void testCreateLive() {
         SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
         
@@ -95,7 +95,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     /**
      * Test creating with a null cursor
      */
-    @MediumTest
+    @SmallTest
     public void testCreateNull() {
         SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, null, mFrom, mTo);
         
@@ -106,7 +106,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     /**
      * Test changeCursor() with live cursor
      */
-    @MediumTest
+    @SmallTest
     public void testChangeCursorLive() {
         SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
         
@@ -125,7 +125,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     /**
      * Test changeCursor() with null cursor
      */
-    @MediumTest
+    @SmallTest
     public void testChangeCursorNull() {
         SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
         
@@ -144,7 +144,7 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
      * deal with cursors that have the same essential data (as defined by the original mFrom
      * array) but it's OK if the physical structure of the cursor changes (columns rearranged).
      */
-    @MediumTest
+    @SmallTest
     public void testChangeCursorColumns() {
         TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, mCursor2x2, 
                 mFrom, mTo);
@@ -167,11 +167,81 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
     }
     
     /**
+     * Test that you can safely construct with a null cursor *and* null to/from arrays.
+     * This is new functionality added in 12/2008.
+     */
+    @SmallTest
+    public void testNullConstructor() {
+        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, null, null, null);
+        assertEquals(0, ca.getCount());
+    }
+    
+    /**
+     * Test going from a null cursor to a non-null cursor *and* setting the to/from arrays
+     * This is new functionality added in 12/2008.
+     */
+    @SmallTest
+    public void testChangeNullToMapped() {
+        TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, null, null, null);
+        assertEquals(0, ca.getCount());
+
+        ca.changeCursorAndColumns(mCursor2x2, mFrom, mTo);
+        assertEquals(2, ca.getCount());
+        
+        // check columns of original - mFrom and mTo should line up
+        int[] columns = ca.getConvertedFrom();
+        assertEquals(2, columns.length);
+        assertEquals(0, columns[0]);
+        assertEquals(1, columns[1]);
+        int[] viewIds = ca.getTo();
+        assertEquals(2, viewIds.length);
+        assertEquals(com.android.internal.R.id.text1, viewIds[0]);
+        assertEquals(com.android.internal.R.id.text2, viewIds[1]);
+    }
+    
+    /**
+     * Test going from one mapping to a different mapping
+     * This is new functionality added in 12/2008.
+     */
+    @SmallTest
+    public void testChangeMapping() {
+        TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, mCursor2x2, 
+                mFrom, mTo);
+        assertEquals(2, ca.getCount());
+
+        // Now create a new configuration with same cursor and just one column mapped
+        String[] singleFrom = new String[]{"Column1"};
+        int[] singleTo = new int[]{com.android.internal.R.id.text1};
+        ca.changeCursorAndColumns(mCursor2x2, singleFrom, singleTo);
+
+        // And examine the results, make sure they're still consistent
+        int[] columns = ca.getConvertedFrom();
+        assertEquals(1, columns.length);
+        assertEquals(0, columns[0]);
+        int[] viewIds = ca.getTo();
+        assertEquals(1, viewIds.length);
+        assertEquals(com.android.internal.R.id.text1, viewIds[0]);
+        
+        // And again, same cursor, different map
+        singleFrom = new String[]{"Column2"};
+        singleTo = new int[]{com.android.internal.R.id.text2};
+        ca.changeCursorAndColumns(mCursor2x2, singleFrom, singleTo);
+
+        // And examine the results, make sure they're still consistent
+        columns = ca.getConvertedFrom();
+        assertEquals(1, columns.length);
+        assertEquals(1, columns[0]);
+        viewIds = ca.getTo();
+        assertEquals(1, viewIds.length);
+        assertEquals(com.android.internal.R.id.text2, viewIds[0]);
+    }
+    
+    /**
      * This is simply a way to sneak a look at the protected mFrom() array.  A more API-
      * friendly way to do this would be to mock out a View and a ViewBinder and exercise
      * it via those seams.
      */
-    private class TestSimpleCursorAdapter extends SimpleCursorAdapter {
+    private static class TestSimpleCursorAdapter extends SimpleCursorAdapter {
         
         public TestSimpleCursorAdapter(Context context, int layout, Cursor c,
                 String[] from, int[] to) {
@@ -181,5 +251,9 @@ public class SimpleCursorAdapterTest extends InstrumentationTestCase {
         int[] getConvertedFrom() {
             return mFrom;
         }
+        
+        int[] getTo() {
+            return mTo;
+        }
     }
 }
diff --git a/tests/SmokeTest/Android.mk b/tests/SmokeTest/Android.mk
new file mode 100644 (file)
index 0000000..0adfd4c
--- /dev/null
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# This builds "SmokeTestApp"
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := SmokeTestApp
+
+include $(BUILD_PACKAGE)
+
+# This builds "SmokeTest"
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/SmokeTest/AndroidManifest.xml b/tests/SmokeTest/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..f141bdc
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smoketest">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".SmokeTestActivity" 
+                  android:label="Smoke Tests">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.TEST" />
+            </intent-filter>
+        </activity>
+
+    </application>
+    
+</manifest>
diff --git a/tests/SmokeTest/README b/tests/SmokeTest/README
new file mode 100644 (file)
index 0000000..40218cf
--- /dev/null
@@ -0,0 +1,8 @@
+The tests in this folder are a very controlled set of tests that will be run by
+the build system on every single build.  They are intended to run very quickly.
+Please use caution when adding tests here.
+
+If you wish to run these tests, issue the command:
+
+adb shell am instrument \
+  -w com.android.smoketest/.tests.SmokeTestInstrumentationTestRunner
diff --git a/tests/SmokeTest/src/com/android/smoketest/SmokeTestActivity.java b/tests/SmokeTest/src/com/android/smoketest/SmokeTestActivity.java
new file mode 100644 (file)
index 0000000..6bc5fd2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smoketest;
+
+import android.app.LauncherActivity;
+import android.content.Intent;
+
+/**
+ * Initial launcher for UI access to smoke tests.  This does not actually launch the tests,
+ * it simply provides manual access to the various UI activities that are used by the tests.
+ * 
+ * To run all of the tests in this suite:
+ * adb shell am instrument \
+ *   -w com.android.smoketest/.tests.SmokeTestInstrumentationTestRunner
+ */
+public class SmokeTestActivity extends LauncherActivity {
+
+    @Override
+    protected Intent getTargetIntent() {
+        // TODO: partition into categories by label like the sample code app
+        Intent targetIntent = new Intent(Intent.ACTION_MAIN, null);
+        // TODO: Do we add a new top-level intent?  Or just leave it hardcoded like this?
+        targetIntent.addCategory("android.intent.category.SMOKETEST_INSTRUMENTATION_TEST");
+        targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return targetIntent;
+    }
+}
diff --git a/tests/SmokeTest/tests/Android.mk b/tests/SmokeTest/tests/Android.mk
new file mode 100644 (file)
index 0000000..86bf23d
--- /dev/null
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Notice that we don't have to include the src files of SmokeTestApp because, by
+# running the tests using an instrumentation targeting SmokeTestApp, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := SmokeTest
+
+LOCAL_INSTRUMENTATION_FOR := SmokeTestApp
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/SmokeTest/tests/AndroidManifest.xml b/tests/SmokeTest/tests/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..20b33ee
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smoketest.tests">
+
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->    
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
+
+    <!--
+    This declares that this app uses the instrumentation test runner targeting
+    the package of com.android.smoketest.  To run the tests use the command:
+    "adb shell am instrument -w com.android.smoketest.tests/android.test.InstrumentationTestRunner"
+    -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.smoketest"
+                     android:label="System Smoke Tests"/>
+
+</manifest>
diff --git a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
new file mode 100644 (file)
index 0000000..85b91f1
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smoketest;
+
+import com.android.internal.os.RuntimeInit;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.server.data.CrashData;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This smoke test is designed to quickly sniff for any error conditions
+ * encountered after initial startup.
+ */
+public class ProcessErrorsTest extends AndroidTestCase {
+    
+    private final String TAG = "ProcessErrorsTest";
+    
+    protected ActivityManager mActivityManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mActivityManager = (ActivityManager) 
+                getContext().getSystemService(Context.ACTIVITY_SERVICE);
+    }
+
+    public void testSetUpConditions() throws Exception {
+        assertNotNull(mActivityManager);
+    }
+
+    public void testNoProcessErrors() throws Exception {
+        List<ActivityManager.ProcessErrorStateInfo> errList;        
+        errList = mActivityManager.getProcessesInErrorState();
+        
+        // note: this contains information about each process that is currently in an error
+        // condition.  if the list is empty (null) then "we're good".  
+        
+        // if the list is non-empty, then it's useful to report the contents of the list
+        // we'll put a copy in the log, and we'll report it back to the framework via the assert.
+        final String reportMsg = reportListContents(errList);
+        if (reportMsg != null) {
+            Log.w(TAG, reportMsg);
+        }
+        
+        // report a non-empty list back to the test framework
+        assertNull(reportMsg, errList);
+    }
+    
+    /**
+     * This helper function will dump the actual error reports.
+     * 
+     * @param errList The error report containing one or more error records.
+     * @return Returns a string containing all of the errors.
+     */
+    private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> errList) {
+        if (errList == null) return null;
+
+        StringBuilder builder = new StringBuilder();
+
+        Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+        while (iter.hasNext()) {
+            ActivityManager.ProcessErrorStateInfo entry = iter.next();
+
+            String condition;
+            switch (entry.condition) {
+            case ActivityManager.ProcessErrorStateInfo.CRASHED:
+                condition = "CRASHED";
+                break;
+            case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING:
+                condition = "ANR";
+                break;
+            default:
+                condition = "<unknown>";
+            break;
+            }
+
+            String stackTrace = null;
+            try {
+                if (entry.crashData != null) {
+                    CrashData cd = RuntimeInit.unmarshallException(entry.crashData);
+                    stackTrace = cd.toString();
+                }
+            } catch (RuntimeException e) { }
+            if (stackTrace == null) {
+                stackTrace = "<no stack trace>";
+            }
+
+            final String entryReport = "Process error " + condition + " " + entry.shortMsg +
+                                        " detected in " + entry.processName + " " + entry.tag + 
+                                        ". \n" + stackTrace;
+
+            builder.append(entryReport).append("  ");
+        }
+        return builder.toString();
+    }
+    
+}
index 9d5937c..7c4963f 100644 (file)
@@ -618,10 +618,151 @@ static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
     return true;
 }
 
+
+static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_bytep alphaPalette,
+                          int *paletteEntries, bool *hasTransparency, int *colorType,
+                          png_bytepp outRows)
+{
+    int w = imageInfo.width;
+    int h = imageInfo.height;
+    bool trans = imageInfo.hasTransparency;
+    int i, j, rr, gg, bb, aa, idx;
+    uint32_t colors[256], col;
+    int num_colors = 0;
+
+    bool isOpaque = true;
+    bool isPalette = true;
+    bool isGrayscale = true;
+
+    // Scan the entire image and determine if:
+    // 1. Every pixel has R == G == B (grayscale)
+    // 2. Every pixel has A == 255 (opaque)
+    // 3. There are no more than 256 distinct RGBA colors
+    for (j = 0; j < h; j++) {
+        png_bytep row = imageInfo.rows[j];
+        png_bytep out = outRows[j];
+        for (i = 0; i < w; i++) {
+            rr = *row++;
+            gg = *row++;
+            bb = *row++;
+            aa = *row++;
+            if (!trans) {
+                // Ignore the actually byte and assume alpha == 255
+                aa = 0xff;
+            }
+
+            // Check if image is really grayscale
+            if (isGrayscale) {
+              if (rr != gg || rr != bb) {
+                isGrayscale = false;
+              }
+            }
+
+            // Check if image is really opaque
+            if (isOpaque) {
+              if (aa != 0xff) {
+                isOpaque = false;
+              }
+            }
+
+            // Check if image is really <= 256 colors
+            if (isPalette) {
+                col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
+                bool match = false;
+                for (idx = 0; idx < num_colors; idx++) {
+                    if (colors[idx] == col) {
+                        match = true;
+                        break;
+                    }
+                }
+
+                // Write the palette index for the pixel to outRows optimistically
+                // We might overwrite it later if we decide to encode as gray or
+                // gray + alpha
+                *out++ = idx;
+                if (!match) {
+                    if (num_colors == 256) {
+                        isPalette = false;
+                    } else {
+                        colors[num_colors++] = col;
+                    }
+                }
+            }
+        }
+    }
+
+    *paletteEntries = 0;
+    *hasTransparency = !isOpaque;
+    int bpp = isOpaque ? 3 : 4;
+    int paletteSize = w * h + bpp * num_colors;
+
+    // Choose the best color type for the image.
+    // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
+    // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
+    //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
+    // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
+    //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
+    if (isGrayscale) {
+        if (isOpaque) {
+            *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
+        } else {
+            // Use a simple heuristic to determine whether using a palette will
+            // save space versus using gray + alpha for each pixel.
+            // This doesn't take into account chunk overhead, filtering, LZ
+            // compression, etc.
+            if (isPalette && (paletteSize < 2 * w * h)) {
+                *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
+            } else {
+                *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
+            }
+        }
+    } else if (isPalette && (paletteSize < bpp * w * h)) {
+        *colorType = PNG_COLOR_TYPE_PALETTE;
+    } else {
+        *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+    }
+
+    // Perform postprocessing of the image or palette data based on the final
+    // color type chosen
+
+    if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+        // Create separate RGB and Alpha palettes and set the number of colors
+        *paletteEntries = num_colors;
+
+        // Create the RGB and alpha palettes
+        for (int idx = 0; idx < num_colors; idx++) {
+            col = colors[idx];
+            rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
+            rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
+            rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
+            alphaPalette[idx]     = (png_byte)  (col        & 0xff);
+        }
+    } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        // If the image is gray or gray + alpha, compact the pixels into outRows
+        for (j = 0; j < h; j++) {
+            png_bytep row = imageInfo.rows[j];
+            png_bytep out = outRows[j];
+            for (i = 0; i < w; i++) {
+                rr = *row++;
+                gg = *row++;
+                bb = *row++;
+                aa = *row++;
+                
+                *out++ = rr;
+                if (!isOpaque) {
+                    *out++ = aa;
+                }
+            }
+        }
+    }
+}
+
+
 static void write_png(const char* imageName,
                       png_structp write_ptr, png_infop write_info,
                       image_info& imageInfo)
 {
+    bool optimize = true;
     png_uint_32 width, height;
     int color_type;
     int bit_depth, interlace_type, compression_type;
@@ -629,17 +770,73 @@ static void write_png(const char* imageName,
 
     png_unknown_chunk unknowns[1];
 
+    png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
+    if (outRows == (png_bytepp) 0) {
+        printf("Can't allocate output buffer!\n");
+        exit(1);
+    }
+    for (i = 0; i < (int) imageInfo.height; i++) {
+        outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
+        if (outRows[i] == (png_bytep) 0) {
+            printf("Can't allocate output buffer!\n");
+            exit(1);
+        }
+    }
+
     png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
 
-    color_type = PNG_COLOR_MASK_COLOR;
-    if (imageInfo.hasTransparency) {
-        color_type |= PNG_COLOR_MASK_ALPHA;
+    NOISY(printf("Writing image %s: w = %d, h = %d, trans = %s\n", imageName,
+          (int) imageInfo.width, (int) imageInfo.height,
+          imageInfo.hasTransparency ? "true" : "false"));
+
+    png_color rgbPalette[256];
+    png_byte alphaPalette[256];
+    bool hasTransparency;
+    int paletteEntries;
+
+    if (optimize) {
+        analyze_image(imageInfo, rgbPalette, alphaPalette, &paletteEntries, &hasTransparency,
+                      &color_type, outRows);
+        switch (color_type) {
+        case PNG_COLOR_TYPE_PALETTE:
+            NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
+                         imageName, paletteEntries,
+                         hasTransparency ? " (with alpha)" : ""));
+            break;
+        case PNG_COLOR_TYPE_GRAY:
+            NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
+            break;
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
+            break;
+        case PNG_COLOR_TYPE_RGB:
+            NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
+            break;
+        case PNG_COLOR_TYPE_RGB_ALPHA:
+            NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
+            break;
+        }
+    } else {
+        // Force RGB or RGB_ALPHA color type, copy transparency from input
+        paletteEntries = 0;
+        hasTransparency = imageInfo.hasTransparency;
+        color_type = hasTransparency ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
     }
 
     png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
                  8, color_type, PNG_INTERLACE_NONE,
                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
+        if (hasTransparency) {
+            png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
+        }
+       png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
+    } else {
+       png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
+    }
+
     if (imageInfo.is9Patch) {
         NOISY(printf("Adding 9-patch info...\n"));
         strcpy((char*)unknowns[0].name, "npTc");
@@ -659,15 +856,24 @@ static void write_png(const char* imageName,
     }
 
     png_write_info(write_ptr, write_info);
-
+         
     if (!imageInfo.hasTransparency) {
         png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
     }
 
-    png_write_image(write_ptr, imageInfo.rows);
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+        png_write_image(write_ptr, imageInfo.rows);
+    } else {
+        png_write_image(write_ptr, outRows);
+    }
 
     png_write_end(write_ptr, write_info);
 
+    for (i = 0; i < (int) imageInfo.height; i++) {
+        free(outRows[i]);
+    }
+    free(outRows);
+
     png_get_IHDR(write_ptr, write_info, &width, &height,
        &bit_depth, &color_type, &interlace_type,
        &compression_type, NULL);
@@ -687,9 +893,9 @@ status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
         return NO_ERROR;
     }
 
-    // Example of renaming a file:     
-    //*outNewLeafName = file->getPath().getBasePath().getFileName();   
-    //outNewLeafName->append(".nupng");        
+    // Example of renaming a file:
+    //*outNewLeafName = file->getPath().getBasePath().getFileName();
+    //outNewLeafName->append(".nupng");
 
     String8 printableName(file->getPrintableSource());
 
index 0df4606..5d9e140 100644 (file)
@@ -22,7 +22,7 @@ static const char* kExcludeExtension = ".EXCLUDE";
 static const char* kNoCompressExt[] = {
     ".jpg", ".jpeg", ".png", ".gif",
     ".wav", ".mp2", ".mp3", ".ogg", ".aac",
-    ".mpg", ".mpeg", ".mid", ".midi", ".smf",
+    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
     ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
     ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
     ".amr", ".awb", ".wma", ".wmv"
index 3641458..33f8f72 100644 (file)
@@ -115,7 +115,7 @@ static const flag_entry gFormatFlags[] = {
       "a floating point value, such as \"<code>1.2</code>\"."},
     { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
       "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
-      "Available units are: px (pixels), db (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
+      "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
       "in (inches), mm (millimeters)." },
     { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
       "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
@@ -3479,4 +3479,3 @@ bool ResourceTable::getItemValue(
     }
     return res;
 }
-
index 927d21e..dc61567 100644 (file)
@@ -10,6 +10,8 @@
 #include <sys/stat.h>
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <map>
 
 #ifdef HAVE_MS_C_RUNTIME
index 7f84ff4..e3c0af0 100644 (file)
@@ -2,6 +2,8 @@
 #include "AST.h"
 #include "Type.h"
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 // =================================================
 class VariableFactory
index a890556..57b10ae 100644 (file)
@@ -1,6 +1,10 @@
 
 #include "options.h"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 static int
 usage()
 {
index 3c4e14b..ffb6cb2 100644 (file)
@@ -1,6 +1,7 @@
 #include <unistd.h>
 #include "search_path.h"
 #include "options.h"
+#include <string.h>
 
 #ifdef HAVE_MS_C_RUNTIME
 #include <io.h>
index 8d85d81..2bf94b1 100644 (file)
@@ -5,6 +5,7 @@
 
 #if __cplusplus
 #include <vector>
+#include <string>
 using namespace std;
 extern "C" {
 #endif
index b898192..f5087d9 100644 (file)
@@ -28,7 +28,7 @@ import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
 import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
 import com.android.ninepatch.NinePatch;
 import com.android.tools.layoutlib.create.OverrideMethod;
-import com.android.tools.layoutlib.create.OverrideMethod.MethodListener;
+import com.android.tools.layoutlib.create.MethodAdapter;
 
 import android.graphics.Bitmap;
 import android.graphics.Rect;
@@ -101,13 +101,10 @@ public final class Bridge implements ILayoutBridge {
         new HashMap<String, NinePatch>();
     
     private static Map<String, Map<String, Integer>> sEnumValueMap;
-    
-    private final static MethodListener sNullMethodListener = new MethodListener() {
-        public void onInvoke(String signature, boolean isNative, Object caller) {
-            // pass
-        }
-    };
 
+    /**
+     * A default logger than prints to stdout/stderr.
+     */
     private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
         public void error(String message) {
             System.err.println(message);
@@ -127,21 +124,16 @@ public final class Bridge implements ILayoutBridge {
         }
     };
 
-    /** Logger defined during a compute layout operation. */
+    /**
+     * Logger defined during a compute layout operation.
+     * <p/>
+     * This logger is generally set to {@link #sDefaultLogger} except during rendering
+     * operations when it might be set to a specific provided logger.
+     * <p/>
+     * To change this value, use a block synchronized on {@link #sDefaultLogger}.
+     */
     private static ILayoutLog sLogger = sDefaultLogger;
 
-    private final static String[] IGNORED_STATIC_METHODS = new String[] {
-        "android.content.res.AssetManager#init()V",
-        "android.content.res.AssetManager#deleteTheme(I)V",
-        "android.content.res.AssetManager#destroy()V",
-        "android.graphics._Original_Paint#native_init()I",
-        "android.graphics.Bitmap#nativeRecycle(I)V",
-        "android.graphics.Bitmap#nativeDestructor(I)V",
-        "android.view.animation.Transformation#<clinit>()V",
-        "android.view.animation.Transformation#<init>()V",
-        "android.view.animation.Transformation#clear()V",
-    };
-    
     /*
      * (non-Javadoc)
      * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel()
@@ -163,32 +155,52 @@ public final class Bridge implements ILayoutBridge {
     private static synchronized boolean sinit(String fontOsLocation,
             Map<String, Map<String, Integer>> enumValueMap) {
 
-        // set an empty method listener for some known static methods we don't care about.
-        for (String method : IGNORED_STATIC_METHODS) {
-            OverrideMethod.setMethodListener(method, sNullMethodListener);
-        }
-
-        
-        // set a the default listener for the rest of the static methods. It prints out
-        // missing stub methods and then throws an exception for native methods if the
-        // environment variable DEBUG_LAYOUT is not defined.
-        OverrideMethod.setDefaultListener(new MethodListener() {
-            public void onInvoke(String signature, boolean isNative, Object caller) {
-                if (isNative) {
+        // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
+        // on static (native) methods which prints the signature on the console and
+        // throws an exception.
+        // This is useful when testing the rendering in ADT to identify static native 
+        // methods that are ignored -- layoutlib_create makes them returns 0/false/null
+        // which is generally OK yet might be a problem, so this is how you'd find out.
+        //
+        // Currently layoutlib_create only overrides static native method.
+        // Static non-natives are not overridden and thus do not get here.
+        final String debug = System.getenv("DEBUG_LAYOUT");
+        if (debug != null && !debug.equals("0") && !debug.equals("false")) {
+
+            OverrideMethod.setDefaultListener(new MethodAdapter() {
+                @Override
+                public void onInvokeV(String signature, boolean isNative, Object caller) {
                     if (sLogger != null) {
-                        sLogger.error("Missing Stub: " + signature +
-                                (isNative ? " (native)" : ""));
+                        synchronized (sDefaultLogger) {
+                            sLogger.error("Missing Stub: " + signature +
+                                    (isNative ? " (native)" : ""));
+                        }
                     }
 
-                    if (System.getenv("DEBUG_LAYOUT") == null) {
-                        // TODO throwing this exception doesn't seem that useful. It breaks
+                    if (debug.equalsIgnoreCase("throw")) {
+                        // Throwing this exception doesn't seem that useful. It breaks
                         // the layout editor yet doesn't display anything meaningful to the
-                        // user. Having the error in the console is just as useful.
+                        // user. Having the error in the console is just as useful. We'll
+                        // throw it only if the environment variable is "throw" or "THROW".
                         throw new StaticMethodNotImplementedException(signature);
                     }
                 }
+            });
+        }
+
+        // Override View.isInEditMode to return true.
+        //
+        // This allows custom views that are drawn in the Graphical Layout Editor to adapt their
+        // rendering for preview. Most important this let custom views know that they can't expect
+        // the rest of their activities to be alive.
+        OverrideMethod.setMethodListener("android.view.View#isInEditMode()Z",
+            new MethodAdapter() {
+                @Override
+                public int onInvokeI(String signature, boolean isNative, Object caller) {
+                    return 1;
+                }
             }
-        });
+        );
 
         // load the fonts.
         FontLoader fontLoader = FontLoader.create(fontOsLocation);
@@ -283,7 +295,9 @@ public final class Bridge implements ILayoutBridge {
             logger = sDefaultLogger;
         }
         
-        sLogger = logger;
+        synchronized (sDefaultLogger) {
+            sLogger = logger;
+        }
         
         // find the current theme and compute the style inheritance map
         Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
@@ -368,7 +382,9 @@ public final class Bridge implements ILayoutBridge {
                     t.getClass().getSimpleName() + ": " + t.getMessage());
         } finally {
             // Remove the global logger
-            sLogger = sDefaultLogger;
+            synchronized (sDefaultLogger) {
+                sLogger = sDefaultLogger;
+            }
         }
     }
 
index b4e2c2b..1adcc17 100644 (file)
@@ -68,7 +68,8 @@ public class AsmGenerator {
      * 
      * @param log Output logger.
      * @param osDestJar The path of the destination JAR to create.
-     * @param stubMethods The list of methods to stub out
+     * @param stubMethods The list of methods to stub out. Each entry must be in the form
+     *          "package.package.OuterClass$InnerClass#MethodName".
      * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN
      *          of class to replace followed by the new FQCN.
      * @param deleteReturns List of classes for which the methods returning them should be deleted.
index 13443d5..76bd8d4 100644 (file)
@@ -45,9 +45,11 @@ public class Main {
             AsmGenerator agen = new AsmGenerator(log, osDestJar[0],
                     new Class<?>[] {  // classes to inject in the final JAR
                         OverrideMethod.class,
-                        OverrideMethod.MethodListener.class
+                        MethodListener.class,
+                        MethodAdapter.class
                     },
                     new String[] {  // methods to force override
+                        "android.view.View#isInEditMode",
                         "android.content.res.Resources$Theme#obtainStyledAttributes",
                     },
                     new String[] {  // classes to rename (so that we can replace them in layoutlib)
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
new file mode 100644 (file)
index 0000000..627ea17
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+/**
+ * An adapter to make it easier to use {@link MethodListener}.
+ * <p/>
+ * The adapter calls the void {@link #onInvokeV(String, boolean, Object)} listener
+ * for all types (I, L, F, D and A), returning 0 or null as appropriate.
+ */
+public class MethodAdapter implements MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller) {
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return null;
+    }
+}
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
new file mode 100644 (file)
index 0000000..6fc2b24
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+/**
+ * Interface to allow a method invocation to be listened upon.
+ * <p/>
+ * This is used by {@link OverrideMethod} to register a listener for methods that
+ * have been stubbed by the {@link AsmGenerator}. At runtime the stub will call either a
+ * default global listener or a specific listener based on the method signature.
+ */
+public interface MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller);
+}
+    
index b061a15..a6aff99 100644 (file)
@@ -20,27 +20,13 @@ import java.util.HashMap;
 
 /**
  * Allows stub methods from LayoutLib to be overriden at runtime.
+ * <p/>
+ * Implementation note: all types required by this class(inner/outer classes & interfaces)
+ * must be referenced by the injectClass argument to {@link AsmGenerator} in Main.java;
+ * Otherwise they won't be accessible in layoutlib.jar at runtime.
  */
 public final class OverrideMethod {
 
-    /**
-     * Interface to allow a method invocation to be listend upon.
-     */
-    public interface MethodListener {
-        /**
-         * A stub method is being invoked.
-         * <p/>
-         * Known limitation: caller arguments are not available. Return value cannot be set.
-         *  
-         * @param signature The signature of the method being invoked, composed of the
-         *                  binary class name followed by the method descriptor (aka argument
-         *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
-         * @param isNative True if the method was a native method.
-         * @param caller The calling object. Null for static methods, "this" for instance methods.
-         */
-        public void onInvoke(String signature, boolean isNative, Object caller);
-    }
-
     /** Map of method overridden. */
     private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
     /** Default listener for all method not listed in sMethods. Nothing if null. */
@@ -71,7 +57,9 @@ public final class OverrideMethod {
     }
     
     /**
-     * Invoke the specific listener for the given signature or the default one if defined. 
+     * Invokes the specific listener for the given signature or the default one if defined.
+     * <p/>
+     * This version invokes the method listener for the void return type. 
      * <p/>
      * Note: this is not intended to be used by the LayoutLib Bridge. It is intended to be called
      * by the stubbed methods generated by the LayoutLib_create tool.
@@ -82,12 +70,82 @@ public final class OverrideMethod {
      * @param isNative True if the method was a native method.
      * @param caller The calling object. Null for static methods, "this" for instance methods.
      */
-    public static void invoke(String signature, boolean isNative, Object caller) {
+    public static void invokeV(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            i.onInvokeV(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            sDefaultListener.onInvokeV(signature, isNative, caller);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the int return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static int invokeI(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeI(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeI(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the long return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static long invokeL(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeL(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeL(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the float return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static float invokeF(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeF(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeF(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the double return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static double invokeD(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeD(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeD(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the object return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static Object invokeA(String signature, boolean isNative, Object caller) {
         MethodListener i = sMethods.get(signature);
         if (i != null) {
-            i.onInvoke(signature, isNative, caller);
+            return i.onInvokeA(signature, isNative, caller);
         } else if (sDefaultListener != null) {
-            sDefaultListener.onInvoke(signature, isNative, caller);
+            return sDefaultListener.onInvokeA(signature, isNative, caller);
         }
+        return null;
     }
 }
index 9bb64bd..9a57a4a 100644 (file)
@@ -74,23 +74,14 @@ class StubMethodAdapter implements MethodVisitor {
         } else {
             mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
         }
-        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
-                "com/android/tools/layoutlib/create/OverrideMethod",
-                "invoke",
-                "(Ljava/lang/String;ZLjava/lang/Object;)V");
-    }
 
-    private void generateReturn() {
-        /* Generates one of, depending on the return type:
-         *   return;
-         *   return 0;
-         *   return 0L;
-         *   return 0.0f;
-         *   return 0.0;
-         *   return null;
-         */
-        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
+        int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
+        switch(sort) {
         case Type.VOID:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeV",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
             mParentVisitor.visitInsn(Opcodes.RETURN);
             break;
         case Type.BOOLEAN:
@@ -98,27 +89,86 @@ class StubMethodAdapter implements MethodVisitor {
         case Type.BYTE:
         case Type.SHORT:
         case Type.INT:
-            mParentVisitor.visitInsn(Opcodes.ICONST_0);
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeI",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
+            switch(sort) {
+            case Type.BOOLEAN:
+                Label l1 = new Label();
+                mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_1);
+                mParentVisitor.visitInsn(Opcodes.IRETURN);
+                mParentVisitor.visitLabel(l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_0);
+                break;
+            case Type.CHAR:
+                mParentVisitor.visitInsn(Opcodes.I2C);
+                break;
+            case Type.BYTE:
+                mParentVisitor.visitInsn(Opcodes.I2B);
+                break;
+            case Type.SHORT:
+                mParentVisitor.visitInsn(Opcodes.I2S);
+                break;
+            }
             mParentVisitor.visitInsn(Opcodes.IRETURN);
             break;
         case Type.LONG:
-            mParentVisitor.visitInsn(Opcodes.LCONST_0);
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeL",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
             mParentVisitor.visitInsn(Opcodes.LRETURN);
             break;
         case Type.FLOAT:
-            mParentVisitor.visitInsn(Opcodes.FCONST_0);
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeF",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
             mParentVisitor.visitInsn(Opcodes.FRETURN);
             break;
         case Type.DOUBLE:
-            mParentVisitor.visitInsn(Opcodes.DCONST_0);
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeD",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
             mParentVisitor.visitInsn(Opcodes.DRETURN);
             break;
         case Type.ARRAY:
         case Type.OBJECT:
-            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeA",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
+            mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
             mParentVisitor.visitInsn(Opcodes.ARETURN);
             break;
         }
+
+    }
+
+    private void generatePop() {
+        /* Pops the stack, depending on the return type.
+         */
+        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
+        case Type.VOID:
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+        case Type.FLOAT:
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitInsn(Opcodes.POP);
+            break;
+        case Type.LONG:
+        case Type.DOUBLE:
+            mParentVisitor.visitInsn(Opcodes.POP2);
+            break;
+        }
     }
 
     /* Pass down to visitor writer. In this implementation, either do nothing. */
@@ -134,7 +184,6 @@ class StubMethodAdapter implements MethodVisitor {
     public void visitMaxs(int maxStack, int maxLocals) {
         if (!mIsInitMethod && !mMessageGenerated) {
             generateInvoke();
-            generateReturn();
             mMessageGenerated = true;
         }
         mParentVisitor.visitMaxs(maxStack, maxLocals);
@@ -148,7 +197,6 @@ class StubMethodAdapter implements MethodVisitor {
     public void visitEnd() {
         if (!mIsInitMethod && !mMessageGenerated) {
             generateInvoke();
-            generateReturn();
             mMessageGenerated = true;
             mParentVisitor.visitMaxs(1, 1);
         }
@@ -198,10 +246,13 @@ class StubMethodAdapter implements MethodVisitor {
             case Opcodes.FRETURN:
             case Opcodes.IRETURN:
             case Opcodes.LRETURN:
+                // Pop the last word from the stack since invoke will generate its own return.
+                generatePop();
                 generateInvoke();
                 mMessageGenerated = true;
+            default:
+                mParentVisitor.visitInsn(opcode);
             }
-            mParentVisitor.visitInsn(opcode);
         }
     }
 
index 3184dfc..3425668 100644 (file)
@@ -1,5 +1,7 @@
 #include "Perforce.h"
 #include "log.h"
+#include <string.h>
+#include <stdlib.h>
 #include <sstream>
 #include <sys/types.h>
 #include <unistd.h>
index 5da05a2..51f81de 100644 (file)
@@ -1,5 +1,6 @@
 #include "XLIFFFile.h"
 
+#include <algorithm>
 #include <sys/time.h>
 #include <time.h>
 
index 64af7af..3fab211 100644 (file)
@@ -1,5 +1,6 @@
 #include "XMLHandler.h"
 
+#include <algorithm>
 #include <expat.h>
 #include <stdio.h>
 #include <string.h>
index 9431709..bb82a9c 100644 (file)
@@ -1,3 +1,6 @@
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
 #include "file_utils.h"
 #include "Perforce.h"
 #include <sys/fcntl.h>
index d03c811..c0d84cc 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <host/pseudolocalize.h>
 
+#include <stdlib.h>
 #include <stdarg.h>
 #include <sstream>
 #include <stdio.h>
index 64be50e..e454cae 100644 (file)
@@ -66,7 +66,7 @@ interface IWifiManager
 
     DhcpInfo getDhcpInfo();
 
-    boolean acquireWifiLock(IBinder lock, String tag);
+    boolean acquireWifiLock(IBinder lock, int lockType, String tag);
 
     boolean releaseWifiLock(IBinder lock);
 }
index 18645b5..db989d0 100644 (file)
@@ -24,8 +24,6 @@ import android.os.IBinder;
 import android.os.Handler;
 import android.os.RemoteException;
 
-import com.android.internal.os.RuntimeInit;
-
 import java.util.List;
 
 /**
@@ -227,6 +225,27 @@ public class WifiManager {
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
 
+    /**
+     * In this mode, Wi-Fi will be kept active,
+     * and will behave normally, i.e., it will attempt to automatically
+     * establish a connection to a remembered access point that is
+     * within range, and will do periodic scans if there are remembered
+     * access points but none are in range.
+     * @hide pending API council review
+     */
+    public static final int WIFI_MODE_FULL = 1;
+    /**
+     * In this mode, Wi-Fi will be kept active,
+     * but the only operation that will be supported is initiation of
+     * scans, and the subsequent reporting of scan results. No attempts
+     * will be made to automatically connect to remembered access points,
+     * nor will periodic scans be automatically performed looking for
+     * remembered access points. Scans must be explicitly requested by
+     * an application in this mode.
+     * @hide pending API council review
+     */
+    public static final int WIFI_MODE_SCAN_ONLY = 2;
+
     /** Anything worse than or equal to this will show 0 bars. */
     private static final int MIN_RSSI = -100;
     
@@ -236,10 +255,6 @@ public class WifiManager {
     IWifiManager mService;
     Handler mHandler;
 
-    /** Don't allow use of default constructor */
-    private WifiManager() {
-    }
-
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -247,7 +262,7 @@ public class WifiManager {
      * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
      * @param service the Binder interface
      * @param handler target for messages
-     * {@hide} - hide this because it takes in a parameter of type IWifiManager, which
+     * @hide - hide this because it takes in a parameter of type IWifiManager, which
      * is a system private class.
      */
     public WifiManager(IWifiManager service, Handler handler) {
@@ -658,13 +673,15 @@ public class WifiManager {
      */
     public class WifiLock {
         private String mTag;
-        private IBinder mBinder;
+        private final IBinder mBinder;
         private int mRefCount;
+        int mLockType;
         private boolean mRefCounted;
         private boolean mHeld;
 
-        private WifiLock(String tag) {
+        private WifiLock(int lockType, String tag) {
             mTag = tag;
+            mLockType = lockType;
             mBinder = new Binder();
             mRefCount = 0;
             mRefCounted = true;
@@ -674,20 +691,20 @@ public class WifiManager {
         /**
          * Locks the Wi-Fi radio on until {@link #release} is called.
          *
-         * If this WifiLock is reference-counted, each call to {@link #acquire} will increment the
+         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
          * reference count, and the radio will remain locked as long as the reference count is 
          * above zero.
          *
-         * If this WifiLock is not reference-counted, the first call to {@link #acquire} will lock
+         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
          * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
-         * will be required, regardless of the number of times that {@link #acquire} is called.
+         * will be required, regardless of the number of times that {@code acquire} is called.
          */
         public void acquire() {
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
                     try {
-                        mService.acquireWifiLock(mBinder, mTag);
-                    } catch (RemoteException e) {
+                        mService.acquireWifiLock(mBinder, mLockType, mTag);
+                    } catch (RemoteException ignore) {
                     }
                     mHeld = true;
                 }
@@ -697,12 +714,12 @@ public class WifiManager {
         /**
          * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
          *
-         * If this WifiLock is reference-counted, each call to {@link #release} will decrement the
+         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
          * reference count, and the radio will be unlocked only when the reference count reaches
-         * zero.  If the reference count goes below zero (that is, if {@link #release} is called 
+         * zero.  If the reference count goes below zero (that is, if {@code release} is called
          * a greater number of times than {@link #acquire}), an exception is thrown.
          *
-         * If this WifiLock is not reference-counted, the first call to {@link #release} (after
+         * If this WifiLock is not reference-counted, the first call to {@code release} (after
          * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
          * calls will be ignored.
          */
@@ -711,7 +728,7 @@ public class WifiManager {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
                         mService.releaseWifiLock(mBinder);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException ignore) {
                     }
                     mHeld = false;
                 }
@@ -763,14 +780,13 @@ public class WifiManager {
 
         @Override
         protected void finalize() throws Throwable {
+            super.finalize();
             synchronized (mBinder) {
                 if (mHeld) {
                     try {
                         mService.releaseWifiLock(mBinder);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException ignore) {
                     }
-                    RuntimeInit.crash("WifiLock", new Exception(
-                            "WifiLock finalized while still held: " + mTag));
                 }
             }
         }
@@ -779,6 +795,8 @@ public class WifiManager {
     /**
      * Creates a new WifiLock.
      *
+     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
+     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
      * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is 
      *            never shown to the user under normal conditions, but should be descriptive 
      *            enough to identify your application and the specific WifiLock within it, if it
@@ -787,9 +805,26 @@ public class WifiManager {
      * @return a new, unacquired WifiLock with the given tag.
      *
      * @see WifiLock
+     *
+     * @hide pending API council review
      */
-    public WifiLock createWifiLock(String tag) {
-        return new WifiLock(tag);
+    public WifiLock createWifiLock(int lockType, String tag) {
+        return new WifiLock(lockType, tag);
     }
     
+    /**
+     * Creates a new WifiLock.
+     *
+     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
+     *            never shown to the user under normal conditions, but should be descriptive
+     *            enough to identify your application and the specific WifiLock within it, if it
+     *            holds multiple WifiLocks.
+     *
+     * @return a new, unacquired WifiLock with the given tag.
+     *
+     * @see WifiLock
+     */
+    public WifiLock createWifiLock(String tag) {
+        return new WifiLock(WIFI_MODE_FULL, tag);
+    }
 }
index 87a0e95..9b96da0 100644 (file)
@@ -50,7 +50,7 @@ import java.net.UnknownHostException;
  * Track the state of Wifi connectivity. All event handling is done here,
  * and all changes in connectivity state are initiated here.
  *
- * {@hide}
+ * @hide
  */
 public class WifiStateTracker extends NetworkStateTracker {
 
@@ -225,7 +225,16 @@ public class WifiStateTracker extends NetworkStateTracker {
     
     private boolean mIsScanModeActive;
     private boolean mIsScanModeSetDueToAHiddenNetwork;
-    private boolean mDriverIsStopped;
+
+    // Wi-Fi run states:
+    private static final int RUN_STATE_STARTING = 1;
+    private static final int RUN_STATE_RUNNING  = 2;
+    private static final int RUN_STATE_STOPPING = 3;
+    private static final int RUN_STATE_STOPPED  = 4;
+    private int mRunState;
+
+    private boolean mIsScanOnly;
+    
     private String mInterfaceName;
     private static String LS = System.getProperty("line.separator");
 
@@ -277,6 +286,7 @@ public class WifiStateTracker extends NetworkStateTracker {
         // Allocate DHCP info object once, and fill it in on each request
         mDhcpInfo = new DhcpInfo();
         mIsScanModeSetDueToAHiddenNetwork = false;
+        mRunState = RUN_STATE_STARTING;
 
         // Setting is in seconds
         NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), 
@@ -367,7 +377,7 @@ public class WifiStateTracker extends NetworkStateTracker {
      * unavailable.
      * @return {@code true} if Wi-Fi connections are possible
      */
-    public boolean isAvailable() {
+    public synchronized boolean isAvailable() {
         /*
          * TODO: Should we also look at scan results to see whether we're
          * in range of any access points?
@@ -375,7 +385,7 @@ public class WifiStateTracker extends NetworkStateTracker {
         SupplicantState suppState = mWifiInfo.getSupplicantState();
         return suppState != SupplicantState.UNINITIALIZED &&
                 suppState != SupplicantState.INACTIVE &&
-                (mTornDownByConnMgr || !mDriverIsStopped);
+                (mTornDownByConnMgr || !isDriverStopped());
     }
 
     /**
@@ -471,15 +481,10 @@ public class WifiStateTracker extends NetworkStateTracker {
 
     /**
      * Send the tracker a notification that the Wi-Fi driver has been stopped.
-     * The notification is in the form of a synthesized DISCONNECTED event.
      */
     void notifyDriverStopped() {
-        setDriverStopped(true);
-        Message msg = Message.obtain(
-                this, EVENT_SUPPLICANT_STATE_CHANGED,
-                new SupplicantStateChangeResult(-1, SupplicantState.DISCONNECTED));
-        msg.sendToTarget();
-        
+       mRunState = RUN_STATE_STOPPED;
+
         // Send a driver stopped message to our handler
         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, 0, 0).sendToTarget();
     }
@@ -489,8 +494,6 @@ public class WifiStateTracker extends NetworkStateTracker {
      * having been stopped.
      */
     void notifyDriverStarted() {
-        setDriverStopped(false);
-
         // Send a driver started message to our handler
         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, 1, 0).sendToTarget();
     }
@@ -505,12 +508,8 @@ public class WifiStateTracker extends NetworkStateTracker {
         }
     }
 
-    private synchronized void setDriverStopped(boolean isStopped) {
-        mDriverIsStopped = isStopped;
-    }
-
     private synchronized boolean isDriverStopped() {
-        return mDriverIsStopped;
+        return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
     }
 
     /**
@@ -543,23 +542,25 @@ public class WifiStateTracker extends NetworkStateTracker {
     }
 
     /**
-     * Start the Wi-Fi driver, if it is in the stopped state. If
-     * the driver has been stopped as a result of a teardown request
-     * by the connectivity manager, then only the connectivity
-     * manager can restart it.
+     * Set the run state to either "normal" or "scan-only".
+     * @param scanOnlyMode true if the new mode should be scan-only.
      */
-    public synchronized void startDriver() {
-        if (mDriverIsStopped && !mTornDownByConnMgr) {
-            WifiNative.startDriverCommand();
-        }
-    }
-
-    /**
-     * Stop the Wi-Fi driver, if it is not already in the stopped state.
-     */
-    public synchronized void stopDriver() {
-        if (!mDriverIsStopped) {
-            WifiNative.stopDriverCommand();
+    public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
+        // do nothing unless scan-only mode is changing
+        if (mIsScanOnly != scanOnlyMode) {
+            int scanType = (scanOnlyMode ?
+                    SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
+            if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
+            if (WifiNative.setScanResultHandlingCommand(scanType)) {
+                mIsScanOnly = scanOnlyMode;
+                if (!isDriverStopped()) {
+                    if (scanOnlyMode) {
+                        WifiNative.disconnectCommand();
+                    } else {
+                        WifiNative.reconnectCommand();
+                    }
+                }
+            }
         }
     }
 
@@ -610,6 +611,7 @@ public class WifiStateTracker extends NetworkStateTracker {
 
         switch (msg.what) {
             case EVENT_SUPPLICANT_CONNECTION:
+                mRunState = RUN_STATE_RUNNING;
                 checkUseStaticIp();
                 /*
                  * DHCP requests are blocking, so run them in a separate thread.
@@ -618,6 +620,7 @@ public class WifiStateTracker extends NetworkStateTracker {
                 dhcpThread.start();
                 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
                 mIsScanModeActive = true;
+                mTornDownByConnMgr = false;
                 mLastBssid = null;
                 mLastSsid = null;
                 requestConnectionInfo();
@@ -745,9 +748,25 @@ public class WifiStateTracker extends NetworkStateTracker {
                         (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
                     setSupplicantState(newState);
                     if (newState == SupplicantState.DORMANT) {
-                        handleDisconnectedState(DetailedState.FAILED);
+                        DetailedState newDetailedState;
+                        if (!mIsScanOnly) {
+                            newDetailedState = DetailedState.FAILED;
+                        } else {
+                            newDetailedState = DetailedState.IDLE;
+                        }
+                        handleDisconnectedState(newDetailedState);
                         sendNetworkStateChangeBroadcast();
-                        sendEmptyMessageDelayed(EVENT_DEFERRED_RECONNECT, RECONNECT_DELAY_MSECS);
+                        if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly) {
+                            sendEmptyMessageDelayed(EVENT_DEFERRED_RECONNECT, RECONNECT_DELAY_MSECS);
+                        } else if (mRunState == RUN_STATE_STOPPING) {
+                            synchronized (this) {
+                                WifiNative.stopDriverCommand();
+                            }
+                        } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
+                            synchronized (this) {
+                                WifiNative.reconnectCommand();
+                            }
+                        }
                     } else if (newState == SupplicantState.DISCONNECTED) {
                         if (isDriverStopped()) {
                             handleDisconnectedState(DetailedState.DISCONNECTED);
@@ -804,6 +823,14 @@ public class WifiStateTracker extends NetworkStateTracker {
                 EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
                 
                 if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
+                /*
+                 * If we're in scan-only mode, don't advance the state machine, and
+                 * don't report the state change to clients.
+                 */
+                if (mIsScanOnly) {
+                    if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
+                    break;
+                }
                 if (result.state != DetailedState.SCANNING) {
                     /*
                      * Reset the scan count since there was a network state
@@ -867,7 +894,6 @@ public class WifiStateTracker extends NetworkStateTracker {
                 break;
 
             case EVENT_SCAN_RESULTS_AVAILABLE:
-
                 mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
                 sendScanResultsAvailable();
                 /**
@@ -908,7 +934,9 @@ public class WifiStateTracker extends NetworkStateTracker {
                     if (++mReconnectCount > getMaxDhcpRetries()) {
                         mWM.disableNetwork(mLastNetworkId);
                     }
-                    WifiNative.reconnectCommand();
+                    synchronized(this) {
+                        WifiNative.reconnectCommand();
+                    }
                 }
                 break;
 
@@ -952,7 +980,9 @@ public class WifiStateTracker extends NetworkStateTracker {
                     mHaveIPAddress = false;
                     mWifiInfo.setIpAddress(0);
                     mObtainingIPAddress = false;
-                    WifiNative.disconnectCommand();
+                    synchronized(this) {
+                        WifiNative.disconnectCommand();
+                    }
                 }
                 break;
                 
@@ -973,9 +1003,16 @@ public class WifiStateTracker extends NetworkStateTracker {
                      */
                     setNumAllowedChannels();
                     synchronized (this) {
-                        // In some situations, supplicant needs to be kickstarted to
-                        // start the background scanning
-                        WifiNative.scanCommand();
+                        if (mRunState == RUN_STATE_STARTING) {
+                            mRunState = RUN_STATE_RUNNING;
+                            if (!mIsScanOnly) {
+                                WifiNative.reconnectCommand();
+                            } else {
+                                // In some situations, supplicant needs to be kickstarted to
+                                // start the background scanning
+                                WifiNative.scanCommand();
+                            }
+                        }
                     }
                 }
                 break;
@@ -1122,16 +1159,6 @@ public class WifiStateTracker extends NetworkStateTracker {
     public WifiInfo requestConnectionInfo() {
         requestConnectionStatus(mWifiInfo);
         requestPolledInfo(mWifiInfo);
-/*
-        WifiInfo info = mWifiInfo;
-
-        Log.v(TAG, "Status from supplicant: NID=" + info.getNetworkId() +
-            " SSID=" + (info.getSSID() == null ? "<none>" : info.getSSID()) +
-            " BSSID=" + (info.getBSSID() == null ? "<none>" : info.getBSSID()) +
-            " WPA_STATE=" + (info.getSupplicantState() == null ? "<none>" : info.getSupplicantState()) +
-            " RSSI=" + info.getRssi());
-*/
-
         return mWifiInfo;
     }
 
@@ -1201,7 +1228,7 @@ public class WifiStateTracker extends NetworkStateTracker {
              * interested in RSSI of all the changes in signal
              * level.
              */
-            // TODO: The "3" below needs to be a symbol somewhere, but
+            // TODO: The second arg to the call below needs to be a symbol somewhere, but
             // it's actually the size of an array of icons that's private
             // to StatusBar Policy.
             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
@@ -1235,14 +1262,14 @@ public class WifiStateTracker extends NetworkStateTracker {
     /**
      * Disable Wi-Fi connectivity by stopping the driver.
      */
-    public synchronized boolean teardown() {
-        // Take down any open network notifications
-        setNotificationVisible(false, 0, false, 0);
-        
+    public boolean teardown() {
         if (!mTornDownByConnMgr) {
-            boolean result = WifiNative.stopDriverCommand();
-            setTornDownByConnMgr(result);
-            return result;
+            if (disconnectAndStop()) {
+                setTornDownByConnMgr(true);
+                return true;
+            } else {
+                return false;
+            }
         } else {
             return true;
         }
@@ -1251,16 +1278,37 @@ public class WifiStateTracker extends NetworkStateTracker {
     /**
      * Reenable Wi-Fi connectivity by restarting the driver.
      */
-    public synchronized boolean reconnect() {
+    public boolean reconnect() {
         if (mTornDownByConnMgr) {
-            boolean result = WifiNative.startDriverCommand();
-            setTornDownByConnMgr(!result);
-            return result;
+            if (restart()) {
+                setTornDownByConnMgr(false);
+                return true;
+            } else {
+                return false;
+            }
         } else {
             return true;
         }
     }
 
+    public synchronized boolean disconnectAndStop() {
+        // Take down any open network notifications
+        setNotificationVisible(false, 0, false, 0);
+
+        mRunState = RUN_STATE_STOPPING;
+        return WifiNative.disconnectCommand();
+    }
+
+    public synchronized boolean restart() {
+        if (mRunState == RUN_STATE_STOPPED) {
+            mRunState = RUN_STATE_STARTING;
+            return WifiNative.startDriverCommand();
+        } else if (mRunState == RUN_STATE_STOPPING) {
+            mRunState = RUN_STATE_STARTING;
+        }
+        return true;
+    }
+
     public boolean setRadio(boolean turnOn) {
         return mWM.setWifiEnabled(turnOn);
     }