diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-02-13 12:57:50 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-02-13 12:57:50 -0800 |
commit | da996f390e17e16f2dfa60e972e7ebc4f868f37e (patch) | |
tree | 00a0f15270d4c7b619fd34d8383257e1761082f4 | |
parent | d24b8183b93e781080b2c16c487e60d51c12da31 (diff) | |
download | frameworks_base-da996f390e17e16f2dfa60e972e7ebc4f868f37e.zip frameworks_base-da996f390e17e16f2dfa60e972e7ebc4f868f37e.tar.gz frameworks_base-da996f390e17e16f2dfa60e972e7ebc4f868f37e.tar.bz2 |
auto import from //branches/cupcake/...@131421
202 files changed, 7468 insertions, 6086 deletions
@@ -167,7 +167,7 @@ aidl_files := \ frameworks/base/core/java/android/content/Intent.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/res/Configuration.aidl \ - frameworks/base/core/java/android/gadget/GadgetInfo.aidl \ + frameworks/base/core/java/android/gadget/GadgetProviderInfo.aidl \ frameworks/base/core/java/android/net/Uri.aidl \ frameworks/base/core/java/android/os/Bundle.aidl \ frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \ diff --git a/api/current.xml b/api/current.xml index 55994fb..2e8db51 100644 --- a/api/current.xml +++ b/api/current.xml @@ -3562,6 +3562,17 @@ visibility="public" > </field> +<field name="hapticFeedbackEnabled" + type="int" + transient="false" + volatile="false" + value="16843358" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="hasCode" type="int" transient="false" @@ -3859,6 +3870,17 @@ visibility="public" > </field> +<field name="innerRadius" + type="int" + transient="false" + volatile="false" + value="16843359" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="innerRadiusRatio" type="int" transient="false" @@ -7434,6 +7456,17 @@ visibility="public" > </field> +<field name="thickness" + type="int" + transient="false" + volatile="false" + value="16843360" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="thicknessRatio" type="int" transient="false" @@ -41922,6 +41955,757 @@ </method> </class> </package> +<package name="android.gadget" +> +<class name="GadgetHost" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="GadgetHost" + type="android.gadget.GadgetHost" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="hostId" type="int"> +</parameter> +</constructor> +<method name="allocateGadgetId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="createView" + return="android.gadget.GadgetHostView" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="gadget" type="android.gadget.GadgetProviderInfo"> +</parameter> +</method> +<method name="deleteAllHosts" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="deleteGadgetId" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetId" type="int"> +</parameter> +</method> +<method name="deleteHost" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="onCreateView" + return="android.gadget.GadgetHostView" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="gadget" type="android.gadget.GadgetProviderInfo"> +</parameter> +</method> +<method name="onProviderChanged" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="gadget" type="android.gadget.GadgetProviderInfo"> +</parameter> +</method> +<method name="startListening" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="stopListening" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +<class name="GadgetHostView" + extends="android.widget.ViewAnimator" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.view.animation.Animation.AnimationListener"> +</implements> +<constructor name="GadgetHostView" + type="android.gadget.GadgetHostView" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</constructor> +<constructor name="GadgetHostView" + type="android.gadget.GadgetHostView" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="animationIn" type="int"> +</parameter> +<parameter name="animationOut" type="int"> +</parameter> +</constructor> +<method name="getDefaultView" + return="android.view.View" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +</method> +<method name="getErrorView" + return="android.view.View" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +</method> +<method name="getGadgetId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getGadgetInfo" + return="android.gadget.GadgetProviderInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="onAnimationEnd" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="animation" type="android.view.animation.Animation"> +</parameter> +</method> +<method name="onAnimationRepeat" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="animation" type="android.view.animation.Animation"> +</parameter> +</method> +<method name="onAnimationStart" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="animation" type="android.view.animation.Animation"> +</parameter> +</method> +<method name="prepareView" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="view" type="android.view.View"> +</parameter> +</method> +<method name="setGadget" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="info" type="android.gadget.GadgetProviderInfo"> +</parameter> +</method> +<method name="updateGadget" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="remoteViews" type="android.widget.RemoteViews"> +</parameter> +</method> +</class> +<class name="GadgetManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="bindGadgetId" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="provider" type="android.content.ComponentName"> +</parameter> +</method> +<method name="getGadgetInfo" + return="android.gadget.GadgetProviderInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetId" type="int"> +</parameter> +</method> +<method name="getInstalledProviders" + return="java.util.List<android.gadget.GadgetProviderInfo>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInstance" + return="android.gadget.GadgetManager" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> +<method name="updateGadget" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetIds" type="int[]"> +</parameter> +<parameter name="views" type="android.widget.RemoteViews"> +</parameter> +</method> +<method name="updateGadget" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="gadgetId" type="int"> +</parameter> +<parameter name="views" type="android.widget.RemoteViews"> +</parameter> +</method> +<method name="updateGadget" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="provider" type="android.content.ComponentName"> +</parameter> +<parameter name="views" type="android.widget.RemoteViews"> +</parameter> +</method> +<field name="ACTION_GADGET_CONFIGURE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_CONFIGURE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_GADGET_DELETED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_DELETED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_GADGET_DISABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_DISABLED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_GADGET_ENABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_ENABLED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_GADGET_PICK" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_PICK"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_GADGET_UPDATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.action.GADGET_UPDATE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_GADGET_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""gadgetId"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_GADGET_IDS" + type="java.lang.String" + transient="false" + volatile="false" + value=""gadgetIds"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="INVALID_GADGET_ID" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="META_DATA_GADGET_PROVIDER" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.gadget.provider"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="GadgetProvider" + extends="android.content.BroadcastReceiver" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="GadgetProvider" + type="android.gadget.GadgetProvider" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="onDeleted" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="gadgetIds" type="int[]"> +</parameter> +</method> +<method name="onDisabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> +<method name="onEnabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> +<method name="onReceive" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +</method> +<method name="onUpdate" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="gadgetManager" type="android.gadget.GadgetManager"> +</parameter> +<parameter name="gadgetIds" type="int[]"> +</parameter> +</method> +</class> +<class name="GadgetProviderInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<constructor name="GadgetProviderInfo" + type="android.gadget.GadgetProviderInfo" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<constructor name="GadgetProviderInfo" + type="android.gadget.GadgetProviderInfo" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="in" type="android.os.Parcel"> +</parameter> +</constructor> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="out" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="configure" + type="android.content.ComponentName" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="icon" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="initialLayout" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="label" + type="java.lang.String" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="minHeight" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="minWidth" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="provider" + type="android.content.ComponentName" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="updatePeriodMillis" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +</package> <package name="android.graphics" > <class name="AvoidXfermode" @@ -54461,6 +55245,17 @@ <parameter name="canvas" type="android.graphics.Canvas"> </parameter> </method> +<method name="getDrawable" + return="android.graphics.drawable.Drawable" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getOpacity" return="int" abstract="false" @@ -54583,6 +55378,17 @@ <parameter name="canvas" type="android.graphics.Canvas"> </parameter> </method> +<method name="getDrawable" + return="android.graphics.drawable.Drawable" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getOpacity" return="int" abstract="false" @@ -57537,6 +58343,17 @@ visibility="public" > </method> +<method name="hideStatusIcon" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="hideWindow" return="void" abstract="false" @@ -57992,6 +58809,28 @@ <parameter name="candidatesEnd" type="int"> </parameter> </method> +<method name="onWindowHidden" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="onWindowShown" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="setCandidatesView" return="void" abstract="false" @@ -58044,7 +58883,7 @@ <parameter name="view" type="android.view.View"> </parameter> </method> -<method name="setStatusIcon" +<method name="showStatusIcon" return="void" abstract="false" native="false" @@ -58370,6 +59209,16 @@ visibility="public" > </field> +<field name="contentTopInsets" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="touchableInsets" type="int" transient="false" @@ -58380,6 +59229,16 @@ visibility="public" > </field> +<field name="visibleTopInsets" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Keyboard" extends="java.lang.Object" @@ -64898,7 +65757,7 @@ <method name="prepare" return="void" abstract="false" - native="true" + native="false" synchronized="false" static="false" final="false" @@ -64962,7 +65821,7 @@ <exception name="IllegalStateException" type="java.lang.IllegalStateException"> </exception> </method> -<method name="setOutputFile" +<method name="setCamera" return="void" abstract="false" native="true" @@ -64972,6 +65831,34 @@ deprecated="not deprecated" visibility="public" > +<parameter name="c" type="android.hardware.Camera"> +</parameter> +</method> +<method name="setOutputFile" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fd" type="java.io.FileDescriptor"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> +<method name="setOutputFile" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> <parameter name="path" type="java.lang.String"> </parameter> <exception name="IllegalStateException" type="java.lang.IllegalStateException"> @@ -65005,6 +65892,68 @@ <parameter name="sv" type="android.view.Surface"> </parameter> </method> +<method name="setVideoEncoder" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="video_encoder" type="int"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> +<method name="setVideoFrameRate" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="rate" type="int"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> +<method name="setVideoSize" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="width" type="int"> +</parameter> +<parameter name="height" type="int"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> +<method name="setVideoSource" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="video_source" type="int"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> <method name="start" return="void" abstract="false" @@ -65147,6 +66096,90 @@ > </field> </class> +<class name="MediaRecorder.VideoEncoder" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<field name="DEFAULT" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="H263" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="H264" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MPEG_4_SP" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="MediaRecorder.VideoSource" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<field name="CAMERA" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DEFAULT" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="MediaScannerConnection" extends="java.lang.Object" abstract="false" @@ -90383,6 +91416,17 @@ visibility="public" > </field> +<field name="HAPTIC_FEEDBACK_ENABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""haptic_feedback_enabled"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="HTTP_PROXY" type="java.lang.String" transient="false" @@ -116836,6 +117880,48 @@ > </field> </class> +<class name="HapticFeedbackConstants" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<field name="FLAG_IGNORE_GLOBAL_SETTING" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FLAG_IGNORE_VIEW_SETTING" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="LONG_PRESS" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="InflateException" extends="java.lang.RuntimeException" abstract="false" @@ -123480,6 +124566,17 @@ visibility="public" > </method> +<method name="isHapticFeedbackEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="isHorizontalFadingEdgeEnabled" return="boolean" abstract="false" @@ -124095,6 +125192,34 @@ visibility="public" > </method> +<method name="performHapticFeedback" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="feedbackConstant" type="int"> +</parameter> +</method> +<method name="performHapticFeedback" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="feedbackConstant" type="int"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> <method name="performLongClick" return="boolean" abstract="false" @@ -124114,7 +125239,7 @@ static="false" final="false" deprecated="not deprecated" - visibility="protected" + visibility="public" > <parameter name="soundConstant" type="int"> </parameter> @@ -124581,6 +125706,19 @@ <parameter name="focusableInTouchMode" type="boolean"> </parameter> </method> +<method name="setHapticFeedbackEnabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="hapticFeedbackEnabled" type="boolean"> +</parameter> +</method> <method name="setHorizontalFadingEdgeEnabled" return="void" abstract="false" @@ -125358,6 +126496,17 @@ visibility="public" > </field> +<field name="HAPTIC_FEEDBACK_ENABLED" + type="int" + transient="false" + volatile="false" + value="268435456" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="INVISIBLE" type="int" transient="false" @@ -133630,17 +134779,6 @@ <parameter name="flags" type="int"> </parameter> </method> -<method name="hideStatusIcon" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="performContextMenuAction" return="boolean" abstract="false" @@ -133682,6 +134820,19 @@ <parameter name="text" type="android.text.Spannable"> </parameter> </method> +<method name="reportFullscreenMode" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="enabled" type="boolean"> +</parameter> +</method> <method name="sendKeyEvent" return="boolean" abstract="false" @@ -133738,21 +134889,6 @@ <parameter name="end" type="int"> </parameter> </method> -<method name="showStatusIcon" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="packageName" type="java.lang.String"> -</parameter> -<parameter name="resId" type="int"> -</parameter> -</method> </class> <class name="CompletionInfo" extends="java.lang.Object" @@ -134561,7 +135697,7 @@ <parameter name="flags" type="int"> </parameter> </method> -<method name="hideStatusIcon" +<method name="performContextMenuAction" return="boolean" abstract="true" native="false" @@ -134571,8 +135707,10 @@ deprecated="not deprecated" visibility="public" > +<parameter name="id" type="int"> +</parameter> </method> -<method name="performContextMenuAction" +<method name="performPrivateCommand" return="boolean" abstract="true" native="false" @@ -134582,10 +135720,12 @@ deprecated="not deprecated" visibility="public" > -<parameter name="id" type="int"> +<parameter name="action" type="java.lang.String"> +</parameter> +<parameter name="data" type="android.os.Bundle"> </parameter> </method> -<method name="performPrivateCommand" +<method name="reportFullscreenMode" return="boolean" abstract="true" native="false" @@ -134595,9 +135735,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="action" type="java.lang.String"> -</parameter> -<parameter name="data" type="android.os.Bundle"> +<parameter name="enabled" type="boolean"> </parameter> </method> <method name="sendKeyEvent" @@ -134643,21 +135781,6 @@ <parameter name="end" type="int"> </parameter> </method> -<method name="showStatusIcon" - return="boolean" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="packageName" type="java.lang.String"> -</parameter> -<parameter name="resId" type="int"> -</parameter> -</method> <field name="GET_EXTRACTED_TEXT_MONITOR" type="int" transient="false" @@ -135158,6 +136281,19 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="hideStatusIcon" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="imeToken" type="android.os.IBinder"> +</parameter> +</method> <method name="isAcceptingText" return="boolean" abstract="false" @@ -135193,6 +136329,17 @@ visibility="public" > </method> +<method name="isFullscreenMode" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="isWatchingCursor" return="boolean" abstract="false" @@ -135277,6 +136424,23 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="showStatusIcon" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="imeToken" type="android.os.IBinder"> +</parameter> +<parameter name="packageName" type="java.lang.String"> +</parameter> +<parameter name="iconId" type="int"> +</parameter> +</method> <method name="updateCursor" return="void" abstract="false" @@ -135336,21 +136500,6 @@ <parameter name="candidatesEnd" type="int"> </parameter> </method> -<method name="updateStatusIcon" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="iconId" type="int"> -</parameter> -<parameter name="iconPackage" type="java.lang.String"> -</parameter> -</method> <field name="HIDE_IMPLICIT_ONLY" type="int" transient="false" @@ -150635,6 +151784,23 @@ <parameter name="level" type="int"> </parameter> </method> +<method name="setFlipping" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="viewId" type="int"> +</parameter> +<parameter name="flipping" type="boolean"> +</parameter> +<parameter name="milliseconds" type="int"> +</parameter> +</method> <method name="setImageViewBitmap" return="void" abstract="false" diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index c363f04..d6fcbb1 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -18,7 +18,6 @@ package android.app; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; @@ -31,9 +30,7 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; -import android.os.AsyncTask; import android.os.Bundle; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -145,17 +142,6 @@ public abstract class LauncherActivity extends ListActivity { return view; } - private char getCandidateLetter(ResolveInfo info) { - PackageManager pm = LauncherActivity.this.getPackageManager(); - CharSequence label = info.loadLabel(pm); - - if (label == null) { - label = info.activityInfo.name; - } - - return Character.toLowerCase(label.charAt(0)); - } - private void bindView(View view, ListItem item) { TextView text = (TextView) view; text.setText(item.label); @@ -191,7 +177,6 @@ public abstract class LauncherActivity extends ListActivity { results.count = list.size(); } } else { - final PackageManager pm = LauncherActivity.this.getPackageManager(); final String prefixString = prefix.toString().toLowerCase(); ArrayList<ListItem> values = mOriginalValues; @@ -243,8 +228,6 @@ public abstract class LauncherActivity extends ListActivity { private int mIconWidth = -1; private int mIconHeight = -1; - private final Paint mPaint = new Paint(); - private final Rect mBounds = new Rect(); private final Rect mOldBounds = new Rect(); private Canvas mCanvas = new Canvas(); diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 64f1ba2..5744ddc 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -835,9 +835,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // also guard against possible race conditions (late arrival after dismiss) if (mSearchable != null) { handled = doSuggestionsKey(v, keyCode, event); - if (!handled) { - handled = refocusingKeyListener(v, keyCode, event); - } } return handled; } @@ -1024,6 +1021,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @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) { + // quick check against race conditions + if (mSearchable == null) { + return; + } + mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing if (jamQuery) { CursorAdapter ca = getSuggestionsAdapter(parent); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 52aae0d..c4d3f9d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1112,6 +1112,8 @@ public class Intent implements Parcelable { * <p>My include the following extras: * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package. + * <li> {@link #EXTRA_REPLACING} is set to true if this is following + * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package. * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java index 9176d18..31aed32 100644 --- a/core/java/android/gadget/GadgetHost.java +++ b/core/java/android/gadget/GadgetHost.java @@ -38,6 +38,7 @@ import com.android.internal.gadget.IGadgetService; public class GadgetHost { static final int HANDLE_UPDATE = 1; + static final int HANDLE_PROVIDER_CHANGED = 2; static Object sServiceLock = new Object(); static IGadgetService sService; @@ -52,6 +53,13 @@ public class GadgetHost { msg.obj = views; msg.sendToTarget(); } + + public void providerChanged(int gadgetId, GadgetProviderInfo info) { + Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); + msg.arg1 = gadgetId; + msg.obj = info; + msg.sendToTarget(); + } } Handler mHandler = new Handler() { @@ -61,6 +69,10 @@ public class GadgetHost { updateGadgetView(msg.arg1, (RemoteViews)msg.obj); break; } + case HANDLE_PROVIDER_CHANGED: { + onProviderChanged(msg.arg1, (GadgetProviderInfo)msg.obj); + break; + } } } }; @@ -183,7 +195,8 @@ public class GadgetHost { } } - public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) { + public final GadgetHostView createView(Context context, int gadgetId, + GadgetProviderInfo gadget) { GadgetHostView view = onCreateView(context, gadgetId, gadget); view.setGadget(gadgetId, gadget); synchronized (mViews) { @@ -203,9 +216,16 @@ public class GadgetHost { * Called to create the GadgetHostView. Override to return a custom subclass if you * need it. {@more} */ - protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { + protected GadgetHostView onCreateView(Context context, int gadgetId, + GadgetProviderInfo gadget) { return new GadgetHostView(context); } + + /** + * Called when the gadget provider for a gadget has been upgraded to a new apk. + */ + protected void onProviderChanged(int gadgetId, GadgetProviderInfo gadget) { + } void updateGadgetView(int gadgetId, RemoteViews views) { GadgetHostView v; diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java index d92c123..a985bd4 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/gadget/GadgetHostView.java @@ -18,7 +18,6 @@ package android.gadget; import android.content.Context; import android.content.pm.PackageManager; -import android.gadget.GadgetInfo; import android.graphics.Color; import android.util.Config; import android.util.Log; @@ -26,12 +25,18 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; import android.widget.ViewAnimator; -public class GadgetHostView extends ViewAnimator { +/** + * Provides the glue to show gadget views. This class offers automatic animation + * between updates, and will try recycling old views for each incoming + * {@link RemoteViews}. + */ +public class GadgetHostView extends ViewAnimator implements Animation.AnimationListener { static final String TAG = "GadgetHostView"; static final boolean LOGD = Config.LOGD || true; @@ -42,57 +47,93 @@ public class GadgetHostView extends ViewAnimator { return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); } }; - + + Context mLocalContext; + int mGadgetId; - GadgetInfo mInfo; - View mActiveView; - View mStaleView; + GadgetProviderInfo mInfo; - protected int mDefaultGravity = Gravity.CENTER; - + View mActiveView = null; + View mStaleView = null; + + int mActiveLayoutId = -1; + int mStaleLayoutId = -1; + + /** + * Last set of {@link RemoteViews} applied to {@link #mActiveView} + */ + RemoteViews mActiveActions = null; + + /** + * Flag indicating that {@link #mActiveActions} has been applied to + * {@link #mStaleView}, meaning it's readyto recycle. + */ + boolean mStalePrepared = false; + + /** + * Create a host view. Uses default fade animations. + */ public GadgetHostView(Context context) { - super(context); + this(context, android.R.anim.fade_in, android.R.anim.fade_out); } - public void setGadget(int gadgetId, GadgetInfo info) { - if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info); + /** + * Create a host view. Uses specified animations when pushing + * {@link #updateGadget(RemoteViews)}. + * + * @param animationIn Resource ID of in animation to use + * @param animationOut Resource ID of out animation to use + */ + public GadgetHostView(Context context, int animationIn, int animationOut) { + super(context); + mLocalContext = context; + + // Prepare our default transition animations + setAnimateFirstView(true); + setInAnimation(context, animationIn); + setOutAnimation(context, animationOut); + + // Watch for animation events to prepare recycling + Animation inAnimation = getInAnimation(); + if (inAnimation != null) { + inAnimation.setAnimationListener(this); + } + } + + /** + * Set the gadget that will be displayed by this view. + */ + public void setGadget(int gadgetId, GadgetProviderInfo info) { if (mInfo != null) { // TODO: remove the old view, or whatever } mGadgetId = gadgetId; mInfo = info; - - View defaultView = getDefaultView(); - flipUpdate(defaultView); } - /** - * Trigger actual animation between current and new content in the - * {@link ViewAnimator}. - */ - protected void flipUpdate(View newContent) { - if (LOGD) Log.d(TAG, "pushing an update to surface"); - - // Take requested dimensions from parent, but apply default gravity. - ViewGroup.LayoutParams requested = newContent.getLayoutParams(); - if (requested == null) { - requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); + public int getGadgetId() { + return mGadgetId; + } + + public GadgetProviderInfo getGadgetInfo() { + return mInfo; + } + + public void onAnimationEnd(Animation animation) { + // When our transition animation finishes, we should try bringing our + // newly-stale view up to the current view. + if (mActiveActions != null && + mStaleLayoutId == mActiveActions.getLayoutId()) { + if (LOGD) Log.d(TAG, "after animation, layoutId matched so we're recycling old view"); + mActiveActions.reapply(mLocalContext, mStaleView); + mStalePrepared = true; } - - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(requested.width, requested.height); - params.gravity = mDefaultGravity; - newContent.setLayoutParams(params); - - // Add new content and animate to it - addView(newContent); - showNext(); - - // Dispose old stale view - removeView(mStaleView); - mStaleView = mActiveView; - mActiveView = newContent; + } + + public void onAnimationRepeat(Animation animation) { + } + + public void onAnimationStart(Animation animation) { } /** @@ -100,26 +141,42 @@ public class GadgetHostView extends ViewAnimator { * gadget provider. Will animate into these new views as needed. */ public void updateGadget(RemoteViews remoteViews) { - if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews); + if (LOGD) Log.d(TAG, "updateGadget called"); + boolean recycled = false; View newContent = null; Exception exception = null; - try { - if (remoteViews == null) { - // there is no remoteViews (yet), so use the initial layout - newContent = getDefaultView(); - } else { - // use the RemoteViews - // TODO: try applying RemoteViews to existing staleView if available - newContent = remoteViews.apply(mContext, this); + if (remoteViews == null) { + newContent = getDefaultView(); + } + + // If our stale view has been prepared to match active, and the new + // layout matches, try recycling it + if (newContent == null && mStalePrepared && + remoteViews.getLayoutId() == mStaleLayoutId) { + try { + remoteViews.reapply(mLocalContext, mStaleView); + newContent = mStaleView; + recycled = true; + if (LOGD) Log.d(TAG, "was able to recycled existing layout"); + } catch (RuntimeException e) { + exception = e; + } + } + + // Try normal RemoteView inflation + if (newContent == null) { + try { + newContent = remoteViews.apply(mLocalContext, this); + if (LOGD) Log.d(TAG, "had to inflate new layout"); + } catch (RuntimeException e) { + exception = e; } - } catch (RuntimeException e) { - exception = e; } if (exception != null && LOGD) { - Log.w(TAG, "Error inflating gadget " + mInfo, exception); + Log.w(TAG, "Error inflating gadget " + getGadgetInfo(), exception); } if (newContent == null) { @@ -128,8 +185,44 @@ public class GadgetHostView extends ViewAnimator { if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error"); newContent = getErrorView(); } - - flipUpdate(newContent); + + if (!recycled) { + prepareView(newContent); + addView(newContent); + } + + showNext(); + + if (!recycled) { + removeView(mStaleView); + } + + mStalePrepared = false; + mActiveActions = remoteViews; + + mStaleView = mActiveView; + mActiveView = newContent; + + mStaleLayoutId = mActiveLayoutId; + mActiveLayoutId = (remoteViews == null) ? -1 : remoteViews.getLayoutId(); + } + + /** + * Prepare the given view to be shown. This might include adjusting + * {@link FrameLayout.LayoutParams} before inserting. + */ + protected void prepareView(View view) { + // Take requested dimensions from parent, but apply default gravity. + ViewGroup.LayoutParams requested = view.getLayoutParams(); + if (requested == null) { + requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(requested.width, requested.height); + params.gravity = Gravity.CENTER; + view.setLayoutParams(params); } /** @@ -141,7 +234,7 @@ public class GadgetHostView extends ViewAnimator { try { if (mInfo != null) { - Context theirContext = mContext.createPackageContext( + Context theirContext = mLocalContext.createPackageContext( mInfo.provider.getPackageName(), 0 /* no flags */); LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -173,11 +266,10 @@ public class GadgetHostView extends ViewAnimator { * Inflate and return a view that represents an error state. */ protected View getErrorView() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(mLocalContext); // TODO: move this error string and background color into resources tv.setText("Error inflating gadget"); tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); return tv; } } - diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java index 20f4014..a9a2c80 100644 --- a/core/java/android/gadget/GadgetManager.java +++ b/core/java/android/gadget/GadgetManager.java @@ -38,68 +38,142 @@ public class GadgetManager { static final String TAG = "GadgetManager"; /** - * Send this when you want to pick a gadget to display. + * Send this from your gadget host activity when you want to pick a gadget to display. + * The gadget picker activity will be launched. + * <p> + * You must supply the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_GADGET_ID}</td> + * <td>A newly allocated gadgetId, which will be bound to the gadget provider + * once the user has selected one.</td> + * </tr> + * </table> * * <p> * The system will respond with an onActivityResult call with the following extras in * the intent: - * <ul> - * <li><b>gadgetIds</b></li> - * <li><b>hostId</b></li> - * </ul> - * TODO: Add constants for these. - * TODO: Where does this go? + * <table> + * <tr> + * <td>{@link #EXTRA_GADGET_ID}</td> + * <td>The gadgetId that you supplied in the original intent.</td> + * </tr> + * </table> + * <p> + * When you receive the result from the gadget pick activity, if the resultCode is + * {@link android.app.Activity#RESULT_OK}, a gadget has been selected. You should then + * check the GadgetProviderInfo for the returned gadget, and if it has one, launch its configuration + * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete + * the gadgetId. + * + * @see #ACTION_GADGET_CONFIGURE + */ + public static final String ACTION_GADGET_PICK = "android.gadget.action.GADGET_PICK"; + + /** + * Sent when it is time to configure your gadget while it is being added to a host. + * This action is not sent as a broadcast to the gadget provider, but as a startActivity + * to the activity specified in the {@link GadgetProviderInfo GadgetProviderInfo meta-data}. + * + * <p> + * The intent will contain the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_GADGET_ID}</td> + * <td>The gadgetId to configure.</td> + * </tr> + * </table> + * + * <p>If you return {@link android.app.Activity#RESULT_OK} using + * {@link android.app.Activity#setResult Activity.setResult()}, the gadget will be added, + * and you will receive an {@link #ACTION_GADGET_UPDATE} broadcast for this gadget. + * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add + * and not display this gadget, and you will receive a {@link #ACTION_GADGET_DELETED} broadcast. */ - public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET"; + public static final String ACTION_GADGET_CONFIGURE = "android.gadget.action.GADGET_CONFIGURE"; + /** + * An intent extra that contains one gadgetId. + * <p> + * The value will be an int that can be retrieved like this: + * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java getExtra_EXTRA_GADGET_ID} + */ public static final String EXTRA_GADGET_ID = "gadgetId"; + + /** + * An intent extra that contains multiple gadgetIds. + * <p> + * The value will be an int array that can be retrieved like this: + * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java getExtra_EXTRA_GADGET_IDS} + */ public static final String EXTRA_GADGET_IDS = "gadgetIds"; - public static final String EXTRA_HOST_ID = "hostId"; + + /** + * A sentiel value that the gadget manager will never return as a gadgetId. + */ + public static final int INVALID_GADGET_ID = 0; /** * Sent when it is time to update your gadget. * * <p>This may be sent in response to a new instance for this gadget provider having - * been instantiated, the requested {@link GadgetInfo#updatePeriodMillis update interval} + * been instantiated, the requested {@link GadgetProviderInfo#updatePeriodMillis update interval} * having lapsed, or the system booting. - */ - public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE"; - - /** - * Sent when it is time to configure your gadget. This action is not sent as a broadcast - * to the gadget provider, but as a startActivity to the activity specified in the - * {@link GadgetInfo GadgetInfo meta-data}. * - * <p>The {@link #EXTRA_GADGET_ID} extra contains the gadget ID. + * <p> + * The intent will contain the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_GADGET_IDS}</td> + * <td>The gadgetIds to update. This may be all of the gadgets created for this + * provider, or just a subset. The system tries to send updates for as few gadget + * instances as possible.</td> + * </tr> + * </table> + * + * @see GadgetProvider#onUpdate GadgetProvider.onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) */ - public static final String GADGET_CONFIGURE_ACTION = "android.gadget.action.GADGET_CONFIGURE"; + public static final String ACTION_GADGET_UPDATE = "android.gadget.action.GADGET_UPDATE"; /** - * Sent when the gadget is added to a host for the first time. This broadcast is sent at - * boot time if there is a gadget host installed with an instance for this provider. + * Sent when an instance of a gadget is deleted from its host. + * + * @see GadgetProvider#onDeleted GadgetProvider.onDeleted(Context context, int[] gadgetIds) */ - public static final String GADGET_ENABLED_ACTION = "android.gadget.action.GADGET_ENABLED"; + public static final String ACTION_GADGET_DELETED = "android.gadget.action.GADGET_DELETED"; /** - * Sent when an instances of a gadget is deleted from the host. + * Sent when an instance of a gadget is removed from the last host. + * + * @see GadgetProvider#onEnabled GadgetProvider.onEnabled(Context context) */ - public static final String GADGET_DELETED_ACTION = "android.gadget.action.GADGET_DELETED"; + public static final String ACTION_GADGET_DISABLED = "android.gadget.action.GADGET_DISABLED"; /** - * Sent when the gadget is removed from the last host. + * Sent when an instance of a gadget is added to a host for the first time. + * This broadcast is sent at boot time if there is a gadget host installed with + * an instance for this provider. + * + * @see GadgetProvider#onEnabled GadgetProvider.onEnabled(Context context) */ - public static final String GADGET_DISABLED_ACTION = "android.gadget.action.GADGET_DISABLED"; + public static final String ACTION_GADGET_ENABLED = "android.gadget.action.GADGET_ENABLED"; /** * Field for the manifest meta-data tag. + * + * @see GadgetProviderInfo */ - public static final String GADGET_PROVIDER_META_DATA = "android.gadget.provider"; + public static final String META_DATA_GADGET_PROVIDER = "android.gadget.provider"; static WeakHashMap<Context, WeakReference<GadgetManager>> sManagerCache = new WeakHashMap(); static IGadgetService sService; Context mContext; + /** + * Get the GadgetManager instance to use for the supplied {@link android.content.Context + * Context} object. + */ public static GadgetManager getInstance(Context context) { synchronized (sManagerCache) { if (sService == null) { @@ -125,9 +199,11 @@ public class GadgetManager { } /** - * Call this with the new RemoteViews for your gadget whenever you need to. + * Set the RemoteViews to use for the specified gadgetIds. * * <p> + * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, + * and outside of the handler. * This method will only work when called from the uid that owns the gadget provider. * * @param gadgetIds The gadget instances for which to set the RemoteViews. @@ -143,9 +219,26 @@ public class GadgetManager { } /** - * Call this with the new RemoteViews for your gadget whenever you need to. + * Set the RemoteViews to use for the specified gadgetId. * * <p> + * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, + * and outside of the handler. + * This method will only work when called from the uid that owns the gadget provider. + * + * @param gadgetId The gadget instance for which to set the RemoteViews. + * @param views The RemoteViews object to show. + */ + public void updateGadget(int gadgetId, RemoteViews views) { + updateGadget(new int[] { gadgetId }, views); + } + + /** + * Set the RemoteViews to use for all gadget instances for the supplied gadget provider. + * + * <p> + * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, + * and outside of the handler. * This method will only work when called from the uid that owns the gadget provider. * * @param provider The {@link ComponentName} for the {@link @@ -165,7 +258,7 @@ public class GadgetManager { /** * Return a list of the gadget providers that are currently installed. */ - public List<GadgetInfo> getInstalledProviders() { + public List<GadgetProviderInfo> getInstalledProviders() { try { return sService.getInstalledProviders(); } @@ -175,12 +268,12 @@ public class GadgetManager { } /** - * Get the available info about the gadget. If the gadgetId has not been bound yet, - * this method will return null. + * Get the available info about the gadget. * - * TODO: throws GadgetNotFoundException ??? if not valid + * @return A gadgetId. If the gadgetId has not been bound to a provider yet, or + * you don't have access to that gadgetId, null is returned. */ - public GadgetInfo getGadgetInfo(int gadgetId) { + public GadgetProviderInfo getGadgetInfo(int gadgetId) { try { return sService.getGadgetInfo(gadgetId); } @@ -190,7 +283,14 @@ public class GadgetManager { } /** - * Set the component for a given gadgetId. You need the GADGET_LIST permission. + * Set the component for a given gadgetId. + * + * <p class="note">You need the GADGET_LIST permission. This method is to be used by the + * gadget picker. + * + * @param gadgetId The gadget instance for which to set the RemoteViews. + * @param provider The {@link android.content.BroadcastReceiver} that will be the gadget + * provider for this gadget. */ public void bindGadgetId(int gadgetId, ComponentName provider) { try { diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java index 1ddfe3f..7e10e78 100755 --- a/core/java/android/gadget/GadgetProvider.java +++ b/core/java/android/gadget/GadgetProvider.java @@ -55,7 +55,7 @@ public class GadgetProvider extends BroadcastReceiver { // Protect against rogue update broadcasts (not really a security issue, // just filter bad broacasts out so subclasses are less likely to crash). String action = intent.getAction(); - if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) { + if (GadgetManager.ACTION_GADGET_UPDATE.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null) { int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); @@ -64,7 +64,7 @@ public class GadgetProvider extends BroadcastReceiver { } } } - else if (GadgetManager.GADGET_DELETED_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_DELETED.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null) { int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); @@ -73,102 +73,81 @@ public class GadgetProvider extends BroadcastReceiver { } } } - else if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_ENABLED.equals(action)) { this.onEnabled(context); } - else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_DISABLED.equals(action)) { this.onDisabled(context); } } // END_INCLUDE(onReceive) /** - * Called in response to the {@link GadgetManager#GADGET_UPDATE_ACTION} broadcast when + * Called in response to the {@link GadgetManager#ACTION_GADGET_UPDATE} broadcast when * this gadget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews} * for a set of gadgets. Override this method to implement your own gadget functionality. * * {@more} - * <p class="note">If you want this method called, you must declare in an intent-filter in - * your AndroidManifest.xml file that you accept the GADGET_UPDATE_ACTION intent action. - * For example: - * <font color=red>TODO: SAMPLE CODE GOES HERE</font> - * </p> * * @param context The {@link android.content.Context Context} in which this receiver is * running. * @param gadgetManager A {@link GadgetManager} object you can call {@link - * GadgetManager#updateGadgets} on. + * GadgetManager#updateGadget} on. * @param gadgetIds The gadgetsIds for which an update is needed. Note that this * may be all of the gadget instances for this provider, or just * a subset of them. * - * @see GadgetManager#GADGET_UPDATE_ACTION + * @see GadgetManager#ACTION_GADGET_UPDATE */ public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) { } /** - * Called in response to the {@link GadgetManager#GADGET_DELETED_ACTION} broadcast when + * Called in response to the {@link GadgetManager#ACTION_GADGET_DELETED} broadcast when * one or more gadget instances have been deleted. Override this method to implement * your own gadget functionality. * * {@more} - * <p class="note">If you want this method called, you must declare in an intent-filter in - * your AndroidManifest.xml file that you accept the GADGET_DELETED_ACTION intent action. - * For example: - * <font color=red>TODO: SAMPLE CODE GOES HERE</font> - * </p> * * @param context The {@link android.content.Context Context} in which this receiver is * running. * @param gadgetIds The gadgetsIds that have been deleted from their host. * - * @see GadgetManager#GADGET_DELETED_ACTION + * @see GadgetManager#ACTION_GADGET_DELETED */ public void onDeleted(Context context, int[] gadgetIds) { } /** - * Called in response to the {@link GadgetManager#GADGET_ENABLED_ACTION} broadcast when + * Called in response to the {@link GadgetManager#ACTION_GADGET_ENABLED} broadcast when * the a gadget for this provider is instantiated. Override this method to implement your * own gadget functionality. * * {@more} * When the last gadget for this provider is deleted, - * {@link GadgetManager#GADGET_DISABLED_ACTION} is sent and {@link #onDisabled} - * is called. If after that, a gadget for this provider is created again, onEnabled() will - * be called again. + * {@link GadgetManager#ACTION_GADGET_DISABLED} is sent by the gadget manager, and + * {@link #onDisabled} is called. If after that, a gadget for this provider is created + * again, onEnabled() will be called again. * - * <p class="note">If you want this method called, you must declare in an intent-filter in - * your AndroidManifest.xml file that you accept the GADGET_ENABLED_ACTION intent action. - * For example: - * <font color=red>TODO: SAMPLE CODE GOES HERE</font> - * </p> - * * @param context The {@link android.content.Context Context} in which this receiver is * running. * - * @see GadgetManager#GADGET_ENABLED_ACTION + * @see GadgetManager#ACTION_GADGET_ENABLED */ public void onEnabled(Context context) { } /** - * Called in response to the {@link GadgetManager#GADGET_DISABLED_ACTION} broadcast, which + * Called in response to the {@link GadgetManager#ACTION_GADGET_DISABLED} broadcast, which * is sent when the last gadget instance for this provider is deleted. Override this method * to implement your own gadget functionality. * * {@more} - * <p class="note">If you want this method called, you must declare in an intent-filter in - * your AndroidManifest.xml file that you accept the GADGET_DISABLED_ACTION intent action. - * For example: - * <font color=red>TODO: SAMPLE CODE GOES HERE</font> - * </p> * * @param context The {@link android.content.Context Context} in which this receiver is * running. * - * @see GadgetManager#GADGET_DISABLED_ACTION + * @see GadgetManager#ACTION_GADGET_DISABLED */ public void onDisabled(Context context) { } diff --git a/core/java/android/gadget/GadgetInfo.aidl b/core/java/android/gadget/GadgetProviderInfo.aidl index 7231545..589f886 100644 --- a/core/java/android/gadget/GadgetInfo.aidl +++ b/core/java/android/gadget/GadgetProviderInfo.aidl @@ -16,4 +16,4 @@ package android.gadget; -parcelable GadgetInfo; +parcelable GadgetProviderInfo; diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetProviderInfo.java index 5ac3da9..95c0432 100644 --- a/core/java/android/gadget/GadgetInfo.java +++ b/core/java/android/gadget/GadgetProviderInfo.java @@ -21,60 +21,88 @@ import android.os.Parcelable; import android.content.ComponentName; /** - * Describes the meta data for an installed gadget. + * Describes the meta data for an installed gadget provider. The fields in this class + * correspond to the fields in the <code><gadget-provider></code> xml tag. */ -public class GadgetInfo implements Parcelable { +public class GadgetProviderInfo implements Parcelable { /** * Identity of this gadget component. This component should be a {@link * android.content.BroadcastReceiver}, and it will be sent the Gadget intents * {@link android.gadget as described in the gadget package documentation}. + * + * <p>This field corresponds to the <code>android:name</code> attribute in + * the <code><receiver></code> element in the AndroidManifest.xml file. */ public ComponentName provider; /** * Minimum width of the gadget, in dp. + * + * <p>This field corresponds to the <code>android:minWidth</code> attribute in + * the gadget meta-data file. */ public int minWidth; /** * Minimum height of the gadget, in dp. + * + * <p>This field corresponds to the <code>android:minHeight</code> attribute in + * the gadget meta-data file. */ public int minHeight; /** * How often, in milliseconds, that this gadget wants to be updated. * The gadget manager may place a limit on how often a gadget is updated. + * + * <p>This field corresponds to the <code>android:updatePeriodMillis</code> attribute in + * the gadget meta-data file. */ public int updatePeriodMillis; /** * The resource id of the initial layout for this gadget. This should be * displayed until the RemoteViews for the gadget is available. + * + * <p>This field corresponds to the <code>android:initialLayout</code> attribute in + * the gadget meta-data file. */ public int initialLayout; /** * The activity to launch that will configure the gadget. + * + * <p>This class name of field corresponds to the <code>android:configure</code> attribute in + * the gadget meta-data file. The package name always corresponds to the package containing + * the gadget provider. */ public ComponentName configure; /** - * The label to display to the user. + * The label to display to the user in the gadget picker. If not supplied in the + * xml, the application label will be used. + * + * <p>This field corresponds to the <code>android:label</code> attribute in + * the <code><receiver></code> element in the AndroidManifest.xml file. */ public String label; /** - * The icon to display for this gadget in the picker list. + * The icon to display for this gadget in the gadget picker. If not supplied in the + * xml, the application icon will be used. + * + * <p>This field corresponds to the <code>android:icon</code> attribute in + * the <code><receiver></code> element in the AndroidManifest.xml file. */ public int icon; - public GadgetInfo() { + public GadgetProviderInfo() { } /** - * Unflatten the GadgetInfo from a parcel. + * Unflatten the GadgetProviderInfo from a parcel. */ - public GadgetInfo(Parcel in) { + public GadgetProviderInfo(Parcel in) { if (0 != in.readInt()) { this.provider = new ComponentName(in); } @@ -116,24 +144,24 @@ public class GadgetInfo implements Parcelable { } /** - * Parcelable.Creator that instantiates GadgetInfo objects + * Parcelable.Creator that instantiates GadgetProviderInfo objects */ - public static final Parcelable.Creator<GadgetInfo> CREATOR - = new Parcelable.Creator<GadgetInfo>() + public static final Parcelable.Creator<GadgetProviderInfo> CREATOR + = new Parcelable.Creator<GadgetProviderInfo>() { - public GadgetInfo createFromParcel(Parcel parcel) + public GadgetProviderInfo createFromParcel(Parcel parcel) { - return new GadgetInfo(parcel); + return new GadgetProviderInfo(parcel); } - public GadgetInfo[] newArray(int size) + public GadgetProviderInfo[] newArray(int size) { - return new GadgetInfo[size]; + return new GadgetProviderInfo[size]; } }; public String toString() { - return "GadgetInfo(provider=" + this.provider + ")"; + return "GadgetProviderInfo(provider=" + this.provider + ")"; } } diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html index 4b8b9d9..4c04396 100644 --- a/core/java/android/gadget/package.html +++ b/core/java/android/gadget/package.html @@ -1,41 +1,126 @@ <body> -{@hide} <p>Android allows applications to publish views to be embedded in other applications. These views are called gadgets, and are published by "gadget providers." The component that can -contain gadgets is called a "gadget host." See the links below for more information. +contain gadgets is called a "gadget host." </p> -<h3><a href="{@toroot}reference/android/gadget/package-descr.html#providers">Gadget Providers</a></h3> +<h3><a href="package-descr.html#providers">Gadget Providers</a></h3> <ul> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_meta_data">Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</a></li> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_GadgetProvider">Using the {@link android.gadget.GadgetProvider GadgetProvider} class</a></li> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_configuration">Gadget Configuration UI</a></li> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li> -</ul> -<h3><a href="{@toroot}reference/android/gadget/package-descr.html#">Gadget Hosts</a></h3> -<ul> - <li><a href="{@toroot}reference/android/gadget/package-descr.html#">asdf</a></li> + <li><a href="package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li> + <li><a href="package-descr.html#provider_meta_data">Adding the GadgetProviderInfo meta-data</a></li> + <li><a href="package-descr.html#provider_GadgetProvider">Using the GadgetProvider class</a></li> + <li><a href="package-descr.html#provider_configuration">Gadget Configuration UI</a></li> + <li><a href="package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li> </ul> +<h3><a href="package-descr.html#">Gadget Hosts</a></h3> + + {@more} + + <h2><a name="providers"></a>Gadget Providers</h2> -<p>Any application can publish gadgets. All an application needs to do to publish a gadget is +<p> +Any application can publish gadgets. All an application needs to do to publish a gadget is to have a {@link android.content.BroadcastReceiver} that receives the {@link -android.gadget.GadgetManager#GADGET_UPDATE_ACTION GadgetManager.GADGET_UPDATE_ACTION} intent, -and provide some meta-data about the gadget. +android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} intent, +and provide some meta-data about the gadget. Android provides the +{@link android.gadget.GadgetProvider} class, which extends BroadcastReceiver, as a convenience +class to aid in handling the broadcasts. <h3><a name="provider_manifest"></a>Declaring a gadget in the AndroidManifest</h3> -<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</h3> +<p> +First, declare the {@link android.content.BroadcastReceiver} in your application's +<code>AndroidManifest.xml</code> file. + +{@sample frameworks/base/tests/gadgets/GadgetHostTest/AndroidManifest.xml GadgetProvider} + +<p> +The <b><code><receiver></b> element has the following attributes: +<ul> + <li><b><code>android:name</code> -</b> which specifies the + {@link android.content.BroadcastReceiver} or {@link android.gadget.GadgetProvider} + class.</li> + <li><b><code>android:label</code> -</b> which specifies the string resource that + will be shown by the gadget picker as the label.</li> + <li><b><code>android:icon</code> -</b> which specifies the drawable resource that + will be shown by the gadget picker as the icon.</li> +</ul> + +<p> +The <b><code><intent-filter></b> element tells the {@link android.content.pm.PackageManager} +that this {@link android.content.BroadcastReceiver} receives the {@link +android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast. +The gadget manager will send other broadcasts directly to your gadget provider as required. +It is only necessary to explicitly declare that you accept the {@link +android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast. + +<p> +The <b><code><meta-data></code></b> element tells the gadget manager which xml resource to +read to find the {@link android.gadget.GadgetProviderInfo} for your gadget provider. It has the following +attributes: +<ul> + <li><b><code>android:name="android.gadget.provider"</code> -</b> identifies this meta-data + as the {@link android.gadget.GadgetProviderInfo} descriptor.</li> + <li><b><code>android:resource</code> -</b> is the xml resource to use as that descriptor.</li> +</ul> + + +<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetProviderInfo GadgetProviderInfo} meta-data</h3> + +<p> +For a gadget, the values in the {@link android.gadget.GadgetProviderInfo} structure are supplied +in an XML resource. In the example above, the xml resource is referenced with +<code>android:resource="@xml/gadget_info"</code>. That XML file would go in your application's +directory at <code>res/xml/gadget_info.xml</code>. Here is a simple example. + +{@sample frameworks/base/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml GadgetProviderInfo} + +<p> +The attributes are as documented in the {@link android.gadget.GadgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day) + <h3><a name="provider_GadgetProvider"></a>Using the {@link android.gadget.GadgetProvider GadgetProvider} class</h3> +<p>The GadgetProvider class is the easiest way to handle the gadget provider intent broadcasts. +See the <code>src/com/example/android/apis/gadget/ExampleGadgetProvider.java</code> +sample class in ApiDemos for an example. + +<p class="note">Keep in mind that since the the GadgetProvider is a BroadcastReceiver, +your process is not guaranteed to keep running after the callback methods return. See +<a href="../../../guide/topics/fundamentals.html#broadlife">Application Fundamentals > +Broadcast Receiver Lifecycle</a> for more information. + + + <h3><a name="provider_configuration"></a>Gadget Configuration UI</h3> +<p> +Gadget hosts have the ability to start a configuration activity when a gadget is instantiated. +The activity should be declared as normal in AndroidManifest.xml, and it should be listed in +the GadgetProviderInfo XML file in the <code>android:configure</code> attribute. + +<p>The activity you specified will be launched with the {@link +android.gadget.GadgetManager#ACTION_GADGET_CONFIGURE} action. See the documentation for that +action for more info. + +<p>See the <code>src/com/example/android/apis/gadget/ExampleGadgetConfigure.java</code> +sample class in ApiDemos for an example. + + + <h3><a name="providers_broadcasts"></a>Gadget Broadcast Intents</h3> -<p>{@link GadgetProvider} is just a convenience class. If you would like to receive the -gadget broadcasts directly, you can. By way of example, the implementation of -{@link GadgetProvider.onReceive} is quite simple:</p> +<p>{@link android.gadget.GadgetProvider} is just a convenience class. If you would like +to receive the gadget broadcasts directly, you can. The four intents you need to care about are: +<ul> + <li>{@link android.gadget.GadgetManager#ACTION_GADGET_UPDATE}</li> + <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DELETED}</li> + <li>{@link android.gadget.GadgetManager#ACTION_GADGET_ENABLED}</li> + <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DISABLED}</li> +</ul> + +<p>By way of example, the implementation of +{@link android.gadget.GadgetProvider#onReceive} is quite simple:</p> {@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive} diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index c09567c..40a5b47 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -18,6 +18,7 @@ package android.hardware; import java.lang.ref.WeakReference; import java.util.HashMap; +import java.util.StringTokenizer; import java.io.IOException; import android.util.Log; @@ -494,11 +495,17 @@ public class Camera { */ public void unflatten(String flattened) { mMap.clear(); - String[] pairs = flattened.split(";"); - for (String p : pairs) { - String[] kv = p.split("="); - if (kv.length == 2) - mMap.put(kv[0], kv[1]); + + StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); + while (tokenizer.hasMoreElements()) { + String kv = tokenizer.nextToken(); + int pos = kv.indexOf('='); + if (pos == -1) { + continue; + } + String k = kv.substring(0, pos); + String v = kv.substring(pos + 1); + mMap.put(k, v); } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ea5f741..c884120 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -206,6 +206,8 @@ public class InputMethodService extends AbstractInputMethodService { static final String TAG = "InputMethodService"; static final boolean DEBUG = false; + InputMethodManager mImm; + LayoutInflater mInflater; View mRootView; SoftInputWindow mWindow; @@ -293,6 +295,8 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + " ic=" + mInputConnection); + InputConnection ic = getCurrentInputConnection(); + if (ic != null) ic.reportFullscreenMode(mIsFullscreen); initialize(); onBindInput(); } @@ -423,7 +427,7 @@ public class InputMethodService extends AbstractInputMethodService { * of the application behind. This value is relative to the top edge * of the input method window. */ - int contentTopInsets; + public int contentTopInsets; /** * This is the top part of the UI that is visibly covering the @@ -436,7 +440,7 @@ public class InputMethodService extends AbstractInputMethodService { * needed to make the focus visible. This value is relative to the top edge * of the input method window. */ - int visibleTopInsets; + public int visibleTopInsets; /** * Option for {@link #touchableInsets}: the entire window frame @@ -469,6 +473,7 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void onCreate() { super.onCreate(); + mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); mWindow = new SoftInputWindow(this); @@ -554,7 +559,6 @@ public class InputMethodService extends AbstractInputMethodService { boolean visible = mWindowVisible; boolean showingInput = mShowInputRequested; boolean showingForced = mShowInputForced; - boolean showingCandidates = mCandidatesVisibility == View.VISIBLE; initViews(); mInputViewStarted = false; mCandidatesViewStarted = false; @@ -577,9 +581,6 @@ public class InputMethodService extends AbstractInputMethodService { // Otherwise just put it back for its candidates. showWindow(false); } - if (showingCandidates) { - setCandidatesViewShown(true); - } } } @@ -670,6 +671,8 @@ public class InputMethodService extends AbstractInputMethodService { if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { changed = true; mIsFullscreen = isFullscreen; + InputConnection ic = getCurrentInputConnection(); + if (ic != null) ic.reportFullscreenMode(isFullscreen); mFullscreenApplied = true; initialize(); Drawable bg = onCreateBackgroundDrawable(); @@ -860,12 +863,14 @@ public class InputMethodService extends AbstractInputMethodService { return isFullscreenMode() ? View.GONE : View.INVISIBLE; } - public void setStatusIcon(int iconResId) { + public void showStatusIcon(int iconResId) { mStatusIcon = iconResId; - InputConnection ic = getCurrentInputConnection(); - if (ic != null && mWindowVisible) { - ic.showStatusIcon(getPackageName(), iconResId); - } + mImm.showStatusIcon(mToken, getPackageName(), iconResId); + } + + public void hideStatusIcon() { + mStatusIcon = 0; + mImm.hideStatusIcon(mToken); } /** @@ -876,8 +881,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param id Unique identifier of the new input method ot start. */ public void switchInputMethod(String id) { - ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) - .setInputMethod(mToken, id); + mImm.setInputMethod(mToken, id); } public void setExtractView(View view) { @@ -1149,15 +1153,9 @@ public class InputMethodService extends AbstractInputMethodService { if (!wasVisible) { if (DEBUG) Log.v(TAG, "showWindow: showing!"); + onWindowShown(); mWindow.show(); } - - if (!wasVisible || !wasCreated) { - InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.showStatusIcon(getPackageName(), mStatusIcon); - } - } } public void hideWindow() { @@ -1173,14 +1171,26 @@ public class InputMethodService extends AbstractInputMethodService { if (mWindowVisible) { mWindow.hide(); mWindowVisible = false; - InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.hideStatusIcon(); - } + onWindowHidden(); } } /** + * Called when the input method window has been shown to the user, after + * previously not being visible. This is done after all of the UI setup + * for the window has occurred (creating its views etc). + */ + public void onWindowShown() { + } + + /** + * Called when the input method window has been hidden from the user, + * after previously being visible. + */ + public void onWindowHidden() { + } + + /** * Called when a new client has bound to the input method. This * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} * and {@link #onFinishInput()} calls as the user navigates through its @@ -1341,8 +1351,7 @@ public class InputMethodService extends AbstractInputMethodService { * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set. */ public void dismissSoftInput(int flags) { - ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)) - .hideSoftInputFromInputMethod(mToken, flags); + mImm.hideSoftInputFromInputMethod(mToken, flags); } /** @@ -1447,17 +1456,19 @@ public class InputMethodService extends AbstractInputMethodService { return true; } } else { - KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); - if (movement.onKeyDown(eet, - (Spannable)eet.getText(), keyCode, down)) { - KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP); - movement.onKeyUp(eet, - (Spannable)eet.getText(), keyCode, up); - while (--count > 0) { - movement.onKeyDown(eet, - (Spannable)eet.getText(), keyCode, down); + if (!movement.onKeyOther(eet, (Spannable)eet.getText(), event)) { + KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); + if (movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down)) { + KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP); movement.onKeyUp(eet, (Spannable)eet.getText(), keyCode, up); + while (--count > 0) { + movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down); + movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, up); + } } } } @@ -1593,5 +1604,9 @@ public class InputMethodService extends AbstractInputMethodService { p.println(" mExtractedToken=" + mExtractedToken); p.println(" mIsInputViewShown=" + mIsInputViewShown + " mStatusIcon=" + mStatusIcon); + p.println("Last computed insets:"); + p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets + + " visibleTopInsets=" + mTmpInsets.visibleTopInsets + + " touchableInsets=" + mTmpInsets.touchableInsets); } } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index b2c74f2..b8bd10d 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -1084,6 +1084,10 @@ public class KeyboardView extends View implements View.OnClickListener { if (mPreviewPopup.isShowing()) { mPreviewPopup.dismiss(); } + mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); + mHandler.removeMessages(MSG_SHOW_PREVIEW); + dismissPopupKeyboard(); } diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java index 70e50b7..a6efcdd 100644 --- a/core/java/android/net/UrlQuerySanitizer.java +++ b/core/java/android/net/UrlQuerySanitizer.java @@ -23,7 +23,7 @@ import java.util.Set; import java.util.StringTokenizer; /** - * + * * Sanitizes the Query portion of a URL. Simple example: * <code> * UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(); @@ -32,7 +32,7 @@ import java.util.StringTokenizer; * String name = sanitizer.getValue("name")); * // name now contains "Joe_User" * </code> - * + * * Register ValueSanitizers to customize the way individual * parameters are sanitized: * <code> @@ -46,7 +46,7 @@ import java.util.StringTokenizer; * unregistered parameter sanitizer does not allow any special characters, * and ' ' is a special character.) * </code> - * + * * There are several ways to create ValueSanitizers. In order of increasing * sophistication: * <ol> @@ -56,7 +56,7 @@ import java.util.StringTokenizer; * <li>Subclass UrlQuerySanitizer.ValueSanitizer to define your own value * sanitizer. * </ol> - * + * */ public class UrlQuerySanitizer { @@ -84,7 +84,7 @@ public class UrlQuerySanitizer { */ public String mValue; } - + final private HashMap<String, ValueSanitizer> mSanitizers = new HashMap<String, ValueSanitizer>(); final private HashMap<String, String> mEntries = @@ -95,9 +95,9 @@ public class UrlQuerySanitizer { private boolean mPreferFirstRepeatedParameter; private ValueSanitizer mUnregisteredParameterValueSanitizer = getAllIllegal(); - + /** - * A functor used to sanitize a single query value. + * A functor used to sanitize a single query value. * */ public static interface ValueSanitizer { @@ -108,7 +108,7 @@ public class UrlQuerySanitizer { */ public String sanitize(String value); } - + /** * Sanitize values based on which characters they contain. Illegal * characters are replaced with either space or '_', depending upon @@ -117,7 +117,7 @@ public class UrlQuerySanitizer { public static class IllegalCharacterValueSanitizer implements ValueSanitizer { private int mFlags; - + /** * Allow space (' ') characters. */ @@ -165,21 +165,21 @@ public class UrlQuerySanitizer { * such as "javascript:" or "vbscript:" */ public final static int SCRIPT_URL_OK = 1 << 10; - + /** * Mask with all fields set to OK */ public final static int ALL_OK = 0x7ff; - + /** * Mask with both regular space and other whitespace OK */ public final static int ALL_WHITESPACE_OK = SPACE_OK | OTHER_WHITESPACE_OK; - + // Common flag combinations: - + /** * <ul> * <li>Deny all special characters. @@ -262,18 +262,18 @@ public class UrlQuerySanitizer { */ public final static int ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL = ALL_OK & ~(NUL_OK | LT_OK | GT_OK); - + /** * Script URL definitions */ - + private final static String JAVASCRIPT_PREFIX = "javascript:"; - + private final static String VBSCRIPT_PREFIX = "vbscript:"; - + private final static int MIN_SCRIPT_PREFIX_LENGTH = Math.min( JAVASCRIPT_PREFIX.length(), VBSCRIPT_PREFIX.length()); - + /** * Construct a sanitizer. The parameters set the behavior of the * sanitizer. @@ -312,7 +312,7 @@ public class UrlQuerySanitizer { } } } - + // If whitespace isn't OK, get rid of whitespace at beginning // and end of value. if ( (mFlags & ALL_WHITESPACE_OK) == 0) { @@ -337,7 +337,7 @@ public class UrlQuerySanitizer { } return stringBuilder.toString(); } - + /** * Trim whitespace from the beginning and end of a string. * <p> @@ -361,7 +361,7 @@ public class UrlQuerySanitizer { } return value.substring(start, end + 1); } - + /** * Check if c is whitespace. * @param c character to test @@ -380,7 +380,7 @@ public class UrlQuerySanitizer { return false; } } - + /** * Check whether an individual character is legal. Uses the * flag bit-set passed into the constructor. @@ -400,11 +400,11 @@ public class UrlQuerySanitizer { case '%' : return (mFlags & PCT_OK) != 0; case '\0': return (mFlags & NUL_OK) != 0; default : return (c >= 32 && c < 127) || - (c >= 128 && c <= 255 && ((mFlags & NON_7_BIT_ASCII_OK) != 0)); - } + ((c >= 128) && ((mFlags & NON_7_BIT_ASCII_OK) != 0)); + } } } - + /** * Get the current value sanitizer used when processing * unregistered parameter values. @@ -412,14 +412,14 @@ public class UrlQuerySanitizer { * <b>Note:</b> The default unregistered parameter value sanitizer is * one that doesn't allow any special characters, similar to what * is returned by calling createAllIllegal. - * + * * @return the current ValueSanitizer used to sanitize unregistered * parameter values. */ public ValueSanitizer getUnregisteredParameterValueSanitizer() { return mUnregisteredParameterValueSanitizer; } - + /** * Set the value sanitizer used when processing unregistered * parameter values. @@ -430,46 +430,46 @@ public class UrlQuerySanitizer { ValueSanitizer sanitizer) { mUnregisteredParameterValueSanitizer = sanitizer; } - - + + // Private fields for singleton sanitizers: - + private static final ValueSanitizer sAllIllegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_ILLEGAL); - + private static final ValueSanitizer sAllButNulLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_NUL_LEGAL); - + private static final ValueSanitizer sAllButWhitespaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_WHITESPACE_LEGAL); - + private static final ValueSanitizer sURLLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.URL_LEGAL); - + private static final ValueSanitizer sUrlAndSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.URL_AND_SPACE_LEGAL); - + private static final ValueSanitizer sAmpLegal = new IllegalCharacterValueSanitizer( - IllegalCharacterValueSanitizer.AMP_LEGAL); - + IllegalCharacterValueSanitizer.AMP_LEGAL); + private static final ValueSanitizer sAmpAndSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.AMP_AND_SPACE_LEGAL); - + private static final ValueSanitizer sSpaceLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.SPACE_LEGAL); - + private static final ValueSanitizer sAllButNulAndAngleBracketsLegal = new IllegalCharacterValueSanitizer( IllegalCharacterValueSanitizer.ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL); - + /** * Return a value sanitizer that does not allow any special characters, * and also does not allow script URLs. @@ -478,7 +478,7 @@ public class UrlQuerySanitizer { public static final ValueSanitizer getAllIllegal() { return sAllIllegal; } - + /** * Return a value sanitizer that allows everything except Nul ('\0') * characters. Script URLs are allowed. @@ -547,7 +547,7 @@ public class UrlQuerySanitizer { public static final ValueSanitizer getAllButNulAndAngleBracketsLegal() { return sAllButNulAndAngleBracketsLegal; } - + /** * Constructs a UrlQuerySanitizer. * <p> @@ -560,7 +560,7 @@ public class UrlQuerySanitizer { */ public UrlQuerySanitizer() { } - + /** * Constructs a UrlQuerySanitizer and parse a URL. * This constructor is provided for convenience when the @@ -585,7 +585,7 @@ public class UrlQuerySanitizer { setAllowUnregisteredParamaters(true); parseUrl(url); } - + /** * Parse the query parameters out of an encoded URL. * Works by extracting the query portion from the URL and then @@ -604,7 +604,7 @@ public class UrlQuerySanitizer { } parseQuery(query); } - + /** * Parse a query. A query string is any number of parameter-value clauses * separated by any non-zero number of ampersands. A parameter-value clause @@ -631,7 +631,7 @@ public class UrlQuerySanitizer { } } } - + /** * Get a set of all of the parameters found in the sanitized query. * <p> @@ -641,7 +641,7 @@ public class UrlQuerySanitizer { public Set<String> getParameterSet() { return mEntries.keySet(); } - + /** * An array list of all of the parameter value pairs in the sanitized * query, in the order they appeared in the query. May contain duplicate @@ -691,7 +691,7 @@ public class UrlQuerySanitizer { } mSanitizers.put(parameter, valueSanitizer); } - + /** * Register a value sanitizer for an array of parameters. * @param parameters An array of unencoded parameter names. @@ -705,7 +705,7 @@ public class UrlQuerySanitizer { mSanitizers.put(parameters[i], valueSanitizer); } } - + /** * Set whether or not unregistered parameters are allowed. If they * are not allowed, then they will be dropped when a query is sanitized. @@ -718,7 +718,7 @@ public class UrlQuerySanitizer { boolean allowUnregisteredParamaters) { mAllowUnregisteredParamaters = allowUnregisteredParamaters; } - + /** * Get whether or not unregistered parameters are allowed. If not * allowed, they will be dropped when a query is parsed. @@ -728,10 +728,10 @@ public class UrlQuerySanitizer { public boolean getAllowUnregisteredParamaters() { return mAllowUnregisteredParamaters; } - + /** * Set whether or not the first occurrence of a repeated parameter is - * preferred. True means the first repeated parameter is preferred. + * preferred. True means the first repeated parameter is preferred. * False means that the last repeated parameter is preferred. * <p> * The preferred parameter is the one that is returned when getParameter @@ -746,7 +746,7 @@ public class UrlQuerySanitizer { boolean preferFirstRepeatedParameter) { mPreferFirstRepeatedParameter = preferFirstRepeatedParameter; } - + /** * Get whether or not the first occurrence of a repeated parameter is * preferred. @@ -757,10 +757,10 @@ public class UrlQuerySanitizer { public boolean getPreferFirstRepeatedParameter() { return mPreferFirstRepeatedParameter; } - + /** * Parse an escaped parameter-value pair. The default implementation - * unescapes both the parameter and the value, then looks up the + * unescapes both the parameter and the value, then looks up the * effective value sanitizer for the parameter and uses it to sanitize * the value. If all goes well then addSanitizedValue is called with * the unescaped parameter and the sanitized unescaped value. @@ -779,7 +779,7 @@ public class UrlQuerySanitizer { String sanitizedValue = valueSanitizer.sanitize(unescapedValue); addSanitizedEntry(unescapedParameter, sanitizedValue); } - + /** * Record a sanitized parameter-value pair. Override if you want to * do additional filtering or validation. @@ -796,7 +796,7 @@ public class UrlQuerySanitizer { } mEntries.put(parameter, value); } - + /** * Get the value sanitizer for a parameter. Returns null if there * is no value sanitizer registered for the parameter. @@ -807,7 +807,7 @@ public class UrlQuerySanitizer { public ValueSanitizer getValueSanitizer(String parameter) { return mSanitizers.get(parameter); } - + /** * Get the effective value sanitizer for a parameter. Like getValueSanitizer, * except if there is no value sanitizer registered for a parameter, and @@ -823,7 +823,7 @@ public class UrlQuerySanitizer { } return sanitizer; } - + /** * Unescape an escaped string. * <ul> @@ -867,7 +867,7 @@ public class UrlQuerySanitizer { } return stringBuilder.toString(); } - + /** * Test if a character is a hexidecimal digit. Both upper case and lower * case hex digits are allowed. @@ -877,7 +877,7 @@ public class UrlQuerySanitizer { protected boolean isHexDigit(char c) { return decodeHexDigit(c) >= 0; } - + /** * Convert a character that represents a hexidecimal digit into an integer. * If the character is not a hexidecimal digit, then -1 is returned. @@ -885,7 +885,7 @@ public class UrlQuerySanitizer { * @param c the hexidecimal digit. * @return the integer value of the hexidecimal digit. */ - + protected int decodeHexDigit(char c) { if (c >= '0' && c <= '9') { return c - '0'; @@ -900,7 +900,7 @@ public class UrlQuerySanitizer { return -1; } } - + /** * Clear the existing entries. Called to get ready to parse a new * query string. diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java index c6615da..1a287c8 100644 --- a/core/java/android/pim/RecurrenceSet.java +++ b/core/java/android/pim/RecurrenceSet.java @@ -140,7 +140,6 @@ public class RecurrenceSet { recurrence = recurrence.substring(tzidx + 1); } Time time = new Time(tz); - boolean rdateNotInUtc = !tz.equals(Time.TIMEZONE_UTC); String[] rawDates = recurrence.split(","); int n = rawDates.length; long[] dates = new long[n]; diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java index 05c2952..02ab1da 100644 --- a/core/java/android/preference/PreferenceGroupAdapter.java +++ b/core/java/android/preference/PreferenceGroupAdapter.java @@ -88,6 +88,9 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) { mPreferenceGroup = preferenceGroup; + // If this group gets or loses any children, let us know + mPreferenceGroup.setOnPreferenceChangeInternalListener(this); + mPreferenceList = new ArrayList<Preference>(); mPreferenceClassNames = new ArrayList<String>(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 054da1d..c6a7b40 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -879,15 +879,6 @@ public final class Settings { public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios"; /** - * The interval in milliseconds after which Wi-Fi is considered idle. - * When idle, it is possible for the device to be switched from Wi-Fi to - * the mobile data network. - * - * @hide pending API Council approval - */ - public static final String WIFI_IDLE_MS = "wifi_idle_ms"; - - /** * The policy for deciding when Wi-Fi should go to sleep (which will in * turn switch to using the mobile data as an Internet connection). * <p> @@ -1288,6 +1279,12 @@ public final class Settings { */ public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; + /** + * Whether the haptic feedback (long presses, ...) are enabled. The value is + * boolean (1 or 0). + */ + public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; + // Settings moved to Settings.Secure /** @@ -2732,6 +2729,13 @@ public final class Settings { "gprs_register_check_period_ms"; /** + * The interval in milliseconds after which Wi-Fi is considered idle. + * When idle, it is possible for the device to be switched from Wi-Fi to + * the mobile data network. + */ + public static final String WIFI_IDLE_MS = "wifi_idle_ms"; + + /** * Screen timeout in milliseconds corresponding to the * PowerManager's POKE_LOCK_SHORT_TIMEOUT flag (i.e. the fastest * possible screen timeout behavior.) diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index d149761..7c15045 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -25,8 +25,8 @@ package android.server; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; // just for dump() import android.bluetooth.BluetoothError; +import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothIntent; import android.bluetooth.IBluetoothDevice; import android.bluetooth.IBluetoothDeviceCallback; @@ -35,23 +35,20 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.os.RemoteException; -import android.provider.Settings; -import android.util.Log; import android.os.Binder; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.SystemService; +import android.provider.Settings; +import android.util.Log; -import java.io.IOException; import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.io.FileWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class BluetoothDeviceService extends IBluetoothDevice.Stub { @@ -119,7 +116,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { public synchronized boolean disable() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - + if (mEnableThread != null && mEnableThread.isAlive()) { return false; } @@ -229,9 +226,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { long origCallerIdentityToken = Binder.clearCallingIdentity(); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, bluetoothOn ? 1 : 0); - Binder.restoreCallingIdentity(origCallerIdentityToken); + Binder.restoreCallingIdentity(origCallerIdentityToken); } - + private native int enableNative(); private native int disableNative(); @@ -247,6 +244,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { public class BondState { private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); + private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>(); public synchronized void loadBondState() { if (!mIsEnabled) { @@ -281,8 +279,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState); if (state == BluetoothDevice.BOND_NOT_BONDED) { if (reason <= 0) { - Log.w(TAG, "setBondState() called to unbond device with invalid reason code " + - "Setting reason = BOND_RESULT_REMOVED"); + Log.w(TAG, "setBondState() called to unbond device, but reason code is " + + "invalid. Overriding reason code with BOND_RESULT_REMOVED"); reason = BluetoothDevice.UNBOND_REASON_REMOVED; } intent.putExtra(BluetoothIntent.REASON, reason); @@ -290,11 +288,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } else { mState.put(address, state); } - if (state == BluetoothDevice.BOND_BONDING) { - mPinAttempt.put(address, Integer.valueOf(0)); - } else { - mPinAttempt.remove(address); - } + mContext.sendBroadcast(intent, BLUETOOTH_PERM); } @@ -316,6 +310,24 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return result.toArray(new String[result.size()]); } + public synchronized void addAutoPairingFailure(String address) { + if (!mAutoPairingFailures.contains(address)) { + mAutoPairingFailures.add(address); + } + } + + public synchronized boolean isAutoPairingAttemptsInProgress(String address) { + return getAttempt(address) != 0; + } + + public synchronized void clearPinAttempts(String address) { + mPinAttempt.remove(address); + } + + public synchronized boolean hasAutoPairingFailed(String address) { + return mAutoPairingFailures.contains(address); + } + public synchronized int getAttempt(String address) { Integer attempt = mPinAttempt.get(address); if (attempt == null) { @@ -326,10 +338,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { public synchronized void attempt(String address) { Integer attempt = mPinAttempt.get(address); + int newAttempt; if (attempt == null) { - return; + newAttempt = 1; + } else { + newAttempt = attempt.intValue() + 1; } - mPinAttempt.put(address, new Integer(attempt.intValue() + 1)); + mPinAttempt.put(address, new Integer(newAttempt)); } } @@ -508,7 +523,11 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return false; } address = address.toUpperCase(); - if (mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) { + + // Check for bond state only if we are not performing auto + // pairing exponential back-off attempts. + if (!mBondState.isAutoPairingAttemptsInProgress(address) && + mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) { return false; } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 0f60fae..b5e4090 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -24,6 +24,8 @@ import android.bluetooth.BluetoothIntent; import android.bluetooth.IBluetoothDeviceCallback; import android.content.Context; import android.content.Intent; +import android.os.Handler; +import android.os.Message; import android.os.RemoteException; import android.util.Log; @@ -48,9 +50,33 @@ class BluetoothEventLoop { private BluetoothDeviceService mBluetoothService; private Context mContext; + private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1; + + // The time (in millisecs) to delay the pairing attempt after the first + // auto pairing attempt fails. We use an exponential delay with + // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and + // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value. + private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; + private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: + String address = (String)msg.obj; + if (address != null) { + mBluetoothService.createBond(address); + return; + } + break; + } + } + }; + static { classInitNative(); } private static native void classInitNative(); @@ -149,16 +175,6 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - private void onPairingRequest() { - Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - } - - private void onPairingCancel() { - Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - } - private void onRemoteDeviceFound(String address, int deviceClass, short rssi) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); @@ -214,12 +230,55 @@ class BluetoothEventLoop { address = address.toUpperCase(); if (result == BluetoothError.SUCCESS) { mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED); + if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { + mBluetoothService.getBondState().clearPinAttempts(address); + } + } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED && + mBluetoothService.getBondState().getAttempt(address) == 1) { + mBluetoothService.getBondState().addAutoPairingFailure(address); + pairingAttempt(address, result); + } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN && + mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { + pairingAttempt(address, result); } else { mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED, result); + if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { + mBluetoothService.getBondState().clearPinAttempts(address); + } } } + private void pairingAttempt(String address, int result) { + // This happens when our initial guess of "0000" as the pass key + // fails. Try to create the bond again and display the pin dialog + // to the user. Use back-off while posting the delayed + // message. The initial value is + // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is + // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is + // reached, display an error to the user. + int attempt = mBluetoothService.getBondState().getAttempt(address); + if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY > + MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) { + mBluetoothService.getBondState().clearPinAttempts(address); + mBluetoothService.getBondState().setBondState(address, + BluetoothDevice.BOND_NOT_BONDED, result); + return; + } + + Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); + message.obj = address; + boolean postResult = mHandler.sendMessageDelayed(message, + attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); + if (!postResult) { + mBluetoothService.getBondState().clearPinAttempts(address); + mBluetoothService.getBondState().setBondState(address, + BluetoothDevice.BOND_NOT_BONDED, result); + return; + } + mBluetoothService.getBondState().attempt(address); + } + private void onBondingCreated(String address) { mBluetoothService.getBondState().setBondState(address.toUpperCase(), BluetoothDevice.BOND_BONDED); @@ -253,12 +312,12 @@ class BluetoothEventLoop { case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - if (mBluetoothService.getBondState().getAttempt(address) < 1) { + if (!mBluetoothService.getBondState().hasAutoPairingFailed(address)) { mBluetoothService.getBondState().attempt(address); mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000")); return; } - } + } } Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index a559b9d..6df0b35 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -16,6 +16,7 @@ package android.text.method; +import android.util.Log; import android.view.KeyEvent; import android.text.*; import android.widget.TextView; @@ -185,15 +186,9 @@ implements MovementMethod if (code != KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) { int repeat = event.getRepeatCount(); - boolean first = true; boolean handled = false; while ((--repeat) > 0) { - if (first && executeDown(view, text, code)) { - handled = true; - MetaKeyKeyListener.adjustMetaAfterKeypress(text); - MetaKeyKeyListener.resetLockedMeta(text); - } - first = false; + handled |= executeDown(view, text, code); } return handled; } diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index d89fbec..39ad976 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -287,10 +287,10 @@ public abstract class MetaKeyKeyListener { } public static void clearMetaKeyState(Editable content, int states) { - 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); + if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP); + if ((states&META_ALT_ON) != 0) content.removeSpan(ALT); + if ((states&META_SYM_ON) != 0) content.removeSpan(SYM); + if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING); } /** diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java index 85adabd..fad4f64 100644 --- a/core/java/android/text/method/PasswordTransformationMethod.java +++ b/core/java/android/text/method/PasswordTransformationMethod.java @@ -105,8 +105,10 @@ implements TransformationMethod, TextWatcher sp.removeSpan(old[i]); } - sp.setSpan(new Visible(sp, this), start, start + count, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + if (count == 1) { + sp.setSpan(new Visible(sp, this), start, start + count, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } } } } diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java new file mode 100644 index 0000000..cc3563c --- /dev/null +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * Constants to be used to perform haptic feedback effects via + * {@link View#performHapticFeedback(int)} + */ +public class HapticFeedbackConstants { + + private HapticFeedbackConstants() {} + + public static final int LONG_PRESS = 0; + + /** @hide pending API council */ + public static final int ZOOM_RING_TICK = 1; + + /** + * Flag for {@link View#performHapticFeedback(int, int) + * View.performHapticFeedback(int, int)}: Ignore the setting in the + * view for whether to perform haptic feedback, do it always. + */ + public static final int FLAG_IGNORE_VIEW_SETTING = 0x0001; + + /** + * Flag for {@link View#performHapticFeedback(int, int) + * View.performHapticFeedback(int, int)}: Ignore the global setting + * for whether to perform haptic feedback, do it always. + */ + public static final int FLAG_IGNORE_GLOBAL_SETTING = 0x0002; +} diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 7276f17..1156856 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -106,5 +106,6 @@ interface IWindowSession { void setInTouchMode(boolean showFocus); boolean getInTouchMode(); + + boolean performHapticFeedback(IWindow window, int effectId, boolean always); } - diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a51b564..1d5e7cd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -836,6 +836,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public static final int SOUND_EFFECTS_ENABLED = 0x08000000; /** + * View flag indicating whether this view should have haptic feedback + * enabled for events such as long presses. + */ + public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; + + /** * Use with {@link #focusSearch}. Move focus to the previous selectable * item. */ @@ -1637,6 +1643,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; + mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED; ++sInstanceCount; } @@ -1703,9 +1710,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY; - viewFlagValues |= SOUND_EFFECTS_ENABLED; - viewFlagMasks |= SOUND_EFFECTS_ENABLED; - final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); @@ -1801,6 +1805,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { viewFlagValues &= ~SOUND_EFFECTS_ENABLED; viewFlagMasks |= SOUND_EFFECTS_ENABLED; } + case com.android.internal.R.styleable.View_hapticFeedbackEnabled: + if (!a.getBoolean(attr, true)) { + viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED; + viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED; + } case R.styleable.View_scrollbars: final int scrollbars = a.getInt(attr, SCROLLBARS_NONE); if (scrollbars != SCROLLBARS_NONE) { @@ -2182,6 +2191,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (!handled) { handled = showContextMenu(); } + if (handled) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } return handled; } @@ -2742,7 +2754,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * Set whether this view should have sound effects enabled for events such as * clicking and touching. * - * You may wish to disable sound effects for a view if you already play sounds, + * <p>You may wish to disable sound effects for a view if you already play sounds, * for instance, a dial key that plays dtmf tones. * * @param soundEffectsEnabled whether sound effects are enabled for this view. @@ -2768,6 +2780,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * Set whether this view should have haptic feedback for events such as + * long presses. + * + * <p>You may wish to disable haptic feedback if your view already controls + * its own haptic feedback. + * + * @param hapticFeedbackEnabled whether haptic feedback enabled for this view. + * @see #isHapticFeedbackEnabled() + * @see #performHapticFeedback(int) + * @attr ref android.R.styleable#View_hapticFeedbackEnabled + */ + public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) { + setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED); + } + + /** + * @return whether this view should have haptic feedback enabled for events + * long presses. + * + * @see #setHapticFeedbackEnabled(boolean) + * @see #performHapticFeedback(int) + * @attr ref android.R.styleable#View_hapticFeedbackEnabled + */ + @ViewDebug.ExportedProperty + public boolean isHapticFeedbackEnabled() { + return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED); + } + + /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. @@ -7312,20 +7353,57 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * Play a sound effect for this view. * - * The framework will play sound effects for some built in actions, such as + * <p>The framework will play sound effects for some built in actions, such as * clicking, but you may wish to play these effects in your widget, * for instance, for internal navigation. * - * The sound effect will only be played if sound effects are enabled by the user, and + * <p>The sound effect will only be played if sound effects are enabled by the user, and * {@link #isSoundEffectsEnabled()} is true. * * @param soundConstant One of the constants defined in {@link SoundEffectConstants} */ - protected void playSoundEffect(int soundConstant) { - if (mAttachInfo == null || mAttachInfo.mSoundEffectPlayer == null || !isSoundEffectsEnabled()) { + public void playSoundEffect(int soundConstant) { + if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) { return; } - mAttachInfo.mSoundEffectPlayer.playSoundEffect(soundConstant); + mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant); + } + + /** + * Provide haptic feedback to the user for this view. + * + * <p>The framework will provide haptic feedback for some built in actions, + * such as long presses, but you may wish to provide feedback for your + * own widget. + * + * <p>The feedback will only be performed if + * {@link #isHapticFeedbackEnabled()} is true. + * + * @param feedbackConstant One of the constants defined in + * {@link HapticFeedbackConstants} + */ + public boolean performHapticFeedback(int feedbackConstant) { + return performHapticFeedback(feedbackConstant, 0); + } + + /** + * Like {@link #performHapticFeedback(int)}, with additional options. + * + * @param feedbackConstant One of the constants defined in + * {@link HapticFeedbackConstants} + * @param flags Additional flags as per {@link HapticFeedbackConstants}. + */ + public boolean performHapticFeedback(int feedbackConstant, int flags) { + if (mAttachInfo == null) { + return false; + } + if ((flags&HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0 + && !isHapticFeedbackEnabled()) { + return false; + } + return mAttachInfo.mRootCallbacks.performHapticFeedback( + feedbackConstant, + (flags&HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0); } /** @@ -7704,8 +7782,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ static class AttachInfo { - interface SoundEffectPlayer { + interface Callbacks { void playSoundEffect(int effectId); + boolean performHapticFeedback(int effectId, boolean always); } /** @@ -7775,7 +7854,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final IBinder mWindowToken; - final SoundEffectPlayer mSoundEffectPlayer; + final Callbacks mRootCallbacks; /** * The top view of the hierarchy. @@ -7922,12 +8001,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @param handler the events handler the view must use */ AttachInfo(IWindowSession session, IWindow window, - Handler handler, SoundEffectPlayer effectPlayer) { + Handler handler, Callbacks effectPlayer) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mHandler = handler; - mSoundEffectPlayer = effectPlayer; + mRootCallbacks = effectPlayer; } } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 4e46397..ccfa6bf 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -61,7 +61,7 @@ import static javax.microedition.khronos.opengles.GL10.*; */ @SuppressWarnings({"EmptyCatchBlock"}) public final class ViewRoot extends Handler implements ViewParent, - View.AttachInfo.SoundEffectPlayer { + View.AttachInfo.Callbacks { private static final String TAG = "ViewRoot"; private static final boolean DBG = false; @SuppressWarnings({"ConstantConditionalExpression"}) @@ -1637,7 +1637,7 @@ public final class ViewRoot extends Handler implements ViewParent, dispatchDetachedFromWindow(); break; case DISPATCH_KEY_FROM_IME: - if (true) Log.v( + if (LOCAL_LOGV) Log.v( "ViewRoot", "Dispatching key " + msg.obj + " from IME to " + mView); deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false); @@ -2238,6 +2238,17 @@ public final class ViewRoot extends Handler implements ViewParent, /** * {@inheritDoc} */ + public boolean performHapticFeedback(int effectId, boolean always) { + try { + return sWindowSession.performHapticFeedback(mWindow, effectId, always); + } catch (RemoteException e) { + return false; + } + } + + /** + * {@inheritDoc} + */ public View focusSearch(View focused, int direction) { checkThread(); if (!(mView instanceof ViewGroup)) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index d08a6fa..406af3e3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -925,7 +925,7 @@ public interface WindowManager extends ViewManager { sb.append(Integer.toHexString(windowAnimations)); } if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - sb.append("or="); + sb.append(" or="); sb.append(screenOrientation); } sb.append('}'); diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 542b35f..051f823 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -771,4 +771,15 @@ public interface WindowManagerPolicy { public boolean isCheekPressedAgainstScreen(MotionEvent ev); public void setCurrentOrientation(int newOrientation); + + /** + * Call from application to perform haptic feedback on its window. + */ + public boolean performHapticFeedback(WindowState win, int effectId, boolean always); + + /** + * Called when we have stopped keeping the screen on because a window + * requesting this is no longer visible. + */ + public void screenOnStopped(); } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 56c6c92..9509b15 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -371,6 +371,14 @@ public class BaseInputConnection implements InputConnection { if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end); final Editable content = getEditable(); if (content == null) return false; + int len = content.length(); + if (start > len || end > len) { + // If the given selection is out of bounds, just ignore it. + // Most likely the text was changed out from under the IME, + // the the IME is going to have to update all of its state + // anyway. + return true; + } Selection.setSelection(content, start, end); return true; } @@ -396,20 +404,10 @@ public class BaseInputConnection implements InputConnection { } /** - * Provides standard implementation for hiding the status icon associated - * with the current input method. + * Updates InputMethodManager with the current fullscreen mode. */ - public boolean hideStatusIcon() { - mIMM.updateStatusIcon(0, null); - return true; - } - - /** - * Provides standard implementation for showing the status icon associated - * with the current input method. - */ - public boolean showStatusIcon(String packageName, int resId) { - mIMM.updateStatusIcon(resId, packageName); + public boolean reportFullscreenMode(boolean enabled) { + mIMM.setFullscreenMode(enabled); return true; } @@ -420,7 +418,11 @@ public class BaseInputConnection implements InputConnection { Editable content = getEditable(); if (content != null) { - if (content.length() == 1) { + final int N = content.length(); + if (N == 0) { + return; + } + if (N == 1) { // If it's 1 character, we have a chance of being // able to generate normal key events... if (mKeyCharacterMap == null) { diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 8c30d3f..13173f6 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -267,6 +267,13 @@ public interface InputConnection { public boolean clearMetaKeyStates(int states); /** + * Called by the IME to tell the client when it switches between fullscreen + * and normal modes. This will normally be called for you by the standard + * implementation of {@link android.inputmethodservice.InputMethodService}. + */ + public boolean reportFullscreenMode(boolean enabled); + + /** * API to send private commands from an input method to its connected * editor. This can be used to provide domain-specific features that are * only known between certain input methods and their clients. Note that @@ -284,23 +291,4 @@ public interface InputConnection { * valid. */ public boolean performPrivateCommand(String action, Bundle data); - - /** - * Show an icon in the status bar. - * - * @param packageName The package holding the icon resource to be shown. - * @param resId The resource id of the icon to show. - * - * @return Returns true on success, false if the input connection is no longer - * valid. - */ - public boolean showStatusIcon(String packageName, int resId); - - /** - * Hide the icon shown in the status bar. - * - * @return Returns true on success, false if the input connection is no longer - * valid. - */ - public boolean hideStatusIcon(); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 99d5aa5..fe14166 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -214,6 +214,11 @@ public final class InputMethodManager { */ boolean mActive = false; + /** + * As reported by IME through InputConnection. + */ + boolean mFullscreenMode; + // ----------------------------------------------------------- /** @@ -374,6 +379,7 @@ public final class InputMethodManager { public void setActive(boolean active) { mActive = active; + mFullscreenMode = false; } }; @@ -443,14 +449,36 @@ public final class InputMethodManager { } } - public void updateStatusIcon(int iconId, String iconPackage) { + public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { + try { + mService.updateStatusIcon(imeToken, packageName, iconId); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + public void hideStatusIcon(IBinder imeToken) { try { - mService.updateStatusIcon(iconId, iconPackage); + mService.updateStatusIcon(imeToken, null, 0); } catch (RemoteException e) { throw new RuntimeException(e); } } + /** @hide */ + public void setFullscreenMode(boolean enabled) { + mFullscreenMode = true; + } + + /** + * Allows you to discover whether the attached input method is running + * in fullscreen mode. Return true if it is fullscreen, entirely covering + * your UI, else returns false. + */ + public boolean isFullscreenMode() { + return mFullscreenMode; + } + /** * Return true if the given view is the currently active view for the * input method. @@ -503,7 +531,6 @@ public final class InputMethodManager { void finishInputLocked() { if (mServedView != null) { if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView); - updateStatusIcon(0, null); if (mCurrentTextBoxAttribute != null) { try { diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 5a37f04..07c1a5d 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -171,6 +171,10 @@ public final class CookieManager { boolean pathMatch(String urlPath) { if (urlPath.startsWith(path)) { int len = path.length(); + if (len == 0) { + Log.w(LOGTAG, "Empty cookie path"); + return false; + } int urlLen = urlPath.length(); if (path.charAt(len-1) != PATH_DELIM && urlLen > len) { // make sure /wee doesn't match /we @@ -864,7 +868,10 @@ public final class CookieManager { "illegal format for max-age: " + value); } } else if (name.equals(PATH)) { - cookie.path = value; + // only allow non-empty path value + if (value.length() > 0) { + cookie.path = value; + } } else if (name.equals(DOMAIN)) { int lastPeriod = value.lastIndexOf(PERIOD); if (lastPeriod == 0) { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 3306700..bdbf38a 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -260,19 +260,8 @@ public class WebView extends AbsoluteLayout // Whether we are in the drag tap mode, which exists starting at the second // tap's down, through its move, and includes its up. These events should be // given to the method on the zoom controller. - private boolean mInZoomTapDragMode; - - // The event time of the previous touch up. - private long mPreviousUpTime; - - private Runnable mRemoveReleaseSingleTap = new Runnable() { - public void run() { - mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - } - }; - + private boolean mInZoomTapDragMode = false; + // Whether to prevent drag during touch. The initial value depends on // mForwardTouchEvents. If WebCore wants touch events, we assume it will // take control of touch events unless it says no for touch down event. @@ -517,6 +506,11 @@ public class WebView extends AbsoluteLayout private ZoomRingController mZoomRingController; + // These keep track of the center point of the zoom ring. They are used to + // determine the point around which we should zoom. + private float mZoomCenterX; + private float mZoomCenterY; + private ZoomRingController.OnZoomListener mZoomListener = new ZoomRingController.OnZoomListener() { @@ -554,12 +548,9 @@ public class WebView extends AbsoluteLayout deltaZoomLevel == 0) { return false; } - - int deltaX = centerX - getViewWidth() / 2; - int deltaY = centerY - getViewHeight() / 2; + mZoomCenterX = (float) centerX; + mZoomCenterY = (float) centerY; - pinScrollBy(deltaX, deltaY, false, 0); - while (deltaZoomLevel != 0) { if (deltaZoomLevel > 0) { if (!zoomIn()) return false; @@ -569,15 +560,16 @@ public class WebView extends AbsoluteLayout deltaZoomLevel++; } } - - pinScrollBy(-deltaX, -deltaY, false, 0); - + return true; } public void onSimpleZoom(boolean zoomIn) { - if (zoomIn) zoomIn(); - else zoomOut(); + if (zoomIn) { + zoomIn(); + } else { + zoomOut(); + } } }; @@ -1586,8 +1578,8 @@ public class WebView extends AbsoluteLayout int oldX = mScrollX; int oldY = mScrollY; float ratio = scale * mInvActualScale; // old inverse - float sx = ratio * oldX + (ratio - 1) * getViewWidth() * 0.5f; - float sy = ratio * oldY + (ratio - 1) * getViewHeight() * 0.5f; + float sx = ratio * oldX + (ratio - 1) * mZoomCenterX; + float sy = ratio * oldY + (ratio - 1) * mZoomCenterY; // now update our new scale and inverse if (scale != mActualScale && !mPreviewZoomOnly) { @@ -2264,8 +2256,8 @@ public class WebView extends AbsoluteLayout zoomScale = mZoomScale; } float scale = (mActualScale - zoomScale) * mInvActualScale; - float tx = scale * ((getLeft() + getRight()) * 0.5f + mScrollX); - float ty = scale * ((getTop() + getBottom()) * 0.5f + mScrollY); + float tx = scale * (mZoomCenterX + mScrollX); + float ty = scale * (mZoomCenterY + mScrollY); // this block pins the translate to "legal" bounds. This makes the // animation a bit non-obvious, but it means we won't pop when the @@ -3025,8 +3017,8 @@ public class WebView extends AbsoluteLayout (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0); break; case KeyEvent.KEYCODE_9: - debugDump(); - break; + nativeInstrumentReport(); + return true; } } @@ -3161,6 +3153,7 @@ public class WebView extends AbsoluteLayout * @hide */ public void emulateShiftHeld() { + mExtendSelection = false; mShiftIsPressed = true; } @@ -3176,6 +3169,7 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection); copiedSomething = true; } + mExtendSelection = false; } mShiftIsPressed = false; if (mTouchMode == TOUCH_SELECT_MODE) { @@ -3218,6 +3212,11 @@ public class WebView extends AbsoluteLayout } } + /** + * @deprecated WebView should not have implemented + * ViewTreeObserver.OnGlobalFocusChangeListener. This method + * does nothing now. + */ @Deprecated public void onGlobalFocusChanged(View oldFocus, View newFocus) { } @@ -3281,7 +3280,11 @@ public class WebView extends AbsoluteLayout @Override protected void onSizeChanged(int w, int h, int ow, int oh) { super.onSizeChanged(w, h, ow, oh); - + // Center zooming to the center of the screen. This is appropriate for + // this case of zooming, and it also sets us up properly if we remove + // the new zoom ring controller + mZoomCenterX = getViewWidth() * .5f; + mZoomCenterY = getViewHeight() * .5f; // we always force, in case our height changed, in which case we still // want to send the notification over to webkit setNewZoomScale(mActualScale, true); @@ -3342,25 +3345,12 @@ public class WebView extends AbsoluteLayout + mTouchMode); } - if (mZoomRingController.isVisible()) { - if (mInZoomTapDragMode) { - mZoomRingController.handleDoubleTapEvent(ev); - if (ev.getAction() == MotionEvent.ACTION_UP) { - // Just released the second tap, no longer in tap-drag mode - mInZoomTapDragMode = false; - } - return true; - } else { - // TODO: properly do this. - /* - * When the zoom widget is showing, the user can tap outside of - * it to dismiss it. Furthermore, he can drag outside of it to - * pan the browser. However, we do not want a tap on a link to - * open the link. - */ - post(mRemoveReleaseSingleTap); - // Continue through to normal processing + if (mZoomRingController.isVisible() && mInZoomTapDragMode) { + if (ev.getAction() == MotionEvent.ACTION_UP) { + // Just released the second tap, no longer in tap-drag mode + mInZoomTapDragMode = false; } + return mZoomRingController.handleDoubleTapEvent(ev); } int action = ev.getAction(); @@ -3418,21 +3408,19 @@ public class WebView extends AbsoluteLayout , viewToContent(mSelectY), false); mTouchSelection = mExtendSelection = true; } else if (!ZoomRingController.useOldZoom(mContext) && - eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT && - getSettings().supportZoom() && - mMinZoomScale < mMaxZoomScale) { + mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) { // Found doubletap, invoke the zoom controller - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); mZoomRingController.setVisible(true); mInZoomTapDragMode = true; - mZoomRingController.handleDoubleTapEvent(ev); + return mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; mPreventDrag = mForwardTouchEvents; } - if (mTouchMode == TOUCH_INIT_MODE) { + // don't trigger the link if zoom ring is visible + if (mTouchMode == TOUCH_INIT_MODE + && !mZoomRingController.isVisible()) { mPrivateHandler.sendMessageDelayed(mPrivateHandler .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT); } @@ -3485,9 +3473,6 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); } - // Prevent double-tap from being invoked - mPreviousUpTime = 0; - // if it starts nearly horizontal or vertical, enforce it int ax = Math.abs(deltaX); int ay = Math.abs(deltaY); @@ -3597,6 +3582,10 @@ public class WebView extends AbsoluteLayout case MotionEvent.ACTION_UP: { switch (mTouchMode) { case TOUCH_INIT_MODE: // tap + if (mZoomRingController.isVisible()) { + // don't trigger the link if zoom ring is visible + break; + } mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); if (getSettings().supportZoom() && (mMinZoomScale < mMaxZoomScale)) { @@ -3611,7 +3600,7 @@ public class WebView extends AbsoluteLayout break; case TOUCH_SELECT_MODE: commitCopy(); - mTouchSelection = mExtendSelection = false; + mTouchSelection = false; break; case SCROLL_ZOOM_ANIMATION_IN: case SCROLL_ZOOM_ANIMATION_OUT: @@ -3679,7 +3668,6 @@ public class WebView extends AbsoluteLayout mVelocityTracker.recycle(); mVelocityTracker = null; } - mPreviousUpTime = eventTime; break; } case MotionEvent.ACTION_CANCEL: { @@ -4110,6 +4098,14 @@ public class WebView extends AbsoluteLayout } /** + * @hide pending API council? Assuming we make ZoomRingController itself + * public, which I think we will. + */ + public ZoomRingController getZoomRingController() { + return mZoomRingController; + } + + /** * Perform zoom in in the webview * @return TRUE if zoom in succeeds. FALSE if no zoom changes. */ @@ -4193,16 +4189,15 @@ public class WebView extends AbsoluteLayout return; } switchOutDrawHistory(); - // FIXME: we don't know if the current (x,y) is on a focus node or - // not -- so playing the sound effect here is premature - if (nativeUpdateFocusNode()) { - playSoundEffect(SoundEffectConstants.CLICK); - } // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); nativeMotionUp(contentX, contentY, contentSize, true); + if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField + && !mFocusNode.mIsTextArea) { + playSoundEffect(SoundEffectConstants.CLICK); + } } @Override @@ -5013,6 +5008,7 @@ public class WebView extends AbsoluteLayout private native boolean nativeUpdateFocusNode(); private native Rect nativeGetFocusRingBounds(); private native Rect nativeGetNavBounds(); + private native void nativeInstrumentReport(); private native void nativeMarkNodeInvalid(int node); private native void nativeMotionUp(int x, int y, int slop, boolean isClick); // returns false if it handled the key diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 8f78887..b979032 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -330,8 +330,7 @@ final class WebViewCore { String currentText, int keyCode, int keyValue, boolean down, boolean cap, boolean fn, boolean sym); - private native void nativeSaveDocumentState(int frame, int node, int x, - int y); + private native void nativeSaveDocumentState(int frame); private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x, int y, boolean block); @@ -777,8 +776,7 @@ final class WebViewCore { case SAVE_DOCUMENT_STATE: { FocusData fDat = (FocusData) msg.obj; - nativeSaveDocumentState(fDat.mFrame, fDat.mNode, - fDat.mX, fDat.mY); + nativeSaveDocumentState(fDat.mFrame); break; } diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 96f3698..1004e30 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -531,33 +531,34 @@ public class WebViewDatabase { * @param url The url * @return CacheResult The CacheManager.CacheResult */ - @SuppressWarnings("deprecation") CacheResult getCache(String url) { if (url == null || mCacheDatabase == null) { return null; } - CacheResult ret = null; - final String s = "SELECT filepath, lastmodify, etag, expires, mimetype, encoding, httpstatus, location, contentlength FROM cache WHERE url = "; - StringBuilder sb = new StringBuilder(256); - sb.append(s); - DatabaseUtils.appendEscapedSQLString(sb, url); - Cursor cursor = mCacheDatabase.rawQuery(sb.toString(), null); + Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, " + + "mimetype, encoding, httpstatus, location, contentlength " + + "FROM cache WHERE url = ?", + new String[] { url }); - if (cursor.moveToFirst()) { - ret = new CacheResult(); - ret.localPath = cursor.getString(0); - ret.lastModified = cursor.getString(1); - ret.etag = cursor.getString(2); - ret.expires = cursor.getLong(3); - ret.mimeType = cursor.getString(4); - ret.encoding = cursor.getString(5); - ret.httpStatusCode = cursor.getInt(6); - ret.location = cursor.getString(7); - ret.contentLength = cursor.getLong(8); + try { + if (cursor.moveToFirst()) { + CacheResult ret = new CacheResult(); + ret.localPath = cursor.getString(0); + ret.lastModified = cursor.getString(1); + ret.etag = cursor.getString(2); + ret.expires = cursor.getLong(3); + ret.mimeType = cursor.getString(4); + ret.encoding = cursor.getString(5); + ret.httpStatusCode = cursor.getInt(6); + ret.location = cursor.getString(7); + ret.contentLength = cursor.getLong(8); + return ret; + } + } finally { + if (cursor != null) cursor.close(); } - cursor.close(); - return ret; + return null; } /** @@ -565,16 +566,12 @@ public class WebViewDatabase { * * @param url The url */ - @SuppressWarnings("deprecation") void removeCache(String url) { if (url == null || mCacheDatabase == null) { return; } - StringBuilder sb = new StringBuilder(256); - sb.append("DELETE FROM cache WHERE url = "); - DatabaseUtils.appendEscapedSQLString(sb, url); - mCacheDatabase.execSQL(sb.toString()); + mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url }); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 378d218..c012e25 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -31,6 +31,7 @@ import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -1622,6 +1623,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId); handled = super.showContextMenuForChild(AbsListView.this); } + if (handled) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } return handled; } diff --git a/core/java/android/widget/CursorFilter.java b/core/java/android/widget/CursorFilter.java index afd5b10..dbded69 100644 --- a/core/java/android/widget/CursorFilter.java +++ b/core/java/android/widget/CursorFilter.java @@ -60,11 +60,10 @@ class CursorFilter extends Filter { } @Override - protected void publishResults(CharSequence constraint, - FilterResults results) { + protected void publishResults(CharSequence constraint, FilterResults results) { Cursor oldCursor = mClient.getCursor(); - if (results.values != oldCursor) { + if (results.values != null && results.values != oldCursor) { mClient.changeCursor((Cursor) results.values); } } diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 67010b2..54f2707 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -47,11 +47,8 @@ public class DatePicker extends FrameLayout { /* UI Components */ private final NumberPicker mDayPicker; private final NumberPicker mMonthPicker; - private final NumberPicker mYearPicker; - - private final int mStartYear; - private final int mEndYear; - + private final NumberPicker mYearPicker; + /** * How we notify users the date has changed. */ @@ -87,12 +84,9 @@ public class DatePicker extends FrameLayout { public DatePicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - LayoutInflater inflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.date_picker, - this, // we are the parent - true); - + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.date_picker, this, true); + mDayPicker = (NumberPicker) findViewById(R.id.day); mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); mDayPicker.setSpeed(100); @@ -134,20 +128,17 @@ public class DatePicker extends FrameLayout { }); // attributes - TypedArray a = context - .obtainStyledAttributes(attrs, R.styleable.DatePicker); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker); - mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR); - mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR); + int mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR); + int mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR); mYearPicker.setRange(mStartYear, mEndYear); a.recycle(); // initialize to current date Calendar cal = Calendar.getInstance(); - init(cal.get(Calendar.YEAR), - cal.get(Calendar.MONTH), - cal.get(Calendar.DAY_OF_MONTH), null); + init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null); // re-order the number pickers to match the current date format reorderPickers(); diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java index 7f1601e..a2316cf 100644 --- a/core/java/android/widget/Filter.java +++ b/core/java/android/widget/Filter.java @@ -20,6 +20,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.util.Log; /** * <p>A filter constrains data with a filtering pattern.</p> @@ -36,6 +37,8 @@ import android.os.Message; * @see android.widget.Filterable */ public abstract class Filter { + private static final String LOG_TAG = "Filter"; + private static final String THREAD_NAME = "Filter"; private static final int FILTER_TOKEN = 0xD0D0F00D; private static final int FINISH_TOKEN = 0xDEADBEEF; @@ -221,6 +224,9 @@ public abstract class Filter { RequestArguments args = (RequestArguments) msg.obj; try { args.results = performFiltering(args.constraint); + } catch (Exception e) { + args.results = new FilterResults(); + Log.w(LOG_TAG, "An exception occured during performFiltering()!", e); } finally { message = mResultHandler.obtainMessage(what); message.obj = args; diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index ffabb02..e7b303a 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -27,6 +27,7 @@ import android.util.Config; import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -994,6 +995,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList return; } + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); long id = getItemIdAtPosition(mDownTouchPosition); dispatchLongPress(mDownTouchView, mDownTouchPosition, id); } @@ -1086,6 +1088,10 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList handled = super.showContextMenuForChild(this); } + if (handled) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + return handled; } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index b5d4e2d..4ae322e 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -48,6 +48,7 @@ import android.widget.RemoteViews.RemoteView; * @attr ref android.R.styleable#ImageView_maxHeight * @attr ref android.R.styleable#ImageView_tint * @attr ref android.R.styleable#ImageView_scaleType + * @attr ref android.R.styleable#ImageView_cropToPadding */ @RemoteView public class ImageView extends View { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a1023bd..25afee8 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -35,6 +35,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater.Filter; import android.view.View.OnClickListener; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -548,6 +550,54 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()} + * or {@link android.widget.ViewFlipper#stopFlipping()} along with + * {@link android.widget.ViewFlipper#setFlipInterval(int)}. + */ + private class SetFlipping extends Action { + public SetFlipping(int id, boolean flipping, int milliseconds) { + this.viewId = id; + this.flipping = flipping; + this.milliseconds = milliseconds; + } + + public SetFlipping(Parcel parcel) { + viewId = parcel.readInt(); + flipping = parcel.readInt() != 0; + milliseconds = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(flipping ? 1 : 0); + dest.writeInt(milliseconds); + } + + @Override + public void apply(View root) { + final View target = root.findViewById(viewId); + if (target instanceof ViewFlipper) { + final ViewFlipper flipper = (ViewFlipper) target; + if (milliseconds != -1) { + flipper.setFlipInterval(milliseconds); + } + if (flipping) { + flipper.startFlipping(); + } else { + flipper.stopFlipping(); + } + } + } + + int viewId; + boolean flipping; + int milliseconds; + + public final static int TAG = 10; + } + + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * @@ -603,6 +653,9 @@ public class RemoteViews implements Parcelable, Filter { case SetTextColor.TAG: mActions.add(new SetTextColor(parcel)); break; + case SetFlipping.TAG: + mActions.add(new SetFlipping(parcel)); + break; default: throw new ActionException("Tag " + tag + "not found"); } @@ -769,6 +822,22 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()} + * or {@link android.widget.ViewFlipper#stopFlipping()} along with + * {@link android.widget.ViewFlipper#setFlipInterval(int)}. + * + * @param viewId The id of the view to apply changes to + * @param flipping True means we should + * {@link android.widget.ViewFlipper#startFlipping()}, otherwise + * {@link android.widget.ViewFlipper#stopFlipping()}. + * @param milliseconds How long to wait before flipping to the next view, or + * -1 to leave unchanged. + */ + public void setFlipping(int viewId, boolean flipping, int milliseconds) { + addAction(new SetFlipping(viewId, flipping, milliseconds)); + } + + /** * Inflates the view hierarchy represented by this object and applies * all of the actions. * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d21c017..2ae5d4e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3844,7 +3844,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean doDown = true; if (otherEvent != null) { try { - boolean handled = mMovement.onKeyOther(this, (Editable) mText, + boolean handled = mMovement.onKeyOther(this, (Spannable) mText, otherEvent); doDown = false; if (handled) { diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index 8c652e5..fa8935e 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -28,6 +28,9 @@ import android.view.animation.AnimationUtils; /** * Base class for a {@link FrameLayout} container that will perform animations * when switching between its views. + * + * @attr ref android.R.styleable#ViewAnimator_inAnimation + * @attr ref android.R.styleable#ViewAnimator_outAnimation */ public class ViewAnimator extends FrameLayout { diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java index a3c15d9..e20bfdf 100644 --- a/core/java/android/widget/ViewFlipper.java +++ b/core/java/android/widget/ViewFlipper.java @@ -22,12 +22,16 @@ import android.content.res.TypedArray; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; +import android.widget.RemoteViews.RemoteView; /** * Simple {@link ViewAnimator} that will animate between two or more views * that have been added to it. Only one child is shown at a time. If * requested, can automatically flip between each child at a regular interval. + * + * @attr ref android.R.styleable#ViewFlipper_flipInterval */ +@RemoteView public class ViewFlipper extends ViewAnimator { private int mFlipInterval = 3000; private boolean mKeepFlipping = false; diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java index df3f307..0df919d 100644 --- a/core/java/android/widget/ZoomButton.java +++ b/core/java/android/widget/ZoomButton.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector; +import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -57,6 +58,7 @@ public class ZoomButton extends ImageButton implements OnLongClickListener { mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); onLongClick(ZoomButton.this); } }); diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java index 20d6056..be3b1fb 100644 --- a/core/java/android/widget/ZoomRing.java +++ b/core/java/android/widget/ZoomRing.java @@ -6,10 +6,9 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; -import android.os.Handler; +import android.graphics.drawable.RotateDrawable; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -18,17 +17,20 @@ import android.view.ViewConfiguration; * @hide */ public class ZoomRing extends View { - + // TODO: move to ViewConfiguration? - private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); + static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); // TODO: get from theme private static final int DISABLED_ALPHA = 160; - + private static final String TAG = "ZoomRing"; + // TODO: Temporary until the trail is done + private static final boolean DRAW_TRAIL = false; + // TODO: xml - private static final int THUMB_DISTANCE = 63; - + private static final int THUMB_DISTANCE = 63; + /** To avoid floating point calculations, we multiply radians by this value. */ public static final int RADIAN_INT_MULTIPLIER = 100000000; /** PI using our multiplier. */ @@ -36,68 +38,81 @@ public class ZoomRing extends View { /** PI/2 using our multiplier. */ private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2; + private int mZeroAngle = HALF_PI_INT_MULTIPLIED * 3; + private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 4; - + /** The cached X of our center. */ private int mCenterX; - /** The cached Y of our center. */ + /** The cached Y of our center. */ private int mCenterY; /** The angle of the thumb (in int radians) */ private int mThumbAngle; private boolean mIsThumbAngleValid; - private int mThumbCenterX; - private int mThumbCenterY; private int mThumbHalfWidth; private int mThumbHalfHeight; - - private int mCallbackThreshold = Integer.MAX_VALUE; - - /** The accumulated amount of drag for the thumb (in int radians). */ - private int mAcculumalatedThumbDrag = 0; - + /** The inner radius of the track. */ private int mBoundInnerRadiusSquared = 0; /** The outer radius of the track. */ private int mBoundOuterRadiusSquared = Integer.MAX_VALUE; - + private int mPreviousWidgetDragX; private int mPreviousWidgetDragY; - + private boolean mDrawThumb = true; private Drawable mThumbDrawable; - + private static final int MODE_IDLE = 0; private static final int MODE_DRAG_THUMB = 1; + /** + * User has his finger down, but we are waiting for him to pass the touch + * slop before going into the #MODE_MOVE_ZOOM_RING. This is a good time to + * show the movable hint. + */ + private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4; private static final int MODE_MOVE_ZOOM_RING = 2; private static final int MODE_TAP_DRAG = 3; private int mMode; - private long mPreviousTapTime; - - private Handler mHandler = new Handler(); - + private long mPreviousDownTime; + private int mPreviousDownX; + private int mPreviousDownY; + private Disabler mDisabler = new Disabler(); - + private OnZoomRingCallback mCallback; - + private int mPreviousCallbackAngle; + private int mCallbackThreshold = Integer.MAX_VALUE; + private boolean mResetThumbAutomatically = true; private int mThumbDragStartAngle; - + private final int mTouchSlop; + private Drawable mTrail; + private double mAcculumalatedTrailAngle; + public ZoomRing(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - // TODO get drawable from style instead + + ViewConfiguration viewConfiguration = ViewConfiguration.get(context); + mTouchSlop = viewConfiguration.getScaledTouchSlop(); + + // TODO get drawables from style instead Resources res = context.getResources(); mThumbDrawable = res.getDrawable(R.drawable.zoom_ring_thumb); - + if (DRAW_TRAIL) { + mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate(); + } + // TODO: add padding to drawable setBackgroundResource(R.drawable.zoom_ring_track); // TODO get from style setBounds(30, Integer.MAX_VALUE); - + mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; - + mCallbackThreshold = PI_INT_MULTIPLIED / 6; } @@ -108,7 +123,7 @@ public class ZoomRing extends View { public ZoomRing(Context context) { this(context, null); } - + public void setCallback(OnZoomRingCallback callback) { mCallback = callback; } @@ -132,26 +147,49 @@ public class ZoomRing extends View { mBoundOuterRadiusSquared = Integer.MAX_VALUE; } } - + public void setThumbAngle(int angle) { mThumbAngle = angle; - mThumbCenterX = (int) (Math.cos(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) - + mCenterX; - mThumbCenterY = (int) (Math.sin(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) - * -1 + mCenterY; + int unoffsetAngle = angle + mZeroAngle; + int thumbCenterX = (int) (Math.cos(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) * + THUMB_DISTANCE) + mCenterX; + int thumbCenterY = (int) (Math.sin(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) * + THUMB_DISTANCE) * -1 + mCenterY; + + mThumbDrawable.setBounds(thumbCenterX - mThumbHalfWidth, + thumbCenterY - mThumbHalfHeight, + thumbCenterX + mThumbHalfWidth, + thumbCenterY + mThumbHalfHeight); + + if (DRAW_TRAIL) { + double degrees; + degrees = Math.min(359.0, Math.abs(mAcculumalatedTrailAngle)); + int level = (int) (10000.0 * degrees / 360.0); + + mTrail.setLevel((int) (10000.0 * + (-Math.toDegrees(angle / (double) RADIAN_INT_MULTIPLIER) - + degrees + 90) / 360.0)); + ((RotateDrawable) mTrail).getDrawable().setLevel(level); + } + invalidate(); } - + + public void resetThumbAngle(int angle) { + mPreviousCallbackAngle = angle; + setThumbAngle(angle); + } + public void resetThumbAngle() { if (mResetThumbAutomatically) { - setThumbAngle(HALF_PI_INT_MULTIPLIED); + resetThumbAngle(0); } } - + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { mResetThumbAutomatically = resetThumbAutomatically; } - + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), @@ -162,7 +200,7 @@ public class ZoomRing extends View { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - + // Cache the center point mCenterX = (right - left) / 2; mCenterY = (bottom - top) / 2; @@ -172,8 +210,12 @@ public class ZoomRing extends View { if (mThumbAngle == Integer.MIN_VALUE) { resetThumbAngle(); } + + if (DRAW_TRAIL) { + mTrail.setBounds(0, 0, right - left, bottom - top); + } } - + @Override public boolean onTouchEvent(MotionEvent event) { return handleTouch(event.getAction(), event.getEventTime(), @@ -184,61 +226,66 @@ public class ZoomRing extends View { private void resetState() { mMode = MODE_IDLE; mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE; - mAcculumalatedThumbDrag = 0; + mAcculumalatedTrailAngle = 0.0; mIsThumbAngleValid = false; } - + public void setTapDragMode(boolean tapDragMode, int x, int y) { resetState(); mMode = tapDragMode ? MODE_TAP_DRAG : MODE_IDLE; mIsThumbAngleValid = false; - + if (tapDragMode && mCallback != null) { onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY)); } } - + public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) { switch (action) { - + case MotionEvent.ACTION_DOWN: - if (mPreviousTapTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) { + if (mPreviousDownTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) { if (mCallback != null) { mCallback.onZoomRingDismissed(); } } else { - mPreviousTapTime = time; + mPreviousDownTime = time; + mPreviousDownX = x; + mPreviousDownY = y; } resetState(); return true; - + case MotionEvent.ACTION_MOVE: // Fall through to code below switch break; - + case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mCallback != null) { - if (mMode == MODE_MOVE_ZOOM_RING) { - mCallback.onZoomRingMovingStopped(); + if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + mCallback.onZoomRingSetMovableHintVisible(false); + if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStopped(); + } } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { onThumbDragStopped(getAngle(x - mCenterX, y - mCenterY)); } } mDisabler.setEnabling(true); return true; - + default: return false; } - + // local{X,Y} will be where the center of the widget is (0,0) int localX = x - mCenterX; int localY = y - mCenterY; boolean isTouchingThumb = true; boolean isInBounds = true; int touchAngle = getAngle(localX, localY); - + int radiusSquared = localX * localX + localY * localY; if (radiusSquared < mBoundInnerRadiusSquared || radiusSquared > mBoundOuterRadiusSquared) { @@ -246,7 +293,7 @@ public class ZoomRing extends View { isTouchingThumb = false; isInBounds = false; } - + int deltaThumbAndTouch = getDelta(touchAngle, mThumbAngle); int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ? deltaThumbAndTouch : -deltaThumbAndTouch; @@ -255,19 +302,35 @@ public class ZoomRing extends View { // Didn't grab close enough to the thumb isTouchingThumb = false; } - + if (mMode == MODE_IDLE) { - mMode = isTouchingThumb ? MODE_DRAG_THUMB : MODE_MOVE_ZOOM_RING; - + if (isTouchingThumb) { + mMode = MODE_DRAG_THUMB; + } else { + mMode = MODE_WAITING_FOR_MOVE_ZOOM_RING; + } + if (mCallback != null) { if (mMode == MODE_DRAG_THUMB) { onThumbDragStarted(touchAngle); - } else if (mMode == MODE_MOVE_ZOOM_RING) { + } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + mCallback.onZoomRingSetMovableHintVisible(true); + } + } + + } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + if (Math.abs(x - mPreviousDownX) > mTouchSlop || + Math.abs(y - mPreviousDownY) > mTouchSlop) { + /* Make sure the user has moved the slop amount before going into that mode. */ + mMode = MODE_MOVE_ZOOM_RING; + + if (mCallback != null) { mCallback.onZoomRingMovingStarted(); } } } - + + // Purposefully not an "else if" if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { if (isInBounds) { onThumbDragged(touchAngle, mIsThumbAngleValid ? deltaThumbAndTouch : 0); @@ -277,13 +340,13 @@ public class ZoomRing extends View { } else if (mMode == MODE_MOVE_ZOOM_RING) { onZoomRingMoved(rawX, rawY); } - + return true; } - + private int getDelta(int angle1, int angle2) { int delta = angle1 - angle2; - + // Assume this is a result of crossing over the discontinuous 0 -> 2pi if (delta > PI_INT_MULTIPLIED || delta < -PI_INT_MULTIPLIED) { // Bring both the radians and previous angle onto a continuous range @@ -295,7 +358,7 @@ public class ZoomRing extends View { delta -= PI_INT_MULTIPLIED * 2; } } - + return delta; } @@ -303,46 +366,69 @@ public class ZoomRing extends View { mThumbDragStartAngle = startAngle; mCallback.onZoomRingThumbDraggingStarted(startAngle); } - + private void onThumbDragged(int touchAngle, int deltaAngle) { - mAcculumalatedThumbDrag += deltaAngle; - if (mAcculumalatedThumbDrag > mCallbackThreshold - || mAcculumalatedThumbDrag < -mCallbackThreshold) { + mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER); + int totalDeltaAngle = getDelta(touchAngle, mPreviousCallbackAngle); + if (totalDeltaAngle > mCallbackThreshold + || totalDeltaAngle < -mCallbackThreshold) { if (mCallback != null) { boolean canStillZoom = mCallback.onZoomRingThumbDragged( - mAcculumalatedThumbDrag / mCallbackThreshold, - mAcculumalatedThumbDrag, mThumbDragStartAngle, touchAngle); + totalDeltaAngle / mCallbackThreshold, + mThumbDragStartAngle, touchAngle); mDisabler.setEnabling(canStillZoom); + + if (canStillZoom) { + // TODO: we're trying the haptics to see how it goes with + // users, so we're ignoring the settings (for now) + performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK, + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING | + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } } - mAcculumalatedThumbDrag = 0; + + // Get the closest tick and lock on there + mPreviousCallbackAngle = getClosestTickAngle(touchAngle); } - + setThumbAngle(touchAngle); mIsThumbAngleValid = true; } - + + private int getClosestTickAngle(int angle) { + int smallerAngleDistance = angle % mCallbackThreshold; + int smallerAngle = angle - smallerAngleDistance; + if (smallerAngleDistance < mCallbackThreshold / 2) { + // Closer to the smaller angle + return smallerAngle; + } else { + // Closer to the bigger angle (premodding) + return (smallerAngle + mCallbackThreshold) % (PI_INT_MULTIPLIED * 2); + } + } + private void onThumbDragStopped(int stopAngle) { mCallback.onZoomRingThumbDraggingStopped(stopAngle); } - + private void onZoomRingMoved(int x, int y) { if (mPreviousWidgetDragX != Integer.MIN_VALUE) { int deltaX = x - mPreviousWidgetDragX; int deltaY = y - mPreviousWidgetDragY; - + if (mCallback != null) { mCallback.onZoomRingMoved(deltaX, deltaY); } } - + mPreviousWidgetDragX = x; mPreviousWidgetDragY = y; } - + @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); - + if (!hasWindowFocus && mCallback != null) { mCallback.onZoomRingDismissed(); } @@ -353,22 +439,25 @@ public class ZoomRing extends View { // Convert from [-pi,pi] to {0,2pi] if (radians < 0) { - return -radians; + radians = -radians; } else if (radians > 0) { - return 2 * PI_INT_MULTIPLIED - radians; + radians = 2 * PI_INT_MULTIPLIED - radians; } else { - return 0; + radians = 0; } + + radians = radians - mZeroAngle; + return radians >= 0 ? radians : radians + 2 * PI_INT_MULTIPLIED; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - + if (mDrawThumb) { - mThumbDrawable.setBounds(mThumbCenterX - mThumbHalfWidth, mThumbCenterY - - mThumbHalfHeight, mThumbCenterX + mThumbHalfWidth, mThumbCenterY - + mThumbHalfHeight); + if (DRAW_TRAIL) { + mTrail.draw(canvas); + } mThumbDrawable.draw(canvas); } } @@ -409,12 +498,14 @@ public class ZoomRing extends View { } public interface OnZoomRingCallback { + void onZoomRingSetMovableHintVisible(boolean visible); + void onZoomRingMovingStarted(); boolean onZoomRingMoved(int deltaX, int deltaY); void onZoomRingMovingStopped(); void onZoomRingThumbDraggingStarted(int startAngle); - boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, int curAngle); + boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle); void onZoomRingThumbDraggingStopped(int endAngle); void onZoomRingDismissed(); diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java index 2ca0374..eb28767 100644 --- a/core/java/android/widget/ZoomRingController.java +++ b/core/java/android/widget/ZoomRingController.java @@ -17,14 +17,17 @@ package android.widget; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.os.SystemClock; +import android.os.Vibrator; import android.provider.Settings; import android.util.Log; import android.view.Gravity; @@ -42,6 +45,7 @@ import android.view.animation.DecelerateInterpolator; /** * TODO: Docs + * * @hide */ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, @@ -222,7 +226,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public ZoomRingController(Context context, View ownerView) { mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - + mOwnerView = ownerView; mZoomRing = new ZoomRing(context); @@ -437,7 +441,15 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, case MotionEvent.ACTION_UP: mTouchMode = TOUCH_MODE_IDLE; + + /* + * This is a power-user feature that only shows the + * zoom while the user is performing the tap-drag. + * That means once it is released, the zoom ring + * should disappear. + */ mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY()); + dismissZoomRingDelayed(0); break; } break; @@ -560,10 +572,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY); } + public void onZoomRingSetMovableHintVisible(boolean visible) { + setPanningArrowsVisible(visible); + } + public void onZoomRingMovingStarted() { mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); mScroller.abortAnimation(); - setPanningArrowsVisible(true); } private void setPanningArrowsVisible(boolean visible) { @@ -641,8 +656,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } - public boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, - int curAngle) { + public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) { if (mCallback != null) { int deltaZoomLevel = -numLevels; int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() + @@ -650,7 +664,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() + mZoomRingHeight / 2; - return mCallback.onDragZoom(deltaZoomLevel, globalZoomCenterX - mOwnerViewBounds.left, + return mCallback.onDragZoom(deltaZoomLevel, + globalZoomCenterX - mOwnerViewBounds.left, globalZoomCenterY - mOwnerViewBounds.top, (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER, (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER); @@ -719,6 +734,45 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, ensureZoomRingIsCentered(); } + /** + * Shows a "tutorial" (some text) to the user teaching her the new zoom + * invocation method. + * <p> + * It checks the global system setting to ensure this has not been seen + * before. Furthermore, if the application does not have privilege to write + * to the system settings, it will store this bit locally in a shared + * preference. + * + * @hide This should only be used by our main apps--browser, maps, and + * gallery + */ + public static void showZoomTutorialOnce(Context context) { + ContentResolver cr = context.getContentResolver(); + if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TOAST, 0) == 1) { + return; + } + + SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE); + if (sp.getInt(SETTING_NAME_SHOWN_TOAST, 0) == 1) { + return; + } + + try { + Settings.System.putInt(cr, SETTING_NAME_SHOWN_TOAST, 1); + } catch (SecurityException e) { + /* + * The app does not have permission to clear this global flag, make + * sure the user does not see the message when he comes back to this + * same app at least. + */ + sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit(); + } + + Toast.makeText(context, + com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short, + Toast.LENGTH_LONG).show(); + } + private class Panner implements Runnable { private static final int RUN_DELAY = 15; private static final float STOP_SLOWDOWN = 0.8f; diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/gadget/IGadgetHost.aidl index a5b8654..e7b5a1e 100644 --- a/core/java/com/android/internal/gadget/IGadgetHost.aidl +++ b/core/java/com/android/internal/gadget/IGadgetHost.aidl @@ -17,11 +17,12 @@ package com.android.internal.gadget; import android.content.ComponentName; -import android.gadget.GadgetInfo; +import android.gadget.GadgetProviderInfo; import android.widget.RemoteViews; /** {@hide} */ oneway interface IGadgetHost { void updateGadget(int gadgetId, in RemoteViews views); + void providerChanged(int gadgetId, in GadgetProviderInfo info); } diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index 1b3946f..a22f3f3 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -17,7 +17,7 @@ package com.android.internal.gadget; import android.content.ComponentName; -import android.gadget.GadgetInfo; +import android.gadget.GadgetProviderInfo; import com.android.internal.gadget.IGadgetHost; import android.widget.RemoteViews; @@ -41,8 +41,8 @@ interface IGadgetService { // void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); void updateGadgetProvider(in ComponentName provider, in RemoteViews views); - List<GadgetInfo> getInstalledProviders(); - GadgetInfo getGadgetInfo(int gadgetId); + List<GadgetProviderInfo> getInstalledProviders(); + GadgetProviderInfo getGadgetInfo(int gadgetId); void bindGadgetId(int gadgetId, in ComponentName provider); } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index b0b00b2..ac72a20 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -32,8 +32,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_DELETE_SURROUNDING_TEXT = 80; private static final int DO_BEGIN_BATCH_EDIT = 90; private static final int DO_END_BATCH_EDIT = 95; - private static final int DO_HIDE_STATUS_ICON = 100; - private static final int DO_SHOW_STATUS_ICON = 110; + private static final int DO_REPORT_FULLSCREEN_MODE = 100; private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; @@ -133,12 +132,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); } - public void hideStatusIcon() { - dispatchMessage(obtainMessage(DO_HIDE_STATUS_ICON)); - } - - public void showStatusIcon(String packageName, int resId) { - dispatchMessage(obtainMessageIO(DO_SHOW_STATUS_ICON, resId, packageName)); + public void reportFullscreenMode(boolean enabled) { + dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0)); } public void performPrivateCommand(String action, Bundle data) { @@ -323,22 +318,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { ic.endBatchEdit(); return; } - case DO_HIDE_STATUS_ICON: { - InputConnection ic = mInputConnection.get(); - if (ic == null || !isActive()) { - Log.w(TAG, "hideStatusIcon on inactive InputConnection"); - return; - } - ic.hideStatusIcon(); - return; - } - case DO_SHOW_STATUS_ICON: { + case DO_REPORT_FULLSCREEN_MODE: { InputConnection ic = mInputConnection.get(); if (ic == null || !isActive()) { Log.w(TAG, "showStatusIcon on inactive InputConnection"); return; } - ic.showStatusIcon((String)msg.obj, msg.arg1); + ic.reportFullscreenMode(msg.arg1 != 1); return; } case DO_PERFORM_PRIVATE_COMMAND: { diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 7cc8ada..02b6044 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -56,13 +56,11 @@ import com.android.internal.view.IInputContextCallback; void endBatchEdit(); + void reportFullscreenMode(boolean enabled); + void sendKeyEvent(in KeyEvent event); void clearMetaKeyStates(int states); void performPrivateCommand(String action, in Bundle data); - - void showStatusIcon(String packageName, int resId); - - void hideStatusIcon(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 2f5cd14..1b1c7f7 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -47,7 +47,7 @@ interface IInputMethodManager { void showInputMethodPickerFromClient(in IInputMethodClient client); void setInputMethod(in IBinder token, String id); void hideMySoftInput(in IBinder token, int flags); - void updateStatusIcon(int iconId, String iconPackage); + void updateStatusIcon(in IBinder token, String packageName, int iconId); boolean setInputMethodEnabled(String id, boolean enabled); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index af4ad25..32d9f3d 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -322,18 +322,9 @@ public class InputConnectionWrapper implements InputConnection { } } - public boolean hideStatusIcon() { + public boolean reportFullscreenMode(boolean enabled) { try { - mIInputContext.showStatusIcon(null, 0); - return true; - } catch (RemoteException e) { - return false; - } - } - - public boolean showStatusIcon(String packageName, int resId) { - try { - mIInputContext.showStatusIcon(packageName, resId); + mIInputContext.reportFullscreenMode(enabled); return true; } catch (RemoteException e) { return false; diff --git a/core/java/com/android/internal/widget/NumberPicker.java b/core/java/com/android/internal/widget/NumberPicker.java index 20ea6a6..1647c20 100644 --- a/core/java/com/android/internal/widget/NumberPicker.java +++ b/core/java/com/android/internal/widget/NumberPicker.java @@ -28,12 +28,8 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.View.OnLongClickListener; -import android.view.animation.Animation; -import android.view.animation.TranslateAnimation; -import android.widget.EditText; -import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.ViewSwitcher; +import android.widget.LinearLayout; import com.android.internal.R; @@ -71,25 +67,18 @@ public class NumberPicker extends LinearLayout implements OnClickListener, private final Runnable mRunnable = new Runnable() { public void run() { if (mIncrement) { - changeCurrent(mCurrent + 1, mSlideUpInAnimation, mSlideUpOutAnimation); + changeCurrent(mCurrent + 1); mHandler.postDelayed(this, mSpeed); } else if (mDecrement) { - changeCurrent(mCurrent - 1, mSlideDownInAnimation, mSlideDownOutAnimation); + changeCurrent(mCurrent - 1); mHandler.postDelayed(this, mSpeed); } } }; - - private final LayoutInflater mInflater; + private final TextView mText; - private final InputFilter mInputFilter; private final InputFilter mNumberInputFilter; - - private final Animation mSlideUpOutAnimation; - private final Animation mSlideUpInAnimation; - private final Animation mSlideDownOutAnimation; - private final Animation mSlideDownInAnimation; - + private String[] mDisplayedValues; private int mStart; private int mEnd; @@ -110,14 +99,14 @@ public class NumberPicker extends LinearLayout implements OnClickListener, this(context, attrs, 0); } - public NumberPicker(Context context, AttributeSet attrs, - int defStyle) { + @SuppressWarnings({"UnusedDeclaration"}) + public NumberPicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); setOrientation(VERTICAL); - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mInflater.inflate(R.layout.number_picker, this, true); + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.number_picker, this, true); mHandler = new Handler(); - mInputFilter = new NumberPickerInputFilter(); + InputFilter inputFilter = new NumberPickerInputFilter(); mNumberInputFilter = new NumberRangeKeyListener(); mIncrementButton = (NumberPickerButton) findViewById(R.id.increment); mIncrementButton.setOnClickListener(this); @@ -130,30 +119,9 @@ public class NumberPicker extends LinearLayout implements OnClickListener, mText = (TextView) findViewById(R.id.timepicker_input); mText.setOnFocusChangeListener(this); - mText.setFilters(new InputFilter[] { mInputFilter }); + mText.setFilters(new InputFilter[] {inputFilter}); mText.setRawInputType(InputType.TYPE_CLASS_NUMBER); - - mSlideUpOutAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, - 0, Animation.RELATIVE_TO_SELF, 0, - Animation.RELATIVE_TO_SELF, -100); - mSlideUpOutAnimation.setDuration(200); - mSlideUpInAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, - 0, Animation.RELATIVE_TO_SELF, 100, - Animation.RELATIVE_TO_SELF, 0); - mSlideUpInAnimation.setDuration(200); - mSlideDownOutAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, - 0, Animation.RELATIVE_TO_SELF, 0, - Animation.RELATIVE_TO_SELF, 100); - mSlideDownOutAnimation.setDuration(200); - mSlideDownInAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, - 0, Animation.RELATIVE_TO_SELF, -100, - Animation.RELATIVE_TO_SELF, 0); - mSlideDownInAnimation.setDuration(200); - + if (!isEnabled()) { setEnabled(false); } @@ -228,9 +196,9 @@ public class NumberPicker extends LinearLayout implements OnClickListener, // now perform the increment/decrement if (R.id.increment == v.getId()) { - changeCurrent(mCurrent + 1, mSlideUpInAnimation, mSlideUpOutAnimation); + changeCurrent(mCurrent + 1); } else if (R.id.decrement == v.getId()) { - changeCurrent(mCurrent - 1, mSlideDownInAnimation, mSlideDownOutAnimation); + changeCurrent(mCurrent - 1); } } @@ -240,7 +208,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener, : String.valueOf(value); } - private void changeCurrent(int current, Animation in, Animation out) { + private void changeCurrent(int current) { // Wrap around the values if we go past the start or end if (current > mEnd) { diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 307c6fd..288433a 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -267,7 +267,7 @@ static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder) { - //LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); + LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); lpRecorder->stop(); delete lpRecorder; } @@ -449,6 +449,39 @@ static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobjec // ---------------------------------------------------------------------------- +// returns the minimum required size for the successful creation of an AudioRecord instance. +// returns 0 if the parameter combination is not supported. +// return -1 if there was an error querying the buffer size. +static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, + jint sampleRateInHertz, jint nbChannels, jint audioFormat) { + + size_t inputBuffSize = 0; + LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat); + + status_t result = AudioSystem::getInputBufferSize( + sampleRateInHertz, + (audioFormat == javaAudioRecordFields.PCM16 ? + AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT), + nbChannels, &inputBuffSize); + switch(result) { + case(NO_ERROR): + if(inputBuffSize == 0) { + LOGV("Recording parameters are not supported: %dHz, %d channel(s), (java) format %d", + sampleRateInHertz, nbChannels, audioFormat); + return 0; + } else { + // the minimum buffer size is twice the hardware input buffer size + return 2*inputBuffSize; + } + break; + case(PERMISSION_DENIED): + default: + return -1; + } +} + + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { // name, signature, funcPtr @@ -470,6 +503,8 @@ static JNINativeMethod gMethods[] = { "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, {"native_get_pos_update_period", "()I", (void *)android_media_AudioRecord_get_pos_update_period}, + {"native_get_min_buff_size", + "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, }; // field names found in android/media/AudioRecord.java diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 6bd3655..692610e 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -53,13 +53,8 @@ static int android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint volume) { LOGV("setVolume(%d)", int(volume)); - if (int(type) == AudioTrack::VOICE_CALL) { - return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(volume) / 100.0)); - } else if (int(type) == AudioTrack::BLUETOOTH_SCO) { - return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(1.0))); - } else { - return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); - } + + return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); } static int @@ -68,12 +63,7 @@ android_media_AudioSystem_getVolume(JNIEnv *env, jobject clazz, jint type) float v; int v_int = -1; if (AudioSystem::getStreamVolume(int(type), &v) == NO_ERROR) { - // voice call volume is converted to log scale in the hardware - if (int(type) == AudioTrack::VOICE_CALL) { - v_int = lrint(v * 100.0); - } else { - v_int = AudioSystem::logToLinear(v); - } + v_int = AudioSystem::logToLinear(v); } return v_int; } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index bbecc1b..6ca821d 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -72,6 +72,7 @@ class AudioTrackJniStorage { sp<MemoryHeapBase> mMemHeap; sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData; + int mStreamType; AudioTrackJniStorage() { } @@ -168,11 +169,11 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th int afSampleRate; int afFrameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { LOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; } - if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { LOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate."); return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; } @@ -183,21 +184,21 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th } // check the stream type - AudioTrack::stream_type atStreamType; + AudioSystem::stream_type atStreamType; if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) { - atStreamType = AudioTrack::VOICE_CALL; + atStreamType = AudioSystem::VOICE_CALL; } else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) { - atStreamType = AudioTrack::SYSTEM; + atStreamType = AudioSystem::SYSTEM; } else if (streamType == javaAudioTrackFields.STREAM_RING) { - atStreamType = AudioTrack::RING; + atStreamType = AudioSystem::RING; } else if (streamType == javaAudioTrackFields.STREAM_MUSIC) { - atStreamType = AudioTrack::MUSIC; + atStreamType = AudioSystem::MUSIC; } else if (streamType == javaAudioTrackFields.STREAM_ALARM) { - atStreamType = AudioTrack::ALARM; + atStreamType = AudioSystem::ALARM; } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) { - atStreamType = AudioTrack::NOTIFICATION; + atStreamType = AudioSystem::NOTIFICATION; } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { - atStreamType = AudioTrack::BLUETOOTH_SCO; + atStreamType = AudioSystem::BLUETOOTH_SCO; } else { LOGE("Error creating AudioTrack: unknown stream type."); return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; @@ -238,6 +239,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th // we use a weak reference so the AudioTrack object can be garbage collected. lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); + lpJniStorage->mStreamType = atStreamType; + // create the native AudioTrack object AudioTrack* lpTrack = new AudioTrack(); if (lpTrack == NULL) { @@ -656,8 +659,14 @@ static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz) { - int afSamplingRate; - if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + int afSamplingRate; + AudioTrackJniStorage* lpJniStorage = (AudioTrackJniStorage *)env->GetIntField( + thiz, javaAudioTrackFields.jniData); + if (lpJniStorage == NULL) { + return DEFAULT_OUTPUT_SAMPLE_RATE; + } + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate, lpJniStorage->mStreamType) != NO_ERROR) { return DEFAULT_OUTPUT_SAMPLE_RATE; } else { return afSamplingRate; diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 75a0fbe..e5ae2ea 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -751,6 +751,10 @@ void onCreateBondingResult(DBusMessage *msg, void *user) { // Other device is not responding at all LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_REMOTE_DEVICE_DOWN; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) { + // already bonded + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_SUCCESS; } else { LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); result = BOND_RESULT_ERROR; diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index d0cac18..5e5103a 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -19,7 +19,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include "jni.h" -#include "utils/logger.h" +#include "cutils/logger.h" #define END_DELIMITER '\n' #define INT_BUFFER_SIZE (sizeof(jbyte)+sizeof(jint)+sizeof(END_DELIMITER)) diff --git a/core/res/res/drawable/presence_away.png b/core/res/res/drawable/presence_away.png Binary files differindex a539ec7..f8120df 100644 --- a/core/res/res/drawable/presence_away.png +++ b/core/res/res/drawable/presence_away.png diff --git a/core/res/res/drawable/presence_busy.png b/core/res/res/drawable/presence_busy.png Binary files differindex 1e3f547..9d7620b 100644 --- a/core/res/res/drawable/presence_busy.png +++ b/core/res/res/drawable/presence_busy.png diff --git a/core/res/res/drawable/presence_invisible.png b/core/res/res/drawable/presence_invisible.png Binary files differindex fb86cf1..21399a4 100644 --- a/core/res/res/drawable/presence_invisible.png +++ b/core/res/res/drawable/presence_invisible.png diff --git a/core/res/res/drawable/presence_offline.png b/core/res/res/drawable/presence_offline.png Binary files differindex da54fe7..3941b82 100644 --- a/core/res/res/drawable/presence_offline.png +++ b/core/res/res/drawable/presence_offline.png diff --git a/core/res/res/drawable/presence_online.png b/core/res/res/drawable/presence_online.png Binary files differindex 879a762..22d5683 100644 --- a/core/res/res/drawable/presence_online.png +++ b/core/res/res/drawable/presence_online.png diff --git a/core/res/res/drawable/zoom_ring_trail.xml b/core/res/res/drawable/zoom_ring_trail.xml new file mode 100644 index 0000000..08931ac --- /dev/null +++ b/core/res/res/drawable/zoom_ring_trail.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="0" + android:toDegrees="360"> + + <shape + android:shape="ring" + android:innerRadius="60dip" + android:thickness="14dip" + android:useLevel="true"> + + <gradient + android:type="sweep" + android:useLevel="true" + android:startColor="#1000ceff" + android:endColor="#ffadd252" /> + + </shape> + + </rotate> diff --git a/core/res/res/layout/date_picker.xml b/core/res/res/layout/date_picker.xml index a398bd0..0760cc0 100644 --- a/core/res/res/layout/date_picker.xml +++ b/core/res/res/layout/date_picker.xml @@ -24,6 +24,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parent" android:orientation="horizontal" + android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml index 879f339..949c8a3 100644 --- a/core/res/res/layout/date_picker_dialog.xml +++ b/core/res/res/layout/date_picker_dialog.xml @@ -17,11 +17,9 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip"> - <DatePicker android:id="@+id/datePicker" - android:layout_width="fill_parent" - android:layout_height="fill_parent"/> -</FrameLayout> +<DatePicker xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/datePicker" + android:padding="5dip" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml index 422733a..bbdb31c 100644 --- a/core/res/res/layout/number_picker.xml +++ b/core/res/res/layout/number_picker.xml @@ -22,23 +22,21 @@ <com.android.internal.widget.NumberPickerButton android:id="@+id/increment" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@drawable/timepicker_up_btn" - /> - - <EditText android:id="@+id/timepicker_input" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:singleLine="true" - style="?android:attr/textAppearanceLargeInverse" - android:textSize="30sp" - android:background="@drawable/timepicker_input" - /> - + android:background="@drawable/timepicker_up_btn" /> + + <EditText android:id="@+id/timepicker_input" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:singleLine="true" + style="?android:attr/textAppearanceLargeInverse" + android:textColor="@android:color/primary_text_light" + android:textSize="30sp" + android:background="@drawable/timepicker_input" /> + <com.android.internal.widget.NumberPickerButton android:id="@+id/decrement" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@drawable/timepicker_down_btn" - /> - + android:background="@drawable/timepicker_down_btn" /> + </merge> diff --git a/core/res/res/layout/number_picker_edit.xml b/core/res/res/layout/number_picker_edit.xml index 46f4845..f3af6e9 100644 --- a/core/res/res/layout/number_picker_edit.xml +++ b/core/res/res/layout/number_picker_edit.xml @@ -23,6 +23,7 @@ android:gravity="center_horizontal" android:singleLine="true" style="?android:attr/textAppearanceLargeInverse" + android:textColor="@android:color/primary_text_light" android:textSize="30sp" android:background="@drawable/timepicker_input" /> diff --git a/core/res/res/layout/time_picker.xml b/core/res/res/layout/time_picker.xml index bdfe490..c601e0e 100644 --- a/core/res/res/layout/time_picker.xml +++ b/core/res/res/layout/time_picker.xml @@ -21,6 +21,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" + android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> @@ -55,5 +56,6 @@ android:paddingLeft="20dip" android:paddingRight="20dip" style="?android:attr/textAppearanceLargeInverse" + android:textColor="@android:color/primary_text_light_nodisable" /> </LinearLayout> diff --git a/core/res/res/layout/time_picker_dialog.xml b/core/res/res/layout/time_picker_dialog.xml index 6dc1bf6..d5a6b5e 100644 --- a/core/res/res/layout/time_picker_dialog.xml +++ b/core/res/res/layout/time_picker_dialog.xml @@ -17,11 +17,9 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="5dip"> - <TimePicker android:id="@+id/timePicker" - android:layout_width="fill_parent" - android:layout_height="fill_parent"/> -</FrameLayout> +<TimePicker xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/timePicker" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dip" /> diff --git a/core/res/res/layout/time_picker_text.xml b/core/res/res/layout/time_picker_text.xml deleted file mode 100644 index bad980b..0000000 --- a/core/res/res/layout/time_picker_text.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?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. -*/ ---> - -<!-- TextView of time picker--> - -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:textAppearance="?attr/textAppearanceLargeInverse" - android:gravity="center" - /> diff --git a/core/res/res/values-cs-rCZ/strings.xml b/core/res/res/values-cs-rCZ/strings.xml deleted file mode 100644 index e1eb3f4..0000000 --- a/core/res/res/values-cs-rCZ/strings.xml +++ /dev/null @@ -1,1112 +0,0 @@ -<?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 byteShort (2850097084724465606) --> - <skip /> - <!-- no translation found for kilobyteShort (5865542430193761682) --> - <skip /> - <!-- no translation found for megabyteShort (112984851085937882) --> - <skip /> - <!-- no translation found for gigabyteShort (8586075069559273847) --> - <skip /> - <!-- no translation found for terabyteShort (5828502357595687794) --> - <skip /> - <!-- no translation found for petabyteShort (7523248732657962413) --> - <skip /> - <string name="untitled">"<bez názvu>"</string> - <string name="ellipsis">"…"</string> - <string name="emptyPhoneNumber">"(žádné telefonní číslo)"</string> - <string name="unknownName">"(neznámý)"</string> - <string name="defaultVoiceMailAlphaTag">"Hlasová schránka"</string> - <string name="defaultMsisdnAlphaTag">"Msisdn1"</string> - <string name="mmiError">"Chyba sítě nebo neplatný kód MMI."</string> - <string name="serviceEnabled">"Služba povolena"</string> - <string name="serviceEnabledFor">"Služba povolena pro:"</string> - <string name="serviceDisabled">"Služba zakázána"</string> - <string name="serviceRegistered">"Registrace úspěšná"</string> - <string name="serviceErased">"Odstranění úspěšné"</string> - <string name="passwordIncorrect">"Nesprávné heslo."</string> - <string name="mmiComplete">"MMI dokončeno"</string> - <!-- no translation found for badPin (5103184589972647739) --> - <skip /> - <!-- no translation found for badPuk (2200634943393540609) --> - <skip /> - <!-- no translation found for mismatchPin (5055729703806180857) --> - <skip /> - <!-- no translation found for invalidPin (6201854814319326475) --> - <skip /> - <!-- no translation found for needPuk (4788728144863892764) --> - <skip /> - <!-- no translation found for needPuk2 (7056908944942451033) --> - <skip /> - <!-- no translation found for ClipMmi (5649729434121615509) --> - <skip /> - <!-- no translation found for ClirMmi (5220979296096544477) --> - <skip /> - <!-- no translation found for CfMmi (4998483717856803914) --> - <skip /> - <!-- no translation found for CwMmi (5678103638951836350) --> - <skip /> - <!-- no translation found for BaMmi (6030555200442855833) --> - <skip /> - <!-- no translation found for PwdMmi (2549941247959366670) --> - <skip /> - <!-- no translation found for PinMmi (2463353963837922189) --> - <skip /> - <string name="CLIRDefaultOnNextCallOn">"Výchozí nastavení omezení ID - omezení. Další hovor: omezení"</string> - <string name="CLIRDefaultOnNextCallOff">"Výchozí nastavení omezení ID - omezení. Další hovor: bez omezení"</string> - <string name="CLIRDefaultOffNextCallOn">"Výchozí nastavení omezení ID - bez omezení. Další hovor: omezení"</string> - <string name="CLIRDefaultOffNextCallOff">"Výchozí nastavení omezení ID - bez omezení. Další hovor: bez omezení"</string> - <string name="serviceNotProvisioned">"Služba není poskytována."</string> - <string name="CLIRPermanent">"Omezení ID v trvalém režimu."</string> - <string name="serviceClassVoice">"Hlasový záznam"</string> - <string name="serviceClassData">"Data"</string> - <string name="serviceClassFAX">"FAX"</string> - <string name="serviceClassSMS">"SMS"</string> - <string name="serviceClassDataAsync">"Asynchronní"</string> - <string name="serviceClassDataSync">"Synchronizace"</string> - <string name="serviceClassPacket">"Pakety"</string> - <string name="serviceClassPAD">"PAD"</string> - <string name="cfTemplateNotForwarded">"{0}: Nepřesměrováno"</string> - <string name="cfTemplateForwarded">"{0}: {1}"</string> - <string name="cfTemplateForwardedTime">"{0}: {1} po {2} sekundách"</string> - <string name="cfTemplateRegistered">"{0}: Nepřesměrováno ({1})"</string> - <string name="cfTemplateRegisteredTime">"{0}: Nepřesměrováno ({1} po {2} sekundách)"</string> - <string name="httpErrorOk">"OK"</string> - <string name="httpError">"Neznámá chyba"</string> - <string name="httpErrorLookup">"Neznámý hostitel"</string> - <string name="httpErrorUnsupportedAuthScheme">"Nepodporované schéma ověření. Ověření se nezdařilo."</string> - <string name="httpErrorAuth">"Ověřování se nezdařilo"</string> - <string name="httpErrorProxyAuth">"Ověření serverem proxy se nezdařilo"</string> - <string name="httpErrorConnect">"Připojení k serveru se nezdařilo"</string> - <string name="httpErrorIO">"Čtení nebo zápis na server se nezdařil"</string> - <string name="httpErrorTimeout">"Časový limit připojení k serveru vypršel"</string> - <string name="httpErrorRedirectLoop">"Příliš mnoho přesměrování serverů"</string> - <string name="httpErrorUnsupportedScheme">"Nepodporovaný protokol"</string> - <string name="httpErrorFailedSslHandshake">"Navázání spojení typu SSL handshake se nezdařilo"</string> - <string name="httpErrorBadUrl">"Nepodařilo se analyzovat URL"</string> - <string name="httpErrorFile">"File error"</string> - <string name="httpErrorFileNotFound">"File not found"</string> - <!-- no translation found for httpErrorTooManyRequests (3764334538393544875) --> - <skip /> - <string name="contentServiceSync">"Synchronizace"</string> - <string name="contentServiceSyncNotificationTitle">"Synchronizace"</string> - <!-- no translation found for contentServiceTooManyDeletesNotificationDesc (8477597194404210723) --> - <skip /> - <!-- no translation found for low_memory (4191592786596642367) --> - <skip /> - <!-- no translation found for me (4616693653158602117) --> - <skip /> - <string name="power_dialog">"Možnosti napájení"</string> - <string name="silent_mode">"Tichý režim"</string> - <string name="turn_on_radio">"Zapnout rádio"</string> - <string name="turn_off_radio">"Vypnout rádio"</string> - <!-- no translation found for screen_lock (1560333453597081877) --> - <skip /> - <string name="power_off">"Vypnuto"</string> - <!-- no translation found for shutdown_progress (3735034517335251808) --> - <skip /> - <!-- no translation found for shutdown_confirm (699224922526414097) --> - <skip /> - <!-- no translation found for no_recent_tasks (1367712919998349373) --> - <skip /> - <!-- no translation found for global_actions (8299888906525675157) --> - <skip /> - <!-- no translation found for global_action_lock (5943677976245541105) --> - <skip /> - <!-- no translation found for global_action_power_off (3143027278596694254) --> - <skip /> - <!-- no translation found for global_action_toggle_silent_mode (5849335789367070450) --> - <skip /> - <!-- no translation found for global_action_silent_mode_on_status (6053429980569202260) --> - <skip /> - <!-- no translation found for global_action_silent_mode_off_status (1994514127029249081) --> - <skip /> - <!-- no translation found for safeMode (3375134507868534320) --> - <skip /> - <!-- no translation found for permgrouplab_costMoney (904087853776533085) --> - <skip /> - <!-- no translation found for permgroupdesc_costMoney (4662370555643969515) --> - <skip /> - <!-- no translation found for permgrouplab_messages (2984053976424233925) --> - <skip /> - <!-- no translation found for permgroupdesc_messages (2129093134354989379) --> - <skip /> - <!-- no translation found for permgrouplab_personalInfo (4548406335021507392) --> - <skip /> - <!-- no translation found for permgroupdesc_personalInfo (8499310823817958034) --> - <skip /> - <!-- no translation found for permgrouplab_location (8535677827151907069) --> - <skip /> - <!-- no translation found for permgroupdesc_location (2341662219604651887) --> - <skip /> - <!-- no translation found for permgrouplab_network (3597781730625751831) --> - <skip /> - <!-- no translation found for permgroupdesc_network (8332572695347918340) --> - <skip /> - <!-- no translation found for permgrouplab_accounts (8631201594657951893) --> - <skip /> - <!-- no translation found for permgroupdesc_accounts (443982868906396781) --> - <skip /> - <!-- no translation found for permgrouplab_hardwareControls (5074512938567152139) --> - <skip /> - <!-- no translation found for permgroupdesc_hardwareControls (8772503144945278440) --> - <skip /> - <!-- no translation found for permgrouplab_phoneCalls (7096448531266882376) --> - <skip /> - <!-- no translation found for permgroupdesc_phoneCalls (6703873478653366233) --> - <skip /> - <!-- no translation found for permgrouplab_systemTools (1840847965111633430) --> - <skip /> - <!-- no translation found for permgroupdesc_systemTools (2810337951496685271) --> - <skip /> - <!-- no translation found for permgrouplab_developmentTools (692844635256963358) --> - <skip /> - <!-- no translation found for permgroupdesc_developmentTools (5253915519857796400) --> - <skip /> - <!-- no translation found for permlab_statusBar (8789506912215455922) --> - <skip /> - <!-- no translation found for permdesc_statusBar (5034247171231682403) --> - <skip /> - <!-- no translation found for permlab_expandStatusBar (6382500803293284173) --> - <skip /> - <!-- no translation found for permdesc_expandStatusBar (90953162060681436) --> - <skip /> - <!-- no translation found for permlab_processOutgoingCalls (786316295241100144) --> - <skip /> - <!-- no translation found for permdesc_processOutgoingCalls (1655242138991854396) --> - <skip /> - <string name="permlab_receiveSms">"Příjem zpráv SMS"</string> - <string name="permdesc_receiveSms">"Umožňuje aplikacím přijímat a zpracovávat zprávy SMS. Škodlivé aplikace mohou sledovat vaše zprávy nebo je odstraňovat, aniž by se zobrazily."</string> - <string name="permlab_receiveMms">"Příjem zpráv MMS"</string> - <string name="permdesc_receiveMms">"Umožňuje aplikacím přijímat a zpracovávat zprávy MMS. Škodlivé aplikace mohou sledovat vaše zprávy nebo je odstraňovat, aniž by se zobrazily."</string> - <!-- no translation found for permlab_sendSms (4713837923748234081) --> - <skip /> - <!-- no translation found for permdesc_sendSms (7126594387176704010) --> - <skip /> - <!-- no translation found for permlab_readSms (4256004535185449429) --> - <skip /> - <!-- no translation found for permdesc_readSms (4586480500886941902) --> - <skip /> - <!-- no translation found for permlab_writeSms (8453452414726246828) --> - <skip /> - <!-- no translation found for permdesc_writeSms (1036408118901361812) --> - <skip /> - <string name="permlab_receiveWapPush">"Příjem zpráv WAP"</string> - <string name="permdesc_receiveWapPush">"Umožňuje aplikacím přijímat a zpracovávat zprávy WAP. Škodlivé aplikace mohou sledovat vaše zprávy nebo je odstraňovat, aniž by se zobrazily."</string> - <string name="permlab_getTasks">"Získat informace o úkolech"</string> - <string name="permdesc_getTasks">"Umožňuje aplikacím načítat informace o aktuálně a naposledy spuštěných úkolech. Umožňuje škodlivým aplikacím zjišťovat soukromé informace o jiných aplikacích."</string> - <!-- no translation found for permlab_reorderTasks (4758862288285224517) --> - <skip /> - <!-- no translation found for permdesc_reorderTasks (7507060843941912021) --> - <skip /> - <!-- no translation found for permlab_setDebugApp (2973363275929449444) --> - <skip /> - <!-- no translation found for permdesc_setDebugApp (5720449860498265972) --> - <skip /> - <!-- no translation found for permlab_changeConfiguration (8581093564179818627) --> - <skip /> - <!-- no translation found for permdesc_changeConfiguration (4055366453803187171) --> - <skip /> - <!-- no translation found for permlab_restartPackages (5836367540766044606) --> - <skip /> - <!-- no translation found for permdesc_restartPackages (1764965996765573321) --> - <skip /> - <!-- no translation found for permlab_setProcessForeground (4860990420780868638) --> - <skip /> - <!-- no translation found for permdesc_setProcessForeground (3795477299954784360) --> - <skip /> - <!-- no translation found for permlab_forceBack (4737517869935566733) --> - <skip /> - <!-- no translation found for permdesc_forceBack (5579316297001154697) --> - <skip /> - <string name="permlab_dump">"Výpis stavu systému"</string> - <string name="permdesc_dump">"Umožňuje aplikacím načítat vnitřní stav systému. Škodlivé aplikace mohou načítat široký rozsah soukromých a důvěrných informací, jež by obvykle neměly nikdy vyžadovat."</string> - <string name="permlab_addSystemService">"Přidat systémovou službu"</string> - <string name="permdesc_addSystemService">"Umožňuje aplikacím vydávat vlastní systémové služby nižší úrovně. Škodlivé aplikace mohou napadnout systém a vykrást nebo poškodit jeho data."</string> - <string name="permlab_runSetActivityWatcher">"Nastavení sledování činností"</string> - <string name="permdesc_runSetActivityWatcher">"Umožňuje aplikacím sledovat a řídit spouštění činností systému. Škodlivé aplikace mohou zcela zničit systém. Toto oprávnění je nutné pouze pro vývoj, nikdy pro normální používání zařízení."</string> - <string name="permlab_broadcastPackageRemoved">"Sada vysílání odebrána"</string> - <string name="permdesc_broadcastPackageRemoved">"Umožňuje aplikacím vysílat oznámení o odebrání sady aplikací. Škodlivé aplikace toho mohou využít k likvidaci jiné spuštěné aplikace."</string> - <!-- no translation found for permlab_broadcastSmsReceived (1994692154847312518) --> - <skip /> - <!-- no translation found for permdesc_broadcastSmsReceived (6072362543164841432) --> - <skip /> - <!-- no translation found for permlab_broadcastWapPush (3070023012636951639) --> - <skip /> - <!-- no translation found for permdesc_broadcastWapPush (726912255218924336) --> - <skip /> - <!-- no translation found for permlab_setProcessLimit (5190694306017260601) --> - <skip /> - <!-- no translation found for permdesc_setProcessLimit (593938303319848578) --> - <skip /> - <!-- no translation found for permlab_setAlwaysFinish (8745533365504920540) --> - <skip /> - <!-- no translation found for permdesc_setAlwaysFinish (2437195869854312148) --> - <skip /> - <string name="permlab_fotaUpdate">"Instalace aktualizace systému"</string> - <string name="permdesc_fotaUpdate">"Umožňuje aplikacím přijímat oznámení o aktualizacích systému čekajících na dokončení a spouštět jejich instalaci. Škodlivé aplikace toho mohou využít k poškození systému neautorizovanými aktualizacemi nebo obecně k zásahům do aktualizačního procesu."</string> - <!-- no translation found for permlab_batteryStats (1598947993704535568) --> - <skip /> - <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> - <skip /> - <string name="permlab_internalSystemWindow">"Okno vnitřního systému"</string> - <string name="permdesc_internalSystemWindow">"Umožňuje vytváření oken určených k použití uživatelským rozhraním vnitřního systému . Není určeno k použití normálními aplikacemi."</string> - <string name="permlab_systemAlertWindow">"Okno systémových výstrah"</string> - <string name="permdesc_systemAlertWindow">"Umožňuje aplikacím zobrazovat okna systémových výstrah. Škodlivé aplikace mohou ovládnout celou obrazovku zařízení."</string> - <!-- no translation found for permlab_setAnimationScale (2419250686027992384) --> - <skip /> - <!-- no translation found for permdesc_setAnimationScale (8518027785481727264) --> - <skip /> - <!-- no translation found for permlab_manageAppTokens (1033424552444304594) --> - <skip /> - <!-- no translation found for permdesc_manageAppTokens (7285840918912623550) --> - <skip /> - <!-- no translation found for permlab_injectEvents (1383601196263145482) --> - <skip /> - <!-- no translation found for permdesc_injectEvents (840097509341464737) --> - <skip /> - <!-- no translation found for permlab_readInputState (2723668746963882102) --> - <skip /> - <!-- no translation found for permdesc_readInputState (4651137638757852001) --> - <skip /> - <!-- no translation found for permlab_setOrientation (1112555600323148680) --> - <skip /> - <!-- no translation found for permdesc_setOrientation (1960269530378827858) --> - <skip /> - <string name="permlab_signalPersistentProcesses">"Signálové trvalé procesy"</string> - <string name="permdesc_signalPersistentProcesses">"Umožňuje aplikacím vyžadovat, aby se přiváděný signál odesílal do všech trvalých procesů."</string> - <!-- no translation found for permlab_persistentActivity (8163108526929094627) --> - <skip /> - <!-- no translation found for permdesc_persistentActivity (5258975883823299624) --> - <skip /> - <string name="permlab_deletePackages">"Odstranit sady"</string> - <string name="permdesc_deletePackages">"Umožňuje aplikacím odstranit sady systému Android. Škodlivé aplikace toho mohou využít k odstranění důležitých aplikací."</string> - <!-- no translation found for permlab_clearAppUserData (3858185484601410171) --> - <skip /> - <!-- no translation found for permdesc_clearAppUserData (7233537744753081136) --> - <skip /> - <!-- no translation found for permlab_deleteCacheFiles (7362746182961997888) --> - <skip /> - <!-- no translation found for permdesc_deleteCacheFiles (8293849509208181266) --> - <skip /> - <!-- no translation found for permlab_getPackageSize (6743556676630447973) --> - <skip /> - <!-- no translation found for permdesc_getPackageSize (2893996655828539776) --> - <skip /> - <string name="permlab_installPackages">"Instalovat sady"</string> - <string name="permdesc_installPackages">"Umožňuje aplikacím instalovat nové nebo aktualizované sady systému Android. Škodlivé aplikace toho mohou využít k přidání nových aplikací s libovolně silnými oprávněními."</string> - <!-- no translation found for permlab_clearAppCache (7860214328511700776) --> - <skip /> - <!-- no translation found for permdesc_clearAppCache (5203820862573167878) --> - <skip /> - <!-- no translation found for permlab_readLogs (6653488552442991707) --> - <skip /> - <!-- no translation found for permdesc_readLogs (356352685800884319) --> - <skip /> - <!-- no translation found for permlab_diagnostic (2955142476313469329) --> - <skip /> - <!-- no translation found for permdesc_diagnostic (1282409892215520166) --> - <skip /> - <string name="permlab_changeComponentState">"Povolit nebo zakázat součásti aplikací"</string> - <string name="permdesc_changeComponentState">"Umožňuje změnu aplikace bez ohledu na to, zda je součást další aplikace povolená nebo zakázaná. Škodlivá aplikace toho může využít k zakázání důležitých funkcí zařízení. Je třeba nakládat s oprávněními opatrně, protože se mohou součásti aplikace dostat do stavu nepoužitelnosti, nekonzistence nebo nestability."</string> - <string name="permlab_setPreferredApplications">"Nastavení upřednostňovaných aplikací"</string> - <string name="permdesc_setPreferredApplications">"Umožňuje aplikacím upravovat oblíbené aplikace. Škodlivé aplikace tak mohou bez upozornění měnit spouštěné aplikace a klamně využívat stávající aplikace ke shromažďování vašich soukromých dat."</string> - <string name="permlab_writeSettings">"Nastavení systému pro zápis"</string> - <string name="permdesc_writeSettings">"Umožňuje aplikacím upravovat data nastavení systému. Škodlivé aplikace mohou narušit systémovou konfiguraci."</string> - <!-- no translation found for permlab_writeSecureSettings (4851801872124242319) --> - <skip /> - <!-- no translation found for permdesc_writeSecureSettings (2080620249472761366) --> - <skip /> - <!-- no translation found for permlab_writeGservices (296370685945777755) --> - <skip /> - <!-- no translation found for permdesc_writeGservices (2496928471286495053) --> - <skip /> - <string name="permlab_receiveBootCompleted">"Spustit při spouštění"</string> - <string name="permdesc_receiveBootCompleted">"Umožňuje aplikacím spouštět se po dokončení spuštění systému. Tím se může prodlužovat doba spouštění zařízení a aplikace může svým stálým spouštěním zpomalovat celé zařízení."</string> - <string name="permlab_broadcastSticky">"Vysílat lepivý obsah (sticky)"</string> - <string name="permdesc_broadcastSticky">"Umožňuje aplikacím odesílat tzv. lepivé (sticky) vysílání, které zůstává i po ukončení vysílání. Škodlivé aplikace mohou zpomalit zařízení nebo narušit jeho stabilitu vynucením využívání příliš velké části paměti."</string> - <string name="permlab_readContacts">"Čtení dat o kontaktech"</string> - <string name="permdesc_readContacts">"Umožňuje aplikacím číst všechna data o kontaktech (adresy) uložená v zařízení. Škodlivé aplikace toho mohou využívat k odesílání vašich dat jiným osobám."</string> - <string name="permlab_writeContacts">"Zápis dat o kontaktech"</string> - <string name="permdesc_writeContacts">"Umožňuje aplikacím upravovat data o kontaktech (adresy) uložená v zařízení. Škodlivé aplikace toho mohou využívat k vymazání nebo úpravě dat o kontaktech."</string> - <!-- no translation found for permlab_writeOwnerData (8036840529708535113) --> - <skip /> - <!-- no translation found for permdesc_writeOwnerData (5873447528845878348) --> - <skip /> - <!-- no translation found for permlab_readOwnerData (1847040178513733757) --> - <skip /> - <!-- no translation found for permdesc_readOwnerData (7563299529149214764) --> - <skip /> - <!-- no translation found for permlab_readCalendar (2111238731453410895) --> - <skip /> - <!-- no translation found for permdesc_readCalendar (4408253940601239114) --> - <skip /> - <!-- no translation found for permlab_writeCalendar (7518052789370653396) --> - <skip /> - <!-- no translation found for permdesc_writeCalendar (8057304232140147596) --> - <skip /> - <!-- no translation found for permlab_accessMockLocation (321094551062270213) --> - <skip /> - <!-- no translation found for permdesc_accessMockLocation (3651565866471419739) --> - <skip /> - <!-- no translation found for permlab_accessLocationExtraCommands (8291822077788811687) --> - <skip /> - <!-- no translation found for permdesc_accessLocationExtraCommands (5135782633548630731) --> - <skip /> - <string name="permlab_accessFineLocation">"Používat službu GPS"</string> - <string name="permdesc_accessFineLocation">"Technologii GPS v zařízení lze používat, pokud je k dispozici. Toto oprávnění vyžaduje oprávnění ACCESS_LOCATION. Škodlivé aplikace toho mohou využívat k určení vaší polohy a mohou spotřebovávat zbytečně energii baterie."</string> - <string name="permlab_accessCoarseLocation">"Používat službu Cell ID"</string> - <string name="permdesc_accessCoarseLocation">"Identifikátory pro technologii využívající polohu vysílačů mobilních sítí (je-li k dispozici) se používají k určení přibližné polohy zařízení. Toto oprávnění vyžaduje oprávnění ACCESS_LOCATION. Škodlivé aplikace toho mohou využívat k určení vaší přibližné polohy."</string> - <string name="permlab_accessSurfaceFlinger">"Používat službu SurfaceFlinger"</string> - <string name="permdesc_accessSurfaceFlinger">"Umožňuje aplikacím používat funkce nižší úrovně SurfaceFlinger."</string> - <string name="permlab_readFrameBuffer">"Čtení vyrovnávací paměti rámce"</string> - <string name="permdesc_readFrameBuffer">"Umožňuje aplikacím používat čtení obsahu vyrovnávací paměti rámce."</string> - <!-- no translation found for permlab_modifyAudioSettings (1587341813207960943) --> - <skip /> - <!-- no translation found for permdesc_modifyAudioSettings (1447143004892708149) --> - <skip /> - <!-- no translation found for permlab_recordAudio (4447848534036991667) --> - <skip /> - <!-- no translation found for permdesc_recordAudio (6936874682400894820) --> - <skip /> - <!-- no translation found for permlab_camera (1944473855727060380) --> - <skip /> - <!-- no translation found for permdesc_camera (5978058582323766022) --> - <skip /> - <!-- no translation found for permlab_brick (4749832243303289777) --> - <skip /> - <!-- no translation found for permdesc_brick (7428524578693695766) --> - <skip /> - <!-- no translation found for permlab_reboot (8844650672567077423) --> - <skip /> - <!-- no translation found for permdesc_reboot (4704919552870918328) --> - <skip /> - <!-- no translation found for permlab_mount_unmount_filesystems (1009574821038043781) --> - <skip /> - <!-- no translation found for permdesc_mount_unmount_filesystems (100792065894811109) --> - <skip /> - <!-- no translation found for permlab_vibrate (61984555644467146) --> - <skip /> - <!-- no translation found for permdesc_vibrate (7831723100758509238) --> - <skip /> - <!-- no translation found for permlab_flashlight (9097145977808182652) --> - <skip /> - <!-- no translation found for permdesc_flashlight (7851502731988978358) --> - <skip /> - <!-- no translation found for permlab_hardware_test (4103324677866524254) --> - <skip /> - <!-- no translation found for permdesc_hardware_test (7315242723603994769) --> - <skip /> - <string name="permlab_callPhone">"Volat telefonní čísla"</string> - <string name="permdesc_callPhone">"Umožňuje aplikacím volat telefonní čísla bez vašeho zásahu. Škodlivé aplikace mohou přinést na váš telefonní účet neočekávané hovory."</string> - <!-- no translation found for permlab_callPrivileged (2166923597287697159) --> - <skip /> - <!-- no translation found for permdesc_callPrivileged (5109789447971735501) --> - <skip /> - <!-- no translation found for permlab_locationUpdates (4216418293360456836) --> - <skip /> - <!-- no translation found for permdesc_locationUpdates (7635814693478743648) --> - <skip /> - <!-- no translation found for permlab_checkinProperties (2260796787386280708) --> - <skip /> - <!-- no translation found for permdesc_checkinProperties (3508022022841741945) --> - <skip /> - <!-- no translation found for permlab_modifyPhoneState (7791696535097912313) --> - <skip /> - <!-- no translation found for permdesc_modifyPhoneState (6352405226410454770) --> - <skip /> - <!-- no translation found for permlab_readPhoneState (7320082586621086653) --> - <skip /> - <!-- no translation found for permdesc_readPhoneState (8004450067066407969) --> - <skip /> - <!-- no translation found for permlab_wakeLock (1591164750935072136) --> - <skip /> - <!-- no translation found for permdesc_wakeLock (160471538196734936) --> - <skip /> - <!-- no translation found for permlab_devicePower (9214865067086065548) --> - <skip /> - <!-- no translation found for permdesc_devicePower (5608364066480036402) --> - <skip /> - <!-- no translation found for permlab_factoryTest (7786199300637896247) --> - <skip /> - <!-- no translation found for permdesc_factoryTest (3466066005210542042) --> - <skip /> - <!-- no translation found for permlab_setWallpaper (2256730637138641725) --> - <skip /> - <!-- no translation found for permdesc_setWallpaper (3034653140208685093) --> - <skip /> - <!-- no translation found for permlab_setWallpaperHints (4192438316932517807) --> - <skip /> - <!-- no translation found for permdesc_setWallpaperHints (738757439960921674) --> - <skip /> - <!-- no translation found for permlab_masterClear (6155403967270586906) --> - <skip /> - <!-- no translation found for permdesc_masterClear (4213553172342689754) --> - <skip /> - <!-- no translation found for permlab_setTimeZone (477196167239548690) --> - <skip /> - <!-- no translation found for permdesc_setTimeZone (8564892020460841198) --> - <skip /> - <!-- no translation found for permlab_getAccounts (2764070033402295170) --> - <skip /> - <!-- no translation found for permdesc_getAccounts (1203491378748649898) --> - <skip /> - <!-- no translation found for permlab_accessNetworkState (2032916924886010827) --> - <skip /> - <!-- no translation found for permdesc_accessNetworkState (7081329402551195933) --> - <skip /> - <!-- no translation found for permlab_createNetworkSockets (4706698319966917864) --> - <skip /> - <!-- no translation found for permdesc_createNetworkSockets (2580337178778551792) --> - <skip /> - <!-- no translation found for permlab_writeApnSettings (3190585220761979369) --> - <skip /> - <!-- no translation found for permdesc_writeApnSettings (4093875220468761052) --> - <skip /> - <!-- no translation found for permlab_changeNetworkState (2710779001260856872) --> - <skip /> - <!-- no translation found for permdesc_changeNetworkState (8076109230787022270) --> - <skip /> - <!-- no translation found for permlab_accessWifiState (3613679494230374297) --> - <skip /> - <!-- no translation found for permdesc_accessWifiState (8226508433563326925) --> - <skip /> - <!-- no translation found for permlab_changeWifiState (6043889338995432957) --> - <skip /> - <!-- no translation found for permdesc_changeWifiState (7829372845909567994) --> - <skip /> - <!-- no translation found for permlab_bluetoothAdmin (5513286736585647334) --> - <skip /> - <!-- no translation found for permdesc_bluetoothAdmin (1838208497914347365) --> - <skip /> - <!-- no translation found for permlab_bluetooth (6378797624765639115) --> - <skip /> - <!-- no translation found for permdesc_bluetooth (8592386018922265273) --> - <skip /> - <!-- no translation found for permlab_disableKeyguard (4574886811903233903) --> - <skip /> - <!-- no translation found for permdesc_disableKeyguard (815972646344251271) --> - <skip /> - <!-- no translation found for permlab_readSyncSettings (8818819977141505127) --> - <skip /> - <!-- no translation found for permdesc_readSyncSettings (8454705401908767847) --> - <skip /> - <!-- no translation found for permlab_writeSyncSettings (4514911143753152941) --> - <skip /> - <!-- no translation found for permdesc_writeSyncSettings (7630627689635091836) --> - <skip /> - <!-- no translation found for permlab_readSyncStats (5748337739678952863) --> - <skip /> - <!-- no translation found for permdesc_readSyncStats (582551457321957183) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsRead (2043206814904506589) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsRead (6977343942680042449) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsWrite (2556727307229571556) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsWrite (4134783294590266220) --> - <skip /> - <!-- no translation found for phoneTypes:7 (1326005699931077792) --> - <string-array name="emailAddressTypes"> - <item>"Výchozí"</item> - <item>"Zaměstnání"</item> - <item>"Primární"</item> - <item>"Vlastní…"</item> - </string-array> - <string-array name="postalAddressTypes"> - <item>"Poštovní"</item> - <item>"Výchozí"</item> - <item>"Zaměstnání"</item> - <item>"Vlastní…"</item> - </string-array> - <!-- no translation found for imAddressTypes:0 (7806620012096518833) --> - <!-- no translation found for imAddressTypes:1 (5748846799950672787) --> - <!-- no translation found for imAddressTypes:2 (6196536810275073680) --> - <!-- no translation found for imAddressTypes:3 (8519128375350623648) --> - <!-- no translation found for organizationTypes:0 (1299224825223821142) --> - <!-- no translation found for organizationTypes:1 (2455717447227299354) --> - <!-- no translation found for organizationTypes:2 (7027570839313438290) --> - <!-- no translation found for imProtocols:0 (3318725788774688043) --> - <!-- no translation found for imProtocols:1 (1787713387022932886) --> - <!-- no translation found for imProtocols:2 (6751174158442316516) --> - <!-- no translation found for imProtocols:3 (1151283347465052653) --> - <!-- no translation found for imProtocols:4 (2157980008878817934) --> - <!-- no translation found for imProtocols:5 (7836237460308230767) --> - <!-- no translation found for imProtocols:6 (1180789904462172516) --> - <!-- no translation found for imProtocols:7 (21955111672779862) --> - <!-- no translation found for keyguard_password_enter_pin_code (6779835451906812518) --> - <skip /> - <!-- no translation found for keyguard_password_wrong_pin_code (230312338493035499) --> - <skip /> - <string name="keyguard_label_text">"Telefon odemknete stisknutím tlačítka nabídky a poté 0."</string> - <!-- no translation found for emergency_call_dialog_number_for_display (6256361184251050511) --> - <skip /> - <!-- no translation found for lockscreen_carrier_default (5222269885486229730) --> - <skip /> - <!-- no translation found for lockscreen_screen_locked (1922273663462058967) --> - <skip /> - <!-- no translation found for lockscreen_instructions_when_pattern_enabled (7535864145009679967) --> - <skip /> - <!-- no translation found for lockscreen_instructions_when_pattern_disabled (6526504555912746785) --> - <skip /> - <!-- no translation found for lockscreen_pattern_instructions (8984964506352089877) --> - <skip /> - <!-- no translation found for lockscreen_emergency_call (422835617844547383) --> - <skip /> - <!-- no translation found for lockscreen_pattern_correct (7104753084746383672) --> - <skip /> - <!-- no translation found for lockscreen_pattern_wrong (7517004470797680361) --> - <skip /> - <!-- no translation found for lockscreen_plugged_in (8806977650003537118) --> - <skip /> - <!-- no translation found for lockscreen_low_battery (9002637795199621345) --> - <skip /> - <!-- no translation found for lockscreen_missing_sim_message_short (5051192587315492957) --> - <skip /> - <!-- no translation found for lockscreen_missing_sim_message (8912914495901434841) --> - <skip /> - <!-- no translation found for lockscreen_missing_sim_instructions (8125847194365725429) --> - <skip /> - <!-- no translation found for lockscreen_network_locked_message (323609607922245071) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_message (1005803622871256359) --> - <skip /> - <!-- no translation found for lockscreen_sim_puk_locked_instructions (5033160098036646955) --> - <skip /> - <!-- no translation found for lockscreen_sim_locked_message (7398401200962556379) --> - <skip /> - <!-- no translation found for lockscreen_sim_unlock_progress_dialog_message (5939537246164692076) --> - <skip /> - <!-- no translation found for lockscreen_too_many_failed_attempts_dialog_message (6709066241494622136) --> - <skip /> - <!-- no translation found for lockscreen_failed_attempts_almost_glogin (1569017295989454551) --> - <skip /> - <!-- no translation found for lockscreen_too_many_failed_attempts_countdown (8823588000022797566) --> - <skip /> - <!-- no translation found for lockscreen_forgot_pattern_button_text (4219994639843985488) --> - <skip /> - <!-- no translation found for lockscreen_glogin_too_many_attempts (7504679498838839295) --> - <skip /> - <!-- no translation found for lockscreen_glogin_instructions (6542400673357252011) --> - <skip /> - <!-- no translation found for lockscreen_glogin_username_hint (6378418320242015111) --> - <skip /> - <!-- no translation found for lockscreen_glogin_password_hint (3224230234042131153) --> - <skip /> - <!-- no translation found for lockscreen_glogin_submit_button (5562051040043760034) --> - <skip /> - <!-- no translation found for lockscreen_glogin_invalid_input (4881057177478491580) --> - <skip /> - <!-- no translation found for status_bar_time_format (2168573805413119180) --> - <skip /> - <!-- no translation found for hour_minute_ampm (1850330605794978742) --> - <skip /> - <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> - <skip /> - <!-- no translation found for hour_ampm (7665432130905376251) --> - <skip /> - <!-- no translation found for hour_cap_ampm (3600295014648400268) --> - <skip /> - <!-- no translation found for status_bar_clear_all_button (2202004591253243750) --> - <skip /> - <!-- no translation found for status_bar_no_notifications_title (5123133188102094464) --> - <skip /> - <!-- no translation found for status_bar_ongoing_events_title (799961521630569167) --> - <skip /> - <!-- no translation found for status_bar_latest_events_title (5414094466807164279) --> - <skip /> - <!-- no translation found for battery_status_text_percent_format (7391464609447031944) --> - <skip /> - <!-- no translation found for battery_status_charging (5078780715755132756) --> - <skip /> - <!-- no translation found for battery_low_title (3665400828395001695) --> - <skip /> - <!-- no translation found for battery_low_subtitle (7537149915372180016) --> - <skip /> - <!-- no translation found for battery_low_percent_format (8635359708781261154) --> - <skip /> - <string name="factorytest_failed">"Výrobní test skončil chybou"</string> - <string name="factorytest_not_system">"Akce FACTORY_TEST je podporována pouze pro sady instalované v adresáři /system/app."</string> - <string name="factorytest_no_action">"Nebyla nalezena žádná sada, která zajišťuje akci FACTORY_TEST."</string> - <string name="factorytest_reboot">"Restartovat"</string> - <!-- no translation found for save_password_label (4129493019621348626) --> - <skip /> - <!-- no translation found for save_password_message (7412617920202682045) --> - <skip /> - <!-- no translation found for save_password_notnow (3887362423496820832) --> - <skip /> - <!-- no translation found for save_password_remember (4319688896716308569) --> - <skip /> - <!-- no translation found for save_password_never (1836981952883642377) --> - <skip /> - <!-- no translation found for open_permission_deny (6408502671105717111) --> - <skip /> - <!-- no translation found for text_copied (6106873823411904723) --> - <skip /> - <string name="more_item_label">"Další"</string> - <string name="prepend_shortcut_label">"Menu+"</string> - <!-- no translation found for menu_space_shortcut_label (194586306440382711) --> - <skip /> - <!-- no translation found for menu_enter_shortcut_label (7214761412193519345) --> - <skip /> - <!-- no translation found for menu_delete_shortcut_label (2854936426194985313) --> - <skip /> - <string name="search_go">"PŘEJÍT"</string> - <string name="today">"Dnes"</string> - <string name="yesterday">"Včera"</string> - <string name="tomorrow">"Zítra"</string> - <!-- no translation found for oneMonthDurationPast (3402179395240209557) --> - <skip /> - <!-- no translation found for beforeOneMonthDurationPast (7578100953282866827) --> - <skip /> - <!-- no translation found for num_seconds_ago:one (7416512229671810725) --> - <!-- no translation found for num_seconds_ago:other (8138756910300398447) --> - <!-- no translation found for num_minutes_ago:one (8620869479299420562) --> - <!-- no translation found for num_minutes_ago:other (5065488162050522741) --> - <!-- no translation found for num_hours_ago:one (853404611989669641) --> - <!-- no translation found for num_hours_ago:other (3558873784561756849) --> - <!-- no translation found for num_days_ago:one (4222479980812128212) --> - <!-- no translation found for num_days_ago:other (5445701370433601703) --> - <!-- no translation found for in_num_seconds:one (4253290037777327003) --> - <!-- no translation found for in_num_seconds:other (1280033870920841404) --> - <!-- no translation found for in_num_minutes:one (1487585791027953091) --> - <!-- no translation found for in_num_minutes:other (6274204576475209932) --> - <!-- no translation found for in_num_hours:one (6501470863235186391) --> - <!-- no translation found for in_num_hours:other (4415358752953289251) --> - <!-- no translation found for in_num_days:one (5608475533104443893) --> - <!-- no translation found for in_num_days:other (3827193006163842267) --> - <!-- no translation found for preposition_for_date (2689847983632851560) --> - <skip /> - <!-- no translation found for preposition_for_time (2613388053493148013) --> - <skip /> - <!-- no translation found for preposition_for_year (6968468294728152393) --> - <skip /> - <string name="day">"den"</string> - <string name="days">"dnů"</string> - <string name="hour">"hodinu"</string> - <string name="hours">"hodin"</string> - <string name="minute">"minutu"</string> - <string name="minutes">"minut"</string> - <string name="second">"sekund"</string> - <string name="seconds">"sekund"</string> - <string name="week">"týden"</string> - <string name="weeks">"týdnů"</string> - <!-- no translation found for year (8024790425994085153) --> - <skip /> - <!-- no translation found for years (8592090054773244417) --> - <skip /> - <string name="sunday">"Neděle"</string> - <string name="monday">"Pondělí"</string> - <string name="tuesday">"Úterý"</string> - <string name="wednesday">"Středa"</string> - <string name="thursday">"Čtvrtek"</string> - <string name="friday">"Pátek"</string> - <string name="saturday">"Sobota"</string> - <string name="every_weekday">"Každý den v týdnu (Po–Pá)"</string> - <string name="daily">"Denně"</string> - <string name="weekly">"Týdně (%s)"</string> - <string name="monthly">"Měsíčně"</string> - <string name="yearly">"Ročně"</string> - <!-- no translation found for VideoView_error_title (1024334251681931859) --> - <skip /> - <!-- no translation found for VideoView_error_text_unknown (3398417247398476771) --> - <skip /> - <!-- no translation found for VideoView_error_button (3144127115413163445) --> - <skip /> - <string name="am">"dop."</string> - <string name="pm">"odp."</string> - <!-- no translation found for numeric_date (5120078478872821100) --> - <skip /> - <!-- no translation found for wday1_date1_time1_wday2_date2_time2 (7066878981949584861) --> - <skip /> - <!-- no translation found for wday1_date1_wday2_date2 (8671068747172261907) --> - <skip /> - <!-- no translation found for date1_time1_date2_time2 (3645498975775629615) --> - <skip /> - <!-- no translation found for date1_date2 (377057563556488062) --> - <skip /> - <!-- no translation found for time1_time2 (3173474242109288305) --> - <skip /> - <!-- no translation found for time_wday_date (8928955562064570313) --> - <skip /> - <!-- no translation found for wday_date (8794741400546136975) --> - <skip /> - <!-- no translation found for time_date (1922644512833014496) --> - <skip /> - <!-- no translation found for time_wday (1422050241301754712) --> - <skip /> - <!-- no translation found for full_date_month_first (6011143962222283357) --> - <skip /> - <!-- no translation found for full_date_day_first (8621594762705478189) --> - <skip /> - <!-- no translation found for medium_date_month_first (48990963718825728) --> - <skip /> - <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <skip /> - <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> - <skip /> - <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> - <skip /> - <!-- no translation found for noon (8390796001560682897) --> - <skip /> - <!-- no translation found for Noon (7698941576181064429) --> - <skip /> - <!-- no translation found for midnight (7773339795626486146) --> - <skip /> - <!-- no translation found for Midnight (1260172107848123187) --> - <skip /> - <!-- no translation found for month_day (3356633704511426364) --> - <skip /> - <!-- no translation found for month (3017405760734206414) --> - <skip /> - <!-- no translation found for month_day_year (2435948225709176752) --> - <skip /> - <!-- no translation found for month_year (6228414124777343135) --> - <skip /> - <!-- no translation found for time_of_day (8375993139317154157) --> - <skip /> - <!-- no translation found for date_and_time (9197690194373107109) --> - <skip /> - <!-- no translation found for same_year_md1_md2 (9199324363135981317) --> - <skip /> - <!-- no translation found for same_year_wday1_md1_wday2_md2 (6006392413355305178) --> - <skip /> - <!-- no translation found for same_year_mdy1_mdy2 (1576657593937827090) --> - <skip /> - <!-- no translation found for same_year_wday1_mdy1_wday2_mdy2 (9135935796468891580) --> - <skip /> - <!-- no translation found for same_year_md1_time1_md2_time2 (2172964106375558081) --> - <skip /> - <!-- no translation found for same_year_wday1_md1_time1_wday2_md2_time2 (1702879534101786310) --> - <skip /> - <!-- no translation found for same_year_mdy1_time1_mdy2_time2 (2476443311723358767) --> - <skip /> - <!-- no translation found for same_year_wday1_mdy1_time1_wday2_mdy2_time2 (1564837340334069879) --> - <skip /> - <!-- no translation found for numeric_md1_md2 (8908376522875100300) --> - <skip /> - <!-- no translation found for numeric_wday1_md1_wday2_md2 (3239690882018292077) --> - <skip /> - <!-- no translation found for numeric_mdy1_mdy2 (8883797176939233525) --> - <skip /> - <!-- no translation found for numeric_wday1_mdy1_wday2_mdy2 (4150475769255828954) --> - <skip /> - <!-- no translation found for numeric_md1_time1_md2_time2 (3624746590607741419) --> - <skip /> - <!-- no translation found for numeric_wday1_md1_time1_wday2_md2_time2 (4258040955467298134) --> - <skip /> - <!-- no translation found for numeric_mdy1_time1_mdy2_time2 (3598215409314517987) --> - <skip /> - <!-- no translation found for numeric_wday1_mdy1_time1_wday2_mdy2_time2 (264076937155877259) --> - <skip /> - <!-- no translation found for same_month_md1_md2 (2393563617438036111) --> - <skip /> - <!-- no translation found for same_month_wday1_md1_wday2_md2 (1208946773794057819) --> - <skip /> - <!-- no translation found for same_month_mdy1_mdy2 (3713236637869030492) --> - <skip /> - <!-- no translation found for same_month_wday1_mdy1_wday2_mdy2 (389638922479870472) --> - <skip /> - <!-- no translation found for same_month_md1_time1_md2_time2 (7477075526337542685) --> - <skip /> - <!-- no translation found for same_month_wday1_md1_time1_wday2_md2_time2 (3516978303779391173) --> - <skip /> - <!-- no translation found for same_month_mdy1_time1_mdy2_time2 (7320410992514057310) --> - <skip /> - <!-- no translation found for same_month_wday1_mdy1_time1_wday2_mdy2_time2 (1332950588774239228) --> - <skip /> - <!-- no translation found for abbrev_month_day_year (5767271534015320250) --> - <skip /> - <!-- no translation found for abbrev_month_year (8058929633673942490) --> - <skip /> - <!-- no translation found for abbrev_month_day (458867920693482757) --> - <skip /> - <!-- no translation found for abbrev_month (1674509986330181349) --> - <skip /> - <!-- no translation found for day_of_week_long_sunday (9057662850446501884) --> - <skip /> - <!-- no translation found for day_of_week_long_monday (7358451993082888343) --> - <skip /> - <!-- no translation found for day_of_week_long_tuesday (2282901451170509613) --> - <skip /> - <!-- no translation found for day_of_week_long_wednesday (2100217950343286482) --> - <skip /> - <!-- no translation found for day_of_week_long_thursday (5475158963242863176) --> - <skip /> - <!-- no translation found for day_of_week_long_friday (4081018004819837155) --> - <skip /> - <!-- no translation found for day_of_week_long_saturday (1929694088305891795) --> - <skip /> - <!-- no translation found for day_of_week_medium_sunday (6462580883948669820) --> - <skip /> - <!-- no translation found for day_of_week_medium_monday (6960587654241349502) --> - <skip /> - <!-- no translation found for day_of_week_medium_tuesday (7004462235990108936) --> - <skip /> - <!-- no translation found for day_of_week_medium_wednesday (5688564741951314696) --> - <skip /> - <!-- no translation found for day_of_week_medium_thursday (1784339868453982400) --> - <skip /> - <!-- no translation found for day_of_week_medium_friday (4314577583604069357) --> - <skip /> - <!-- no translation found for day_of_week_medium_saturday (70321191398427845) --> - <skip /> - <!-- no translation found for day_of_week_short_sunday (7403409454572591357) --> - <skip /> - <!-- no translation found for day_of_week_short_monday (5278358100012478239) --> - <skip /> - <!-- no translation found for day_of_week_short_tuesday (5121116040712487059) --> - <skip /> - <!-- no translation found for day_of_week_short_wednesday (1601079579293330319) --> - <skip /> - <!-- no translation found for day_of_week_short_thursday (5863422096017401812) --> - <skip /> - <!-- no translation found for day_of_week_short_friday (2916686031099723960) --> - <skip /> - <!-- no translation found for day_of_week_short_saturday (8521564973195542073) --> - <skip /> - <!-- no translation found for day_of_week_shorter_sunday (1650484495176707638) --> - <skip /> - <!-- no translation found for day_of_week_shorter_monday (9133193697786876074) --> - <skip /> - <!-- no translation found for day_of_week_shorter_tuesday (4012095408481489663) --> - <skip /> - <!-- no translation found for day_of_week_shorter_wednesday (6279056612496078470) --> - <skip /> - <!-- no translation found for day_of_week_shorter_thursday (2748599403545071011) --> - <skip /> - <!-- no translation found for day_of_week_shorter_friday (5037282109124849673) --> - <skip /> - <!-- no translation found for day_of_week_shorter_saturday (3208167155877833783) --> - <skip /> - <!-- no translation found for day_of_week_shortest_sunday (4683862964821549758) --> - <skip /> - <!-- no translation found for day_of_week_shortest_monday (6701142261471667000) --> - <skip /> - <!-- no translation found for day_of_week_shortest_tuesday (9098171980161292477) --> - <skip /> - <!-- no translation found for day_of_week_shortest_wednesday (655049238289460956) --> - <skip /> - <!-- no translation found for day_of_week_shortest_thursday (7816913627500884083) --> - <skip /> - <!-- no translation found for day_of_week_shortest_friday (903301878650619398) --> - <skip /> - <!-- no translation found for day_of_week_shortest_saturday (5359692489649817988) --> - <skip /> - <!-- no translation found for month_long_january (7128497801440564337) --> - <skip /> - <!-- no translation found for month_long_february (7808570514581190617) --> - <skip /> - <!-- no translation found for month_long_march (2061328556983796034) --> - <skip /> - <!-- no translation found for month_long_april (6575007959043269919) --> - <skip /> - <!-- no translation found for month_long_may (8404051103463071121) --> - <skip /> - <!-- no translation found for month_long_june (6255771619238859451) --> - <skip /> - <!-- no translation found for month_long_july (4129177743136800884) --> - <skip /> - <!-- no translation found for month_long_august (5494331003296804494) --> - <skip /> - <!-- no translation found for month_long_september (2691137479752033087) --> - <skip /> - <!-- no translation found for month_long_october (7501261567327243313) --> - <skip /> - <!-- no translation found for month_long_november (8759690753068763664) --> - <skip /> - <!-- no translation found for month_long_december (4505008719696569497) --> - <skip /> - <!-- no translation found for month_medium_january (2315492772833932512) --> - <skip /> - <!-- no translation found for month_medium_february (118412521324313430) --> - <skip /> - <!-- no translation found for month_medium_march (5546835583839352358) --> - <skip /> - <!-- no translation found for month_medium_april (7052559668687733702) --> - <skip /> - <!-- no translation found for month_medium_may (2825303871720116018) --> - <skip /> - <!-- no translation found for month_medium_june (829843667101495271) --> - <skip /> - <!-- no translation found for month_medium_july (5029778226925324789) --> - <skip /> - <!-- no translation found for month_medium_august (8851230594641162805) --> - <skip /> - <!-- no translation found for month_medium_september (8420590486625304647) --> - <skip /> - <!-- no translation found for month_medium_october (1787382806172930239) --> - <skip /> - <!-- no translation found for month_medium_november (675513809622370603) --> - <skip /> - <!-- no translation found for month_medium_december (2934948295928978783) --> - <skip /> - <!-- no translation found for month_shortest_january (6070060405144675883) --> - <skip /> - <!-- no translation found for month_shortest_february (5632605004902176653) --> - <skip /> - <!-- no translation found for month_shortest_march (4304231552356086624) --> - <skip /> - <!-- no translation found for month_shortest_april (1166434066469385532) --> - <skip /> - <!-- no translation found for month_shortest_may (9131326028845529001) --> - <skip /> - <!-- no translation found for month_shortest_june (1875723154506665289) --> - <skip /> - <!-- no translation found for month_shortest_july (2003596275389810773) --> - <skip /> - <!-- no translation found for month_shortest_august (9120245162625763214) --> - <skip /> - <!-- no translation found for month_shortest_september (7980651111022693669) --> - <skip /> - <!-- no translation found for month_shortest_october (3640405450427788312) --> - <skip /> - <!-- no translation found for month_shortest_november (4002935318566146993) --> - <skip /> - <!-- no translation found for month_shortest_december (6213739417171334040) --> - <skip /> - <!-- no translation found for elapsed_time_short_format_mm_ss (1294409362352514646) --> - <skip /> - <!-- no translation found for elapsed_time_short_format_h_mm_ss (2997059666628785039) --> - <skip /> - <!-- no translation found for selectAll (691691810023908884) --> - <skip /> - <!-- no translation found for cut (5845613239192595662) --> - <skip /> - <!-- no translation found for cutAll (4474519683293791451) --> - <skip /> - <!-- no translation found for copy (8603721575469529820) --> - <skip /> - <!-- no translation found for copyAll (4777548804630476932) --> - <skip /> - <!-- no translation found for paste (6458036735811828538) --> - <skip /> - <!-- no translation found for copyUrl (5785708478767435812) --> - <skip /> - <!-- no translation found for inputMethod (7911866729148111492) --> - <skip /> - <!-- no translation found for editTextMenuTitle (3984253728638788023) --> - <skip /> - <!-- no translation found for low_internal_storage_view_title (5997772070488639934) --> - <skip /> - <!-- no translation found for low_internal_storage_view_text (2230118755295375293) --> - <skip /> - <!-- no translation found for ok (4003878536083514869) --> - <skip /> - <!-- no translation found for cancel (1527674037280267012) --> - <skip /> - <!-- no translation found for yes (8185296114406773873) --> - <skip /> - <!-- no translation found for no (2300685350903156262) --> - <skip /> - <!-- no translation found for capital_on (8418242581217554942) --> - <skip /> - <!-- no translation found for capital_off (8870368560477693851) --> - <skip /> - <!-- no translation found for whichApplication (2828159696176255212) --> - <skip /> - <!-- no translation found for alwaysUse (6433627451071144629) --> - <skip /> - <!-- no translation found for clearDefaultHintMsg (5742432113023174321) --> - <skip /> - <!-- no translation found for chooseActivity (7588691622928031978) --> - <skip /> - <!-- no translation found for noApplications (4068560364116066745) --> - <skip /> - <!-- no translation found for aerr_title (2654390351574026098) --> - <skip /> - <!-- no translation found for aerr_application (4917288809565116720) --> - <skip /> - <!-- no translation found for aerr_process (1273819861108073461) --> - <skip /> - <!-- no translation found for anr_title (3305935690891435915) --> - <skip /> - <!-- no translation found for anr_activity_application (1653036325679156678) --> - <skip /> - <!-- no translation found for anr_activity_process (2674027618362070465) --> - <skip /> - <!-- no translation found for anr_application_process (2163656674970221928) --> - <skip /> - <!-- no translation found for anr_process (7747550780123472160) --> - <skip /> - <!-- no translation found for force_close (9020954128872810669) --> - <skip /> - <!-- no translation found for wait (7973775702304037058) --> - <skip /> - <!-- no translation found for debug (857932504764728770) --> - <skip /> - <!-- no translation found for sendText (6158329286172492543) --> - <skip /> - <!-- no translation found for volume_ringtone (4121694816346562058) --> - <skip /> - <!-- no translation found for volume_music (4869950240104717493) --> - <skip /> - <!-- no translation found for volume_call (5723421277753250395) --> - <skip /> - <!-- no translation found for volume_alarm (2752102730973081294) --> - <skip /> - <!-- no translation found for volume_unknown (6908187627672375742) --> - <skip /> - <!-- no translation found for ringtone_default (2873893375149093475) --> - <skip /> - <!-- no translation found for ringtone_default_with_actual (5474076151665761913) --> - <skip /> - <!-- no translation found for ringtone_silent (7477159279081654685) --> - <skip /> - <!-- no translation found for ringtone_picker_title (7055241890764367884) --> - <skip /> - <!-- no translation found for ringtone_unknown (6888219771401173795) --> - <skip /> - <!-- no translation found for wifi_available:one (8168012881468888470) --> - <!-- no translation found for wifi_available:other (4666122955807117718) --> - <!-- no translation found for wifi_available_detailed:one (5107769161192143259) --> - <!-- no translation found for wifi_available_detailed:other (853347657960575809) --> - <!-- no translation found for select_character (3735110139249491726) --> - <skip /> - <!-- no translation found for sms_control_default_app_name (7522184737840550841) --> - <skip /> - <!-- no translation found for sms_control_title (2742400596989418394) --> - <skip /> - <!-- no translation found for sms_control_message (3447126217666595989) --> - <skip /> - <!-- no translation found for sms_control_yes (8839660939359273650) --> - <skip /> - <!-- no translation found for sms_control_no (909756849988183801) --> - <skip /> - <!-- no translation found for date_time_set (2495199891239480952) --> - <skip /> - <!-- no translation found for default_permission_group (7742780381379652409) --> - <skip /> - <!-- no translation found for no_permissions (85461124044682315) --> - <skip /> - <!-- no translation found for perms_hide (4145325555929151849) --> - <skip /> - <!-- no translation found for perms_show_all (6040194843455403173) --> - <skip /> - <!-- no translation found for googlewebcontenthelper_loading (2140804350507245589) --> - <skip /> - <!-- no translation found for usb_storage_title (8699631567051394409) --> - <skip /> - <!-- no translation found for usb_storage_message (5344039189213308733) --> - <skip /> - <!-- no translation found for usb_storage_button_mount (6700104384375121662) --> - <skip /> - <!-- no translation found for usb_storage_button_unmount (465869657252626688) --> - <skip /> - <!-- no translation found for usb_storage_error_message (3192564550748426087) --> - <skip /> - <!-- no translation found for usb_storage_notification_title (6237028017872246940) --> - <skip /> - <!-- no translation found for usb_storage_notification_message (7371717280517625905) --> - <skip /> - <!-- no translation found for select_input_method (2658280517827502015) --> - <skip /> - <!-- no translation found for fast_scroll_alphabet (1017432309285755759) --> - <skip /> - <!-- no translation found for fast_scroll_numeric_alphabet (3092587363718901074) --> - <skip /> - <!-- no translation found for candidates_style (7738463880139922176) --> - <skip /> -</resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 37632f8..e0c0a64 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 3015957..d9c4174 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -818,6 +818,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 4cb3ee1..d9cf3d5 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1,765 +1,5 @@ <?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">"<untitled>"</string> - <string name="ellipsis">"…"</string> - <string name="emptyPhoneNumber">"(No phone number)"</string> - <string name="unknownName">"(Unknown)"</string> - <string name="defaultVoiceMailAlphaTag">"Voicemail"</string> - <string name="defaultMsisdnAlphaTag">"MSISDN1"</string> - <string name="mmiError">"Connection problem or invalid MMI code."</string> - <string name="serviceEnabled">"Service was enabled."</string> - <string name="serviceEnabledFor">"Service was enabled for:"</string> - <string name="serviceDisabled">"Service has been disabled."</string> - <string name="serviceRegistered">"Registration was successful."</string> - <string name="serviceErased">"Erasure was successful."</string> - <string name="passwordIncorrect">"Incorrect password."</string> - <string name="mmiComplete">"MMI complete."</string> - <string name="badPin">"The old PIN you typed is not correct."</string> - <string name="badPuk">"The PUK you typed is not correct."</string> - <string name="mismatchPin">"The PINs you entered do not match."</string> - <string name="invalidPin">"Type a PIN that is 4 to 8 numbers."</string> - <!-- no translation found for needPuk (4788728144863892764) --> - <skip /> - <string name="needPuk2">"Type PUK2 to unblock SIM card."</string> - <string name="ClipMmi">"Incoming Caller ID"</string> - <string name="ClirMmi">"Outgoing Caller ID"</string> - <string name="CfMmi">"Call forwarding"</string> - <string name="CwMmi">"Call waiting"</string> - <string name="BaMmi">"Call barring"</string> - <string name="PwdMmi">"Password change"</string> - <string name="PinMmi">"PIN change"</string> - <string name="CLIRDefaultOnNextCallOn">"Caller ID defaults to restricted. Next call: Restricted"</string> - <string name="CLIRDefaultOnNextCallOff">"Caller ID defaults to restricted. Next call: Not restricted"</string> - <string name="CLIRDefaultOffNextCallOn">"Caller ID defaults to not restricted. Next call: Restricted"</string> - <string name="CLIRDefaultOffNextCallOff">"Caller ID defaults to not restricted. Next call: Not restricted"</string> - <string name="serviceNotProvisioned">"Service not provisioned."</string> - <string name="CLIRPermanent">"The caller ID setting cannot be changed."</string> - <string name="serviceClassVoice">"Voice"</string> - <string name="serviceClassData">"Data"</string> - <string name="serviceClassFAX">"FAX"</string> - <string name="serviceClassSMS">"SMS"</string> - <string name="serviceClassDataAsync">"Async"</string> - <string name="serviceClassDataSync">"Sync"</string> - <string name="serviceClassPacket">"Packet"</string> - <string name="serviceClassPAD">"PAD"</string> - <string name="cfTemplateNotForwarded">"{0}: Not forwarded"</string> - <string name="cfTemplateForwarded">"{0}: {1}"</string> - <string name="cfTemplateForwardedTime">"{0}: {1} after {2} seconds"</string> - <string name="cfTemplateRegistered">"{0}: Not forwarded"</string> - <string name="cfTemplateRegisteredTime">"{0}: Not forwarded"</string> - <string name="httpErrorOk">"OK"</string> - <string name="httpError">"The Web page contains an error."</string> - <string name="httpErrorLookup">"The URL could not be found."</string> - <string name="httpErrorUnsupportedAuthScheme">"The site authentication scheme is not supported."</string> - <string name="httpErrorAuth">"Authentication was unsuccessful."</string> - <string name="httpErrorProxyAuth">"Authentication via the proxy server was unsuccessful."</string> - <string name="httpErrorConnect">"The connection to the server was unsuccessful."</string> - <string name="httpErrorIO">"The server failed to communicate. Try again later."</string> - <string name="httpErrorTimeout">"The connection to the server timed out."</string> - <string name="httpErrorRedirectLoop">"The page contains too many server redirects."</string> - <string name="httpErrorUnsupportedScheme">"The protocol is not supported."</string> - <string name="httpErrorFailedSslHandshake">"A secure connection could not be established."</string> - <string name="httpErrorBadUrl">"The page could not be opened because the URL is invalid."</string> - <string name="httpErrorFile">"The file could not be accessed."</string> - <string name="httpErrorFileNotFound">"The requested file was not found."</string> - <string name="httpErrorTooManyRequests">"Too many requests are being processed. Try again later."</string> - <string name="contentServiceSync">"Sync"</string> - <string name="contentServiceSyncNotificationTitle">"Sync"</string> - <string name="contentServiceTooManyDeletesNotificationDesc">"Too many %s deletes."</string> - <string name="low_memory">"Phone storage is full! Delete some files to free space."</string> - <string name="me">"Me"</string> - <string name="power_dialog">"Phone options"</string> - <string name="silent_mode">"Silent mode"</string> - <string name="turn_on_radio">"Turn on wireless"</string> - <string name="turn_off_radio">"Turn off wireless"</string> - <string name="screen_lock">"Screen lock"</string> - <string name="power_off">"Power off"</string> - <string name="shutdown_progress">"Shutting down…"</string> - <string name="shutdown_confirm">"Your phone will shut down."</string> - <string name="no_recent_tasks">"No recent applications."</string> - <string name="global_actions">"Phone options"</string> - <string name="global_action_lock">"Screen lock"</string> - <string name="global_action_power_off">"Power off"</string> - <string name="global_action_toggle_silent_mode">"Silent mode"</string> - <string name="global_action_silent_mode_on_status">"Sound is OFF"</string> - <string name="global_action_silent_mode_off_status">"Sound is ON"</string> - <string name="safeMode">"Safe mode"</string> - <string name="permgrouplab_costMoney">"Cost you money"</string> - <string name="permgroupdesc_costMoney">"Allow applications to do things that can cost you money."</string> - <string name="permgrouplab_messages">"Your messages"</string> - <string name="permgroupdesc_messages">"Read and write your SMS, e-mail, and other messages."</string> - <string name="permgrouplab_personalInfo">"Your personal information"</string> - <string name="permgroupdesc_personalInfo">"Direct access to your contacts and calendar stored on the phone."</string> - <string name="permgrouplab_location">"Your location"</string> - <string name="permgroupdesc_location">"Monitor your physical location"</string> - <string name="permgrouplab_network">"Network communication"</string> - <string name="permgroupdesc_network">"Allow applications to access various network features."</string> - <string name="permgrouplab_accounts">"Your Google accounts"</string> - <string name="permgroupdesc_accounts">"Access the available Google accounts."</string> - <string name="permgrouplab_hardwareControls">"Hardware controls"</string> - <string name="permgroupdesc_hardwareControls">"Direct access to hardware on the handset."</string> - <string name="permgrouplab_phoneCalls">"Phone calls"</string> - <string name="permgroupdesc_phoneCalls">"Monitor, record, and process phone calls."</string> - <string name="permgrouplab_systemTools">"System tools"</string> - <string name="permgroupdesc_systemTools">"Lower-level access and control of the system."</string> - <string name="permgrouplab_developmentTools">"Development tools"</string> - <string name="permgroupdesc_developmentTools">"Features only needed for application developers."</string> - <string name="permlab_statusBar">"disable or modify status bar"</string> - <string name="permdesc_statusBar">"Allows application to disable the status bar or add and remove system icons."</string> - <string name="permlab_expandStatusBar">"expand/collapse status bar"</string> - <string name="permdesc_expandStatusBar">"Allows application to expand or collapse the status bar."</string> - <string name="permlab_processOutgoingCalls">"intercept outgoing calls"</string> - <string name="permdesc_processOutgoingCalls">"Allows application to process outgoing calls and change the number to be dialed. Malicious applications may monitor, redirect, or prevent outgoing calls."</string> - <string name="permlab_receiveSms">"receive SMS"</string> - <string name="permdesc_receiveSms">"Allows application to receive and process SMS messages. Malicious applications may monitor your messages or delete them without showing them to you."</string> - <string name="permlab_receiveMms">"receive MMS"</string> - <string name="permdesc_receiveMms">"Allows application to receive and process MMS messages. Malicious applications may monitor your messages or delete them without showing them to you."</string> - <string name="permlab_sendSms">"send SMS messages"</string> - <string name="permdesc_sendSms">"Allows application to send SMS messages. Malicious applications may cost you money by sending messages without your confirmation."</string> - <string name="permlab_readSms">"read SMS or MMS"</string> - <string name="permdesc_readSms">"Allows application to read SMS messages stored on your phone or SIM card. Malicious applications may read your confidential messages."</string> - <string name="permlab_writeSms">"edit SMS or MMS"</string> - <string name="permdesc_writeSms">"Allows application to write to SMS messages stored on your phone or SIM card. Malicious applications may delete your messages."</string> - <string name="permlab_receiveWapPush">"receive WAP"</string> - <string name="permdesc_receiveWapPush">"Allows application to receive and process WAP messages. Malicious applications may monitor your messages or delete them without showing them to you."</string> - <string name="permlab_getTasks">"retrieve running applications"</string> - <string name="permdesc_getTasks">"Allows application to retrieve information about currently and recently running tasks. May allow malicious applications to discover private information about other applications."</string> - <string name="permlab_reorderTasks">"reorder running applications"</string> - <string name="permdesc_reorderTasks">"Allows an application to move tasks to the foreground and background. Malicious applications can force themselves to the front without your control."</string> - <string name="permlab_setDebugApp">"enable application debugging"</string> - <string name="permdesc_setDebugApp">"Allows an application to turn on debugging for another application. Malicious applications can use this to kill other applications."</string> - <string name="permlab_changeConfiguration">"change your UI settings"</string> - <string name="permdesc_changeConfiguration">"Allows an application to change the current configuration, such as the locale or overall font size."</string> - <string name="permlab_restartPackages">"restart other applications"</string> - <string name="permdesc_restartPackages">"Allows an application to forcibly restart other applications."</string> - <string name="permlab_setProcessForeground">"keep from being stopped"</string> - <!-- unknown placeholder BREAK in permdesc_setProcessForeground --> - <skip /> - <string name="permlab_forceBack">"force application to close"</string> - <string name="permdesc_forceBack">"Allows an application to force any activity that is in the foreground to close and go back. Should never be needed for normal applications."</string> - <string name="permlab_dump">"retrieve system internal state"</string> - <string name="permdesc_dump">"Allows application to retrieve internal state of the system. Malicious applications may retrieve a wide variety of private and secure information that they should never normally need."</string> - <string name="permlab_addSystemService">"publish low-level services"</string> - <string name="permdesc_addSystemService">"Allows application to publish its own low-level system services. Malicious applications may hijack the system, and steal or corrupt any data on it."</string> - <string name="permlab_runSetActivityWatcher">"monitor and control all application launching"</string> - <string name="permdesc_runSetActivityWatcher">"Allows an application to monitor and control how the system launches activities. Malicious applications may completely compromise the system. This permission is only needed for development, never for normal phone usage."</string> - <string name="permlab_broadcastPackageRemoved">"send package removed broadcast"</string> - <string name="permdesc_broadcastPackageRemoved">"Allows an application to broadcast a notification that an application package has been removed. Malicious applications may use this to kill any other running application."</string> - <!-- no translation found for permlab_broadcastSmsReceived (1994692154847312518) --> - <skip /> - <!-- no translation found for permdesc_broadcastSmsReceived (6072362543164841432) --> - <skip /> - <!-- no translation found for permlab_broadcastWapPush (3070023012636951639) --> - <skip /> - <!-- no translation found for permdesc_broadcastWapPush (726912255218924336) --> - <skip /> - <string name="permlab_setProcessLimit">"limit number of running processes"</string> - <string name="permdesc_setProcessLimit">"Allows an application to control the maximum number of processes that will run. Never needed for normal applications."</string> - <string name="permlab_setAlwaysFinish">"make all background applications close"</string> - <string name="permdesc_setAlwaysFinish">"Allows an application to control whether activities are always finished as soon as they go to the background. Never needed for normal applications."</string> - <string name="permlab_fotaUpdate">"automatically install system updates"</string> - <string name="permdesc_fotaUpdate">"Allows an application to receive notifications about pending system updates and trigger their installation. Malicious applications may use this to corrupt the system with unauthorized updates, or generally interfere with the update process."</string> - <string name="permlab_batteryStats">"modify battery statistics"</string> - <string name="permdesc_batteryStats">"Allows the modification of collected battery statistics. Not for use by normal applications."</string> - <string name="permlab_internalSystemWindow">"display unauthorized windows"</string> - <string name="permdesc_internalSystemWindow">"Allows the creation of windows that are intended to be used by the internal system user interface. Not for use by normal applications."</string> - <string name="permlab_systemAlertWindow">"display system-level alerts"</string> - <string name="permdesc_systemAlertWindow">"Allows an application to show system alert windows. Malicious applications can take over the entire screen of the phone."</string> - <string name="permlab_setAnimationScale">"modify global animation speed"</string> - <string name="permdesc_setAnimationScale">"Allows an application to change the global animation speed (faster or slower animations) at any time."</string> - <string name="permlab_manageAppTokens">"manage application tokens"</string> - <string name="permdesc_manageAppTokens">"Allows applications to create and manage their own tokens, bypassing their normal Z-ordering. Should never be needed for normal applications."</string> - <string name="permlab_injectEvents">"press keys and control buttons"</string> - <string name="permdesc_injectEvents">"Allows an application to deliver its own input events (key presses, etc.) to other applications. Malicious applications can use this to take over the phone."</string> - <string name="permlab_readInputState">"record what you type and actions you take"</string> - <string name="permdesc_readInputState">"Allows applications to watch the keys you press even when interacting with another application (such as entering a password). Should never be needed for normal applications."</string> - <string name="permlab_setOrientation">"change screen orientation"</string> - <string name="permdesc_setOrientation">"Allows an application to change the rotation of the screen at any time. Should never be needed for normal applications."</string> - <string name="permlab_signalPersistentProcesses">"send Linux signals to applications"</string> - <string name="permdesc_signalPersistentProcesses">"Allows application to request that the supplied signal be sent to all persistent processes."</string> - <string name="permlab_persistentActivity">"make application always run"</string> - <!-- unknown placeholder BREAK in permdesc_persistentActivity --> - <skip /> - <string name="permlab_deletePackages">"delete applications"</string> - <string name="permdesc_deletePackages">"Allows an application to delete Android packages. Malicious applications can use this to delete important applications."</string> - <string name="permlab_clearAppUserData">"delete other applications data"</string> - <string name="permdesc_clearAppUserData">"Allows an application to clear user data."</string> - <string name="permlab_deleteCacheFiles">"delete other applications cache"</string> - <string name="permdesc_deleteCacheFiles">"Allows an application to delete cache files."</string> - <string name="permlab_getPackageSize">"measure application storage space"</string> - <string name="permdesc_getPackageSize">"Allows an application to retrieve its code, data, and cache sizes"</string> - <string name="permlab_installPackages">"directly install applications"</string> - <string name="permdesc_installPackages">"Allows an application to install new or updated Android packages. Malicious applications can use this to add new applications with arbitrarily powerful permissions."</string> - <string name="permlab_clearAppCache">"delete all application cache data"</string> - <string name="permdesc_clearAppCache">"Allows an application to free phone storage by deleting files in application cache directory. Access is very restricted usually to system process."</string> - <string name="permlab_readLogs">"read system log files"</string> - <!-- unknown placeholder BREAK_0 in permdesc_readLogs --> - <skip /> - <!-- no translation found for permlab_diagnostic (2955142476313469329) --> - <skip /> - <!-- no translation found for permdesc_diagnostic (1282409892215520166) --> - <skip /> - <string name="permlab_changeComponentState">"enable or disable application components"</string> - <string name="permdesc_changeComponentState">"Allows an application to change whether a component of another application is enabled or not. Malicious applications can use this to disable important phone capabilities. Care must be used with permission, as it is possible to get application components into an unusable, inconsistant, or unstable state."</string> - <string name="permlab_setPreferredApplications">"set preferred applications"</string> - <string name="permdesc_setPreferredApplications">"Allows an application to modify your preferred applications. This can allow malicious applications to silently change the applications that are run, spoofing your existing applications to collect private data from you."</string> - <string name="permlab_writeSettings">"modify global system settings"</string> - <string name="permdesc_writeSettings">"Allows an application to modify the systems settings data. Malicious applications can corrupt your systems configuration."</string> - <!-- no translation found for permlab_writeSecureSettings (4851801872124242319) --> - <skip /> - <!-- no translation found for permdesc_writeSecureSettings (2080620249472761366) --> - <skip /> - <!-- no translation found for permlab_writeGservices (296370685945777755) --> - <skip /> - <!-- no translation found for permdesc_writeGservices (2496928471286495053) --> - <skip /> - <string name="permlab_receiveBootCompleted">"automatically start at boot"</string> - <string name="permdesc_receiveBootCompleted">"Allows an application to have itself started as soon as the system has finished booting. This can make it take longer to start the phone and allow the application to slow down the overall phone by always running."</string> - <string name="permlab_broadcastSticky">"send sticky broadcast"</string> - <string name="permdesc_broadcastSticky">"Allows an application to send sticky broadcasts, which remain after the broadcast ends. Malicious applications can make the phone slow or unstable by causing it to use too much memory."</string> - <string name="permlab_readContacts">"read contact data"</string> - <string name="permdesc_readContacts">"Allows an application to read all of the contact (address) data stored on your phone. Malicious applications can use this to send your data to other people."</string> - <string name="permlab_writeContacts">"write contact data"</string> - <string name="permdesc_writeContacts">"Allows an application to modify the contact (address) data stored on your phone. Malicious applications can use this to erase or modify your contact data."</string> - <string name="permlab_writeOwnerData">"write owner data"</string> - <string name="permdesc_writeOwnerData">"Allows an application to modify the phone owner data stored on your phone. Malicious applications can use this to erase or modify owner data."</string> - <string name="permlab_readOwnerData">"read owner data"</string> - <string name="permdesc_readOwnerData">"Allows an application read the phone owner data stored on your phone. Malicious applications can use this to read phone owner data."</string> - <string name="permlab_readCalendar">"read calendar data"</string> - <string name="permdesc_readCalendar">"Allows an application to read all of the calendar events stored on your phone. Malicious applications can use this to send your calendar events to other people."</string> - <string name="permlab_writeCalendar">"write calendar data"</string> - <string name="permdesc_writeCalendar">"Allows an application to modify the calendar events stored on your phone. Malicious applications can use this to erase or modify your calendar data."</string> - <string name="permlab_accessMockLocation">"mock location sources for testing"</string> - <string name="permdesc_accessMockLocation">"Create mock location sources for testing. Malicious applications can use this to override the location and/or status returned by real location sources such as GPS or Network providers."</string> - <!-- no translation found for permlab_accessLocationExtraCommands (8291822077788811687) --> - <skip /> - <!-- no translation found for permdesc_accessLocationExtraCommands (5135782633548630731) --> - <skip /> - <string name="permlab_accessFineLocation">"fine (GPS) location"</string> - <string name="permdesc_accessFineLocation">"Access fine location sources such as the Global Positioning System on the phone, where available. Malicious applications can use this to determine where you are, and may consume additional battery power."</string> - <string name="permlab_accessCoarseLocation">"coarse (network-based) location"</string> - <string name="permdesc_accessCoarseLocation">"Access coarse location sources such as the cellular network database to determine an approximate phone location, where available. Malicious applications can use this to determine approximately where you are."</string> - <string name="permlab_accessSurfaceFlinger">"access SurfaceFlinger"</string> - <string name="permdesc_accessSurfaceFlinger">"Allows application to use SurfaceFlinger low-level features."</string> - <string name="permlab_readFrameBuffer">"read frame buffer"</string> - <string name="permdesc_readFrameBuffer">"Allows application to use read the content of the frame buffer."</string> - <string name="permlab_modifyAudioSettings">"change your audio settings"</string> - <string name="permdesc_modifyAudioSettings">"Allows application to modify global audio settings such as volume and routing."</string> - <string name="permlab_recordAudio">"record audio"</string> - <string name="permdesc_recordAudio">"Allows application to access the audio record path."</string> - <string name="permlab_camera">"take pictures"</string> - <string name="permdesc_camera">"Allows application to take pictures with the camera. This allows the application at any time to collect images the camera is seeing."</string> - <string name="permlab_brick">"permanently disable phone"</string> - <string name="permdesc_brick">"Allows the application to disable the entire phone permanently. This is very dangerous."</string> - <!-- no translation found for permlab_reboot (8844650672567077423) --> - <skip /> - <!-- no translation found for permdesc_reboot (4704919552870918328) --> - <skip /> - <string name="permlab_mount_unmount_filesystems">"mount and unmount filesystems"</string> - <string name="permdesc_mount_unmount_filesystems">"Allows the application to mount and unmount filesystems for removable storage."</string> - <string name="permlab_vibrate">"control vibrator"</string> - <string name="permdesc_vibrate">"Allows the application to control the vibrator."</string> - <string name="permlab_flashlight">"control flashlight"</string> - <string name="permdesc_flashlight">"Allows the application to control the flashlight."</string> - <string name="permlab_hardware_test">"test hardware"</string> - <string name="permdesc_hardware_test">"Allows the application to control various peripherals for the purpose of hardware testing."</string> - <string name="permlab_callPhone">"directly call phone numbers"</string> - <string name="permdesc_callPhone">"Allows the application to call phone numbers without your intervention. Malicious applications may cause unexpected calls on your phone bill. Note that this does not allow the application to call emergency numbers."</string> - <!-- no translation found for permlab_callPrivileged (2166923597287697159) --> - <skip /> - <!-- no translation found for permdesc_callPrivileged (5109789447971735501) --> - <skip /> - <!-- no translation found for permlab_locationUpdates (4216418293360456836) --> - <skip /> - <!-- no translation found for permdesc_locationUpdates (7635814693478743648) --> - <skip /> - <!-- no translation found for permlab_checkinProperties (2260796787386280708) --> - <skip /> - <!-- no translation found for permdesc_checkinProperties (3508022022841741945) --> - <skip /> - <string name="permlab_modifyPhoneState">"modify phone state"</string> - <string name="permdesc_modifyPhoneState">"Allows the application to control the phone features of the device. An application with this permission can switch networks, turn the phone radio on and off and the like without ever notifying you."</string> - <string name="permlab_readPhoneState">"read phone state"</string> - <string name="permdesc_readPhoneState">"Allows the application to access the phone features of the device. An application with this permission can determine the phone number of this phone, whether a call is active, the number that call is connected to and the like."</string> - <string name="permlab_wakeLock">"prevent phone from sleeping"</string> - <string name="permdesc_wakeLock">"Allows an application to prevent the phone from going to sleep."</string> - <string name="permlab_devicePower">"power phone on or off"</string> - <string name="permdesc_devicePower">"Allows the application to turn the phone on or off."</string> - <string name="permlab_factoryTest">"run in factory test mode"</string> - <string name="permdesc_factoryTest">"Run as a low-level manufacturer test, allowing complete access to the phone hardware. Only available when a phone is running in manufacturer test mode."</string> - <string name="permlab_setWallpaper">"set wallpaper"</string> - <string name="permdesc_setWallpaper">"Allows the application to set the system wallpaper."</string> - <string name="permlab_setWallpaperHints">"set wallpaper size hints"</string> - <string name="permdesc_setWallpaperHints">"Allows the application to set the system wallpaper size hints."</string> - <string name="permlab_masterClear">"reset system to factory defaults"</string> - <string name="permdesc_masterClear">"Allows an application to completely reset the system to its factory settings, erasing all data, configuration, and installed applications."</string> - <string name="permlab_setTimeZone">"set time zone"</string> - <string name="permdesc_setTimeZone">"Allows an application to change the phones time zone."</string> - <string name="permlab_getAccounts">"discover known accounts"</string> - <string name="permdesc_getAccounts">"Allows an application to get the list of accounts known by the phone."</string> - <string name="permlab_accessNetworkState">"view network state"</string> - <string name="permdesc_accessNetworkState">"Allows an application to view the state of all networks."</string> - <string name="permlab_createNetworkSockets">"full Internet access"</string> - <string name="permdesc_createNetworkSockets">"Allows an application to create network sockets."</string> - <!-- no translation found for permlab_writeApnSettings (3190585220761979369) --> - <skip /> - <!-- no translation found for permdesc_writeApnSettings (4093875220468761052) --> - <skip /> - <string name="permlab_changeNetworkState">"change network connectivity"</string> - <string name="permdesc_changeNetworkState">"Allows an application to change the state network connectivity."</string> - <string name="permlab_accessWifiState">"view Wi-Fi state"</string> - <string name="permdesc_accessWifiState">"Allows an application to view the information about the state of Wi-Fi."</string> - <string name="permlab_changeWifiState">"change Wi-Fi state"</string> - <string name="permdesc_changeWifiState">"Allows an application to connect to and disconnect from Wi-Fi access points, and to make changes to configured Wi-Fi networks."</string> - <string name="permlab_bluetoothAdmin">"bluetooth administration"</string> - <string name="permdesc_bluetoothAdmin">"Allows an application to configure the local Bluetooth phone, and to discover and pair with remote devices."</string> - <string name="permlab_bluetooth">"create Bluetooth connections"</string> - <string name="permdesc_bluetooth">"Allows an application to view configuration of the local Bluetooth phone, and to make and accept connections with paired devices."</string> - <string name="permlab_disableKeyguard">"disable keylock"</string> - <string name="permdesc_disableKeyguard">"Allows an application to disable the keylock and any associated password security. A legitimate example of this is the phone disabling the keylock when receiving an incoming phone call, then re-enabling the keylock when the call is finished."</string> - <string name="permlab_readSyncSettings">"read sync settings"</string> - <string name="permdesc_readSyncSettings">"Allows an application to read the sync settings, such as whether sync is enabled for Contacts."</string> - <string name="permlab_writeSyncSettings">"write sync settings"</string> - <string name="permdesc_writeSyncSettings">"Allows an application to modify the sync settings, such as whether sync is enabled for Contacts."</string> - <string name="permlab_readSyncStats">"read sync statistics"</string> - <string name="permdesc_readSyncStats">"Allows an application to reafocusd the sync stats; e.g., the history of syncs that have occurred."</string> - <!-- no translation found for permlab_subscribedFeedsRead (2043206814904506589) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsRead (6977343942680042449) --> - <skip /> - <!-- no translation found for permlab_subscribedFeedsWrite (2556727307229571556) --> - <skip /> - <!-- no translation found for permdesc_subscribedFeedsWrite (4134783294590266220) --> - <skip /> - <!-- no translation found for phoneTypes:0 (6070018634209800981) --> - <!-- no translation found for phoneTypes:1 (1514509689885965711) --> - <!-- no translation found for phoneTypes:2 (497473201754095234) --> - <!-- no translation found for phoneTypes:3 (5554432614281047787) --> - <!-- no translation found for phoneTypes:4 (2222084401110150993) --> - <!-- no translation found for phoneTypes:5 (2290007103906353121) --> - <!-- no translation found for phoneTypes:6 (6930783706213719251) --> - <!-- no translation found for phoneTypes:7 (1326005699931077792) --> - <!-- no translation found for emailAddressTypes:0 (1540640638077615417) --> - <!-- no translation found for emailAddressTypes:1 (4252853367575831977) --> - <!-- no translation found for emailAddressTypes:2 (7158046581744435718) --> - <!-- no translation found for emailAddressTypes:3 (3625034471181268169) --> - <!-- no translation found for postalAddressTypes:0 (5732960259696659380) --> - <!-- no translation found for postalAddressTypes:1 (7132240704786130285) --> - <!-- no translation found for postalAddressTypes:2 (1317604357745852817) --> - <!-- no translation found for postalAddressTypes:3 (1582953598462826702) --> - <!-- no translation found for imAddressTypes:0 (7806620012096518833) --> - <!-- no translation found for imAddressTypes:1 (5748846799950672787) --> - <!-- no translation found for imAddressTypes:2 (6196536810275073680) --> - <!-- no translation found for imAddressTypes:3 (8519128375350623648) --> - <!-- no translation found for organizationTypes:0 (1299224825223821142) --> - <!-- no translation found for organizationTypes:1 (2455717447227299354) --> - <!-- no translation found for organizationTypes:2 (7027570839313438290) --> - <!-- no translation found for imProtocols:0 (3318725788774688043) --> - <!-- no translation found for imProtocols:1 (1787713387022932886) --> - <!-- no translation found for imProtocols:2 (6751174158442316516) --> - <!-- no translation found for imProtocols:3 (1151283347465052653) --> - <!-- no translation found for imProtocols:4 (2157980008878817934) --> - <!-- no translation found for imProtocols:5 (7836237460308230767) --> - <!-- no translation found for imProtocols:6 (1180789904462172516) --> - <!-- no translation found for imProtocols:7 (21955111672779862) --> - <string name="keyguard_password_enter_pin_code">"Enter PIN code:"</string> - <string name="keyguard_password_wrong_pin_code">"Incorrect PIN code!"</string> - <string name="keyguard_label_text">"To unlock, press Menu then 0."</string> - <string name="emergency_call_dialog_number_for_display">"Emergency number"</string> - <string name="lockscreen_carrier_default">"(No service)"</string> - <string name="lockscreen_screen_locked">"Screen locked"</string> - <string name="lockscreen_instructions_when_pattern_enabled">"Press Menu to unlock or place emergency call."</string> - <string name="lockscreen_instructions_when_pattern_disabled">"Press Menu to unlock."</string> - <string name="lockscreen_pattern_instructions">"Draw pattern to unlock:"</string> - <string name="lockscreen_emergency_call">"Emergency call"</string> - <string name="lockscreen_pattern_correct">"Correct!"</string> - <string name="lockscreen_pattern_wrong">"Sorry, try again:"</string> - <string name="lockscreen_plugged_in">"Charging (<xliff:g id="NUMBER">%d%%</xliff:g>)"</string> - <string name="lockscreen_low_battery">"Connect your charger."</string> - <string name="lockscreen_missing_sim_message_short">"No SIM card."</string> - <string name="lockscreen_missing_sim_message">"No SIM card in phone."</string> - <string name="lockscreen_missing_sim_instructions">"Please insert a SIM card."</string> - <!-- no translation found for lockscreen_network_locked_message (323609607922245071) --> - <skip /> - <string name="lockscreen_sim_puk_locked_message">"SIM card is PUK-locked."</string> - <string name="lockscreen_sim_puk_locked_instructions">"Please contact Customer Care."</string> - <string name="lockscreen_sim_locked_message">"SIM card is locked."</string> - <string name="lockscreen_sim_unlock_progress_dialog_message">"Unlocking SIM card…"</string> - <string name="lockscreen_too_many_failed_attempts_dialog_message">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Please try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string> - <string name="lockscreen_failed_attempts_almost_glogin">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in."\n\n" Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string> - <string name="lockscreen_too_many_failed_attempts_countdown">"Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string> - <string name="lockscreen_forgot_pattern_button_text">"Forgot pattern?"</string> - <string name="lockscreen_glogin_too_many_attempts">"Too many pattern attempts!"</string> - <string name="lockscreen_glogin_instructions">"To unlock,"\n"sign in with your Google account:"</string> - <string name="lockscreen_glogin_username_hint">"Username (email)"</string> - <string name="lockscreen_glogin_password_hint">"Password"</string> - <string name="lockscreen_glogin_submit_button">"Sign in"</string> - <string name="lockscreen_glogin_invalid_input">"Invalid username or password."</string> - <!-- unknown placeholder FORMAT in status_bar_time_format --> - <skip /> - <!-- no translation found for hour_minute_ampm (1850330605794978742) --> - <skip /> - <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> - <skip /> - <!-- no translation found for hour_ampm (7665432130905376251) --> - <skip /> - <!-- no translation found for hour_cap_ampm (3600295014648400268) --> - <skip /> - <string name="status_bar_clear_all_button">"Clear notifications"</string> - <string name="status_bar_no_notifications_title">"No notifications"</string> - <string name="status_bar_ongoing_events_title">"Ongoing"</string> - <string name="status_bar_latest_events_title">"Notifications"</string> - <string name="battery_status_text_percent_format">"<xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="battery_status_charging">"Charging…"</string> - <string name="battery_low_title">"Please connect charger"</string> - <string name="battery_low_subtitle">"The battery is getting low:"</string> - <string name="battery_low_percent_format">"less than <xliff:g id="NUMBER">%d%%</xliff:g> remaining."</string> - <string name="factorytest_failed">"Factory test failed"</string> - <string name="factorytest_not_system">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string> - <string name="factorytest_no_action">"No package was found that provides the FACTORY_TEST action."</string> - <string name="factorytest_reboot">"Reboot"</string> - <string name="save_password_label">"Confirm"</string> - <string name="save_password_message">"Do you want the browser to remember this password?"</string> - <string name="save_password_notnow">"Not now"</string> - <string name="save_password_remember">"Remember"</string> - <string name="save_password_never">"Never"</string> - <string name="open_permission_deny">"You do not have permission to open this page."</string> - <string name="text_copied">"Text copied to clipboard."</string> - <string name="more_item_label">"More"</string> - <string name="prepend_shortcut_label">"Menu+"</string> - <!-- no translation found for menu_space_shortcut_label (194586306440382711) --> - <skip /> - <!-- no translation found for menu_enter_shortcut_label (7214761412193519345) --> - <skip /> - <!-- no translation found for menu_delete_shortcut_label (2854936426194985313) --> - <skip /> - <string name="search_go">"Search"</string> - <string name="today">"Today"</string> - <string name="yesterday">"Yesterday"</string> - <string name="tomorrow">"Tomorrow"</string> - <string name="oneMonthDurationPast">"1 month ago"</string> - <!-- no translation found for beforeOneMonthDurationPast (7578100953282866827) --> - <skip /> - <!-- no translation found for num_seconds_ago:one (7416512229671810725) --> - <!-- no translation found for num_seconds_ago:other (8138756910300398447) --> - <!-- no translation found for num_minutes_ago:one (8620869479299420562) --> - <!-- no translation found for num_minutes_ago:other (5065488162050522741) --> - <!-- no translation found for num_hours_ago:one (853404611989669641) --> - <!-- no translation found for num_hours_ago:other (3558873784561756849) --> - <!-- no translation found for num_days_ago:one (4222479980812128212) --> - <!-- no translation found for num_days_ago:other (5445701370433601703) --> - <!-- no translation found for in_num_seconds:one (4253290037777327003) --> - <!-- no translation found for in_num_seconds:other (1280033870920841404) --> - <!-- no translation found for in_num_minutes:one (1487585791027953091) --> - <!-- no translation found for in_num_minutes:other (6274204576475209932) --> - <!-- no translation found for in_num_hours:one (6501470863235186391) --> - <!-- no translation found for in_num_hours:other (4415358752953289251) --> - <!-- no translation found for in_num_days:one (5608475533104443893) --> - <!-- no translation found for in_num_days:other (3827193006163842267) --> - <string name="preposition_for_date">"on %s"</string> - <string name="preposition_for_time">"at %s"</string> - <string name="preposition_for_year">"in %s"</string> - <string name="day">"day"</string> - <string name="days">"days"</string> - <string name="hour">"hour"</string> - <string name="hours">"hours"</string> - <string name="minute">"min"</string> - <string name="minutes">"mins"</string> - <string name="second">"sec"</string> - <string name="seconds">"secs"</string> - <string name="week">"week"</string> - <string name="weeks">"weeks"</string> - <string name="year">"year"</string> - <string name="years">"years"</string> - <string name="sunday">"Sunday"</string> - <string name="monday">"Monday"</string> - <string name="tuesday">"Tuesday"</string> - <string name="wednesday">"Wednesday"</string> - <string name="thursday">"Thursday"</string> - <string name="friday">"Friday"</string> - <string name="saturday">"Saturday"</string> - <string name="every_weekday">"Every weekday (Mon–Fri)"</string> - <string name="daily">"Daily"</string> - <string name="weekly">"Weekly on <xliff:g id="DAY">%s</xliff:g>"</string> - <string name="monthly">"Monthly"</string> - <string name="yearly">"Yearly"</string> - <string name="VideoView_error_title">"Cannot play video"</string> - <string name="VideoView_error_text_unknown">"Sorry, this video cannot be played."</string> - <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <!-- unknown placeholder FORMAT in numeric_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_time1_wday2_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in wday1_date1_wday2_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_time1_date2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in date1_date2 --> - <skip /> - <!-- unknown placeholder FORMAT in time1_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in wday_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_date --> - <skip /> - <!-- unknown placeholder FORMAT in time_wday --> - <skip /> - <!-- no translation found for full_date_month_first (6011143962222283357) --> - <skip /> - <!-- no translation found for full_date_day_first (8621594762705478189) --> - <skip /> - <!-- no translation found for medium_date_month_first (48990963718825728) --> - <skip /> - <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <skip /> - <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> - <skip /> - <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> - <skip /> - <string name="noon">"noon"</string> - <string name="Noon">"Noon"</string> - <string name="midnight">"midnight"</string> - <string name="Midnight">"Midnight"</string> - <!-- unknown placeholder FORMAT in month_day --> - <skip /> - <!-- unknown placeholder FORMAT in month --> - <skip /> - <!-- unknown placeholder FORMAT in month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in month_year --> - <skip /> - <!-- no translation found for time_of_day (8375993139317154157) --> - <skip /> - <!-- no translation found for date_and_time (9197690194373107109) --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_year_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in numeric_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_wday2_md2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_wday2_mdy2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_md1_time1_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_md1_time1_wday2_md2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_mdy1_time1_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in same_month_wday1_mdy1_time1_wday2_mdy2_time2 --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_year --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month_day --> - <skip /> - <!-- unknown placeholder FORMAT in abbrev_month --> - <skip /> - <string name="day_of_week_long_sunday">"Sunday"</string> - <string name="day_of_week_long_monday">"Monday"</string> - <string name="day_of_week_long_tuesday">"Tuesday"</string> - <string name="day_of_week_long_wednesday">"Wednesday"</string> - <string name="day_of_week_long_thursday">"Thursday"</string> - <string name="day_of_week_long_friday">"Friday"</string> - <string name="day_of_week_long_saturday">"Saturday"</string> - <string name="day_of_week_medium_sunday">"Sun"</string> - <string name="day_of_week_medium_monday">"Mon"</string> - <string name="day_of_week_medium_tuesday">"Tue"</string> - <string name="day_of_week_medium_wednesday">"Wed"</string> - <string name="day_of_week_medium_thursday">"Thu"</string> - <string name="day_of_week_medium_friday">"Fri"</string> - <string name="day_of_week_medium_saturday">"Sat"</string> - <string name="day_of_week_short_sunday">"Su"</string> - <string name="day_of_week_short_monday">"Mo"</string> - <string name="day_of_week_short_tuesday">"Tu"</string> - <string name="day_of_week_short_wednesday">"We"</string> - <string name="day_of_week_short_thursday">"Th"</string> - <string name="day_of_week_short_friday">"Fr"</string> - <string name="day_of_week_short_saturday">"Sa"</string> - <string name="day_of_week_shorter_sunday">"Su"</string> - <string name="day_of_week_shorter_monday">"M"</string> - <string name="day_of_week_shorter_tuesday">"Tu"</string> - <string name="day_of_week_shorter_wednesday">"W"</string> - <string name="day_of_week_shorter_thursday">"Th"</string> - <string name="day_of_week_shorter_friday">"F"</string> - <string name="day_of_week_shorter_saturday">"Sa"</string> - <string name="day_of_week_shortest_sunday">"S"</string> - <string name="day_of_week_shortest_monday">"M"</string> - <string name="day_of_week_shortest_tuesday">"T"</string> - <string name="day_of_week_shortest_wednesday">"W"</string> - <string name="day_of_week_shortest_thursday">"T"</string> - <string name="day_of_week_shortest_friday">"F"</string> - <string name="day_of_week_shortest_saturday">"S"</string> - <string name="month_long_january">"January"</string> - <string name="month_long_february">"February"</string> - <string name="month_long_march">"March"</string> - <string name="month_long_april">"April"</string> - <string name="month_long_may">"May"</string> - <string name="month_long_june">"June"</string> - <string name="month_long_july">"July"</string> - <string name="month_long_august">"August"</string> - <string name="month_long_september">"September"</string> - <string name="month_long_october">"October"</string> - <string name="month_long_november">"November"</string> - <string name="month_long_december">"December"</string> - <string name="month_medium_january">"Jan"</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">"May"</string> - <string name="month_medium_june">"Jun"</string> - <string name="month_medium_july">"Jul"</string> - <string name="month_medium_august">"Aug"</string> - <string name="month_medium_september">"Sep"</string> - <string name="month_medium_october">"Oct"</string> - <string name="month_medium_november">"Nov"</string> - <string name="month_medium_december">"Dec"</string> - <string name="month_shortest_january">"J"</string> - <string name="month_shortest_february">"F"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"J"</string> - <string name="month_shortest_july">"J"</string> - <string name="month_shortest_august">"A"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_mm_ss --> - <skip /> - <!-- unknown placeholder FORMAT in elapsed_time_short_format_h_mm_ss --> - <skip /> - <string name="selectAll">"Select all"</string> - <string name="cut">"Cut"</string> - <string name="cutAll">"Cut all"</string> - <string name="copy">"Copy"</string> - <string name="copyAll">"Copy all"</string> - <string name="paste">"Paste"</string> - <string name="copyUrl">"Copy URL"</string> - <!-- no translation found for inputMethod (7911866729148111492) --> - <skip /> - <!-- no translation found for editTextMenuTitle (3984253728638788023) --> - <skip /> - <string name="low_internal_storage_view_title">"Low on space"</string> - <string name="low_internal_storage_view_text">"Your phone is running low on internal storage space."</string> - <string name="ok">"OK"</string> - <string name="cancel">"Cancel"</string> - <string name="yes">"OK"</string> - <string name="no">"Cancel"</string> - <string name="capital_on">"ON"</string> - <string name="capital_off">"OFF"</string> - <string name="whichApplication">"Complete action using"</string> - <string name="alwaysUse">"Use by default for this action."</string> - <string name="clearDefaultHintMsg">"Clear default in Home Settings > Applications > Manage applications."</string> - <string name="chooseActivity">"Select an action"</string> - <string name="noApplications">"No applications can perform this action."</string> - <string name="aerr_title">"Sorry!"</string> - <string name="aerr_application">"The application <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has stopped unexpectedly. Please try again."</string> - <string name="aerr_process">"The process <xliff:g id="PROCESS">%1$s</xliff:g> has stopped unexpectedly. Please try again."</string> - <string name="anr_title">"Application unresponsive"</string> - <string name="anr_activity_application">"Activity <xliff:g id="ACTIVITY">%1$s</xliff:g> (in application <xliff:g id="APPLICATION">%2$s</xliff:g>) is not responding."</string> - <string name="anr_activity_process">"Activity <xliff:g id="ACTIVITY">%1$s</xliff:g> (in process <xliff:g id="PROCESS">%2$s</xliff:g>) is not responding."</string> - <string name="anr_application_process">"Application <xliff:g id="APPLICATION">%1$s</xliff:g> (in process <xliff:g id="PROCESS">%2$s</xliff:g>) is not responding."</string> - <string name="anr_process">"Process <xliff:g id="PROCESS">%1$s</xliff:g> is not responding."</string> - <string name="force_close">"Force close"</string> - <string name="wait">"Wait"</string> - <string name="debug">"Debug"</string> - <string name="sendText">"Select an action for text"</string> - <string name="volume_ringtone">"Ringer volume"</string> - <string name="volume_music">"Music/video volume"</string> - <string name="volume_call">"In-call volume"</string> - <string name="volume_alarm">"Alarm volume"</string> - <string name="volume_unknown">"Volume"</string> - <string name="ringtone_default">"Default ringtone"</string> - <string name="ringtone_default_with_actual">"Default ringtone (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> - <string name="ringtone_silent">"Silent"</string> - <string name="ringtone_picker_title">"Select a ringtone"</string> - <string name="ringtone_unknown">"Unknown ringtone"</string> - <!-- no translation found for wifi_available:one (8168012881468888470) --> - <!-- no translation found for wifi_available:other (4666122955807117718) --> - <!-- no translation found for wifi_available_detailed:one (5107769161192143259) --> - <!-- no translation found for wifi_available_detailed:other (853347657960575809) --> - <string name="select_character">"Select character to insert"</string> - <!-- no translation found for sms_control_default_app_name (7522184737840550841) --> - <skip /> - <string name="sms_control_title">"Sending SMS messages"</string> - <string name="sms_control_message">"A large number of SMS messages are being sent. Select \"OK\" to continue, or \"Cancel\" to stop sending."</string> - <string name="sms_control_yes">"OK"</string> - <string name="sms_control_no">"Cancel"</string> - <string name="date_time_set">"Set"</string> - <string name="default_permission_group">"Default"</string> - <string name="no_permissions">"No permissions required"</string> - <!-- no translation found for perms_hide (4145325555929151849) --> - <skip /> - <!-- no translation found for perms_show_all (6040194843455403173) --> - <skip /> - <!-- no translation found for googlewebcontenthelper_loading (2140804350507245589) --> - <skip /> - <!-- no translation found for usb_storage_title (8699631567051394409) --> - <skip /> - <!-- no translation found for usb_storage_message (5344039189213308733) --> - <skip /> - <!-- no translation found for usb_storage_button_mount (6700104384375121662) --> - <skip /> - <!-- no translation found for usb_storage_button_unmount (465869657252626688) --> - <skip /> - <!-- no translation found for usb_storage_error_message (3192564550748426087) --> - <skip /> - <!-- no translation found for usb_storage_notification_title (6237028017872246940) --> - <skip /> - <!-- no translation found for usb_storage_notification_message (7371717280517625905) --> - <skip /> - <!-- no translation found for select_input_method (2658280517827502015) --> - <skip /> - <!-- no translation found for fast_scroll_alphabet (1017432309285755759) --> - <skip /> - <!-- no translation found for fast_scroll_numeric_alphabet (3092587363718901074) --> - <skip /> - <!-- no translation found for candidates_style (7738463880139922176) --> - <skip /> + <string name="byteShort">B</string> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index f8881b6..dc17445 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 47f1bf4..95e3052 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 710c493..2cf7b43 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 0eba7f5..24e3cb6 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -558,12 +558,12 @@ <!-- 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">d</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">d</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">d</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">d</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="full_date_month_first">"<xliff:g id="YEAR">yyyy</xliff:g>'\'\'年\'\''<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">d</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">d</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">d</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">d</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">"午前0時"</string> @@ -573,7 +573,8 @@ <!-- 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> - <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g>年 <xliff:g id="MONTH">%B</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> @@ -816,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 4272684..140f32f 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 9f49557..466f2b1 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -262,10 +262,8 @@ <string name="permdesc_reboot">"Lar applikasjonen tvinge telefonen til å starte på nytt."</string> <string name="permlab_mount_unmount_filesystems">"montere og avmontere filsystemer"</string> <string name="permdesc_mount_unmount_filesystems">"Lar applikasjonen montere og avmontere filsystemer for uttagbar lagring."</string> - <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> - <skip /> - <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> - <skip /> + <string name="permlab_mount_format_filesystems">"formatere ekstern lagringsplass"</string> + <string name="permdesc_mount_format_filesystems">"Lar applikasjonen formatere ekstern lagringsplass."</string> <string name="permlab_vibrate">"kontrollere vibratoren"</string> <string name="permdesc_vibrate">"Lar applikasjonen kontrollere vibratoren."</string> <string name="permlab_flashlight">"kontrollere lommelykten"</string> @@ -280,10 +278,8 @@ <string name="permdesc_locationUpdates">"Lar applikasjonen slå av/på varsling om plasseringsendringer fra radioen. Ikke ment for vanlige applikasjoner."</string> <string name="permlab_checkinProperties">"få tilgang til egenskaper for innsjekking"</string> <string name="permdesc_checkinProperties">"Gir lese- og skrivetilgang til egenskaper lastet opp av innsjekkingstjenesten. Ikke ment for vanlige applikasjoner."</string> - <!-- no translation found for permlab_bindGadget (2519859363977647275) --> - <skip /> - <!-- no translation found for permdesc_bindGadget (7865866514555126333) --> - <skip /> + <string name="permlab_bindGadget">"velg gadgeter"</string> + <string name="permdesc_bindGadget">"Lar applikasjonen fortelle systemet hvilke gadgeter som kan brukes av hvilke applikasjoner. Med denne rettigheten kan applikasjoner andre applikasjoner tilgang til personlig data. Ikke ment for vanlige applikasjoner."</string> <string name="permlab_modifyPhoneState">"endre telefontilstand"</string> <string name="permdesc_modifyPhoneState">"Lar applikasjonen kontrollere telefonfunksjonaliteten i enheten. En applikasjon med denne rettigheten kan endre nettverk, slå telefonens radio av eller på og lignende uten noensinne å varsle brukeren."</string> <string name="permlab_readPhoneState">"lese telefontilstand"</string> @@ -312,10 +308,8 @@ <string name="permdesc_writeApnSettings">"Lar applikasjonen to endre APN-innstillinger slik som mellomtjener eller port for hvilket som helst aksesspunkt."</string> <string name="permlab_changeNetworkState">"endre nettverkskonnektivitet"</string> <string name="permdesc_changeNetworkState">"Lar applikasjonen endre tilstanden til nettverkskonnektivitet."</string> - <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> - <skip /> - <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> - <skip /> + <string name="permlab_changeBackgroundDataSetting">"endre innstilling for bakgrunnsdata"</string> + <string name="permdesc_changeBackgroundDataSetting">"Lar applikasjonen endre innstillingen for bakgrunnsdata."</string> <string name="permlab_accessWifiState">"se tilstand for trådløse nettverk"</string> <string name="permdesc_accessWifiState">"Lar applikasjonen få se informasjon om tilstanden til de trådløse nettene."</string> <string name="permlab_changeWifiState">"endre tilstand for trådløse nettverk"</string> @@ -336,14 +330,10 @@ <string name="permdesc_subscribedFeedsRead">"Lar applikasjonen hente detaljer om hvilke nyhetskilder som synkroniseres."</string> <string name="permlab_subscribedFeedsWrite">"endre abonnement på nyhetskilder"</string> <string name="permdesc_subscribedFeedsWrite">"Lar applikasjonen redigere hvilke nyhetskilder som synkroniseres. Dette kan gi en ondsinnet applikasjon tilgang til å endre hvilke nyhetskilder som synkroniseres."</string> - <!-- no translation found for permlab_readDictionary (432535716804748781) --> - <skip /> - <!-- no translation found for permdesc_readDictionary (1082972603576360690) --> - <skip /> - <!-- no translation found for permlab_writeDictionary (6703109511836343341) --> - <skip /> - <!-- no translation found for permdesc_writeDictionary (2241256206524082880) --> - <skip /> + <string name="permlab_readDictionary">"lese brukerdefinert ordliste"</string> + <string name="permdesc_readDictionary">"Lar applikasjonen lese private ord, navn og uttrykk som brukeren har lagret i den brukerdefinerte ordlisten."</string> + <string name="permlab_writeDictionary">"skrive til brukerdefinert ordliste"</string> + <string name="permdesc_writeDictionary">"Lar applikasjonen skrive nye ord til den brukerdefinerte ordlisten."</string> <string-array name="phoneTypes"> <item>"Hjemme"</item> <item>"Mobil"</item> @@ -440,12 +430,9 @@ <string name="factorytest_not_system">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string> <string name="factorytest_no_action">"No package was found that provides the FACTORY_TEST action."</string> <string name="factorytest_reboot">"Reboot"</string> - <!-- no translation found for js_dialog_title (8143918455087008109) --> - <skip /> - <!-- no translation found for js_dialog_title_default (6961903213729667573) --> - <skip /> - <!-- no translation found for js_dialog_before_unload (1901675448179653089) --> - <skip /> + <string name="js_dialog_title">"Siden \\\'<xliff:g id="TITLE">%s</xliff:g>\\\' sier:"</string> + <string name="js_dialog_title_default">"JavaScript"</string> + <string name="js_dialog_before_unload">"Naviger bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Velg OK for å fortsette, eller Avbryt for å forbli på denne siden."</string> <string name="save_password_label">"Bekreft"</string> <string name="save_password_message">"Ønsker du at nettleseren skal huske dette passordet?"</string> <string name="save_password_notnow">"Ikke nå"</string> @@ -569,17 +556,15 @@ <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="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <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">d</xliff:g>'., '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first">"<xliff:g id="DAY">d</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">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first">"<xliff:g id="DAY">d</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="full_date_month_first">"<xliff:g id="MONTH">MMMM</xliff:g>'\\\'\'\\\'\' \\\'\'\\\'\''<xliff:g id="DAY">d</xliff:g>'\\\'\'\\\'\'., \\\'\'\\\'\''<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="full_date_day_first">"<xliff:g id="DAY">d</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">d</xliff:g>'\\\'\'\\\'\', \\\'\'\\\'\''<xliff:g id="YEAR">yyyy</xliff:g>"</string> + <string name="medium_date_day_first">"<xliff:g id="DAY">d</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">"middag"</string> <string name="Noon">"Middag"</string> <string name="midnight">"midnatt"</string> @@ -596,18 +581,14 @@ <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="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> - <!-- no translation found for same_year_wday1_mdy1_wday2_mdy2 (1345612987720788874) --> - <skip /> + <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$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> - <!-- no translation found for same_year_mdy1_time1_mdy2_time2 (2959466512848524124) --> - <skip /> - <!-- no translation found for same_year_wday1_mdy1_time1_wday2_mdy2_time2 (5129735426508861428) --> - <skip /> + <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> - <!-- no translation found for numeric_mdy1_mdy2 (3933951218078638018) --> - <skip /> + <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> @@ -711,8 +692,7 @@ <string name="paste">"Lim inn"</string> <string name="copyUrl">"Kopier URL"</string> <string name="inputMethod">"Inndatametode"</string> - <!-- no translation found for addToDictionary (726256909274177272) --> - <skip /> + <string name="addToDictionary">"Legg \\\"%s\\\" til ordlisten"</string> <string name="editTextMenuTitle">"Rediger tekst"</string> <string name="low_internal_storage_view_title">"Lite plass"</string> <string name="low_internal_storage_view_text">"Det begynner å bli lite lagringsplass på telefonen."</string> @@ -720,8 +700,7 @@ <string name="cancel">"Avbryt"</string> <string name="yes">"OK"</string> <string name="no">"Avbryt"</string> - <!-- no translation found for dialog_alert_title (2049658708609043103) --> - <skip /> + <string name="dialog_alert_title">"Merk"</string> <string name="capital_on">"På"</string> <string name="capital_off">"Av"</string> <string name="whichApplication">"Complete action using"</string> @@ -745,8 +724,7 @@ <string name="volume_music">"Medievolum"</string> <string name="volume_music_hint_playing_through_bluetooth">"Spiller over Bluetooth"</string> <string name="volume_call">"Samtalevolum"</string> - <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> - <skip /> + <string name="volume_bluetooth_call">"Bluetooth-samtalevolum"</string> <string name="volume_alarm">"Alarmvolum"</string> <string name="volume_notification">"Varslingsvolum"</string> <string name="volume_unknown">"Volum"</string> @@ -766,7 +744,7 @@ <string name="select_character">"Sett inn tegn"</string> <string name="sms_control_default_app_name">"Ukjent applikasjon"</string> <string name="sms_control_title">"Sending SMS messages"</string> - <string name="sms_control_message">"A large number of SMS messages are being sent. Select \\\"OK\\\" to continue, or \\\"Cancel\\\" to stop sending."</string> + <string name="sms_control_message">"A large number of SMS messages are being sent. Select \\\\\\\"OK\\\\\\\" to continue, or \\\\\\\"Cancel\\\\\\\" to stop sending."</string> <string name="sms_control_yes">"OK"</string> <string name="sms_control_no">"Avbryt"</string> <string name="date_time_set">"Lagre"</string> @@ -776,67 +754,39 @@ <string name="perms_show_all"><b>"Vis alle"</b></string> <string name="googlewebcontenthelper_loading">"Laster inn…"</string> <string name="usb_storage_title">"USB koblet til"</string> - <string name="usb_storage_message">"Du har koblet telefonen til en datamaskin via USB. Velg \\\"Monter\\\" dersom du ønsker å kopiere filer mellom datmaskinen og minnekortet i telefonen."</string> + <string name="usb_storage_message">"Du har koblet telefonen til en datamaskin via USB. Velg \\\\\\\"Monter\\\\\\\" dersom du ønsker å kopiere filer mellom datmaskinen og minnekortet i telefonen."</string> <string name="usb_storage_button_mount">"Monter"</string> <string name="usb_storage_button_unmount">"Ikke monter"</string> <string name="usb_storage_error_message">"Det oppsto et problem med å bruke minnekortet ditt for USB-lagring."</string> <string name="usb_storage_notification_title">"USB tilkoblet"</string> <string name="usb_storage_notification_message">"Velg om du ønsker å kopiere filer til/fra en datamaskin."</string> - <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> - <skip /> - <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> - <skip /> - <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> - <skip /> - <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> - <skip /> - <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> - <skip /> - <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> - <skip /> - <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> - <skip /> - <!-- no translation found for extmedia_format_title (8663247929551095854) --> - <skip /> - <!-- no translation found for extmedia_format_message (3621369962433523619) --> - <skip /> - <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> - <skip /> + <string name="usb_storage_stop_notification_title">"Slå av USB-lagring"</string> + <string name="usb_storage_stop_notification_message">"Velg for å slå av USB-lagring."</string> + <string name="usb_storage_stop_title">"Slå av USB-lagring"</string> + <string name="usb_storage_stop_message">"Før du slår av USB-lagring, sjekk at du har avmontert enheten i USB-verten. Velg «slå av» for å slå av USB-lagring."</string> + <string name="usb_storage_stop_button_mount">"Slå av"</string> + <string name="usb_storage_stop_button_unmount">"Avbryt"</string> + <string name="usb_storage_stop_error_message">"Det oppsto et problem under avslutningen av USB-lagring. Sjekk at USB-verten har avmontert og prøv igjen."</string> + <string name="extmedia_format_title">"Formatere minnekort"</string> + <string name="extmedia_format_message">"Er du sikker på at du ønsker å formatere minnekortet? Alle data på kortet vil gå tapt."</string> + <string name="extmedia_format_button_format">"Format"</string> <string name="select_input_method">"Velg inndatametode"</string> <string name="fast_scroll_alphabet">"ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string> <string name="fast_scroll_numeric_alphabet">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string> - <!-- unknown quoting pattern: original -1, translation 1 --> - <string name="candidates_style">"TAG_FONT"<u>"kandidater"</u>"u&gt;CLOSE_FONT"</string> - <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> - <skip /> - <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> - <skip /> - <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> - <skip /> - <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> - <skip /> - <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> - <skip /> - <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> - <skip /> - <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> - <skip /> - <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> - <skip /> - <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> - <skip /> - <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> - <skip /> - <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> - <skip /> - <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> - <skip /> - <!-- no translation found for activity_list_empty (4168820609403385789) --> - <skip /> - <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> - <skip /> - <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> - <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> - <skip /> + <string name="candidates_style">"TAG_FONT"<u>"kandidater"</u>"CLOSE_FONT"</string> + <string name="ext_media_checking_notification_title">"Forbereder minnekort"</string> + <string name="ext_media_checking_notification_message">"Sjekker for feil"</string> + <string name="ext_media_nofs_notification_title">"Tomt minnekort"</string> + <string name="ext_media_nofs_notification_message">"Minnekortet er tomt eller bruker et ustøttet filsystem."</string> + <string name="ext_media_unmountable_notification_title">"Skadet minnekort"</string> + <string name="ext_media_unmountable_notification_message">"Minnekortet er skadet. Det kan være du må formatere kortet."</string> + <string name="ext_media_badremoval_notification_title">"Minnekortet ble tatt ut uventet"</string> + <string name="ext_media_badremoval_notification_message">"Avmonter minnekortet før det tas ut, for å unngå datatap."</string> + <string name="ext_media_safe_unmount_notification_title">"Trygt å ta ut minnekort"</string> + <string name="ext_media_safe_unmount_notification_message">"Minnekortet kan nå trygt tas ut."</string> + <string name="ext_media_nomedia_notification_title">"Minnekortet ble tatt ut"</string> + <string name="ext_media_nomedia_notification_message">"Minnekortet ble tatt ut. Sett inn et nytt minnekort for å øke lagringsplassen."</string> + <string name="activity_list_empty">"Fant ingen tilsvarende aktiviteter"</string> + <string name="permlab_pkgUsageStats">"oppdater statistikk over komponentbruk"</string> + <string name="permdesc_pkgUsageStats">"Tillater endring av innsamlet data om bruk av komponenter. Ikke ment for vanlige applikasjoner."</string> </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 7ce4c66..a2810a1 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 03c4f8b..12f1616 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index d25ab8d..76a358d 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 447fccd..13d4e9c 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index e97c142..419e8c2 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -817,6 +817,5 @@ <skip /> <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> <skip /> - <!-- no translation found for tutorial_double_tap_to_zoom_message (9111326385548696308) --> <skip /> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 3f21303..593d1ff 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1056,6 +1056,10 @@ enabled for events such as clicking and touching. --> <attr name="soundEffectsEnabled" format="boolean" /> + <!-- Boolean that controls whether a view should have haptic feedback + enabled for events such as long presses. --> + <attr name="hapticFeedbackEnabled" format="boolean" /> + </declare-styleable> <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any @@ -2035,8 +2039,18 @@ <enum name="line" value="2" /> <enum name="ring" value="3" /> </attr> + <!-- Inner radius of the ring expressed as a ratio of the ring's width. For instance, + if innerRadiusRatio=9, then the inner radius equals the ring's width divided by 9. + This value is ignored if innerRadius is defined. Default value is 9. --> <attr name="innerRadiusRatio" format="float" /> + <!-- Thickness of the ring expressed as a ratio of the ring's width. For instance, + if thicknessRatio=3, then the thickness equals the ring's width divided by 3. + This value is ignored if innerRadius is defined. Default value is 3. --> <attr name="thicknessRatio" format="float" /> + <!-- Inner radius of the ring. When defined, innerRadiusRatio is ignored. --> + <attr name="innerRadius" format="dimension" /> + <!-- Thickness of the ring. When defined, thicknessRatio is ignored. --> + <attr name="thickness" format="dimension" /> <attr name="useLevel" /> </declare-styleable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 1031585..9175f31 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1001,6 +1001,9 @@ <public type="attr" name="content" id="0x0101025b" /> <public type="attr" name="animateOnClick" id="0x0101025c" /> <public type="attr" name="configure" id="0x0101025d" /> + <public type="attr" name="hapticFeedbackEnabled" id="0x0101025e" /> + <public type="attr" name="innerRadius" id="0x0101025f" /> + <public type="attr" name="thickness" id="0x01010260" /> <!-- The part of the UI shown by an {@link android.inputmethodservice.InputMethodService} that contains the diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4700b93..1174996 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2263,8 +2263,7 @@ <string name="permdesc_pkgUsageStats">Allows the modification of collected component usage statistics. Not for use by normal applications.</string> <!-- Shown in the tutorial for double tap to zoom. --> - <string name="tutorial_double_tap_to_zoom_message">Congratulations on downloading the Android software update. This update includes a number of great new features for you to enjoy. One major improvement we've made is to how you zoom. Now, when you want to zoom, just double tap on the screen. This will bring up a zoom widget. Drag the widget's handle clockwise to zoom in, and counter-clockwise to zoom out.</string> - + <string name="tutorial_double_tap_to_zoom_message_short">Double tap to zoom</string> </resources> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 82cb795..3db45f0 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -47,7 +47,9 @@ import java.io.IOException; * @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_innerRadius * @attr ref android.R.styleable#GradientDrawable_thicknessRatio + * @attr ref android.R.styleable#GradientDrawable_thickness * @attr ref android.R.styleable#GradientDrawable_useLevel * @attr ref android.R.styleable#GradientDrawableSize_width * @attr ref android.R.styleable#GradientDrawableSize_height @@ -121,6 +123,8 @@ public class GradientDrawable extends Drawable { private Paint mLayerPaint; // internal, used if we use saveLayer() private boolean mRectIsDirty; // internal state private boolean mMutated; + private Path mRingPath; + private boolean mPathIsDirty; /** * Controls how the gradient is oriented relative to the drawable's bounds @@ -213,6 +217,7 @@ public class GradientDrawable extends Drawable { } public void setShape(int shape) { + mRingPath = null; mGradientState.setShape(shape); } @@ -248,14 +253,12 @@ public class GradientDrawable extends Drawable { // remember the alpha values, in case we temporarily overwrite them // when we modulate them with mAlpha final int prevFillAlpha = mFillPaint.getAlpha(); - final int prevStrokeAlpha = mStrokePaint != null ? - mStrokePaint.getAlpha() : 0; + final int prevStrokeAlpha = mStrokePaint != null ? mStrokePaint.getAlpha() : 0; // compute the modulate alpha values final int currFillAlpha = modulateAlpha(prevFillAlpha); final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha); - final boolean haveStroke = currStrokeAlpha > 0 && - mStrokePaint.getStrokeWidth() > 0; + final boolean haveStroke = currStrokeAlpha > 0 && mStrokePaint.getStrokeWidth() > 0; final boolean haveFill = currFillAlpha > 0; final GradientState st = mGradientState; /* we need a layer iff we're drawing both a fill and stroke, and the @@ -264,7 +267,7 @@ public class GradientDrawable extends Drawable { of the fill (if any) without worrying about blending artifacts. */ final boolean useLayer = haveStroke && haveFill && st.mShape != LINE && - currStrokeAlpha < 255; + currStrokeAlpha < 255; /* Drawing with a layer is slower than direct drawing, but it allows us to apply paint effects like alpha and colorfilter to @@ -336,10 +339,10 @@ public class GradientDrawable extends Drawable { break; } case RING: - Path ring = buildRing(st); - canvas.drawPath(ring, mFillPaint); + Path path = buildRing(st); + canvas.drawPath(path, mFillPaint); if (haveStroke) { - canvas.drawPath(ring, mStrokePaint); + canvas.drawPath(path, mStrokePaint); } break; } @@ -355,6 +358,9 @@ public class GradientDrawable extends Drawable { } private Path buildRing(GradientState st) { + if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath; + mPathIsDirty = false; + float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f; RectF bounds = new RectF(mRect); @@ -362,9 +368,11 @@ public class GradientDrawable extends Drawable { float x = bounds.width() / 2.0f; float y = bounds.height() / 2.0f; - float thickness = bounds.width() / st.mThickness; + float thickness = st.mThickness != -1 ? + st.mThickness : bounds.width() / st.mThicknessRatio; // inner radius - float radius = bounds.width() / st.mInnerRadius; + float radius = st.mInnerRadius != -1 ? + st.mInnerRadius : bounds.width() / st.mInnerRadiusRatio; RectF innerBounds = new RectF(bounds); innerBounds.inset(x - radius, y - radius); @@ -372,27 +380,33 @@ public class GradientDrawable extends Drawable { bounds = new RectF(innerBounds); bounds.inset(-thickness, -thickness); - Path path = new Path(); + if (mRingPath == null) { + mRingPath = new Path(); + } else { + mRingPath.reset(); + } + + final Path ringPath = mRingPath; // arcTo treats the sweep angle mod 360, so check for that, since we // think 360 means draw the entire oval if (sweep < 360 && sweep > -360) { - path.setFillType(Path.FillType.EVEN_ODD); + ringPath.setFillType(Path.FillType.EVEN_ODD); // inner top - path.moveTo(x + radius, y); + ringPath.moveTo(x + radius, y); // outer top - path.lineTo(x + radius + thickness, y); + ringPath.lineTo(x + radius + thickness, y); // outer arc - path.arcTo(bounds, 0.0f, sweep, false); + ringPath.arcTo(bounds, 0.0f, sweep, false); // inner arc - path.arcTo(innerBounds, sweep, -sweep, false); - path.close(); + ringPath.arcTo(innerBounds, sweep, -sweep, false); + ringPath.close(); } else { // add the entire ovals - path.addOval(bounds, Path.Direction.CW); - path.addOval(innerBounds, Path.Direction.CCW); + ringPath.addOval(bounds, Path.Direction.CW); + ringPath.addOval(innerBounds, Path.Direction.CCW); } - return path; + return ringPath; } public void setColor(int argb) { @@ -430,6 +444,8 @@ public class GradientDrawable extends Drawable { @Override protected void onBoundsChange(Rect r) { super.onBoundsChange(r); + mRingPath = null; + mPathIsDirty = true; mRectIsDirty = true; } @@ -437,6 +453,7 @@ public class GradientDrawable extends Drawable { protected boolean onLevelChange(int level) { super.onLevelChange(level); mRectIsDirty = true; + mPathIsDirty = true; invalidateSelf(); return true; } @@ -462,8 +479,9 @@ public class GradientDrawable extends Drawable { mRect.set(bounds.left + inset, bounds.top + inset, bounds.right - inset, bounds.bottom - inset); - - if (st.mColors != null) { + + final int[] colors = st.mColors; + if (colors != null) { RectF r = mRect; float x0, x1, y0, y1; @@ -505,8 +523,7 @@ public class GradientDrawable extends Drawable { } mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1, - st.mColors, st.mPositions, - Shader.TileMode.CLAMP)); + colors, st.mPositions, Shader.TileMode.CLAMP)); } else if (st.mGradient == RADIAL_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; y0 = r.top + (r.bottom - r.top) * st.mCenterY; @@ -514,30 +531,38 @@ public class GradientDrawable extends Drawable { final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f; mFillPaint.setShader(new RadialGradient(x0, y0, - level * st.mGradientRadius, st.mColors, null, + level * st.mGradientRadius, colors, null, Shader.TileMode.CLAMP)); } else if (st.mGradient == SWEEP_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; y0 = r.top + (r.bottom - r.top) * st.mCenterY; - float[] positions = null; - int[] colors = st.mColors; + int[] tempColors = colors; + float[] tempPositions = null; if (st.mUseLevel) { - final int length = st.mColors.length; - colors = new int[length + 1]; - System.arraycopy(st.mColors, 0, colors, 0, length); - colors[length] = st.mColors[length - 1]; + tempColors = st.mTempColors; + final int length = colors.length; + if (tempColors == null || tempColors.length != length + 1) { + tempColors = st.mTempColors = new int[length + 1]; + } + System.arraycopy(colors, 0, tempColors, 0, length); + tempColors[length] = colors[length - 1]; + tempPositions = st.mTempPositions; final float fraction = 1.0f / (float) (length - 1); - positions = new float[length + 1]; + if (tempPositions == null || tempPositions.length != length + 1) { + tempPositions = st.mTempPositions = new float[length + 1]; + } + final float level = (float) getLevel() / 10000.0f; for (int i = 0; i < length; i++) { - positions[i] = i * fraction * level; + tempPositions[i] = i * fraction * level; } - positions[length] = 1.0f; + tempPositions[length] = 1.0f; + } - mFillPaint.setShader(new SweepGradient(x0, y0, colors, positions)); + mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions)); } } } @@ -561,10 +586,18 @@ public class GradientDrawable extends Drawable { com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE); if (shapeType == RING) { - st.mInnerRadius = a.getFloat( - com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f); - st.mThickness = a.getFloat( - com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f); + st.mInnerRadius = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawable_innerRadius, -1); + if (st.mInnerRadius == -1) { + st.mInnerRadiusRatio = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f); + } + st.mThickness = a.getDimensionPixelSize( + com.android.internal.R.styleable.GradientDrawable_thickness, -1); + if (st.mThickness == -1) { + st.mThicknessRatio = a.getFloat( + com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f); + } st.mUseLevelForShape = a.getBoolean( com.android.internal.R.styleable.GradientDrawable_useLevel, true); } @@ -808,6 +841,8 @@ public class GradientDrawable extends Drawable { public int mGradient = LINEAR_GRADIENT; public Orientation mOrientation; public int[] mColors; + public int[] mTempColors; // no need to copy + public float[] mTempPositions; // no need to copy public float[] mPositions; public boolean mHasSolidColor; public int mSolidColor; @@ -820,8 +855,10 @@ public class GradientDrawable extends Drawable { public Rect mPadding; public int mWidth = -1; public int mHeight = -1; - public float mInnerRadius; - public float mThickness; + public float mInnerRadiusRatio; + public float mThicknessRatio; + public int mInnerRadius; + public int mThickness; private float mCenterX = 0.5f; private float mCenterY = 0.5f; private float mGradientRadius = 0.5f; @@ -844,17 +881,25 @@ public class GradientDrawable extends Drawable { mGradient = state.mGradient; mOrientation = state.mOrientation; mColors = state.mColors.clone(); - mPositions = state.mPositions.clone(); + if (state.mPositions != null) { + mPositions = state.mPositions.clone(); + } mHasSolidColor = state.mHasSolidColor; mStrokeWidth = state.mStrokeWidth; mStrokeColor = state.mStrokeColor; mStrokeDashWidth = state.mStrokeDashWidth; mStrokeDashGap = state.mStrokeDashGap; mRadius = state.mRadius; - mRadiusArray = state.mRadiusArray.clone(); - mPadding = new Rect(state.mPadding); + if (state.mRadiusArray != null) { + mRadiusArray = state.mRadiusArray.clone(); + } + if (state.mPadding != null) { + mPadding = new Rect(state.mPadding); + } mWidth = state.mWidth; mHeight = state.mHeight; + mInnerRadiusRatio = state.mInnerRadiusRatio; + mThicknessRatio = state.mThicknessRatio; mInnerRadius = state.mInnerRadius; mThickness = state.mThickness; mCenterX = state.mCenterX; diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index e4b821a..cb16cb7 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -88,6 +88,13 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { canvas.restoreToCount(saveCount); } + /** + * Returns the drawable rotated by this RotateDrawable. + */ + public Drawable getDrawable() { + return mState.mDrawable; + } + @Override public int getChangingConfigurations() { return super.getChangingConfigurations() diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index b3322c9..7125ab1 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -63,6 +63,13 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { } } + /** + * Returns the drawable scaled by this ScaleDrawable. + */ + public Drawable getDrawable() { + return mScaleState.mDrawable; + } + private static float getPercent(TypedArray a, int name) { String s = a.getString(name); if (s != null) { diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 6bd54ba..7437f65 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -29,8 +29,27 @@ class AudioSystem { public: + enum stream_type { + DEFAULT =-1, + VOICE_CALL = 0, + SYSTEM = 1, + RING = 2, + MUSIC = 3, + ALARM = 4, + NOTIFICATION = 5, + BLUETOOTH_SCO = 6, + NUM_STREAM_TYPES + }; + + enum audio_output_type { + AUDIO_OUTPUT_DEFAULT =-1, + AUDIO_OUTPUT_HARDWARE = 0, + AUDIO_OUTPUT_A2DP = 1, + NUM_AUDIO_OUTPUT_TYPES + }; + enum audio_format { - DEFAULT = 0, + FORMAT_DEFAULT = 0, PCM_16_BIT, PCM_8_BIT, INVALID_FORMAT @@ -96,9 +115,11 @@ public: static float linearToLog(int volume); static int logToLinear(float volume); - static status_t getOutputSamplingRate(int* samplingRate); - static status_t getOutputFrameCount(int* frameCount); - static status_t getOutputLatency(uint32_t* latency); + static status_t getOutputSamplingRate(int* samplingRate, int stream = DEFAULT); + static status_t getOutputFrameCount(int* frameCount, int stream = DEFAULT); + static status_t getOutputLatency(uint32_t* latency, int stream = DEFAULT); + + static bool routedToA2dpOutput(int streamType); static status_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount, size_t* buffSize); @@ -117,9 +138,10 @@ private: virtual void binderDied(const wp<IBinder>& who); // IAudioFlingerClient - virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency); + virtual void a2dpEnabledChanged(bool enabled); }; + static int getOutput(int streamType); static sp<AudioFlingerClient> gAudioFlingerClient; @@ -128,9 +150,10 @@ private: static Mutex gLock; static sp<IAudioFlinger> gAudioFlinger; static audio_error_callback gAudioErrorCallback; - static int gOutSamplingRate; - static int gOutFrameCount; - static uint32_t gOutLatency; + static int gOutSamplingRate[NUM_AUDIO_OUTPUT_TYPES]; + static int gOutFrameCount[NUM_AUDIO_OUTPUT_TYPES]; + static uint32_t gOutLatency[NUM_AUDIO_OUTPUT_TYPES]; + static bool gA2dpEnabled; static size_t gInBuffSize; // previous parameters for recording buffer size queries diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 5b2bab9..659f5f8 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -42,19 +42,6 @@ class audio_track_cblk_t; class AudioTrack { public: - - enum stream_type { - DEFAULT =-1, - VOICE_CALL = 0, - SYSTEM = 1, - RING = 2, - MUSIC = 3, - ALARM = 4, - NOTIFICATION = 5, - BLUETOOTH_SCO = 6, - NUM_STREAM_TYPES - }; - enum channel_index { MONO = 0, LEFT = 0, @@ -128,7 +115,7 @@ public: * Parameters: * * streamType: Select the type of audio stream this track is attached to - * (e.g. AudioTrack::MUSIC). + * (e.g. AudioSystem::MUSIC). * sampleRate: Track sampling rate in Hz. * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed * 16 bits per sample). diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index df601d7..6f13fe0 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -65,11 +65,11 @@ public: /* query the audio hardware state. This state never changes, * and therefore can be cached. */ - virtual uint32_t sampleRate() const = 0; - virtual int channelCount() const = 0; - virtual int format() const = 0; - virtual size_t frameCount() const = 0; - virtual uint32_t latency() const = 0; + virtual uint32_t sampleRate(int output) const = 0; + virtual int channelCount(int output) const = 0; + virtual int format(int output) const = 0; + virtual size_t frameCount(int output) const = 0; + virtual uint32_t latency(int output) const = 0; /* set/get the audio hardware state. This will probably be used by * the preference panel, mostly. @@ -117,6 +117,9 @@ public: // force AudioFlinger thread out of standby virtual void wakeUp() = 0; + + // is A2DP output enabled + virtual bool isA2dpEnabled() const = 0; }; diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h index 10c3e0f..c3deb0b 100644 --- a/include/media/IAudioFlingerClient.h +++ b/include/media/IAudioFlingerClient.h @@ -32,7 +32,7 @@ public: DECLARE_META_INTERFACE(AudioFlingerClient); // Notifies a change of audio output from/to hardware to/from A2DP. - virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) = 0; + virtual void a2dpEnabledChanged(bool enabled) = 0; }; diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h index 49e45d1..0dff84e 100644 --- a/include/media/IMediaRecorder.h +++ b/include/media/IMediaRecorder.h @@ -38,6 +38,7 @@ public: virtual status_t setVideoEncoder(int ve) = 0; virtual status_t setAudioEncoder(int ae) = 0; virtual status_t setOutputFile(const char* path) = 0; + virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0; virtual status_t setVideoSize(int width, int height) = 0; virtual status_t setVideoFrameRate(int frames_per_second) = 0; virtual status_t prepare() = 0; diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h index 5fee0d6..f795d04 100644 --- a/include/media/PVMediaRecorder.h +++ b/include/media/PVMediaRecorder.h @@ -43,6 +43,7 @@ public: status_t setCamera(const sp<ICamera>& camera); status_t setPreviewSurface(const sp<ISurface>& surface); status_t setOutputFile(const char *path); + status_t setOutputFile(int fd, int64_t offset, int64_t length); status_t prepare(); status_t start(); status_t stop(); diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index a901d32..436e8f1 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -102,6 +102,7 @@ public: status_t setVideoEncoder(int ve); status_t setAudioEncoder(int ae); status_t setOutputFile(const char* path); + status_t setOutputFile(int fd, int64_t offset, int64_t length); status_t setVideoSize(int width, int height); status_t setVideoFrameRate(int frames_per_second); status_t prepare(); diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h index 1c8043d..87b320f 100644 --- a/include/ui/ISurface.h +++ b/include/ui/ISurface.h @@ -25,6 +25,8 @@ #include <utils/RefBase.h> #include <ui/PixelFormat.h> +#include <hardware/hardware.h> + namespace android { typedef int32_t SurfaceID; @@ -49,16 +51,8 @@ public: class BufferHeap { public: enum { - /* flip source image horizontally */ - FLIP_H = 0x01, - /* flip source image vertically */ - FLIP_V = 0x02, /* rotate source image 90 degrees */ - ROT_90 = 0x04, - /* rotate source image 180 degrees */ - ROT_180 = 0x03, - /* rotate source image 270 degrees */ - ROT_270 = 0x07, + ROT_90 = HAL_TRANSFORM_ROT_90, }; BufferHeap(); diff --git a/include/utils/logger.h b/include/utils/logger.h deleted file mode 100644 index 3a08019..0000000 --- a/include/utils/logger.h +++ /dev/null @@ -1,46 +0,0 @@ -/* utils/logger.h -** -** Copyright 2007, The Android Open Source Project -** -** This file is dual licensed. It may be redistributed and/or modified -** under the terms of the Apache 2.0 License OR version 2 of the GNU -** General Public License. -*/ - -#ifndef _UTILS_LOGGER_H -#define _UTILS_LOGGER_H - -#include <stdint.h> - -struct logger_entry { - uint16_t len; /* length of the payload */ - uint16_t __pad; /* no matter what, we get 2 bytes of padding */ - int32_t pid; /* generating process's pid */ - int32_t tid; /* generating process's tid */ - int32_t sec; /* seconds since Epoch */ - int32_t nsec; /* nanoseconds */ - char msg[0]; /* the entry's payload */ -}; - -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" - -#define LOGGER_ENTRY_MAX_LEN (4*1024) -#define LOGGER_ENTRY_MAX_PAYLOAD \ - (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) - -#ifdef HAVE_IOCTL - -#include <sys/ioctl.h> - -#define __LOGGERIO 0xAE - -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ - -#endif // HAVE_IOCTL - -#endif /* _UTILS_LOGGER_H */ diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 017a298..d347f14 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -47,6 +47,15 @@ #include "A2dpAudioInterface.h" #endif +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- + namespace android { //static const nsecs_t kStandbyTimeInNsecs = seconds(3); @@ -59,6 +68,13 @@ static const float MAX_GAIN = 4096.0f; static const int8_t kMaxTrackRetries = 50; static const int8_t kMaxTrackStartupRetries = 50; +static const int kStartSleepTime = 30000; +static const int kStopSleepTime = 30000; + +// Maximum number of pending buffers allocated by OutputTrack::write() +static const uint8_t kMaxOutputTrackBuffers = 5; + + #define AUDIOFLINGER_SECURITY_ENABLED 1 // ---------------------------------------------------------------------------- @@ -98,13 +114,10 @@ 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), mRequestedOutput(0), mAudioRecordThread(0), - mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), - mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), - mInWrite(false), mA2dpDisableCount(0), mA2dpSuppressed(false) + : BnAudioFlinger(), + mAudioHardware(0), mA2dpAudioInterface(0), + mA2dpEnabled(false), mA2dpEnabledReq(false), + mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false) { mHardwareStatus = AUDIO_HW_IDLE; mAudioHardware = AudioHardwareInterface::create(); @@ -113,42 +126,43 @@ AudioFlinger::AudioFlinger() // open 16-bit output stream for s/w mixer mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; status_t status; - mHardwareOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); mHardwareStatus = AUDIO_HW_IDLE; - if (mHardwareOutput) { - mHardwareAudioMixer = new AudioMixer(getOutputFrameCount(mHardwareOutput), mHardwareOutput->sampleRate()); - mRequestedOutput = mHardwareOutput; - doSetOutput(mHardwareOutput); - - // FIXME - this should come from settings - setMasterVolume(1.0f); - setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); - setMode(AudioSystem::MODE_NORMAL); - mMasterMute = false; + if (hwOutput) { + mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); } else { - LOGE("Failed to initialize output stream, status: %d", status); + LOGE("Failed to initialize hardware output stream, status: %d", status); } #ifdef WITH_A2DP // Create A2DP interface mA2dpAudioInterface = new A2dpAudioInterface(); - mA2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); - mA2dpAudioMixer = new AudioMixer(getOutputFrameCount(mA2dpOutput), mA2dpOutput->sampleRate()); - - // create a buffer big enough for both hardware and A2DP audio output. - size_t hwFrameCount = getOutputFrameCount(mHardwareOutput); - size_t a2dpFrameCount = getOutputFrameCount(mA2dpOutput); - size_t frameCount = (hwFrameCount > a2dpFrameCount ? hwFrameCount : a2dpFrameCount); -#else - size_t frameCount = getOutputFrameCount(mHardwareOutput); + AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + if (a2dpOutput) { + mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); + if (hwOutput) { + uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); + MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, + hwOutput->sampleRate(), + AudioSystem::PCM_16_BIT, + hwOutput->channelCount(), + frameCount); + mHardwareMixerThread->setOuputTrack(a2dpOutTrack); + } + } else { + LOGE("Failed to initialize A2DP output stream, status: %d", status); + } #endif - // FIXME - Current mixer implementation only supports stereo output: Always - // Allocate a stereo buffer even if HW output is mono. - mMixBuffer = new int16_t[frameCount * 2]; - memset(mMixBuffer, 0, frameCount * 2 * sizeof(int16_t)); - + + // FIXME - this should come from settings + setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); + + setMasterVolume(1.0f); + setMasterMute(false); + // Start record thread mAudioRecordThread = new AudioRecordThread(mAudioHardware); if (mAudioRecordThread != 0) { @@ -162,7 +176,7 @@ AudioFlinger::AudioFlinger() property_get("ro.audio.silent", value, "0"); if (atoi(value)) { LOGD("Silence is golden"); - mMasterMute = true; + setMasterMute(true); } } @@ -172,63 +186,35 @@ AudioFlinger::~AudioFlinger() mAudioRecordThread->exit(); mAudioRecordThread.clear(); } + mHardwareMixerThread.clear(); delete mAudioHardware; // deleting mA2dpAudioInterface also deletes mA2dpOutput; +#ifdef WITH_A2DP + mA2dpMixerThread.clear(); delete mA2dpAudioInterface; - delete [] mMixBuffer; - delete mHardwareAudioMixer; - delete mA2dpAudioMixer; -} - -void AudioFlinger::setOutput(AudioStreamOut* output) -{ - mRequestedOutput = output; - mWaitWorkCV.broadcast(); -} - -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; - notifyOutputChange_l(); +#endif } -size_t AudioFlinger::getOutputFrameCount(AudioStreamOut* output) -{ - return output->bufferSize() / output->channelCount() / sizeof(int16_t); -} #ifdef WITH_A2DP -bool AudioFlinger::streamDisablesA2dp(int streamType) +void AudioFlinger::setA2dpEnabled(bool enable) { - return (streamType == AudioTrack::SYSTEM || - streamType == AudioTrack::RING || - streamType == AudioTrack::ALARM || - streamType == AudioTrack::VOICE_CALL || - streamType == AudioTrack::BLUETOOTH_SCO || - streamType == AudioTrack::NOTIFICATION); + LOGV_IF(enable, "set output to A2DP\n"); + LOGV_IF(!enable, "set output to hardware audio\n"); + + mA2dpEnabledReq = enable; + mA2dpMixerThread->wakeUp(); } +#endif // WITH_A2DP -void AudioFlinger::setA2dpEnabled(bool enable) +bool AudioFlinger::streamForcedToSpeaker(int streamType) { - if (enable) { - LOGD("set output to A2DP\n"); - setOutput(mA2dpOutput); - } else { - LOGD("set output to hardware audio\n"); - setOutput(mHardwareOutput); - } + // NOTE that streams listed here must not be routed to A2DP by default: + // AudioSystem::routedToA2dpOutput(streamType) == false + return (streamType == AudioSystem::RING || + streamType == AudioSystem::ALARM || + streamType == AudioSystem::NOTIFICATION); } -#endif // WITH_A2DP status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) { @@ -251,40 +237,6 @@ status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) return NO_ERROR; } -status_t AudioFlinger::dumpTracks(int fd, const Vector<String16>& args) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - result.append("Tracks:\n"); - result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); - for (size_t i = 0; i < mTracks.size(); ++i) { - wp<Track> wTrack = mTracks[i]; - if (wTrack != 0) { - sp<Track> track = wTrack.promote(); - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); - } - } - } - - result.append("Active Tracks:\n"); - result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); - for (size_t i = 0; i < mActiveTracks.size(); ++i) { - wp<Track> wTrack = mTracks[i]; - if (wTrack != 0) { - sp<Track> track = wTrack.promote(); - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); - } - } - } - write(fd, result.string(), result.size()); - return NO_ERROR; -} status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) { @@ -292,18 +244,6 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer()->trackNames()); - result.append(buffer); - snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); - result.append(buffer); - snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); - result.append(buffer); - snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); - result.append(buffer); - snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); - result.append(buffer); - snprintf(buffer, SIZE, "standby: %d\n", mStandby); - result.append(buffer); snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus); result.append(buffer); write(fd, result.string(), result.size()); @@ -332,8 +272,12 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) AutoMutex lock(&mLock); dumpClients(fd, args); - dumpTracks(fd, args); dumpInternals(fd, args); + mHardwareMixerThread->dump(fd, args); +#ifdef WITH_A2DP + mA2dpMixerThread->dump(fd, args); +#endif + if (mAudioHardware) { mAudioHardware->dumpState(fd, args); } @@ -341,226 +285,9 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) return NO_ERROR; } -// Thread virtuals -bool AudioFlinger::threadLoop() -{ - unsigned long sleepTime = kBufferRecoveryInUsecs; - int16_t* curBuf = mMixBuffer; - Vector< sp<Track> > tracksToRemove; - size_t enabledTracks = 0; - nsecs_t standbyTime = systemTime(); - nsecs_t outputSwitchStandbyTime = 0; - - do { - enabledTracks = 0; - { // scope for the mLock - - Mutex::Autolock _l(mLock); - const SortedVector< wp<Track> >& activeTracks = mActiveTracks; - - // put audio hardware into standby after short delay - if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { - // wait until we have something to do... - LOGV("Audio hardware entering standby\n"); - mHardwareStatus = AUDIO_HW_STANDBY; - if (!mStandby) { - mOutput->standby(); - mStandby = true; - } - if (outputSwitchStandbyTime) { - AudioStreamOut *output = (mOutput == mHardwareOutput) ? mA2dpOutput : mHardwareOutput; - output->standby(); - outputSwitchStandbyTime = 0; - } - mHardwareStatus = AUDIO_HW_IDLE; - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - mWaitWorkCV.wait(mLock); - LOGV("Audio hardware exiting standby\n"); - standbyTime = systemTime() + kStandbyTimeInNsecs; - continue; - } - - // check for change in output - if (mRequestedOutput != mOutput) { - - // put current output into standby mode - if (mOutput) { - outputSwitchStandbyTime = systemTime() + milliseconds(mOutput->latency()); - } - - // change output - doSetOutput(mRequestedOutput); - } - if (outputSwitchStandbyTime && systemTime() > outputSwitchStandbyTime) { - AudioStreamOut *output = (mOutput == mHardwareOutput) ? mA2dpOutput : mHardwareOutput; - output->standby(); - outputSwitchStandbyTime = 0; - } - - // find out which tracks need to be processed - size_t count = activeTracks.size(); - for (size_t i=0 ; i<count ; i++) { - sp<Track> t = activeTracks[i].promote(); - if (t == 0) continue; - - Track* const track = t.get(); - audio_track_cblk_t* cblk = track->cblk(); - - // The first time a track is added we wait - // for all its buffers to be filled before processing it - mAudioMixer->setActiveTrack(track->name()); - if (cblk->framesReady() && (track->isReady() || track->isStopped()) && - !track->isPaused()) - { - //LOGD("u=%08x, s=%08x [OK]", u, s); - - // compute volume for this track - int16_t left, right; - if (track->isMuted() || mMasterMute || track->isPausing()) { - left = right = 0; - if (track->isPausing()) { - LOGV("paused(%d)", track->name()); - track->setPaused(); - } - } else { - float typeVolume = mStreamTypes[track->type()].volume; - float v = mMasterVolume * typeVolume; - float v_clamped = v * cblk->volume[0]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - left = int16_t(v_clamped); - v_clamped = v * cblk->volume[1]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - right = int16_t(v_clamped); - } - - // XXX: these things DON'T need to be done each time - mAudioMixer->setBufferProvider(track); - mAudioMixer->enable(AudioMixer::MIXING); - - int param; - if ( track->mFillingUpStatus == Track::FS_FILLED) { - // no ramp for the first volume setting - track->mFillingUpStatus = Track::FS_ACTIVE; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; - param = AudioMixer::RAMP_VOLUME; - } else { - param = AudioMixer::VOLUME; - } - } else { - param = AudioMixer::RAMP_VOLUME; - } - mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); - mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); - mAudioMixer->setParameter( - AudioMixer::TRACK, - AudioMixer::FORMAT, track->format()); - mAudioMixer->setParameter( - AudioMixer::TRACK, - AudioMixer::CHANNEL_COUNT, track->channelCount()); - mAudioMixer->setParameter( - AudioMixer::RESAMPLE, - AudioMixer::SAMPLE_RATE, - int(cblk->sampleRate)); - - // reset retry count - track->mRetryCount = kMaxTrackRetries; - enabledTracks++; - } else { - //LOGD("u=%08x, s=%08x [NOT READY]", u, s); - if (track->isStopped()) { - track->reset(); - } - if (track->isTerminated() || track->isStopped() || track->isPaused()) { - // We have consumed all the buffers of this track. - // Remove it from the list of active tracks. - LOGV("remove(%d) from active list", track->name()); - tracksToRemove.add(track); - } else { - // No buffers for this track. Give it a few chances to - // fill a buffer, then remove it from active list. - if (--(track->mRetryCount) <= 0) { - LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); - tracksToRemove.add(track); - } - } - // LOGV("disable(%d)", track->name()); - mAudioMixer->disable(AudioMixer::MIXING); - } - } - - // remove all the tracks that need to be... - count = tracksToRemove.size(); - if (UNLIKELY(count)) { - for (size_t i=0 ; i<count ; i++) { - const sp<Track>& track = tracksToRemove[i]; - removeActiveTrack(track); - if (track->isTerminated()) { - mTracks.remove(track); - deleteTrackName(track->mName); - } - } - } - } - if (LIKELY(enabledTracks)) { - // mix buffers... - mAudioMixer->process(curBuf); - - // output audio to hardware - mLastWriteTime = systemTime(); - mInWrite = true; - 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(mFrameCount) / mSampleRate * 2; - if (delta > maxPeriod) { - LOGW("write blocked for %llu msecs", ns2ms(delta)); - mNumDelayedWrites++; - } - sleepTime = kBufferRecoveryInUsecs; - } else { - // There was nothing to mix this round, which means all - // active tracks were late. Sleep a little bit to give - // them another chance. If we're too late, the audio - // hardware will zero-fill for us. -// LOGV("no buffers - usleep(%lu)", sleepTime); - usleep(sleepTime); - if (sleepTime < kMaxBufferRecoveryInUsecs) { - sleepTime += kBufferRecoveryInUsecs; - } - } - - // finally let go of all our tracks, without the lock held - // since we can't guarantee the destructors won't acquire that - // same lock. - tracksToRemove.clear(); - } while (true); - - return false; -} - -status_t AudioFlinger::readyToRun() -{ - if (mSampleRate == 0) { - LOGE("No working audio driver found."); - return NO_INIT; - } - LOGI("AudioFlinger's main thread ready to run."); - return NO_ERROR; -} +// IAudioFlinger interface -void AudioFlinger::onFirstRef() -{ - run("AudioFlinger", ANDROID_PRIORITY_URGENT_AUDIO); -} -// IAudioFlinger interface sp<IAudioTrack> AudioFlinger::createTrack( pid_t pid, int streamType, @@ -572,34 +299,21 @@ sp<IAudioTrack> AudioFlinger::createTrack( const sp<IMemory>& sharedBuffer, status_t *status) { - sp<Track> track; + sp<MixerThread::Track> track; sp<TrackHandle> trackHandle; sp<Client> client; wp<Client> wclient; status_t lStatus; - if (streamType >= AudioTrack::NUM_STREAM_TYPES) { + if (streamType >= AudioSystem::NUM_STREAM_TYPES) { LOGE("invalid stream type"); lStatus = BAD_VALUE; goto Exit; } - // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { - LOGE("Sample rate out of range: %d", sampleRate); - lStatus = BAD_VALUE; - goto Exit; - } - { Mutex::Autolock _l(mLock); - if (mSampleRate == 0) { - LOGE("Audio driver not initialized."); - lStatus = NO_INIT; - goto Exit; - } - wclient = mClients.valueFor(pid); if (wclient != NULL) { @@ -608,13 +322,20 @@ sp<IAudioTrack> AudioFlinger::createTrack( client = new Client(this, pid); mClients.add(pid, client); } - - track = new Track(this, client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer); - mTracks.add(track); - trackHandle = new TrackHandle(track); - - lStatus = NO_ERROR; +#ifdef WITH_A2DP + if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { + track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } else +#endif + { + track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } + if (track != NULL) { + trackHandle = new TrackHandle(track); + lStatus = NO_ERROR; + } } Exit: @@ -624,34 +345,54 @@ Exit: return trackHandle; } -uint32_t AudioFlinger::sampleRate() const +uint32_t AudioFlinger::sampleRate(int output) const { - return mSampleRate; +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->sampleRate(); + } +#endif + return mHardwareMixerThread->sampleRate(); } -int AudioFlinger::channelCount() const +int AudioFlinger::channelCount(int output) const { - return mChannelCount; +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->channelCount(); + } +#endif + return mHardwareMixerThread->channelCount(); } -int AudioFlinger::format() const +int AudioFlinger::format(int output) const { - return mFormat; +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->format(); + } +#endif + return mHardwareMixerThread->format(); } -size_t AudioFlinger::frameCount() const +size_t AudioFlinger::frameCount(int output) const { - return mFrameCount; +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->frameCount(); + } +#endif + return mHardwareMixerThread->frameCount(); } -uint32_t AudioFlinger::latency() const +uint32_t AudioFlinger::latency(int output) const { - if (mOutput) { - return mOutput->latency(); - } - else { - return 0; - } +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->latency(); + } +#endif + return mHardwareMixerThread->latency(); } status_t AudioFlinger::setMasterVolume(float value) @@ -665,12 +406,14 @@ status_t AudioFlinger::setMasterVolume(float value) AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; if (mAudioHardware->setMasterVolume(value) == NO_ERROR) { - mMasterVolume = 1.0f; - } - else { - mMasterVolume = value; + value = 1.0f; } mHardwareStatus = AUDIO_HW_IDLE; + mHardwareMixerThread->setMasterVolume(value); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterVolume(value); +#endif + return NO_ERROR; } @@ -688,20 +431,17 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) } #ifdef WITH_A2DP - LOGD("setRouting %d %d %d\n", mode, routes, mask); + LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid()); if (mode == AudioSystem::MODE_NORMAL && (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { AutoMutex lock(&mLock); bool enableA2dp = false; if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { - if (mA2dpDisableCount > 0) - mA2dpSuppressed = true; - else - enableA2dp = true; + enableA2dp = true; } setA2dpEnabled(enableA2dp); - LOGD("setOutput done\n"); + LOGV("setOutput done\n"); } #endif @@ -714,6 +454,12 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) err = mAudioHardware->getRouting(mode, &r); if (err == NO_ERROR) { r = (r & ~mask) | (routes & mask); + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + mSavedRoute = r; + r |= mForcedRoute; + LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); + } mHardwareStatus = AUDIO_HW_SET_ROUTING; err = mAudioHardware->setRouting(mode, r); } @@ -726,9 +472,14 @@ uint32_t AudioFlinger::getRouting(int mode) const { uint32_t routes = 0; if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { - mHardwareStatus = AUDIO_HW_GET_ROUTING; - mAudioHardware->getRouting(mode, &routes); - mHardwareStatus = AUDIO_HW_IDLE; + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + routes = mSavedRoute; + } else { + mHardwareStatus = AUDIO_HW_GET_ROUTING; + mAudioHardware->getRouting(mode, &routes); + mHardwareStatus = AUDIO_HW_IDLE; + } } else { LOGW("Illegal value: getRouting(%d)", mode); } @@ -791,19 +542,21 @@ status_t AudioFlinger::setMasterMute(bool muted) if (!settingsAllowed()) { return PERMISSION_DENIED; } - - mMasterMute = muted; + mHardwareMixerThread->setMasterMute(muted); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterMute(muted); +#endif return NO_ERROR; } float AudioFlinger::masterVolume() const { - return mMasterVolume; + return mHardwareMixerThread->masterVolume(); } bool AudioFlinger::masterMute() const { - return mMasterMute; + return mHardwareMixerThread->masterMute(); } status_t AudioFlinger::setStreamVolume(int stream, float value) @@ -813,14 +566,25 @@ status_t AudioFlinger::setStreamVolume(int stream, float value) return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return BAD_VALUE; } - mStreamTypes[stream].volume = value; + mHardwareMixerThread->setStreamVolume(stream, value); +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamVolume(stream, value); +#endif + status_t ret = NO_ERROR; - if (stream == AudioTrack::VOICE_CALL || - stream == AudioTrack::BLUETOOTH_SCO) { + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::BLUETOOTH_SCO) { + + if (stream == AudioSystem::VOICE_CALL) { + value = (float)AudioSystem::logToLinear(value)/100.0f; + } else { // (type == AudioSystem::BLUETOOTH_SCO) + value = 1.0f; + } + AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_VOICE_VOLUME; ret = mAudioHardware->setVoiceVolume(value); @@ -837,59 +601,58 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted) return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return BAD_VALUE; } + #ifdef WITH_A2DP - if (stream == AudioTrack::MUSIC) + mA2dpMixerThread->setStreamMute(stream, muted); +#endif + if (stream == AudioSystem::MUSIC) { - AutoMutex lock(&mLock); - if (mA2dpDisableCount > 0) + AutoMutex lock(&mHardwareLock); + if (mForcedRoute != 0) mMusicMuteSaved = muted; else - mStreamTypes[stream].mute = muted; + mHardwareMixerThread->setStreamMute(stream, muted); } else { - mStreamTypes[stream].mute = muted; + mHardwareMixerThread->setStreamMute(stream, muted); } -#else - mStreamTypes[stream].mute = muted; -#endif + + + return NO_ERROR; } float AudioFlinger::streamVolume(int stream) const { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return 0.0f; } - return mStreamTypes[stream].volume; + return mHardwareMixerThread->streamVolume(stream); } bool AudioFlinger::streamMute(int stream) const { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return true; } -#ifdef WITH_A2DP - if (stream == AudioTrack::MUSIC && mA2dpDisableCount > 0) + + if (stream == AudioSystem::MUSIC && mForcedRoute != 0) { return mMusicMuteSaved; } -#endif - return mStreamTypes[stream].mute; + return mHardwareMixerThread->streamMute(stream); } bool AudioFlinger::isMusicActive() const { - size_t count = mActiveTracks.size(); - for (size_t i = 0 ; i < count ; ++i) { - sp<Track> t = mActiveTracks[i].promote(); - if (t == 0) continue; - Track* const track = t.get(); - if (t->mStreamType == AudioTrack::MUSIC) - return true; - } - return false; + #ifdef WITH_A2DP + if (isA2dpEnabled()) { + return mA2dpMixerThread->isMusicActive(); + } + #endif + return mHardwareMixerThread->isMusicActive(); } status_t AudioFlinger::setParameter(const char* key, const char* value) @@ -897,6 +660,8 @@ status_t AudioFlinger::setParameter(const char* key, const char* value) status_t result, result2; AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; + + LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); result = mAudioHardware->setParameter(key, value); if (mA2dpAudioInterface) { result2 = mA2dpAudioInterface->setParameter(key, value); @@ -907,9 +672,15 @@ status_t AudioFlinger::setParameter(const char* key, const char* value) return result; } +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) { + + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); sp<IBinder> binder = client->asBinder(); @@ -917,21 +688,13 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) LOGV("Adding notification client %p", binder.get()); binder->linkToDeath(this); mNotificationClients.add(binder); + client->a2dpEnabledChanged(isA2dpEnabled()); } } - -size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) -{ - return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); -} - -void AudioFlinger::wakeUp() -{ - mWaitWorkCV.broadcast(); -} - void AudioFlinger::binderDied(const wp<IBinder>& who) { + + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); IBinder *binder = who.unsafe_get(); @@ -945,29 +708,680 @@ void AudioFlinger::binderDied(const wp<IBinder>& who) { } } -// must be called with mLock held -void AudioFlinger::notifyOutputChange_l() +void AudioFlinger::handleOutputSwitch() { - size_t size = mNotificationClients.size(); - uint32_t latency = mOutput->latency(); - for (size_t i = 0; i < size; i++) { - sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); - if (binder != NULL) { - LOGV("Notifying output change to client %p", binder.get()); - sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); - client->audioOutputChanged(mFrameCount, mSampleRate, latency); + if (mA2dpEnabled != mA2dpEnabledReq) + { + Mutex::Autolock _l(mLock); + + if (mA2dpEnabled != mA2dpEnabledReq) + { + mA2dpEnabled = mA2dpEnabledReq; + SortedVector < sp<MixerThread::Track> > tracks; + SortedVector < wp<MixerThread::Track> > activeTracks; + + // We hold mA2dpMixerThread mLock already + Mutex::Autolock _l(mHardwareMixerThread->mLock); + + // Transfer tracks playing on MUSIC stream from one mixer to the other + if (mA2dpEnabled) { + mHardwareMixerThread->getTracks(tracks, activeTracks); + mA2dpMixerThread->putTracks(tracks, activeTracks); + } else { + mA2dpMixerThread->getTracks(tracks, activeTracks); + mHardwareMixerThread->putTracks(tracks, activeTracks); + } + + // Notify AudioSystem of the A2DP activation/deactivation + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); + if (binder != NULL) { + LOGV("Notifying output change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->a2dpEnabledChanged(mA2dpEnabled); + } + } + + mHardwareMixerThread->wakeUp(); } } } void AudioFlinger::removeClient(pid_t pid) { + LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); mClients.removeItem(pid); } -status_t AudioFlinger::addTrack(const sp<Track>& track) +void AudioFlinger::wakeUp() +{ + mHardwareMixerThread->wakeUp(); +#ifdef WITH_A2DP + mA2dpMixerThread->wakeUp(); +#endif // WITH_A2DP +} + +bool AudioFlinger::isA2dpEnabled() const +{ + return mA2dpEnabledReq; +} + +void AudioFlinger::handleForcedSpeakerRoute(int command) +{ + switch(command) { + case ACTIVE_TRACK_ADDED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount++ == 0) { + mRouteRestoreTime = 0; + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(0); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(mHardwareMixerThread->masterVolume()); + mHardwareStatus = AUDIO_HW_IDLE; + } + mForcedRoute = AudioSystem::ROUTE_SPEAKER; + } + LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); + } + break; + case ACTIVE_TRACK_REMOVED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount > 0){ + if (--mForcedSpeakerCount == 0) { + mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); + } + LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); + } else { + LOGE("mForcedSpeakerCount is already zero"); + } + } + break; + case CHECK_ROUTE_RESTORE_TIME: + case FORCE_ROUTE_RESTORE: + if (mRouteRestoreTime) { + AutoMutex lock(mHardwareLock); + if (mRouteRestoreTime && + (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); + mForcedRoute = 0; + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); + mHardwareStatus = AUDIO_HW_IDLE; + LOGV("Route forced to Speaker OFF %08x", mSavedRoute); + } + mRouteRestoreTime = 0; + } + } + break; + } +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType) + : Thread(false), + mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), + mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), + mInWrite(false) +{ + 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 = output->bufferSize() / output->channelCount() / sizeof(int16_t); + mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); + + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete [] mMixBuffer; + delete mAudioMixer; +} + +status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + + snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mActiveTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); + result.append(buffer); + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + result.append(buffer); + snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); + result.append(buffer); + snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); + result.append(buffer); + snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); + result.append(buffer); + snprintf(buffer, SIZE, "standby: %d\n", mStandby); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// Thread virtuals +bool AudioFlinger::MixerThread::threadLoop() { + unsigned long sleepTime = kBufferRecoveryInUsecs; + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; + +#ifdef WITH_A2DP + bool outputTrackActive = false; +#endif + + do { + enabledTracks = 0; + { // scope for the mLock + + Mutex::Autolock _l(mLock); + +#ifdef WITH_A2DP + if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) { + mAudioFlinger->handleOutputSwitch(); + } + if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } + } +#endif + + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; + + // put audio hardware into standby after short delay + if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { + // wait until we have something to do... + LOGV("Audio hardware entering standby, output %d\n", mOutputType); +// mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY; + if (!mStandby) { + mOutput->standby(); + mStandby = true; + } + +#ifdef WITH_A2DP + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } +#endif + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); + } +// mHardwareStatus = AUDIO_HW_IDLE; + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mWaitWorkCV.wait(mLock); + LOGV("Audio hardware exiting standby, output %d\n", mOutputType); + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + + // Forced route to speaker is handled by hardware mixer thread + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); + } + + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || mMasterMute || track->isPausing()) { + left = right = 0; + if (track->isPausing()) { + LOGV("paused(%d)", track->name()); + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param; + if ( track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } else { + param = AudioMixer::VOLUME; + } + } else { + param = AudioMixer::RAMP_VOLUME; + } + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + LOGV("remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } + } + // LOGV("disable(%d)", track->name()); + mAudioMixer->disable(AudioMixer::MIXING); + } + } + + // remove all the tracks that need to be... + count = tracksToRemove.size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i<count ; i++) { + const sp<Track>& track = tracksToRemove[i]; + removeActiveTrack(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName(track->mName); + } + } + } + } + + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (!outputTrackActive) { + LOGV("starting output track in mixer for output %d", mOutputType); + mOutputTrack->start(); + outputTrackActive = true; + } + mOutputTrack->write(curBuf, mFrameCount); + } +#endif + + // output audio to hardware + mLastWriteTime = systemTime(); + mInWrite = true; + mOutput->write(curBuf, mixBufferSize); + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + nsecs_t delta = temp - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs", ns2ms(delta)); + mNumDelayedWrites++; + } + sleepTime = kBufferRecoveryInUsecs; + } else { +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->write(curBuf, 0); + if (mOutputTrack->bufferQueueEmpty()) { + mOutputTrack->stop(); + outputTrackActive = false; + } else { + standbyTime = systemTime() + kStandbyTimeInNsecs; + } + } + } +#endif + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, the audio + // hardware will zero-fill for us. +// LOGV("no buffers - usleep(%lu)", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + } while (true); + + return false; +} + +status_t AudioFlinger::MixerThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + + +sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<Track> track; + status_t lStatus; + + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + mTracks.add(track); + lStatus = NO_ERROR; + } + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +void AudioFlinger::MixerThread::getTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + size_t size = mTracks.size(); + LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); + for (size_t i = 0; i < size; i++) { + sp<Track> t = mTracks[i]; + if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + tracks.add(t); + int j = mActiveTracks.indexOf(t); + if (j >= 0) { + t = mActiveTracks[j].promote(); + if (t != NULL) { + activeTracks.add(t); + } + } + } + } + + size = activeTracks.size(); + for (size_t i = 0; i < size; i++) { + removeActiveTrack(activeTracks[i]); + } + + size = tracks.size(); + for (size_t i = 0; i < size; i++) { + sp<Track> t = tracks[i]; + mTracks.remove(t); + deleteTrackName(t->name()); + } +} + +void AudioFlinger::MixerThread::putTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + + LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); + + size_t size = tracks.size(); + for (size_t i = 0; i < size ; i++) { + sp<Track> t = tracks[i]; + int name = getTrackName(); + + if (name < 0) return; + + t->mName = name; + t->mMixerThread = this; + mTracks.add(t); + + int j = activeTracks.indexOf(t); + if (j >= 0) { + addActiveTrack(t); + } + } +} + +uint32_t AudioFlinger::MixerThread::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::MixerThread::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::MixerThread::format() const +{ + return mFormat; +} + +size_t AudioFlinger::MixerThread::frameCount() const +{ + return mFrameCount; +} + +uint32_t AudioFlinger::MixerThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::MixerThread::setMasterVolume(float value) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setMasterMute(bool muted) +{ + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::MixerThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) +{ + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::MixerThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::MixerThread::isMusicActive() const +{ + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp<Track> t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->mStreamType == AudioSystem::MUSIC) + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::addTrack(const sp<Track>& track) +{ + status_t status = ALREADY_EXISTS; Mutex::Autolock _l(mLock); // here the track could be either new, or restarted @@ -981,9 +1395,6 @@ status_t AudioFlinger::addTrack(const sp<Track>& track) } // set retry count for buffer fill track->mRetryCount = kMaxTrackStartupRetries; - LOGV("mWaitWorkCV.broadcast"); - mWaitWorkCV.broadcast(); - if (mActiveTracks.indexOf(track) < 0) { // the track is newly added, make sure it fills up all its // buffers before playing. This is to ensure the client will @@ -991,12 +1402,16 @@ status_t AudioFlinger::addTrack(const sp<Track>& track) track->mFillingUpStatus = Track::FS_FILLING; track->mResetDone = false; addActiveTrack(track); - return NO_ERROR; + status = NO_ERROR; } - return ALREADY_EXISTS; + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; } -void AudioFlinger::removeTrack(wp<Track> track, int name) +void AudioFlinger::MixerThread::removeTrack(wp<Track> track, int name) { Mutex::Autolock _l(mLock); sp<Track> t = track.promote(); @@ -1005,7 +1420,7 @@ void AudioFlinger::removeTrack(wp<Track> track, int name) } } -void AudioFlinger::remove_track_l(wp<Track> track, int name) +void AudioFlinger::MixerThread::remove_track_l(wp<Track> track, int name) { sp<Track> t = track.promote(); if (t!=NULL) { @@ -1016,7 +1431,7 @@ void AudioFlinger::remove_track_l(wp<Track> track, int name) mWaitWorkCV.broadcast(); } -void AudioFlinger::destroyTrack(const sp<Track>& track) +void AudioFlinger::MixerThread::destroyTrack(const sp<Track>& track) { // NOTE: We're acquiring a strong reference on the track before // acquiring the lock, this is to make sure removing it from @@ -1033,99 +1448,58 @@ void AudioFlinger::destroyTrack(const sp<Track>& track) } } -void AudioFlinger::addActiveTrack(const wp<Track>& t) + +void AudioFlinger::MixerThread::addActiveTrack(const wp<Track>& t) { mActiveTracks.add(t); -#ifdef WITH_A2DP - // disable A2DP for certain stream types - sp<Track> track = t.promote(); - if (streamDisablesA2dp(track->type())) { - if (mA2dpDisableCount++ == 0 && isA2dpEnabled()) { - setA2dpEnabled(false); - mA2dpSuppressed = true; - mMusicMuteSaved = mStreamTypes[AudioTrack::MUSIC].mute; - mStreamTypes[AudioTrack::MUSIC].mute = true; - LOGV("mA2dpSuppressed = true, track %d\n", track->name()); - } - LOGV("mA2dpDisableCount incremented to %d, track %d\n", mA2dpDisableCount, track->name()); + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); + } } -#endif } -void AudioFlinger::removeActiveTrack(const wp<Track>& t) +void AudioFlinger::MixerThread::removeActiveTrack(const wp<Track>& t) { mActiveTracks.remove(t); -#ifdef WITH_A2DP - // disable A2DP for certain stream types - sp<Track> track = t.promote(); - if (streamDisablesA2dp(track->type())) { - if (mA2dpDisableCount > 0) { - mA2dpDisableCount--; - LOGV("mA2dpDisableCount decremented to %d, track %d\n", mA2dpDisableCount, track->name()); - if (mA2dpDisableCount == 0 && mA2dpSuppressed) { - setA2dpEnabled(true); - mA2dpSuppressed = false; - mStreamTypes[AudioTrack::MUSIC].mute = mMusicMuteSaved; - LOGV("mA2dpSuppressed = false, track %d\n", track->name()); - } - } else - LOGE("mA2dpDisableCount is already zero"); - } -#endif -} -int AudioFlinger::getTrackName() -{ - // Both mixers must have the same set of track used to avoid mismatches when - // switching from A2DP output to hardware output - int a2DpName; - int hwName; -#ifdef WITH_A2DP - a2DpName = mA2dpAudioMixer->getTrackName(); -#endif - hwName = mHardwareAudioMixer->getTrackName(); - - LOGW_IF((a2DpName != hwName), "getTrackName track name mismatch! A2DP %d, HW %d", a2DpName, hwName); - - return hwName; -} + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; -void AudioFlinger::deleteTrackName(int name) -{ - // Both mixers must have the same set of track used to avoid mismatches when - // switching from A2DP output to hardware output - mHardwareAudioMixer->deleteTrackName(name); -#ifdef WITH_A2DP - mA2dpAudioMixer->deleteTrackName(name); -#endif + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + } + } } -// ---------------------------------------------------------------------------- - -AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) - : RefBase(), - mAudioFlinger(audioFlinger), - mMemoryDealer(new MemoryDealer(1024*1024)), - mPid(pid) +int AudioFlinger::MixerThread::getTrackName() { - // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer + return mAudioMixer->getTrackName(); } -AudioFlinger::Client::~Client() +void AudioFlinger::MixerThread::deleteTrackName(int name) { - mAudioFlinger->removeClient(mPid); + mAudioMixer->deleteTrackName(name); } -const sp<MemoryDealer>& AudioFlinger::Client::heap() const +size_t AudioFlinger::MixerThread::getOutputFrameCount() { - return mMemoryDealer; + return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); } // ---------------------------------------------------------------------------- -AudioFlinger::TrackBase::TrackBase( - const sp<AudioFlinger>& audioFlinger, +AudioFlinger::MixerThread::TrackBase::TrackBase( + const sp<MixerThread>& mixerThread, const sp<Client>& client, int streamType, uint32_t sampleRate, @@ -1134,7 +1508,7 @@ AudioFlinger::TrackBase::TrackBase( int frameCount, const sp<IMemory>& sharedBuffer) : RefBase(), - mAudioFlinger(audioFlinger), + mMixerThread(mixerThread), mClient(client), mStreamType(streamType), mFrameCount(0), @@ -1143,7 +1517,7 @@ AudioFlinger::TrackBase::TrackBase( mFormat(format), mFlags(0) { - mName = audioFlinger->getTrackName(); + mName = mixerThread->getTrackName(); LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); if (mName < 0) { LOGE("no more track names availlable"); @@ -1160,41 +1534,60 @@ AudioFlinger::TrackBase::TrackBase( size += bufferSize; } - mCblkMemory = client->heap()->allocate(size); - if (mCblkMemory != 0) { - mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); - if (mCblk) { // construct the shared structure in-place. - new(mCblk) audio_track_cblk_t(); - // clear all buffers - mCblk->frameCount = frameCount; - mCblk->sampleRate = sampleRate; - mCblk->channels = channelCount; - if (sharedBuffer == 0) { - mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); - memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); - // Force underrun condition to avoid false underrun callback until first data is - // written to buffer - mCblk->flowControlFlag = 1; - } else { - mBuffer = sharedBuffer->pointer(); + if (client != NULL) { + mCblkMemory = client->heap()->allocate(size); + if (mCblkMemory != 0) { + mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + if (sharedBuffer == 0) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + } else { + mBuffer = sharedBuffer->pointer(); + } + mBufferEnd = (uint8_t *)mBuffer + bufferSize; } - mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; } - } else { - LOGE("not enough memory for AudioTrack size=%u", size); - client->heap()->dump("AudioTrack"); - return; - } + } else { + mCblk = (audio_track_cblk_t *)(new uint8_t[size]); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } } -AudioFlinger::TrackBase::~TrackBase() +AudioFlinger::MixerThread::TrackBase::~TrackBase() { - mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + if (mCblk) { + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + } mCblkMemory.clear(); // and free the shared memory mClient.clear(); } -void AudioFlinger::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { buffer->raw = 0; mFrameCount = buffer->frameCount; @@ -1202,7 +1595,7 @@ void AudioFlinger::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) buffer->frameCount = 0; } -bool AudioFlinger::TrackBase::step() { +bool AudioFlinger::MixerThread::TrackBase::step() { bool result; audio_track_cblk_t* cblk = this->cblk(); @@ -1214,7 +1607,7 @@ bool AudioFlinger::TrackBase::step() { return result; } -void AudioFlinger::TrackBase::reset() { +void AudioFlinger::MixerThread::TrackBase::reset() { audio_track_cblk_t* cblk = this->cblk(); cblk->user = 0; @@ -1225,20 +1618,20 @@ void AudioFlinger::TrackBase::reset() { LOGV("TrackBase::reset"); } -sp<IMemory> AudioFlinger::TrackBase::getCblk() const +sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const { return mCblkMemory; } -int AudioFlinger::TrackBase::sampleRate() const { +int AudioFlinger::MixerThread::TrackBase::sampleRate() const { return mCblk->sampleRate; } -int AudioFlinger::TrackBase::channelCount() const { +int AudioFlinger::MixerThread::TrackBase::channelCount() const { return mCblk->channels; } -void* AudioFlinger::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { +void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { audio_track_cblk_t* cblk = this->cblk(); int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; int16_t *bufferEnd = bufferStart + frames * cblk->channels; @@ -1257,8 +1650,8 @@ void* AudioFlinger::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const // ---------------------------------------------------------------------------- -AudioFlinger::Track::Track( - const sp<AudioFlinger>& audioFlinger, +AudioFlinger::MixerThread::Track::Track( + const sp<MixerThread>& mixerThread, const sp<Client>& client, int streamType, uint32_t sampleRate, @@ -1266,7 +1659,7 @@ AudioFlinger::Track::Track( int channelCount, int frameCount, const sp<IMemory>& sharedBuffer) - : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer) + : TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer) { mVolume[0] = 1.0f; mVolume[1] = 1.0f; @@ -1274,23 +1667,23 @@ AudioFlinger::Track::Track( mSharedBuffer = sharedBuffer; } -AudioFlinger::Track::~Track() +AudioFlinger::MixerThread::Track::~Track() { wp<Track> weak(this); // never create a strong ref from the dtor mState = TERMINATED; - mAudioFlinger->removeTrack(weak, mName); + mMixerThread->removeTrack(weak, mName); } -void AudioFlinger::Track::destroy() +void AudioFlinger::MixerThread::Track::destroy() { - mAudioFlinger->destroyTrack(this); + mMixerThread->destroyTrack(this); } -void AudioFlinger::Track::dump(char* buffer, size_t size) +void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) { snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", mName - AudioMixer::TRACK0, - mClient->pid(), + (mClient == NULL) ? getpid() : mClient->pid(), mStreamType, mFormat, mCblk->channels, @@ -1305,7 +1698,7 @@ void AudioFlinger::Track::dump(char* buffer, size_t size) mCblk->user); } -status_t AudioFlinger::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesReady; @@ -1345,53 +1738,54 @@ getNextBuffer_exit: return NOT_ENOUGH_DATA; } -bool AudioFlinger::Track::isReady() const { +bool AudioFlinger::MixerThread::Track::isReady() const { if (mFillingUpStatus != FS_FILLING) return true; if (mCblk->framesReady() >= mCblk->frameCount || mCblk->forceReady) { mFillingUpStatus = FS_FILLED; mCblk->forceReady = 0; + LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); return true; } return false; } -status_t AudioFlinger::Track::start() +status_t AudioFlinger::MixerThread::Track::start() { - LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - mAudioFlinger->addTrack(this); + LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + mMixerThread->addTrack(this); return NO_ERROR; } -void AudioFlinger::Track::stop() +void AudioFlinger::MixerThread::Track::stop() { - LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - Mutex::Autolock _l(mAudioFlinger->mLock); + LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + Mutex::Autolock _l(mMixerThread->mLock); if (mState > STOPPED) { mState = STOPPED; // If the track is not active (PAUSED and buffers full), flush buffers - if (mAudioFlinger->mActiveTracks.indexOf(this) < 0) { + if (mMixerThread->mActiveTracks.indexOf(this) < 0) { reset(); } LOGV("(> STOPPED) => STOPPED (%d)", mName); } } -void AudioFlinger::Track::pause() +void AudioFlinger::MixerThread::Track::pause() { LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - Mutex::Autolock _l(mAudioFlinger->mLock); + Mutex::Autolock _l(mMixerThread->mLock); if (mState == ACTIVE || mState == RESUMING) { mState = PAUSING; LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); } } -void AudioFlinger::Track::flush() +void AudioFlinger::MixerThread::Track::flush() { LOGV("flush(%d)", mName); - Mutex::Autolock _l(mAudioFlinger->mLock); + Mutex::Autolock _l(mMixerThread->mLock); if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { return; } @@ -1407,7 +1801,7 @@ void AudioFlinger::Track::flush() reset(); } -void AudioFlinger::Track::reset() +void AudioFlinger::MixerThread::Track::reset() { // Do not reset twice to avoid discarding data written just after a flush and before // the audioflinger thread detects the track is stopped. @@ -1422,12 +1816,12 @@ void AudioFlinger::Track::reset() } } -void AudioFlinger::Track::mute(bool muted) +void AudioFlinger::MixerThread::Track::mute(bool muted) { mMute = muted; } -void AudioFlinger::Track::setVolume(float left, float right) +void AudioFlinger::MixerThread::Track::setVolume(float left, float right) { mVolume[0] = left; mVolume[1] = right; @@ -1435,7 +1829,289 @@ void AudioFlinger::Track::setVolume(float left, float right) // ---------------------------------------------------------------------------- -AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::Track>& track) +AudioFlinger::MixerThread::RecordTrack::RecordTrack( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : TrackBase(mixerThread, client, streamType, sampleRate, format, + channelCount, frameCount, 0), + mOverflow(false) +{ +} + +AudioFlinger::MixerThread::RecordTrack::~RecordTrack() +{ + mMixerThread->deleteTrackName(mName); +} + +status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesAvail; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesAvail = cblk->framesAvailable_l(); + + if (LIKELY(framesAvail)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::MixerThread::RecordTrack::start() +{ + return mMixerThread->mAudioFlinger->startRecord(this); +} + +void AudioFlinger::MixerThread::RecordTrack::stop() +{ + mMixerThread->mAudioFlinger->stopRecord(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::OutputTrack::OutputTrack( + const sp<MixerThread>& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), + mOutputMixerThread(mixerThread) +{ + + mCblk->out = 1; + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mOutBuffer.frameCount = 0; + mCblk->bufferTimeoutMs = 10; + + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + +} + +AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +{ + stop(); +} + +status_t AudioFlinger::MixerThread::OutputTrack::start() +{ + status_t status = Track::start(); + + mRetryCount = 127; + return status; +} + +void AudioFlinger::MixerThread::OutputTrack::stop() +{ + Track::stop(); + clearBufferQueue(); + mOutBuffer.frameCount = 0; +} + +void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +{ + Buffer *pInBuffer; + Buffer inBuffer; + uint32_t channels = mCblk->channels; + + inBuffer.frameCount = frames; + inBuffer.i16 = data; + + if (mCblk->user == 0) { + if (mOutputMixerThread->isMusicActive()) { + mCblk->forceReady = 1; + LOGV("OutputTrack::start() force ready"); + } else if (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + LOGV("OutputTrack::start() write %d frames", startFrames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() no more buffers"); + } + } + } + + while (1) { + // First write pending buffers, then new data + if (mBufferQueue.size()) { + pInBuffer = mBufferQueue.itemAt(0); + } else { + pInBuffer = &inBuffer; + } + + if (pInBuffer->frameCount == 0) { + break; + } + + if (mOutBuffer.frameCount == 0) { + mOutBuffer.frameCount = pInBuffer->frameCount; + if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + break; + } + } + + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + mCblk->stepUser(outFrames); + pInBuffer->frameCount -= outFrames; + pInBuffer->i16 += outFrames * channels; + mOutBuffer.frameCount -= outFrames; + mOutBuffer.i16 += outFrames * channels; + + if (pInBuffer->frameCount == 0) { + if (mBufferQueue.size()) { + mBufferQueue.removeAt(0); + delete [] pInBuffer->mBuffer; + delete pInBuffer; + } else { + break; + } + } + } + + // If we could not write all frames, allocate a buffer and queue it for next time. + if (inBuffer.frameCount) { + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW("OutputTrack::write() no more buffers"); + } + } + + // Calling write() with a 0 length buffer, means that no more data will be written: + // If no more buffers are pending, fill output track buffer to make sure it is started + // by output mixer. + if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } + +} + +status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = buffer->frameCount; + + LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); + buffer->frameCount = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + if (framesAvail == 0) { + return AudioTrack::NO_MORE_BUFFERS; + } + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + buffer->frameCount = framesReq; + buffer->raw = (void *)cblk->buffer(u); + return NO_ERROR; +} + + +void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +{ + size_t size = mBufferQueue.size(); + Buffer *pBuffer; + + for (size_t i = 0; i < size; i++) { + pBuffer = mBufferQueue.itemAt(i); + delete [] pBuffer->mBuffer; + delete pBuffer; + } + mBufferQueue.clear(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) + : RefBase(), + mAudioFlinger(audioFlinger), + mMemoryDealer(new MemoryDealer(1024*1024)), + mPid(pid) +{ + // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer +} + +AudioFlinger::Client::~Client() +{ + mAudioFlinger->removeClient(mPid); +} + +const sp<MemoryDealer>& AudioFlinger::Client::heap() const +{ + return mMemoryDealer; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track) : BnAudioTrack(), mTrack(track) { @@ -1496,7 +2172,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( status_t *status) { sp<AudioRecordThread> thread; - sp<RecordTrack> recordTrack; + sp<MixerThread::RecordTrack> recordTrack; sp<RecordHandle> recordHandle; sp<Client> client; wp<Client> wclient; @@ -1523,12 +2199,6 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (mSampleRate == 0) { - LOGE("Audio driver not initialized"); - lStatus = NO_INIT; - goto Exit; - } - if (mAudioRecordThread == 0) { LOGE("Audio record thread not started"); lStatus = NO_INIT; @@ -1561,7 +2231,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; // create new record track and pass to record thread - recordTrack = new RecordTrack(this, client, streamType, sampleRate, + recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, format, channelCount, frameCount); // return to handle to client @@ -1575,97 +2245,22 @@ Exit: return recordHandle; } -status_t AudioFlinger::startRecord(RecordTrack* recordTrack) { +status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { if (mAudioRecordThread != 0) { return mAudioRecordThread->start(recordTrack); } return NO_INIT; } -void AudioFlinger::stopRecord(RecordTrack* recordTrack) { +void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { if (mAudioRecordThread != 0) { mAudioRecordThread->stop(recordTrack); } } - -// ---------------------------------------------------------------------------- - -AudioFlinger::RecordTrack::RecordTrack( - const sp<AudioFlinger>& audioFlinger, - const sp<Client>& client, - int streamType, - uint32_t sampleRate, - int format, - int channelCount, - int frameCount) - : TrackBase(audioFlinger, client, streamType, sampleRate, format, - channelCount, frameCount, 0), - mOverflow(false) -{ -} - -AudioFlinger::RecordTrack::~RecordTrack() -{ - mAudioFlinger->deleteTrackName(mName); -} - -status_t AudioFlinger::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) -{ - audio_track_cblk_t* cblk = this->cblk(); - uint32_t framesAvail; - uint32_t framesReq = buffer->frameCount; - - // Check if last stepServer failed, try to step now - if (mFlags & TrackBase::STEPSERVER_FAILED) { - if (!step()) goto getNextBuffer_exit; - LOGV("stepServer recovered"); - mFlags &= ~TrackBase::STEPSERVER_FAILED; - } - - framesAvail = cblk->framesAvailable_l(); - - if (LIKELY(framesAvail)) { - uint32_t s = cblk->server; - uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; - - if (framesReq > framesAvail) { - framesReq = framesAvail; - } - if (s + framesReq > bufferEnd) { - framesReq = bufferEnd - s; - } - - buffer->raw = getBuffer(s, framesReq); - if (buffer->raw == 0) goto getNextBuffer_exit; - - buffer->frameCount = framesReq; - return NO_ERROR; - } - -getNextBuffer_exit: - buffer->raw = 0; - buffer->frameCount = 0; - return NOT_ENOUGH_DATA; -} - -status_t AudioFlinger::RecordTrack::start() -{ - return mAudioFlinger->startRecord(this); -} - -void AudioFlinger::RecordTrack::stop() -{ - mAudioFlinger->stopRecord(this); - TrackBase::reset(); - // Force overerrun condition to avoid false overrun callback until first data is - // read from buffer - mCblk->flowControlFlag = 1; -} - // ---------------------------------------------------------------------------- -AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordTrack>& recordTrack) +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack) : BnAudioRecord(), mRecordTrack(recordTrack) { @@ -1786,7 +2381,7 @@ bool AudioFlinger::AudioRecordThread::threadLoop() return false; } -status_t AudioFlinger::AudioRecordThread::start(RecordTrack* recordTrack) +status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) { LOGV("AudioRecordThread::start"); AutoMutex lock(&mLock); @@ -1807,7 +2402,7 @@ status_t AudioFlinger::AudioRecordThread::start(RecordTrack* recordTrack) return mStartStatus; } -void AudioFlinger::AudioRecordThread::stop(RecordTrack* recordTrack) { +void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { LOGV("AudioRecordThread::stop"); AutoMutex lock(&mLock); if (mActive && (recordTrack == mRecordTrack.get())) { diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 38fa001..3b5932d 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -33,6 +33,7 @@ #include <utils/MemoryDealer.h> #include <utils/KeyedVector.h> #include <utils/SortedVector.h> +#include <utils/Vector.h> #include <hardware_legacy/AudioHardwareInterface.h> @@ -55,18 +56,13 @@ class AudioBuffer; static const nsecs_t kStandbyTimeInNsecs = seconds(3); -class AudioFlinger : public BnAudioFlinger, protected Thread, public IBinder::DeathRecipient +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient { public: static void instantiate(); virtual status_t dump(int fd, const Vector<String16>& args); - // Thread virtuals - virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); - // IAudioFlinger interface virtual sp<IAudioTrack> createTrack( pid_t pid, @@ -79,11 +75,11 @@ public: const sp<IMemory>& sharedBuffer, status_t *status); - virtual uint32_t sampleRate() const; - virtual int channelCount() const; - virtual int format() const; - virtual size_t frameCount() const; - virtual size_t latency() const; + virtual uint32_t sampleRate(int output) const; + virtual int channelCount(int output) const; + virtual int format(int output) const; + virtual size_t frameCount(int output) const; + virtual uint32_t latency(int output) const; virtual status_t setMasterVolume(float value); virtual status_t setMasterMute(bool muted); @@ -108,6 +104,8 @@ public: virtual bool isMusicActive() const; + virtual bool isA2dpEnabled() const; + virtual status_t setParameter(const char* key, const char* value); virtual void registerClient(const sp<IAudioFlingerClient>& client); @@ -159,23 +157,26 @@ private: AudioFlinger(); virtual ~AudioFlinger(); - void setOutput(AudioStreamOut* output); - void doSetOutput(AudioStreamOut* output); - size_t getOutputFrameCount(AudioStreamOut* output); + void setOutput(int outputType); + void doSetOutput(int outputType); #ifdef WITH_A2DP - static bool streamDisablesA2dp(int streamType); - inline bool isA2dpEnabled() const { - return (mRequestedOutput == mA2dpOutput || - (mOutput && mOutput == mA2dpOutput)); - } void setA2dpEnabled(bool enable); #endif + static bool streamForcedToSpeaker(int streamType); + + // Management of forced route to speaker for certain track types. + enum force_speaker_command { + ACTIVE_TRACK_ADDED = 0, + ACTIVE_TRACK_REMOVED, + CHECK_ROUTE_RESTORE_TIME, + FORCE_ROUTE_RESTORE + }; + void handleForcedSpeakerRoute(int command); // Internal dump utilites. status_t dumpPermissionDenial(int fd, const Vector<String16>& args); status_t dumpClients(int fd, const Vector<String16>& args); - status_t dumpTracks(int fd, const Vector<String16>& args); status_t dumpInternals(int fd, const Vector<String16>& args); // --- Client --- @@ -194,168 +195,348 @@ private: }; - // --- Track --- class TrackHandle; class RecordHandle; class AudioRecordThread; - // base for record and playback - class TrackBase : public AudioBufferProvider, public RefBase { - + + // --- MixerThread --- + class MixerThread : public Thread { public: - enum track_state { - IDLE, - TERMINATED, - STOPPED, - RESUMING, - ACTIVE, - PAUSING, - PAUSED + + // --- Track --- + + // base for record and playback + class TrackBase : public AudioBufferProvider, public RefBase { + + public: + enum track_state { + IDLE, + TERMINATED, + STOPPED, + RESUMING, + ACTIVE, + PAUSING, + PAUSED + }; + + enum track_flags { + STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex + }; + + TrackBase( const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer); + ~TrackBase(); + + virtual status_t start() = 0; + virtual void stop() = 0; + sp<IMemory> getCblk() const; + + protected: + friend class MixerThread; + friend class RecordHandle; + friend class AudioRecordThread; + + TrackBase(const TrackBase&); + TrackBase& operator = (const TrackBase&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + audio_track_cblk_t* cblk() const { + return mCblk; + } + + int type() const { + return mStreamType; + } + + int format() const { + return mFormat; + } + + int channelCount() const ; + + int sampleRate() const; + + void* getBuffer(uint32_t offset, uint32_t frames) const; + + int name() const { + return mName; + } + + bool isStopped() const { + return mState == STOPPED; + } + + bool isTerminated() const { + return mState == TERMINATED; + } + + bool step(); + void reset(); + + sp<MixerThread> mMixerThread; + sp<Client> mClient; + sp<IMemory> mCblkMemory; + audio_track_cblk_t* mCblk; + int mStreamType; + void* mBuffer; + void* mBufferEnd; + uint32_t mFrameCount; + int mName; + // we don't really need a lock for these + int mState; + int mClientTid; + uint8_t mFormat; + uint8_t mFlags; }; - enum track_flags { - STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex + // playback track + class Track : public TrackBase { + public: + Track( const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer); + ~Track(); + + void dump(char* buffer, size_t size); + virtual status_t start(); + virtual void stop(); + void pause(); + + void flush(); + void destroy(); + void mute(bool); + void setVolume(float left, float right); + + protected: + friend class MixerThread; + friend class AudioFlinger; + friend class AudioFlinger::TrackHandle; + + Track(const Track&); + Track& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool isMuted() const { + return (mMute || mMixerThread->mStreamTypes[mStreamType].mute); + } + + bool isPausing() const { + return mState == PAUSING; + } + + bool isPaused() const { + return mState == PAUSED; + } + + bool isReady() const; + + void setPaused() { mState = PAUSED; } + void reset(); + + // we don't really need a lock for these + float mVolume[2]; + volatile bool mMute; + // FILLED state is used for suppressing volume ramp at begin of playing + enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; + mutable uint8_t mFillingUpStatus; + int8_t mRetryCount; + sp<IMemory> mSharedBuffer; + bool mResetDone; + }; // end of Track + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack( const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount); + ~RecordTrack(); + + virtual status_t start(); + virtual void stop(); + + bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + + private: + friend class AudioFlinger; + friend class AudioFlinger::RecordHandle; + friend class AudioFlinger::AudioRecordThread; + friend class MixerThread; + + RecordTrack(const Track&); + RecordTrack& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; }; - TrackBase( const sp<AudioFlinger>& audioFlinger, - const sp<Client>& client, - int streamType, - uint32_t sampleRate, - int format, - int channelCount, - int frameCount, - const sp<IMemory>& sharedBuffer); - ~TrackBase(); - - virtual status_t start() = 0; - virtual void stop() = 0; - sp<IMemory> getCblk() const; - - protected: - friend class AudioFlinger; - friend class RecordHandle; - friend class AudioRecordThread; - - TrackBase(const TrackBase&); - TrackBase& operator = (const TrackBase&); - - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; - virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); - - audio_track_cblk_t* cblk() const { - return mCblk; - } - - int type() const { - return mStreamType; - } - - int format() const { - return mFormat; - } - - int channelCount() const ; - - int sampleRate() const; - - void* getBuffer(uint32_t offset, uint32_t frames) const; - - int name() const { - return mName; - } - - bool isStopped() const { - return mState == STOPPED; - } - - bool isTerminated() const { - return mState == TERMINATED; - } - - bool step(); - void reset(); - - sp<AudioFlinger> mAudioFlinger; - sp<Client> mClient; - sp<IMemory> mCblkMemory; - audio_track_cblk_t* mCblk; - int mStreamType; - void* mBuffer; - void* mBufferEnd; - uint32_t mFrameCount; - int mName; - // we don't really need a lock for these - int mState; - int mClientTid; - uint8_t mFormat; - uint8_t mFlags; - }; - - // playback track - class Track : public TrackBase { - public: - Track( const sp<AudioFlinger>& audioFlinger, - const sp<Client>& client, + // playback track + class OutputTrack : public Track { + public: + + class Buffer: public AudioBufferProvider::Buffer { + public: + int16_t *mBuffer; + }; + + OutputTrack( const sp<MixerThread>& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount); + ~OutputTrack(); + + virtual status_t start(); + virtual void stop(); + void write(int16_t* data, uint32_t frames); + bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; } + + private: + + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer); + void clearBufferQueue(); + + sp<MixerThread> mOutputMixerThread; + Vector < Buffer* > mBufferQueue; + AudioBufferProvider::Buffer mOutBuffer; + uint32_t mFramesWritten; + + }; // end of OutputTrack + + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType); + virtual ~MixerThread(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // Thread virtuals + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + virtual uint32_t sampleRate() const; + virtual int channelCount() const; + virtual int format() const; + virtual size_t frameCount() const; + virtual uint32_t latency() const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + bool isMusicActive() const; + + + sp<Track> createTrack( + const sp<AudioFlinger::Client>& client, int streamType, uint32_t sampleRate, int format, int channelCount, int frameCount, - const sp<IMemory>& sharedBuffer); - ~Track(); - - void dump(char* buffer, size_t size); - virtual status_t start(); - virtual void stop(); - void pause(); - - void flush(); - void destroy(); - void mute(bool); - void setVolume(float left, float right); + const sp<IMemory>& sharedBuffer, + status_t *status); + + void wakeUp() { mWaitWorkCV.broadcast(); } + + void getTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + void putTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + void setOuputTrack(OutputTrack *track) { mOutputTrack = track; } + + struct stream_type_t { + stream_type_t() + : volume(1.0f), + mute(false) + { + } + float volume; + bool mute; + }; private: - friend class AudioFlinger; - friend class TrackHandle; - - Track(const Track&); - Track& operator = (const Track&); - - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - - bool isMuted() const { - return (mMute || mAudioFlinger->mStreamTypes[mStreamType].mute); - } - - bool isPausing() const { - return mState == PAUSING; - } - - bool isPaused() const { - return mState == PAUSED; - } - - bool isReady() const; - void setPaused() { mState = PAUSED; } - void reset(); - // we don't really need a lock for these - float mVolume[2]; - volatile bool mMute; - // FILLED state is used for suppressing volume ramp at begin of playing - enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; - mutable uint8_t mFillingUpStatus; - int8_t mRetryCount; - sp<IMemory> mSharedBuffer; - bool mResetDone; - }; // end of Track + friend class AudioFlinger; + friend class Track; + friend class TrackBase; + friend class RecordTrack; + + MixerThread(const Client&); + MixerThread& operator = (const MixerThread&); + + status_t addTrack(const sp<Track>& track); + void removeTrack(wp<Track> track, int name); + void remove_track_l(wp<Track> track, int name); + void destroyTrack(const sp<Track>& track); + int getTrackName(); + void deleteTrackName(int name); + void addActiveTrack(const wp<Track>& t); + void removeActiveTrack(const wp<Track>& t); + size_t getOutputFrameCount(); + + status_t dumpInternals(int fd, const Vector<String16>& args); + status_t dumpTracks(int fd, const Vector<String16>& args); + + sp<AudioFlinger> mAudioFlinger; + mutable Mutex mLock; + mutable Condition mWaitWorkCV; + SortedVector< wp<Track> > mActiveTracks; + SortedVector< sp<Track> > mTracks; + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; + AudioMixer* mAudioMixer; + AudioStreamOut* mOutput; + int mOutputType; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + int16_t* mMixBuffer; + float mMasterVolume; + bool mMasterMute; + nsecs_t mLastWriteTime; + int mNumWrites; + int mNumDelayedWrites; + bool mStandby; + bool mInWrite; + sp <OutputTrack> mOutputTrack; + }; + friend class AudioBuffer; class TrackHandle : public android::BnAudioTrack { public: - TrackHandle(const sp<Track>& track); + TrackHandle(const sp<MixerThread::Track>& track); virtual ~TrackHandle(); virtual status_t start(); virtual void stop(); @@ -367,72 +548,20 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<Track> mTrack; - }; - - struct stream_type_t { - stream_type_t() - : volume(1.0f), - mute(false) - { - } - float volume; - bool mute; + sp<MixerThread::Track> mTrack; }; friend class Client; - friend class Track; + friend class MixerThread::Track; void removeClient(pid_t pid); - status_t addTrack(const sp<Track>& track); - void removeTrack(wp<Track> track, int name); - void remove_track_l(wp<Track> track, int name); - void destroyTrack(const sp<Track>& track); - void addActiveTrack(const wp<Track>& track); - void removeActiveTrack(const wp<Track>& track); - int getTrackName(); - void deleteTrackName(int name); - - AudioMixer* audioMixer() { - return mAudioMixer; - } - - // record track - class RecordTrack : public TrackBase { - public: - RecordTrack( const sp<AudioFlinger>& audioFlinger, - const sp<Client>& client, - int streamType, - uint32_t sampleRate, - int format, - int channelCount, - int frameCount); - ~RecordTrack(); - - virtual status_t start(); - virtual void stop(); - - bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } - bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } - - private: - friend class AudioFlinger; - friend class RecordHandle; - friend class AudioRecordThread; - - RecordTrack(const Track&); - RecordTrack& operator = (const Track&); - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - - bool mOverflow; - }; class RecordHandle : public android::BnAudioRecord { public: - RecordHandle(const sp<RecordTrack>& recordTrack); + RecordHandle(const sp<MixerThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); virtual status_t start(); virtual void stop(); @@ -440,7 +569,7 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<RecordTrack> mRecordTrack; + sp<MixerThread::RecordTrack> mRecordTrack; }; // record thread @@ -453,14 +582,14 @@ private: virtual status_t readyToRun() { return NO_ERROR; } virtual void onFirstRef() {} - status_t start(RecordTrack* recordTrack); - void stop(RecordTrack* recordTrack); + status_t start(MixerThread::RecordTrack* recordTrack); + void stop(MixerThread::RecordTrack* recordTrack); void exit(); private: AudioRecordThread(); AudioHardwareInterface *mAudioHardware; - sp<RecordTrack> mRecordTrack; + sp<MixerThread::RecordTrack> mRecordTrack; Mutex mLock; Condition mWaitWorkCV; volatile bool mActive; @@ -468,48 +597,31 @@ private: }; friend class AudioRecordThread; + friend class MixerThread; - status_t startRecord(RecordTrack* recordTrack); - void stopRecord(RecordTrack* recordTrack); - - void notifyOutputChange_l(); + status_t startRecord(MixerThread::RecordTrack* recordTrack); + void stopRecord(MixerThread::RecordTrack* recordTrack); + + void handleOutputSwitch(); mutable Mutex mHardwareLock; mutable Mutex mLock; - mutable Condition mWaitWorkCV; DefaultKeyedVector< pid_t, wp<Client> > mClients; - SortedVector< wp<Track> > mActiveTracks; - SortedVector< sp<Track> > mTracks; - float mMasterVolume; - uint32_t mMasterRouting; - bool mMasterMute; - stream_type_t mStreamTypes[AudioTrack::NUM_STREAM_TYPES]; - - AudioMixer* mHardwareAudioMixer; - AudioMixer* mA2dpAudioMixer; - AudioMixer* mAudioMixer; + + sp<MixerThread> mA2dpMixerThread; + sp<MixerThread> mHardwareMixerThread; AudioHardwareInterface* mAudioHardware; AudioHardwareInterface* mA2dpAudioInterface; - AudioStreamOut* mHardwareOutput; - AudioStreamOut* mA2dpOutput; - AudioStreamOut* mOutput; - AudioStreamOut* mRequestedOutput; sp<AudioRecordThread> mAudioRecordThread; - uint32_t mSampleRate; - size_t mFrameCount; - int mChannelCount; - int mFormat; - int16_t* mMixBuffer; + bool mA2dpEnabled; + bool mA2dpEnabledReq; mutable int mHardwareStatus; - nsecs_t mLastWriteTime; - int mNumWrites; - int mNumDelayedWrites; - bool mStandby; - bool mInWrite; - int mA2dpDisableCount; - bool mA2dpSuppressed; - bool mMusicMuteSaved; SortedVector< wp<IBinder> > mNotificationClients; + int mForcedSpeakerCount; + uint32_t mSavedRoute; + uint32_t mForcedRoute; + nsecs_t mRouteRestoreTime; + bool mMusicMuteSaved; }; // ---------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 53ba3bc..496e271 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -16,9 +16,7 @@ LOCAL_SRC_FILES:= \ LayerBitmap.cpp \ LayerDim.cpp \ LayerOrientationAnim.cpp \ - LayerScreenshot.cpp \ OrientationAnimation.cpp \ - RFBServer.cpp \ SurfaceFlinger.cpp \ Tokenizer.cpp \ Transform.cpp \ diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 31e63ef..f65d669 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -186,7 +186,9 @@ void Layer::onDraw(const Region& clip) const copybit_device_t* copybit = mFlinger->getBlitEngine(); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + copybit->set_parameter(copybit, COPYBIT_DITHER, + s.flags & ISurfaceComposer::eLayerDither ? + COPYBIT_ENABLE : COPYBIT_DISABLE); region_iterator it(clip); err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index 9277a64..0cf53f7 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -26,6 +26,8 @@ #include <GLES/gl.h> #include <GLES/glext.h> +#include <hardware/hardware.h> + #include "clz.h" #include "LayerBase.h" #include "LayerBlur.h" @@ -229,15 +231,10 @@ Point LayerBase::getPhysicalSize() const return Point(front.w, front.h); } -Transform LayerBase::getDrawingStateTransform() const -{ - return drawingState().transform; -} - void LayerBase::validateVisibility(const Transform& planeTransform) { const Layer::State& s(drawingState()); - const Transform tr(planeTransform * getDrawingStateTransform()); + const Transform tr(planeTransform * s.transform); const bool transformed = tr.transformed(); const Point size(getPhysicalSize()); @@ -420,7 +417,7 @@ void LayerBase::clearWithOpenGL(const Region& clip) const } void LayerBase::drawWithOpenGL(const Region& clip, - GLint textureName, const GGLSurface& t) const + GLint textureName, const GGLSurface& t, int transform) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); @@ -492,6 +489,12 @@ void LayerBase::drawWithOpenGL(const Region& clip, glMatrixMode(GL_TEXTURE); glLoadIdentity(); + + if (transform == HAL_TRANSFORM_ROT_90) { + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + } + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { // find the smallest power-of-two that will accommodate our surface GLuint tw = 1 << (31 - clz(t.width)); diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 2377a14..a020f44 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -169,13 +169,6 @@ public: virtual void validateVisibility(const Transform& globalTransform); /** - * getDrawingStateTransform - returns the drawing state's transform. - * This is used in validateVisibility() and can be use to override or - * modify the transform (if so make sure to trigger a transaction). - */ - virtual Transform getDrawingStateTransform() 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 @@ -200,10 +193,15 @@ public: * needsBlending - true if this surface needs blending */ virtual bool needsBlending() const { return false; } - + + /** + * transformed -- true is this surface needs a to be transformed + */ + virtual bool transformed() const { return mTransformed; } + /** - * isSecure - true if this surface is secure, that is if it prevents a - * screenshot to be taken, + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or vns servers. */ virtual bool isSecure() const { return false; } @@ -222,7 +220,6 @@ public: } int32_t getOrientation() const { return mOrientation; } - bool transformed() const { return mTransformed; } int tx() const { return mLeft; } int ty() const { return mTop; } @@ -233,7 +230,9 @@ protected: GLuint createTexture() const; void drawWithOpenGL(const Region& clip, - GLint textureName, const GGLSurface& surface) const; + GLint textureName, + const GGLSurface& surface, + int transform = 0) const; void clearWithOpenGL(const Region& clip) const; diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index fc0a603..00fab70 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -103,15 +103,6 @@ void LayerBuffer::unregisterBuffers() source->unregisterBuffers(); } -Transform LayerBuffer::getDrawingStateTransform() const -{ - Transform tr(LayerBaseClient::getDrawingStateTransform()); - sp<Source> source(getSource()); - if (source != 0) - source->updateTransform(&tr); - return tr; -} - uint32_t LayerBuffer::doTransaction(uint32_t flags) { sp<Source> source(getSource()); @@ -141,6 +132,14 @@ void LayerBuffer::onDraw(const Region& clip) const } } +bool LayerBuffer::transformed() const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) + return source->transformed(); + return false; +} + /** * This creates a "buffer" source for this surface */ @@ -316,7 +315,8 @@ void LayerBuffer::Source::postBuffer(ssize_t offset) { } void LayerBuffer::Source::unregisterBuffers() { } -void LayerBuffer::Source::updateTransform(Transform* tr) const { +bool LayerBuffer::Source::transformed() const { + return mLayer.mTransformed; } // --------------------------------------------------------------------------- @@ -363,6 +363,7 @@ LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; mLayer.forceVisibilityTransaction(); + } LayerBuffer::BufferSource::~BufferSource() @@ -419,15 +420,9 @@ void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) mBuffer = buffer; } -void LayerBuffer::BufferSource::updateTransform(Transform* tr) const +bool LayerBuffer::BufferSource::transformed() const { - uint32_t bufTransform = mBufferHeap.transform; - // TODO: handle all transforms - if (bufTransform == ISurface::BufferHeap::ROT_90) { - Transform rot90; - rot90.set(0, -1, 1, 0); - *tr = (*tr) * rot90; - } + return mBufferHeap.transform ? true : Source::transformed(); } void LayerBuffer::BufferSource::onDraw(const Region& clip) const @@ -476,7 +471,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const if (UNLIKELY(mTemporaryDealer == 0)) { // allocate a memory-dealer for this the first time mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager() - ->createHeap(ISurfaceComposer::eHardware); + ->createHeap(ISurfaceComposer::eHardware); mTempBitmap.init(mTemporaryDealer); } @@ -506,10 +501,23 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const copybit_image_t dst; hw.getDisplaySurface(&dst); const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(transformedBounds); + = reinterpret_cast<const copybit_rect_t&>(transformedBounds); const State& s(mLayer.drawingState()); region_iterator it(clip); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, mLayer.getOrientation()); + + // pick the right orientation for this buffer + int orientation = mLayer.getOrientation(); + if (UNLIKELY(mBufferHeap.transform)) { + Transform rot90; + GraphicPlane::orientationToTransfrom( + ISurfaceComposer::eOrientation90, 0, 0, &rot90); + const Transform& planeTransform(mLayer.graphicPlane(0).transform()); + const Layer::State& s(mLayer.drawingState()); + Transform tr(planeTransform * s.transform * rot90); + orientation = tr.getOrientation(); + } + + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); @@ -536,10 +544,11 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset); const Region dirty(Rect(t.width, t.height)); mLayer.loadTexture(dirty, mTextureName, t, w, h); - mLayer.drawWithOpenGL(clip, mTextureName, t); + mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform); } } + // --------------------------------------------------------------------------- LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 5532532..2dc77f1 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -46,7 +46,7 @@ class LayerBuffer : public LayerBaseClient virtual void onVisibilityResolved(const Transform& planeTransform); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual void updateTransform(Transform* tr) const; + virtual bool transformed() const; protected: LayerBuffer& mLayer; }; @@ -68,7 +68,7 @@ public: virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual Transform getDrawingStateTransform() const; + virtual bool transformed() const; status_t registerBuffers(const ISurface::BufferHeap& buffers); void postBuffer(ssize_t offset); @@ -116,10 +116,10 @@ private: sp<Buffer> getBuffer() const; void setBuffer(const sp<Buffer>& buffer); - virtual void updateTransform(Transform* tr) const; virtual void onDraw(const Region& clip) const; virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); + virtual bool transformed() const; private: mutable Mutex mLock; sp<Buffer> mBuffer; diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp index 46b3b19..2b72d7c 100644 --- a/libs/surfaceflinger/LayerOrientationAnim.cpp +++ b/libs/surfaceflinger/LayerOrientationAnim.cpp @@ -84,7 +84,7 @@ Point LayerOrientationAnim::getPhysicalSize() const void LayerOrientationAnim::validateVisibility(const Transform&) { const Layer::State& s(drawingState()); - const Transform tr(getDrawingStateTransform()); + const Transform tr(s.transform); const Point size(getPhysicalSize()); uint32_t w = size.x; uint32_t h = size.y; diff --git a/libs/surfaceflinger/LayerScreenshot.cpp b/libs/surfaceflinger/LayerScreenshot.cpp deleted file mode 100644 index 40c47b0..0000000 --- a/libs/surfaceflinger/LayerScreenshot.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "SurfaceFlinger" - -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/Log.h> - -#include <core/SkBitmap.h> - -#include <ui/EGLDisplaySurface.h> - -#include "LayerBase.h" -#include "LayerScreenshot.h" -#include "SurfaceFlinger.h" -#include "DisplayHardware/DisplayHardware.h" - -namespace android { -// --------------------------------------------------------------------------- - -const uint32_t LayerScreenshot::typeInfo = LayerBase::typeInfo | 0x40; -const char* const LayerScreenshot::typeID = "LayerScreenshot"; - -// --------------------------------------------------------------------------- - -LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger, DisplayID display) - : LayerBase(flinger, display), mReply(0) -{ -} - -LayerScreenshot::~LayerScreenshot() -{ -} - -void LayerScreenshot::onDraw(const Region& clip) const -{ - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - copybit_image_t dst; - hw.getDisplaySurface(&dst); - if (dst.base != 0) { - uint8_t const* src = (uint8_t const*)(intptr_t(dst.base) + dst.offset); - const int fbWidth = dst.w; - const int fbHeight = dst.h; - const int fbFormat = dst.format; - - int x = mTransformedBounds.left; - int y = mTransformedBounds.top; - int w = mTransformedBounds.width(); - int h = mTransformedBounds.height(); - Parcel* const reply = mReply; - if (reply) { - const size_t Bpp = bytesPerPixel(fbFormat); - const size_t size = w * h * Bpp; - int32_t cfg = SkBitmap::kNo_Config; - switch (fbFormat) { - case PIXEL_FORMAT_RGBA_4444: cfg = SkBitmap::kARGB_4444_Config; - case PIXEL_FORMAT_RGBA_8888: cfg = SkBitmap::kARGB_8888_Config; - case PIXEL_FORMAT_RGB_565: cfg = SkBitmap::kRGB_565_Config; - case PIXEL_FORMAT_A_8: cfg = SkBitmap::kA8_Config; - } - reply->writeInt32(0); - reply->writeInt32(cfg); - reply->writeInt32(w); - reply->writeInt32(h); - reply->writeInt32(w * Bpp); - void* data = reply->writeInplace(size); - if (data) { - uint8_t* d = (uint8_t*)data; - uint8_t const* s = src + (x + y*fbWidth) * Bpp; - if (w == fbWidth) { - memcpy(d, s, w*h*Bpp); - } else { - for (int y=0 ; y<h ; y++) { - memcpy(d, s, w*Bpp); - d += w*Bpp; - s += fbWidth*Bpp; - } - } - } - } - } - mCV.broadcast(); -} - -void LayerScreenshot::takeScreenshot(Mutex& lock, Parcel* reply) -{ - mReply = reply; - mCV.wait(lock); -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/surfaceflinger/LayerScreenshot.h b/libs/surfaceflinger/LayerScreenshot.h deleted file mode 100644 index 2d9a8ec..0000000 --- a/libs/surfaceflinger/LayerScreenshot.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_LAYER_SCREENSHOT_H -#define ANDROID_LAYER_SCREENSHOT_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/threads.h> -#include <utils/Parcel.h> - -#include "LayerBase.h" - -namespace android { - -// --------------------------------------------------------------------------- - -class LayerScreenshot : public LayerBase -{ -public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - - LayerScreenshot(SurfaceFlinger* flinger, DisplayID display); - virtual ~LayerScreenshot(); - - virtual void onDraw(const Region& clip) const; - virtual bool needsBlending() const { return true; } - virtual bool isSecure() const { return false; } - - void takeScreenshot(Mutex& lock, Parcel* reply); - -private: - mutable Condition mCV; - Parcel* mReply; -}; - -// --------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_LAYER_SCREENSHOT_H diff --git a/libs/surfaceflinger/RFBServer.cpp b/libs/surfaceflinger/RFBServer.cpp deleted file mode 100644 index c2c1989..0000000 --- a/libs/surfaceflinger/RFBServer.cpp +++ /dev/null @@ -1,722 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "RFBServer" - -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <errno.h> -#include <fcntl.h> - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> - -#include <netinet/in.h> - -#include <cutils/sockets.h> - -#include <utils/Log.h> -#include <ui/Rect.h> - -#ifdef HAVE_ANDROID_OS -#include <linux/input.h> -#endif - -#include "RFBServer.h" -#include "SurfaceFlinger.h" - -/* BUG=773511: this is a temporary hack required while developing the new - set of "clean kernel headers" for the Bionic C library. */ -#ifndef KEY_STAR -#define KEY_STAR 227 -#endif -#ifndef KEY_SHARP -#define KEY_SHARP 228 -#endif -#ifndef KEY_SOFT1 -#define KEY_SOFT1 229 -#endif -#ifndef KEY_SOFT2 -#define KEY_SOFT2 230 -#endif -#ifndef KEY_CENTER -#define KEY_CENTER 232 -#endif - -// ---------------------------------------------------------------------------- - -#define DEBUG_MSG 0 - -// ---------------------------------------------------------------------------- - -namespace android { - -const int VNC_PORT = 5900; - -RFBServer::RFBServer(uint32_t w, uint32_t h, android::PixelFormat format) - : Thread(false), mFD(-1), mStatus(NO_INIT), mIoVec(0) -{ - mFrameBuffer.version = sizeof(mFrameBuffer); - mFrameBuffer.width = w; - mFrameBuffer.height = h; - mFrameBuffer.stride = w; - mFrameBuffer.format = format; - mFrameBuffer.data = 0; -} - -RFBServer::~RFBServer() -{ - if (mRobinThread != 0) { - // ask the thread to exit first - mRobinThread->exitAndWait(); - } - - free(mFrameBuffer.data); - - delete [] mIoVec; -} - -void RFBServer::onFirstRef() -{ - run("Batman"); -} - -status_t RFBServer::readyToRun() -{ - LOGI("RFB server ready to run"); - return NO_ERROR; -} - -bool RFBServer::threadLoop() -{ - struct sockaddr addr; - socklen_t alen; - int serverfd = -1; - int port = VNC_PORT; - - do { - retry: - if (serverfd < 0) { - serverfd = socket_loopback_server(port, SOCK_STREAM); - if (serverfd < 0) { - if ((errno == EADDRINUSE) && (port < (VNC_PORT+10))) { - LOGW("port %d already in use, trying %d", port, port+1); - port++; - goto retry; - } - LOGE("couldn't create socket, port=%d, error %d (%s)", - port, errno, strerror(errno)); - sleep(1); - break; - } - fcntl(serverfd, F_SETFD, FD_CLOEXEC); - } - - alen = sizeof(addr); - mFD = accept(serverfd, &addr, &alen); - - if (mFD < 0) { - LOGE("couldn't accept(), error %d (%s)", errno, strerror(errno)); - // we could have run out of file descriptors, wait a bit and - // try again. - sleep(1); - goto retry; - } - fcntl(mFD, F_SETFD, FD_CLOEXEC); - - // send protocol version and Authentication method - mStatus = NO_ERROR; - handshake(3, 3, Authentication::None); - - if (alive()) { - // create the thread we use to send data to the client - mRobinThread = new ServerThread(this); - } - - while( alive() ) { - // client message must be destroyed at each iteration - // (most of the time this is a no-op) - ClientMessage msg; - waitForClientMessage(msg); - if (alive()) { - handleClientMessage(msg); - } - } - - } while( alive() ); - - // free-up some resources - if (mRobinThread != 0) { - mRobinThread->exitAndWait(); - mRobinThread.clear(); - } - - free(mFrameBuffer.data); - mFrameBuffer.data = 0; - - close(mFD); - close(serverfd); - mFD = -1; - - // we'll try again - return true; -} - -// ---------------------------------------------------------------------------- - -RFBServer::ServerThread::ServerThread(const sp<RFBServer>& receiver) - : Thread(false), mReceiver(receiver) -{ - LOGD("RFB Server Thread created"); -} - -RFBServer::ServerThread::~ServerThread() -{ - LOGD("RFB Server Thread destroyed"); -} - -void RFBServer::ServerThread::onFirstRef() -{ - mUpdateBarrier.close(); - run("Robin"); -} - -status_t RFBServer::ServerThread::readyToRun() -{ - return NO_ERROR; -} - -void RFBServer::ServerThread::wake() -{ - mUpdateBarrier.open(); -} - -void RFBServer::ServerThread::exitAndWait() -{ - requestExit(); - mUpdateBarrier.open(); - requestExitAndWait(); -} - -bool RFBServer::ServerThread::threadLoop() -{ - sp<RFBServer> receiver(mReceiver.promote()); - if (receiver == 0) - return false; - - // wait for something to do - mUpdateBarrier.wait(); - - // we're asked to quit, abort everything - if (exitPending()) - return false; - - mUpdateBarrier.close(); - - // process updates - receiver->sendFrameBufferUpdates(); - return !exitPending(); -} - -// ---------------------------------------------------------------------------- - -void RFBServer::handshake(uint8_t major, uint8_t minor, uint32_t auth) -{ - ProtocolVersion protocolVersion(major, minor); - if( !write(protocolVersion) ) - return; - - if ( !read(protocolVersion) ) - return; - - int maj, min; - if ( protocolVersion.decode(maj, min) != NO_ERROR ) { - mStatus = -1; - return; - } - -#if DEBUG_MSG - LOGD("client protocol string: <%s>", (char*)protocolVersion.payload()); - LOGD("client wants protocol version %d.%d\n", maj, min); -#endif - - Authentication authentication(auth); - if( !write(authentication) ) - return; - - ClientInitialization clientInit; - if ( !read(clientInit) ) - return; - -#if DEBUG_MSG - LOGD("client initialization: sharedFlags = %d\n", clientInit.sharedFlags()); -#endif - - ServerInitialization serverInit("Android RFB"); - ServerInitialization::Payload& message(serverInit.message()); - message.framebufferWidth = htons(mFrameBuffer.width); - message.framebufferHeight = htons(mFrameBuffer.height); - message.serverPixelFormat.bitsPerPixel = 16; - message.serverPixelFormat.depth = 16; - message.serverPixelFormat.bigEndianFlag = 0; - message.serverPixelFormat.trueColorFlag = 1; - message.serverPixelFormat.redMax = htons((1<<5)-1); - message.serverPixelFormat.greenMax = htons((1<<6)-1); - message.serverPixelFormat.blueMax = htons((1<<5)-1); - message.serverPixelFormat.redShift = 11; - message.serverPixelFormat.greenShift = 5; - message.serverPixelFormat.blueShift = 0; - - mIoVec = new iovec[mFrameBuffer.height]; - - write(serverInit); -} - -void RFBServer::handleClientMessage(const ClientMessage& msg) -{ - switch(msg.type()) { - case SET_PIXEL_FORMAT: - handleSetPixelFormat(msg.messages().setPixelFormat); - break; - case SET_ENCODINGS: - handleSetEncodings(msg.messages().setEncodings); - break; - case FRAME_BUFFER_UPDATE_REQ: - handleFrameBufferUpdateReq(msg.messages().frameBufferUpdateRequest); - break; - case KEY_EVENT: - handleKeyEvent(msg.messages().keyEvent); - break; - } -} - -void RFBServer::handleSetPixelFormat(const SetPixelFormat& msg) -{ - if (!validatePixelFormat(msg.pixelFormat)) { - LOGE("The builtin VNC server only supports the RGB 565 pixel format"); - LOGD("requested pixel format:"); - LOGD("bitsPerPixel: %d", msg.pixelFormat.bitsPerPixel); - LOGD("depth: %d", msg.pixelFormat.depth); - LOGD("bigEndianFlag: %d", msg.pixelFormat.bigEndianFlag); - LOGD("trueColorFlag: %d", msg.pixelFormat.trueColorFlag); - LOGD("redmax: %d", ntohs(msg.pixelFormat.redMax)); - LOGD("bluemax: %d", ntohs(msg.pixelFormat.greenMax)); - LOGD("greenmax: %d", ntohs(msg.pixelFormat.blueMax)); - LOGD("redshift: %d", msg.pixelFormat.redShift); - LOGD("greenshift: %d", msg.pixelFormat.greenShift); - LOGD("blueshift: %d", msg.pixelFormat.blueShift); - mStatus = -1; - } -} - -bool RFBServer::validatePixelFormat(const PixelFormat& pf) -{ - if ((pf.bitsPerPixel != 16) || (pf.depth != 16)) - return false; - - if (pf.bigEndianFlag || !pf.trueColorFlag) - return false; - - if (ntohs(pf.redMax)!=0x1F || - ntohs(pf.greenMax)!=0x3F || - ntohs(pf.blueMax)!=0x1F) { - return false; - } - - if (pf.redShift!=11 || pf.greenShift!=5 || pf.blueShift!=0) - return false; - - return true; -} - -void RFBServer::handleSetEncodings(const SetEncodings& msg) -{ - /* From the RFB specification: - Sets the encoding types in which pixel data can be sent by the server. - The order of the encoding types given in this message is a hint by the - client as to its preference (the first encoding specified being most - preferred). The server may or may not choose to make use of this hint. - Pixel data may always be sent in raw encoding even if not specified - explicitly here. - */ - - LOGW("SetEncodings received. Only RAW is supported."); -} - -void RFBServer::handleFrameBufferUpdateReq(const FrameBufferUpdateRequest& msg) -{ -#if DEBUG_MSG - LOGD("handle FrameBufferUpdateRequest"); -#endif - - Rect r; - r.left = ntohs(msg.x); - r.top = ntohs(msg.y); - r.right = r.left + ntohs(msg.width); - r.bottom = r.top + ntohs(msg.height); - - Mutex::Autolock _l(mRegionLock); - mClientRegionRequest.set(r); - if (!msg.incremental) - mDirtyRegion.orSelf(r); - - mRobinThread->wake(); -} - -void RFBServer::handleKeyEvent(const KeyEvent& msg) -{ -#ifdef HAVE_ANDROID_OS - - int scancode = 0; - int code = ntohl(msg.key); - - if (code>='0' && code<='9') { - scancode = (code & 0xF) - 1; - if (scancode<0) scancode += 10; - scancode += KEY_1; - } else if (code>=0xFF50 && code<=0xFF58) { - static const uint16_t map[] = - { KEY_HOME, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, - KEY_SOFT1, KEY_SOFT2, KEY_END, 0 }; - scancode = map[code & 0xF]; - } else if (code>=0xFFE1 && code<=0xFFEE) { - static const uint16_t map[] = - { KEY_LEFTSHIFT, KEY_LEFTSHIFT, - KEY_COMPOSE, KEY_COMPOSE, - KEY_LEFTSHIFT, KEY_LEFTSHIFT, - 0,0, - KEY_LEFTALT, KEY_RIGHTALT, - 0, 0, 0, 0 }; - scancode = map[code & 0xF]; - } else if ((code>='A' && code<='Z') || (code>='a' && code<='z')) { - static const uint16_t map[] = { - KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, - KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, - KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, - KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, - KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z }; - scancode = map[(code & 0x5F) - 'A']; - } else { - switch (code) { - case 0x0003: scancode = KEY_CENTER; break; - case 0x0020: scancode = KEY_SPACE; break; - case 0x0023: scancode = KEY_SHARP; break; - case 0x0033: scancode = KEY_SHARP; break; - case 0x002C: scancode = KEY_COMMA; break; - case 0x003C: scancode = KEY_COMMA; break; - case 0x002E: scancode = KEY_DOT; break; - case 0x003E: scancode = KEY_DOT; break; - case 0x002F: scancode = KEY_SLASH; break; - case 0x003F: scancode = KEY_SLASH; break; - case 0x0032: scancode = KEY_EMAIL; break; - case 0x0040: scancode = KEY_EMAIL; break; - case 0xFF08: scancode = KEY_BACKSPACE; break; - case 0xFF1B: scancode = KEY_BACK; break; - case 0xFF09: scancode = KEY_TAB; break; - case 0xFF0D: scancode = KEY_ENTER; break; - case 0x002A: scancode = KEY_STAR; break; - case 0xFFBE: scancode = KEY_SEND; break; // F1 - case 0xFFBF: scancode = KEY_END; break; // F2 - case 0xFFC0: scancode = KEY_HOME; break; // F3 - case 0xFFC5: scancode = KEY_POWER; break; // F8 - } - } - -#if DEBUG_MSG - LOGD("handle KeyEvent 0x%08x, %d, scancode=%d\n", code, msg.downFlag, scancode); -#endif - - if (scancode) { - mEventInjector.injectKey(uint16_t(scancode), - msg.downFlag ? EventInjector::DOWN : EventInjector::UP); - } -#endif -} - -void RFBServer::waitForClientMessage(ClientMessage& msg) -{ - if ( !read(msg.payload(), 1) ) - return; - - switch(msg.type()) { - - case SET_PIXEL_FORMAT: - read(msg.payload(1), sizeof(SetPixelFormat)-1); - break; - - case FIX_COLOUR_MAP_ENTRIES: - mStatus = UNKNOWN_ERROR; - return; - - case SET_ENCODINGS: - { - if ( !read(msg.payload(1), sizeof(SetEncodings)-1) ) - return; - - size_t size = ntohs( msg.messages().setEncodings.numberOfEncodings ) * 4; - if (msg.resize(sizeof(SetEncodings) + size) != NO_ERROR) { - mStatus = NO_MEMORY; - return; - } - - if ( !read(msg.payload(sizeof(SetEncodings)), size) ) - return; - - break; - } - - case FRAME_BUFFER_UPDATE_REQ: - read(msg.payload(1), sizeof(FrameBufferUpdateRequest)-1); - break; - - case KEY_EVENT: - read(msg.payload(1), sizeof(KeyEvent)-1); - break; - - case POINTER_EVENT: - read(msg.payload(1), sizeof(PointerEvent)-1); - break; - - case CLIENT_CUT_TEXT: - { - if ( !read(msg.payload(1), sizeof(ClientCutText)-1) ) - return; - - size_t size = ntohl( msg.messages().clientCutText.length ); - if (msg.resize(sizeof(ClientCutText) + size) != NO_ERROR) { - mStatus = NO_MEMORY; - return; - } - - if ( !read(msg.payload(sizeof(SetEncodings)), size) ) - return; - - break; - } - - default: - LOGE("Unknown Message %d", msg.type()); - mStatus = UNKNOWN_ERROR; - return; - } -} - -// ---------------------------------------------------------------------------- - -bool RFBServer::write(const Message& msg) -{ - write(msg.payload(), msg.size()); - return alive(); -} - -bool RFBServer::read(Message& msg) -{ - read(msg.payload(), msg.size()); - return alive(); -} - -bool RFBServer::write(const void* buffer, int size) -{ - int wr = ::write(mFD, buffer, size); - if (wr != size) { - //LOGE("write(%d) error %d (%s)", size, wr, strerror(errno)); - mStatus = (wr == -1) ? errno : -1; - } - return alive(); -} - -bool RFBServer::read(void* buffer, int size) -{ - int rd = ::read(mFD, buffer, size); - if (rd != size) { - //LOGE("read(%d) error %d (%s)", size, rd, strerror(errno)); - mStatus = (rd == -1) ? errno : -1; - } - return alive(); -} - -bool RFBServer::alive() const -{ - return mStatus == 0; -} - -bool RFBServer::isConnected() const -{ - return alive(); -} - -// ---------------------------------------------------------------------------- - -void RFBServer::frameBufferUpdated(const GGLSurface& front, const Region& reg) -{ - Mutex::Autolock _l(mRegionLock); - - // update dirty region - mDirtyRegion.orSelf(reg); - - // remember the front-buffer - mFrontBuffer = front; - - // The client has not requested anything, don't do anything more - if (mClientRegionRequest.isEmpty()) - return; - - // wake the sending thread up - mRobinThread->wake(); -} - -void RFBServer::sendFrameBufferUpdates() -{ - Vector<Rect> rects; - size_t countRects; - GGLSurface fb; - - { // Scope for the lock - Mutex::Autolock _l(mRegionLock); - if (mFrontBuffer.data == 0) - return; - - const Region reg( mDirtyRegion.intersect(mClientRegionRequest) ); - if (reg.isEmpty()) - return; - - mDirtyRegion.subtractSelf(reg); - countRects = reg.rects(rects); - - // copy the frame-buffer so we can stay responsive - size_t bytesPerPix = bytesPerPixel(mFrameBuffer.format); - size_t bpr = mFrameBuffer.stride * bytesPerPix; - if (mFrameBuffer.data == 0) { - mFrameBuffer.data = (GGLubyte*)malloc(bpr * mFrameBuffer.height); - if (mFrameBuffer.data == 0) - return; - } - - memcpy(mFrameBuffer.data, mFrontBuffer.data, bpr*mFrameBuffer.height); - fb = mFrameBuffer; - } - - FrameBufferUpdate msgHeader; - msgHeader.type = 0; - msgHeader.numberOfRectangles = htons(countRects); - write(&msgHeader, sizeof(msgHeader)); - - Rectangle rectangle; - for (size_t i=0 ; i<countRects ; i++) { - const Rect& r = rects[i]; - rectangle.x = htons( r.left ); - rectangle.y = htons( r.top ); - rectangle.w = htons( r.width() ); - rectangle.h = htons( r.height() ); - rectangle.encoding = htons( SetEncodings::Raw ); - write(&rectangle, sizeof(rectangle)); - size_t h = r.height(); - size_t w = r.width(); - size_t bytesPerPix = bytesPerPixel(fb.format); - size_t bpr = fb.stride * bytesPerPix; - size_t bytes = w * bytesPerPix; - size_t offset = (r.top * bpr) + (r.left * bytesPerPix); - uint8_t* src = static_cast<uint8_t*>(fb.data) + offset; - iovec* iov = mIoVec; - while (h--) { - iov->iov_base = src; - iov->iov_len = bytes; - src += bpr; - iov++; - } - size_t iovcnt = iov - mIoVec; - int wr = ::writev(mFD, mIoVec, iovcnt); - if (wr < 0) { - //LOGE("write(%d) error %d (%s)", size, wr, strerror(errno)); - mStatus = errno; - } - } -} - -// ---------------------------------------------------------------------------- - -RFBServer::Message::Message(size_t size) - : mSize(size), mAllocatedSize(size) -{ - mPayload = malloc(size); -} - -RFBServer::Message::Message(void* payload, size_t size) - : mPayload(payload), mSize(size), mAllocatedSize(0) -{ -} - -RFBServer::Message::~Message() -{ - if (mAllocatedSize) - free(mPayload); -} - -status_t RFBServer::Message::resize(size_t size) -{ - if (size > mAllocatedSize) { - void* newp; - if (mAllocatedSize) { - newp = realloc(mPayload, size); - if (!newp) return NO_MEMORY; - } else { - newp = malloc(size); - if (!newp) return NO_MEMORY; - memcpy(newp, mPayload, mSize); - mAllocatedSize = size; - } - mPayload = newp; - } - mSize = size; - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -RFBServer::EventInjector::EventInjector() - : mFD(-1) -{ -} - -RFBServer::EventInjector::~EventInjector() -{ -} - -void RFBServer::EventInjector::injectKey(uint16_t code, uint16_t value) -{ -#ifdef HAVE_ANDROID_OS - // XXX: we need to open the right event device - int version; - mFD = open("/dev/input/event0", O_RDWR); - ioctl(mFD, EVIOCGVERSION, &version); - - input_event ev; - memset(&ev, 0, sizeof(ev)); - ev.type = EV_KEY; - ev.code = code; - ev.value = value; - ::write(mFD, &ev, sizeof(ev)); - - close(mFD); - mFD = -1; -#endif -} - - -}; // namespace android - diff --git a/libs/surfaceflinger/RFBServer.h b/libs/surfaceflinger/RFBServer.h deleted file mode 100644 index 420912e..0000000 --- a/libs/surfaceflinger/RFBServer.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_RFB_SERVER_H -#define ANDROID_RFB_SERVER_H - -#include <stdint.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> -#include <arpa/inet.h> - -#include <utils/Errors.h> -#include <utils/threads.h> -#include <ui/Region.h> -#include <ui/PixelFormat.h> - -#include <pixelflinger/pixelflinger.h> - -#include "Barrier.h" - -namespace android { - -class SurfaceFlinger; - -class RFBServer : public Thread -{ -public: - RFBServer(uint32_t w, uint32_t h, android::PixelFormat format); - virtual ~RFBServer(); - - void frameBufferUpdated(const GGLSurface& front, const Region& reg); - bool isConnected() const; - -private: - typedef uint8_t card8; - typedef uint16_t card16; - typedef uint32_t card32; - - struct Message { - Message(size_t size); - virtual ~Message(); - void* payload(int offset=0) { - return static_cast<char*>(mPayload)+offset; - } - void const * payload(int offset=0) const { - return static_cast<char const *>(mPayload)+offset; - } - size_t size() const { return mSize; } - protected: - Message(void* payload, size_t size); - status_t resize(size_t size); - private: - void* mPayload; - size_t mSize; - size_t mAllocatedSize; - }; - - struct ProtocolVersion : public Message { - ProtocolVersion(uint8_t major, uint8_t minor) - : Message(&messageData, 12) { - char* p = static_cast<char*>(payload()); - snprintf(p, 13, "RFB %03u.%03u%c", major, minor, 0xA); - } - status_t decode(int& maj, int& min) { - char* p = static_cast<char*>(payload()); - int n = sscanf(p, "RFB %03u.%03u", &maj, &min); - return (n == 2) ? NO_ERROR : NOT_ENOUGH_DATA; - } - private: - char messageData[12+1]; - }; - - struct Authentication : public Message { - enum { Failed=0, None=1, Vnc=2 }; - Authentication(int auth) : Message(&messageData, 4) { - *static_cast<card32*>(payload()) = htonl(auth); - } - private: - card32 messageData; - }; - - struct ClientInitialization : public Message { - ClientInitialization() : Message(&messageData, 1) { } - int sharedFlags() { - return messageData; - } - private: - card8 messageData; - }; - - struct PixelFormat { - card8 bitsPerPixel; - card8 depth; - card8 bigEndianFlag; - card8 trueColorFlag; - card16 redMax; - card16 greenMax; - card16 blueMax; - card8 redShift; - card8 greenShift; - card8 blueShift; - uint8_t padding[3]; - } __attribute__((packed)); - - struct ServerInitialization : public Message { - ServerInitialization(char const * name) - : Message(sizeof(Payload) + strlen(name)) - { - const size_t nameLength = size() - sizeof(Payload); - message().nameLength = htonl(nameLength); - memcpy((char*)message().nameString, name,nameLength); - } - struct Payload { - card16 framebufferWidth; - card16 framebufferHeight; - PixelFormat serverPixelFormat; - card32 nameLength; - card8 nameString[0]; - } __attribute__((packed)); - Payload& message() { - return *static_cast<Payload*>(payload()); - } - }; - - // client messages... - - struct SetPixelFormat { - card8 type; - uint8_t padding[3]; - PixelFormat pixelFormat; - } __attribute__((packed)); - - struct SetEncodings { - enum { Raw=0, CoR=1, RRE=2, CoRRE=4, Hextile=5 }; - card8 type; - uint8_t padding; - card16 numberOfEncodings; - card32 encodings[0]; - } __attribute__((packed)); - - struct FrameBufferUpdateRequest { - card8 type; - card8 incremental; - card16 x; - card16 y; - card16 width; - card16 height; - } __attribute__((packed)); - - struct KeyEvent { - card8 type; - card8 downFlag; - uint8_t padding[2]; - card32 key; - } __attribute__((packed)); - - struct PointerEvent { - card8 type; - card8 buttonMask; - card16 x; - card16 y; - } __attribute__((packed)); - - struct ClientCutText { - card8 type; - uint8_t padding[3]; - card32 length; - card8 text[0]; - } __attribute__((packed)); - - union ClientMessages { - card8 type; - SetPixelFormat setPixelFormat; - SetEncodings setEncodings; - FrameBufferUpdateRequest frameBufferUpdateRequest; - KeyEvent keyEvent; - PointerEvent pointerEvent; - ClientCutText clientCutText; - }; - - struct Rectangle { - card16 x; - card16 y; - card16 w; - card16 h; - card32 encoding; - } __attribute__((packed)); - - struct FrameBufferUpdate { - card8 type; - uint8_t padding; - card16 numberOfRectangles; - Rectangle rectangles[0]; - } __attribute__((packed)); - - enum { - SET_PIXEL_FORMAT = 0, - FIX_COLOUR_MAP_ENTRIES = 1, - SET_ENCODINGS = 2, - FRAME_BUFFER_UPDATE_REQ = 3, - KEY_EVENT = 4, - POINTER_EVENT = 5, - CLIENT_CUT_TEXT = 6, - }; - - struct ClientMessage : public Message { - ClientMessage() - : Message(&messageData, sizeof(messageData)) { - } - const ClientMessages& messages() const { - return *static_cast<ClientMessages const *>(payload()); - } - const int type() const { - return messages().type; - } - status_t resize(size_t size) { - return Message::resize(size); - } - - ClientMessages messageData; - }; - - - class ServerThread : public Thread - { - friend class RFBServer; - public: - ServerThread(const sp<RFBServer>& receiver); - virtual ~ServerThread(); - void wake(); - void exitAndWait(); - private: - virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); - wp<RFBServer> mReceiver; - bool (RFBServer::*mAction)(); - Barrier mUpdateBarrier; - }; - - class EventInjector { - public: - enum { UP=0, DOWN=1 }; - EventInjector(); - ~EventInjector(); - void injectKey(uint16_t code, uint16_t value); - private: - struct input_event { - struct timeval time; - uint16_t type; - uint16_t code; - uint32_t value; - }; - int mFD; - }; - - void handshake(uint8_t major, uint8_t minor, uint32_t auth); - void waitForClientMessage(ClientMessage& msg); - void handleClientMessage(const ClientMessage& msg); - void handleSetPixelFormat(const SetPixelFormat& msg); - void handleSetEncodings(const SetEncodings& msg); - void handleFrameBufferUpdateReq(const FrameBufferUpdateRequest& msg); - void handleKeyEvent(const KeyEvent& msg); - void sendFrameBufferUpdates(); - - bool validatePixelFormat(const PixelFormat& pf); - bool alive() const; - bool write(const Message& msg); - bool read(Message& msg); - - bool write(const void* buffer, int size); - bool read(void* buffer, int size); - - virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); - - sp<ServerThread> mRobinThread; - - int mFD; - int mStatus; - iovec* mIoVec; - - EventInjector mEventInjector; - - Mutex mRegionLock; - // This is the region requested by the client since the last - // time we updated it - Region mClientRegionRequest; - // This is the region of the screen that needs to be sent to the - // client since the last time we updated it. - // Typically this is the dirty region, but not necessarily, for - // instance if the client asked for a non incremental update. - Region mDirtyRegion; - - GGLSurface mFrameBuffer; - GGLSurface mFrontBuffer; -}; - -}; // namespace android - -#endif // ANDROID_RFB_SERVER_H diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 4e457c9..900282a 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -53,21 +53,14 @@ #include "LayerDim.h" #include "LayerBitmap.h" #include "LayerOrientationAnim.h" -#include "LayerScreenshot.h" #include "OrientationAnimation.h" #include "SurfaceFlinger.h" -#include "RFBServer.h" #include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" #include "GPUHardware/GPUHardware.h" -// the VNC server even on local ports presents a significant -// thread as it can allow an application to control and "see" other -// applications, de-facto bypassing security permissions. -#define ENABLE_VNC_SERVER 0 - #define DISPLAY_COUNT 1 namespace android { @@ -460,9 +453,6 @@ status_t SurfaceFlinger::readyToRun() if (mDebugNoBootAnimation == false) mBootAnimation = new BootAnimation(this); - if (ENABLE_VNC_SERVER) - mRFBServer = new RFBServer(w, h, f); - return NO_ERROR; } @@ -572,18 +562,6 @@ void SurfaceFlinger::postFramebuffer() debugShowFPS(); } - if (UNLIKELY(ENABLE_VNC_SERVER && - mRFBServer!=0 && mRFBServer->isConnected())) { - if (!mSecureFrameBuffer) { - GGLSurface fb; - // backbufer, is going to become the front buffer really soon - hw.getDisplaySurface(&fb); - if (LIKELY(fb.data != 0)) { - mRFBServer->frameBufferUpdated(fb, mInvalidRegion); - } - } - } - hw.flip(mInvalidRegion); mInvalidRegion.clear(); @@ -1571,55 +1549,17 @@ status_t SurfaceFlinger::onTransact( status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { - if (code == 1012) { - // take screen-shot of the front buffer - if (UNLIKELY(checkCallingPermission( - String16("android.permission.READ_FRAME_BUFFER")) == false)) - { // not allowed - LOGE("Permission Denial: " - "can't take screenshots from pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); - return PERMISSION_DENIED; - } - - if (UNLIKELY(mSecureFrameBuffer)) { - LOGE("A secure window is on screen: " - "can't take screenshots from pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); - return PERMISSION_DENIED; - } - - LOGI("Taking a screenshot..."); - - LayerScreenshot* l = new LayerScreenshot(this, 0); - - Mutex::Autolock _l(mStateLock); - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - l->initStates(hw.getWidth(), hw.getHeight(), 0); - l->setLayer(INT_MAX); - - addLayer_l(l); - setTransactionFlags(eTransactionNeeded|eTraversalNeeded); - - l->takeScreenshot(mStateLock, reply); - - removeLayer_l(l); - setTransactionFlags(eTransactionNeeded); - return NO_ERROR; - } else { - // HARDWARE_TEST stuff... - if (UNLIKELY(checkCallingPermission( - String16("android.permission.HARDWARE_TEST")) == false)) - { // not allowed - LOGE("Permission Denial: pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); - return PERMISSION_DENIED; - } - int n; - switch (code) { + // HARDWARE_TEST stuff... + if (UNLIKELY(checkCallingPermission( + String16("android.permission.HARDWARE_TEST")) == false)) + { // not allowed + LOGE("Permission Denial: pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + return PERMISSION_DENIED; + } + int n; + switch (code) { case 1000: // SHOW_CPU n = data.readInt32(); mDebugCpu = n ? 1 : 0; @@ -1652,8 +1592,8 @@ status_t SurfaceFlinger::onTransact( const DisplayHardware& hw(graphicPlane(0).displayHardware()); mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe signalEvent(); - } - return NO_ERROR; + } + return NO_ERROR; case 1005: // ask GPU revoke mGPU->friendlyRevoke(); return NO_ERROR; @@ -1669,13 +1609,12 @@ status_t SurfaceFlinger::onTransact( reply->writeInt32(mDebugRegion); reply->writeInt32(mDebugBackground); return NO_ERROR; - case 1013: { // screenshot + case 1013: { Mutex::Autolock _l(mStateLock); const DisplayHardware& hw(graphicPlane(0).displayHardware()); reply->writeInt32(hw.getPageFlipCount()); } return NO_ERROR; - } } } return err; @@ -1829,10 +1768,33 @@ void GraphicPlane::setTransform(const Transform& tr) { mGlobalTransform = mOrientationTransform * mTransform; } -status_t GraphicPlane::setOrientation(int orientation) -{ +status_t GraphicPlane::orientationToTransfrom( + int orientation, int w, int h, Transform* tr) +{ float a, b, c, d, x, y; + switch (orientation) { + case ISurfaceComposer::eOrientationDefault: + a=1; b=0; c=0; d=1; x=0; y=0; + break; + case ISurfaceComposer::eOrientation90: + a=0; b=-1; c=1; d=0; x=w; y=0; + break; + case ISurfaceComposer::eOrientation180: + a=-1; b=0; c=0; d=-1; x=w; y=h; + break; + case ISurfaceComposer::eOrientation270: + a=0; b=1; c=-1; d=0; x=0; y=h; + break; + default: + return BAD_VALUE; + } + tr->set(a, b, c, d); + tr->set(x, y); + return NO_ERROR; +} +status_t GraphicPlane::setOrientation(int orientation) +{ const DisplayHardware& hw(displayHardware()); const float w = hw.getWidth(); const float h = hw.getHeight(); @@ -1846,30 +1808,21 @@ status_t GraphicPlane::setOrientation(int orientation) // If the rotation can be handled in hardware, this is where // the magic should happen. - - switch (orientation) { - case ISurfaceComposer::eOrientation90: - a=0; b=-1; c=1; d=0; x=w; y=0; - break; - case ISurfaceComposer::eOrientation180: - a=-1; b=0; c=0; d=-1; x=w; y=h; - break; - case ISurfaceComposer::eOrientation270: - a=0; b=1; c=-1; d=0; x=0; y=h; - break; - case 42: { + if (UNLIKELY(orientation == 42)) { + float a, b, c, d, x, y; const float r = (3.14159265f / 180.0f) * 42.0f; const float si = sinf(r); const float co = cosf(r); a=co; b=-si; c=si; d=co; x = si*(h*0.5f) + (1-co)*(w*0.5f); y =-si*(w*0.5f) + (1-co)*(h*0.5f); - } break; - default: - return BAD_VALUE; + mOrientationTransform.set(a, b, c, d); + mOrientationTransform.set(x, y); + } else { + GraphicPlane::orientationToTransfrom(orientation, w, h, + &mOrientationTransform); } - mOrientationTransform.set(a, b, c, d); - mOrientationTransform.set(x, y); + mGlobalTransform = mOrientationTransform * mTransform; return NO_ERROR; } diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index 8e5fd88..f7d7764 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -58,7 +58,6 @@ class Layer; class LayerBuffer; class LayerOrientationAnim; class OrientationAnimation; -class RFBServer; class SurfaceHeapManager; typedef int32_t ClientID; @@ -112,6 +111,8 @@ private: class GraphicPlane { public: + static status_t orientationToTransfrom(int orientation, int w, int h, + Transform* tr); GraphicPlane(); ~GraphicPlane(); @@ -344,7 +345,6 @@ private: sp<GPUHardwareInterface> mGPU; GLuint mWormholeTexName; sp<BootAnimation> mBootAnimation; - sp<RFBServer> mRFBServer; nsecs_t mBootTime; // Can only accessed from the main thread, these members diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp index abd3634..d5e9f81 100644 --- a/libs/ui/ISurface.cpp +++ b/libs/ui/ISurface.cpp @@ -37,7 +37,7 @@ ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, int32_t hor_stride, int32_t ver_stride, PixelFormat format, const sp<IMemoryHeap>& heap) : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), - format(format), heap(heap) + format(format), transform(0), flags(0), heap(heap) { } diff --git a/location/java/com/android/internal/location/INetworkLocationManager.java b/location/java/com/android/internal/location/INetworkLocationManager.java index 83bbe1f..6b632fd 100644 --- a/location/java/com/android/internal/location/INetworkLocationManager.java +++ b/location/java/com/android/internal/location/INetworkLocationManager.java @@ -16,6 +16,8 @@ package com.android.internal.location; +import android.content.Context; + /** * Used to register network location and collection services * with the Location Manager Service. @@ -23,6 +25,13 @@ package com.android.internal.location; * {@hide} */ public interface INetworkLocationManager { + + /* callback to allow installation to occur in Location Manager's thread */ + public interface InstallCallback { + void installNetworkLocationProvider(Context context, INetworkLocationManager manager); + } + + void setInstallCallback(InstallCallback callback); void setNetworkLocationProvider(INetworkLocationProvider provider); void setLocationCollector(ILocationCollector collector); }
\ No newline at end of file diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 2978774..4d2e725 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -135,7 +135,7 @@ public class AudioManager { public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL; /** The audio stream for system sounds */ public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM; - /** The audio stream for the phone ring and message alerts */ + /** The audio stream for the phone ring */ public static final int STREAM_RING = AudioSystem.STREAM_RING; /** The audio stream for music playback */ public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC; @@ -214,14 +214,13 @@ public class AudioManager { /** * Whether to include ringer modes as possible options when changing volume. * For example, if true and volume level is 0 and the volume is adjusted - * with {@link #ADJUST_LOWER}, then the ringer mode may switch the silent - * or vibrate mode. + * with {@link #ADJUST_LOWER}, then the ringer mode may switch the silent or + * vibrate mode. * <p> - * By default this is on for stream types that are affected by the ringer - * mode (for example, the ring stream type). If this flag is included, this - * behavior will be present regardless of the stream type being affected by - * the ringer mode. - * + * By default this is on for the ring stream. If this flag is included, + * this behavior will be present regardless of the stream type being + * affected by the ringer mode. + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 1314ba1..fd990fe 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -339,7 +339,11 @@ public class AudioRecord * Releases the native AudioRecord resources. */ public void release() { - stop(); + try { + stop(); + } catch(IllegalStateException ise) { + // don't raise an exception, we're releasing the resources. + } native_release(); mState = STATE_UNINITIALIZED; } @@ -427,6 +431,56 @@ public class AudioRecord public int getPositionNotificationPeriod() { return native_get_pos_update_period(); } + + /** + * {@hide} + * Returns the minimum buffer size required for the successful creation of an AudioRecord + * object. + * @param sampleRateInHz the sample rate expressed in Hertz. + * @param channelConfig describes the configuration of the audio channels. + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT}. + * @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the + * hardware, or an invalid parameter was passed, + * or {@link #ERROR} if the implementation was unable to query the hardware for its + * output properties, + * or the minimum buffer size expressed in of bytes. + */ + static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { + int channelCount = 0; + switch(channelConfig) { + case AudioFormat.CHANNEL_CONFIGURATION_DEFAULT: + case AudioFormat.CHANNEL_CONFIGURATION_MONO: + channelCount = 1; + break; + case AudioFormat.CHANNEL_CONFIGURATION_STEREO: + channelCount = 2; + break; + case AudioFormat.CHANNEL_CONFIGURATION_INVALID: + default: + loge("getMinBufferSize(): Invalid channel configuration."); + return AudioRecord.ERROR_BAD_VALUE; + } + + // PCM_8BIT is not supported at the moment + if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) { + loge("getMinBufferSize(): Invalid audio format."); + return AudioRecord.ERROR_BAD_VALUE; + } + + int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); + if (size == 0) { + return AudioRecord.ERROR_BAD_VALUE; + } + else if (size == -1) { + return AudioRecord.ERROR; + } + else { + return size; + } + } //--------------------------------------------------------- @@ -699,6 +753,9 @@ public class AudioRecord private native final int native_set_pos_update_period(int updatePeriod); private native final int native_get_pos_update_period(); + + static private native final int native_get_min_buff_size( + int sampleRateInHz, int channelCount, int audioFormat); //--------------------------------------------------------- diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index f3d8957..bdabda7 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -349,9 +349,9 @@ public class AudioService extends IAudioService.Stub { // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0 - || isStreamAffectedByRingerMode(streamType)) { + || streamType == AudioManager.STREAM_RING) { // Check if the ringer mode changes with this volume adjustment. If - // it does, it will handle adjusting the volome, so we won't below + // it does, it will handle adjusting the volume, so we won't below adjustVolume = checkForRingerModeChange(oldIndex, direction); } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 9bb1df9..74ffc1a 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -395,7 +395,11 @@ public class AudioTrack public void release() { // even though native_release() stops the native AudioTrack, we need to stop // AudioTrack subclasses too. - stop(); + try { + stop(); + } catch(IllegalStateException ise) { + // don't raise an exception, we're releasing the resources. + } native_release(); mState = STATE_UNINITIALIZED; } @@ -1007,4 +1011,4 @@ public class AudioTrack Log.e(TAG, "[ android.media.AudioTrack ] " + msg); } -}
\ No newline at end of file +} diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 4a30114..3609826 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -19,6 +19,10 @@ package android.media; import android.view.Surface; import android.hardware.Camera; import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileDescriptor; +import android.util.Log; /** * Used to record audio and video. The recording control is based on a @@ -50,6 +54,7 @@ public class MediaRecorder static { System.loadLibrary("media_jni"); } + private final static String TAG = "MediaRecorder"; // The two fields below are accessed by native methods @SuppressWarnings("unused") @@ -57,7 +62,10 @@ public class MediaRecorder @SuppressWarnings("unused") private Surface mSurface; - + + private String mPath; + private FileDescriptor mFd; + /** * Default constructor. */ @@ -71,8 +79,6 @@ public class MediaRecorder * the camera object. Must call before prepare(). * * @param c the Camera to use for recording - * FIXME: Temporarily hidden until API approval - * @hide */ public native void setCamera(Camera c); @@ -104,7 +110,6 @@ public class MediaRecorder /** * Defines the video source. These constants are used with * {@link MediaRecorder#setVideoSource(int)}. - * @hide */ public final class VideoSource { /* Do not change these values without updating their counterparts @@ -152,7 +157,6 @@ public class MediaRecorder /** * Defines the video encoding. These constants are used with * {@link MediaRecorder#setVideoEncoder(int)}. - * @hide */ public final class VideoEncoder { /* Do not change these values without updating their counterparts @@ -187,7 +191,6 @@ public class MediaRecorder * @param video_source the video source to use * @throws IllegalStateException if it is called after setOutputFormat() * @see android.media.MediaRecorder.VideoSource - * @hide */ public native void setVideoSource(int video_source) throws IllegalStateException; @@ -214,7 +217,6 @@ public class MediaRecorder * @param height the height of the video to be captured * @throws IllegalStateException if it is called after * prepare() or before setOutputFormat() - * @hide */ public native void setVideoSize(int width, int height) throws IllegalStateException; @@ -227,7 +229,10 @@ public class MediaRecorder * @param rate the number of frames per second of video to capture * @throws IllegalStateException if it is called after * prepare() or before setOutputFormat(). - * @hide + * + * NOTE: On some devices that have auto-frame rate, this sets the + * maximum frame rate, not a constant frame rate. Actual frame rate + * will vary according to lighting conditions. */ public native void setVideoFrameRate(int rate) throws IllegalStateException; @@ -253,21 +258,43 @@ public class MediaRecorder * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare() * @see android.media.MediaRecorder.VideoEncoder - * @hide */ public native void setVideoEncoder(int video_encoder) throws IllegalStateException; /** + * Pass in the file descriptor of the file to be written. Call this after + * setOutputFormat() but before prepare(). + * + * @param fd an open file descriptor to be written into. + * @throws IllegalStateException if it is called before + * setOutputFormat() or after prepare() + */ + public void setOutputFile(FileDescriptor fd) throws IllegalStateException + { + mPath = null; + mFd = fd; + } + + /** * Sets the path of the output file to be produced. Call this after * setOutputFormat() but before prepare(). * - * @param path The pathname to use() + * @param path The pathname to use. * @throws IllegalStateException if it is called before * setOutputFormat() or after prepare() */ - public native void setOutputFile(String path) throws IllegalStateException; + public void setOutputFile(String path) throws IllegalStateException + { + mFd = null; + mPath = path; + } + // native implementation + private native void _setOutputFile(FileDescriptor fd, long offset, long length) + throws IllegalStateException, IOException; + private native void _prepare() throws IllegalStateException, IOException; + /** * Prepares the recorder to begin capturing and encoding data. This method * must be called after setting up the desired audio and video sources, @@ -277,7 +304,18 @@ public class MediaRecorder * start() or before setOutputFormat(). * @throws IOException if prepare fails otherwise. */ - public native void prepare() throws IllegalStateException, IOException; + public void prepare() throws IllegalStateException, IOException + { + if (mPath != null) { + FileOutputStream f = new FileOutputStream(mPath); + _setOutputFile(f.getFD(), 0, 0); + } else if (mFd != null) { + _setOutputFile(mFd, 0, 0); + } else { + throw new IOException("No valid output file"); + } + _prepare(); + } /** * Begins capturing and encoding data to the file specified with diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 8eb638e..095749b 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -143,25 +143,17 @@ android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae) } static void -android_media_MediaRecorder_setOutputFile(JNIEnv *env, jobject thiz, jstring path) +android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { LOGV("setOutputFile"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); - - if (path == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Path is a NULL pointer"); - return; - } - const char *pathStr = env->GetStringUTFChars(path, NULL); - if (pathStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - status_t opStatus = mr->setOutputFile(pathStr); - - // Make sure that local ref is released before a potential exception - env->ReleaseStringUTFChars(path, pathStr); - process_media_recorder_call(env, opStatus, "java/lang/RuntimeException", "setOutputFile failed."); + int fd = getParcelFileDescriptorFD(env, fileDescriptor); + MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + status_t opStatus = mr->setOutputFile(fd, offset, length); + process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); } static void @@ -273,23 +265,23 @@ android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz) // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { - {"setCamera", "(Landroid/hardware/Camera;)V",(void *)android_media_MediaRecorder_setCamera}, - {"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource}, - {"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource}, - {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, - {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, - {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, - {"setOutputFile", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setOutputFile}, - {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, - {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, - {"prepare", "()V", (void *)android_media_MediaRecorder_prepare}, - {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, - {"start", "()V", (void *)android_media_MediaRecorder_start}, - {"stop", "()V", (void *)android_media_MediaRecorder_stop}, - {"reset", "()V", (void *)android_media_MediaRecorder_reset}, - {"release", "()V", (void *)android_media_MediaRecorder_release}, - {"native_setup", "()V", (void *)android_media_MediaRecorder_native_setup}, - {"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize}, + {"setCamera", "(Landroid/hardware/Camera;)V", (void *)android_media_MediaRecorder_setCamera}, + {"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource}, + {"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource}, + {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, + {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, + {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, + {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, + {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, + {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, + {"_prepare", "()V", (void *)android_media_MediaRecorder_prepare}, + {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, + {"start", "()V", (void *)android_media_MediaRecorder_start}, + {"stop", "()V", (void *)android_media_MediaRecorder_stop}, + {"reset", "()V", (void *)android_media_MediaRecorder_reset}, + {"release", "()V", (void *)android_media_MediaRecorder_release}, + {"native_setup", "()V", (void *)android_media_MediaRecorder_native_setup}, + {"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize}, }; static const char* const kClassPathName = "android/media/MediaRecorder"; diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 559f9d5..02731825 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -491,10 +491,11 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV // initialize track int afFrameCount; int afSampleRate; - if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + int streamType = mSoundPool->streamType(); + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; } - if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { afSampleRate = kDefaultSampleRate; } int numChannels = sample->numChannels(); @@ -522,10 +523,10 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV void *userData = (void *)((unsigned long)this | toggle); #ifdef USE_SHARED_MEM_BUFFER - newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), numChannels, sample->getIMemory(), 0, callback, userData); #else - newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), numChannels, frameCount, 0, callback, userData, bufferFrames); #endif if (newTrack->initCheck() != NO_ERROR) { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index a987b92..3d39181 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -128,22 +128,8 @@ status_t AudioRecord::set( return BAD_VALUE; } - size_t inputBuffSizeInBytes = -1; - if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes) - != NO_ERROR) { - LOGE("AudioSystem could not query the input buffer size."); - return NO_INIT; - } - if (inputBuffSizeInBytes == 0) { - LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d", - sampleRate, channelCount, format); - return BAD_VALUE; - } - int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1); - - // We use 2* size of input buffer for ping pong use of record buffer. - int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes; - LOGV("AudioRecord::set() minFrameCount = %d", minFrameCount); + // TODO: Get input frame count from hardware. + int minFrameCount = 1024*2; if (frameCount == 0) { frameCount = minFrameCount; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index cf91105..63dfc3b 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -20,7 +20,6 @@ #include <utils/Log.h> #include <utils/IServiceManager.h> #include <media/AudioSystem.h> -#include <media/AudioTrack.h> #include <math.h> namespace android { @@ -31,9 +30,10 @@ sp<IAudioFlinger> AudioSystem::gAudioFlinger; sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; // Cached values -int AudioSystem::gOutSamplingRate = 0; -int AudioSystem::gOutFrameCount = 0; -uint32_t AudioSystem::gOutLatency = 0; +int AudioSystem::gOutSamplingRate[NUM_AUDIO_OUTPUT_TYPES]; +int AudioSystem::gOutFrameCount[NUM_AUDIO_OUTPUT_TYPES]; +uint32_t AudioSystem::gOutLatency[NUM_AUDIO_OUTPUT_TYPES]; +bool AudioSystem::gA2dpEnabled; // Cached values for recording queries uint32_t AudioSystem::gPrevInSamplingRate = 16000; int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT; @@ -66,9 +66,12 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() gAudioFlinger = interface_cast<IAudioFlinger>(binder); gAudioFlinger->registerClient(gAudioFlingerClient); // Cache frequently accessed parameters - gOutFrameCount = (int)gAudioFlinger->frameCount(); - gOutSamplingRate = (int)gAudioFlinger->sampleRate(); - gOutLatency = gAudioFlinger->latency(); + for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) { + gOutFrameCount[output] = (int)gAudioFlinger->frameCount(output); + gOutSamplingRate[output] = (int)gAudioFlinger->sampleRate(output); + gOutLatency[output] = gAudioFlinger->latency(output); + } + gA2dpEnabled = gAudioFlinger->isA2dpEnabled(); } LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); return gAudioFlinger; @@ -147,7 +150,7 @@ status_t AudioSystem::getMasterMute(bool* mute) status_t AudioSystem::setStreamVolume(int stream, float value) { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; af->setStreamVolume(stream, value); @@ -156,7 +159,7 @@ status_t AudioSystem::setStreamVolume(int stream, float value) status_t AudioSystem::setStreamMute(int stream, bool mute) { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; af->setStreamMute(stream, mute); @@ -165,7 +168,7 @@ status_t AudioSystem::setStreamMute(int stream, bool mute) status_t AudioSystem::getStreamVolume(int stream, float* volume) { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; *volume = af->streamVolume(stream); @@ -174,7 +177,7 @@ status_t AudioSystem::getStreamVolume(int stream, float* volume) status_t AudioSystem::getStreamMute(int stream, bool* mute) { - if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; *mute = af->streamMute(stream); @@ -252,37 +255,48 @@ int AudioSystem::logToLinear(float volume) return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; } -status_t AudioSystem::getOutputSamplingRate(int* samplingRate) +status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) { - if (gOutSamplingRate == 0) { + int output = getOutput(streamType); + + if (gOutSamplingRate[output] == 0) { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; // gOutSamplingRate is updated by get_audio_flinger() } - *samplingRate = gOutSamplingRate; + LOGV("getOutputSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, gOutSamplingRate[output]); + *samplingRate = gOutSamplingRate[output]; return NO_ERROR; } -status_t AudioSystem::getOutputFrameCount(int* frameCount) +status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) { - if (gOutFrameCount == 0) { + int output = getOutput(streamType); + + if (gOutFrameCount[output] == 0) { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; // gOutFrameCount is updated by get_audio_flinger() } - *frameCount = gOutFrameCount; + LOGV("getOutputFrameCount() streamType %d, output %d, frame count %d", streamType, output, gOutFrameCount[output]); + + *frameCount = gOutFrameCount[output]; return NO_ERROR; } -status_t AudioSystem::getOutputLatency(uint32_t* latency) +status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType) { - if (gOutLatency == 0) { + int output = getOutput(streamType); + + if (gOutLatency[output] == 0) { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; // gOutLatency is updated by get_audio_flinger() - } - *latency = gOutLatency; + } + LOGV("getOutputLatency() streamType %d, output %d, latency %d", streamType, output, gOutLatency[output]); + + *latency = gOutLatency[output]; return NO_ERROR; } @@ -315,24 +329,23 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int ch void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(AudioSystem::gLock); AudioSystem::gAudioFlinger.clear(); - AudioSystem::gOutSamplingRate = 0; - AudioSystem::gOutFrameCount = 0; - AudioSystem::gOutLatency = 0; + + for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) { + gOutFrameCount[output] = 0; + gOutSamplingRate[output] = 0; + gOutLatency[output] = 0; + } AudioSystem::gInBuffSize = 0; - + if (gAudioErrorCallback) { gAudioErrorCallback(DEAD_OBJECT); } LOGW("AudioFlinger server died!"); } -void AudioSystem::AudioFlingerClient::audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) { - - AudioSystem::gOutFrameCount = frameCount; - AudioSystem::gOutSamplingRate = samplingRate; - AudioSystem::gOutLatency = latency; - - LOGV("AudioFlinger output changed!"); +void AudioSystem::AudioFlingerClient::a2dpEnabledChanged(bool enabled) { + gA2dpEnabled = enabled; + LOGV("AudioFlinger A2DP enabled status changed! %d", enabled); } void AudioSystem::setErrorCallback(audio_error_callback cb) { @@ -340,5 +353,31 @@ void AudioSystem::setErrorCallback(audio_error_callback cb) { gAudioErrorCallback = cb; } +int AudioSystem::getOutput(int streamType) +{ + if (streamType == DEFAULT) { + streamType = MUSIC; + } + if (gA2dpEnabled && routedToA2dpOutput(streamType)) { + return AUDIO_OUTPUT_A2DP; + } else { + return AUDIO_OUTPUT_HARDWARE; + } +} + +bool AudioSystem::routedToA2dpOutput(int streamType) { + switch(streamType) { + case MUSIC: + case VOICE_CALL: + case BLUETOOTH_SCO: + case SYSTEM: + return true; + default: + return false; + } +} + + + }; // namespace android diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 63b2012..1ffad46 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -128,22 +128,21 @@ status_t AudioTrack::set( return NO_INIT; } int afSampleRate; - if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } int afFrameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { return NO_INIT; } uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { return NO_INIT; } - // handle default values first. - if (streamType == DEFAULT) { - streamType = MUSIC; + if (streamType == AudioSystem::DEFAULT) { + streamType = AudioSystem::MUSIC; } if (sampleRate == 0) { sampleRate = afSampleRate; @@ -260,7 +259,7 @@ status_t AudioTrack::set( mMarkerPosition = 0; mNewPosition = 0; mUpdatePeriod = 0; - + return NO_ERROR; } @@ -434,7 +433,7 @@ void AudioTrack::setSampleRate(int rate) { int afSamplingRate; - if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) { return; } // Resampler implementation limits input sampling rate to 2 x output sampling rate. diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 4215820..5cbb25c 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -53,7 +53,8 @@ enum { SET_PARAMETER, REGISTER_CLIENT, GET_INPUTBUFFERSIZE, - WAKE_UP + WAKE_UP, + IS_A2DP_ENABLED }; class BpAudioFlinger : public BpInterface<IAudioFlinger> @@ -123,42 +124,47 @@ public: return interface_cast<IAudioRecord>(reply.readStrongBinder()); } - virtual uint32_t sampleRate() const + virtual uint32_t sampleRate(int output) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); remote()->transact(SAMPLE_RATE, data, &reply); return reply.readInt32(); } - virtual int channelCount() const + virtual int channelCount(int output) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); remote()->transact(CHANNEL_COUNT, data, &reply); return reply.readInt32(); } - virtual int format() const + virtual int format(int output) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); remote()->transact(FORMAT, data, &reply); return reply.readInt32(); } - virtual size_t frameCount() const + virtual size_t frameCount(int output) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); remote()->transact(FRAME_COUNT, data, &reply); return reply.readInt32(); } - virtual uint32_t latency() const + virtual uint32_t latency(int output) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); remote()->transact(LATENCY, data, &reply); return reply.readInt32(); } @@ -333,6 +339,14 @@ public: remote()->transact(WAKE_UP, data, &reply); return; } + + virtual bool isA2dpEnabled() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(IS_A2DP_ENABLED, data, &reply); + return (bool)reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); @@ -385,27 +399,32 @@ status_t BnAudioFlinger::onTransact( } break; case SAMPLE_RATE: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( sampleRate() ); + int output = data.readInt32(); + reply->writeInt32( sampleRate(output) ); return NO_ERROR; } break; case CHANNEL_COUNT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( channelCount() ); + int output = data.readInt32(); + reply->writeInt32( channelCount(output) ); return NO_ERROR; } break; case FORMAT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( format() ); + int output = data.readInt32(); + reply->writeInt32( format(output) ); return NO_ERROR; } break; case FRAME_COUNT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( frameCount() ); + int output = data.readInt32(); + reply->writeInt32( frameCount(output) ); return NO_ERROR; } break; case LATENCY: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( latency() ); + int output = data.readInt32(); + reply->writeInt32( latency(output) ); return NO_ERROR; } break; case SET_MASTER_VOLUME: { @@ -519,7 +538,11 @@ status_t BnAudioFlinger::onTransact( wakeUp(); return NO_ERROR; } break; - + case IS_A2DP_ENABLED: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( (int)isA2dpEnabled() ); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index d956266..5feb11f 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -38,13 +38,11 @@ public: { } - void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) + void a2dpEnabledChanged(bool enabled) { Parcel data, reply; data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor()); - data.writeInt32(frameCount); - data.writeInt32(samplingRate); - data.writeInt32(latency); + data.writeInt32((int)enabled); remote()->transact(AUDIO_OUTPUT_CHANGED, data, &reply); } }; @@ -65,10 +63,8 @@ status_t BnAudioFlingerClient::onTransact( switch(code) { case AUDIO_OUTPUT_CHANGED: { CHECK_INTERFACE(IAudioFlingerClient, data, reply); - uint32_t frameCount = data.readInt32(); - uint32_t samplingRate = data.readInt32(); - uint32_t latency = data.readInt32(); - audioOutputChanged(frameCount, samplingRate, latency); + bool enabled = (bool)data.readInt32(); + a2dpEnabledChanged(enabled); return NO_ERROR; } break; default: diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index 1f6d599..507d03e 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -39,7 +39,8 @@ enum { SET_OUTPUT_FORMAT, SET_VIDEO_ENCODER, SET_AUDIO_ENCODER, - SET_OUTPUT_FILE, + SET_OUTPUT_FILE_PATH, + SET_OUTPUT_FILE_FD, SET_VIDEO_SIZE, SET_VIDEO_FRAMERATE, SET_PREVIEW_SURFACE, @@ -139,7 +140,18 @@ public: Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); data.writeCString(path); - remote()->transact(SET_OUTPUT_FILE, data, &reply); + remote()->transact(SET_OUTPUT_FILE_PATH, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFile(int fd, int64_t offset, int64_t length) { + LOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(SET_OUTPUT_FILE_FD, data, &reply); return reply.readInt32(); } @@ -330,13 +342,22 @@ status_t BnMediaRecorder::onTransact( return NO_ERROR; } break; - case SET_OUTPUT_FILE: { - LOGV("SET_OUTPUT_FILE"); + case SET_OUTPUT_FILE_PATH: { + LOGV("SET_OUTPUT_FILE_PATH"); CHECK_INTERFACE(IMediaRecorder, data, reply); const char* path = data.readCString(); reply->writeInt32(setOutputFile(path)); return NO_ERROR; } break; + case SET_OUTPUT_FILE_FD: { + LOGV("SET_OUTPUT_FILE_FD"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + reply->writeInt32(setOutputFile(fd, offset, length)); + return NO_ERROR; + } break; case SET_VIDEO_SIZE: { LOGV("SET_VIDEO_SIZE"); CHECK_INTERFACE(IMediaRecorder, data, reply); diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index ead24d4..9bd75c2 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -96,7 +96,7 @@ int JetPlayer::init() // create the output AudioTrack mAudioTrack = new AudioTrack(); - mAudioTrack->set(AudioTrack::MUSIC, //TODO parametrize this + mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this pLibConfig->sampleRate, 1, // format = PCM 16bits per sample, pLibConfig->numChannels, diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index fa36460..7fafc56 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -93,7 +93,7 @@ ToneGenerator::ToneGenerator(int streamType, float volume) { mState = TONE_IDLE; - if (AudioSystem::getOutputSamplingRate(&mSamplingRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) { LOGE("Unable to marshal AudioFlinger"); return; } @@ -182,7 +182,7 @@ bool ToneGenerator::startTone(int toneType) { mLock.lock(); if (mState == TONE_STARTING) { if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) { - LOGE("--- timed out"); + LOGE("--- Immediate start timed out"); mState = TONE_IDLE; } } @@ -200,7 +200,7 @@ bool ToneGenerator::startTone(int toneType) { } LOGV("cond received"); } else { - LOGE("--- timed out"); + LOGE("--- Delayed start timed out"); mState = TONE_IDLE; } } @@ -235,7 +235,7 @@ void ToneGenerator::stopTone() { if (lStatus == NO_ERROR) { LOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); } else { - LOGE("--- timed out"); + LOGE("--- Stop timed out"); mState = TONE_IDLE; mpAudioTrack->stop(); } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 31ff507..bd8579c 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -82,7 +82,7 @@ MediaPlayer::MediaPlayer() mListener = NULL; mCookie = NULL; mDuration = -1; - mStreamType = AudioTrack::MUSIC; + mStreamType = AudioSystem::MUSIC; mCurrentPosition = -1; mSeekPosition = -1; mCurrentState = MEDIA_PLAYER_IDLE; diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 6ee4c0d..4ab26ac 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -248,7 +248,33 @@ status_t MediaRecorder::setOutputFile(const char* path) status_t ret = mMediaRecorder->setOutputFile(path); if (OK != ret) { - LOGV("setAudioEncoder failed: %d", ret); + LOGV("setOutputFile failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsOutputFileSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFile(int fd, int64_t offset, int64_t length) +{ + LOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsOutputFileSet) { + LOGE("output file has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setOutputFile called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFile(fd, offset, length); + if (OK != ret) { + LOGV("setOutputFile failed: %d", ret); mCurrentState = MEDIA_RECORDER_ERROR; return UNKNOWN_ERROR; } @@ -306,7 +332,7 @@ status_t MediaRecorder::prepare() return INVALID_OPERATION; } if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { - LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + LOGE("prepare called in an invalid state: %d", mCurrentState); return INVALID_OPERATION; } @@ -328,7 +354,7 @@ status_t MediaRecorder::getMaxAmplitude(int* max) return INVALID_OPERATION; } if (mCurrentState & MEDIA_RECORDER_ERROR) { - LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + LOGE("getMaxAmplitude called in an invalid state: %d", mCurrentState); return INVALID_OPERATION; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 9e366e2..97e3536 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -818,7 +818,7 @@ Exit: MediaPlayerService::AudioOutput::AudioOutput() { mTrack = 0; - mStreamType = AudioTrack::MUSIC; + mStreamType = AudioSystem::MUSIC; mLeftVolume = 1.0; mRightVolume = 1.0; mLatency = 0; @@ -900,15 +900,15 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC int afFrameCount; int frameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { return NO_INIT; } - if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { return NO_INIT; } - frameCount = (sampleRate*afFrameCount)/afSampleRate; - AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount*bufferCount); + frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate; + AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount); if ((t == 0) || (t->initCheck() != NO_ERROR)) { LOGE("Unable to create audio track"); delete t; diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index f326a0e..e8ba17f 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -121,6 +121,17 @@ status_t MediaRecorderClient::setOutputFile(const char* path) return mRecorder->setOutputFile(path); } +status_t MediaRecorderClient::setOutputFile(int fd, int64_t offset, int64_t length) +{ + LOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setOutputFile(fd, offset, length); +} + status_t MediaRecorderClient::setVideoSize(int width, int height) { LOGV("setVideoSize(%dx%d)", width, height); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 3158017..2b80c10 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -36,6 +36,7 @@ public: virtual status_t setVideoEncoder(int ve); virtual status_t setAudioEncoder(int ae); virtual status_t setOutputFile(const char* path); + virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setVideoSize(int width, int height); virtual status_t setVideoFrameRate(int frames_per_second); virtual status_t prepare(); diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 7ce2fab..d03caa5 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -58,7 +58,7 @@ static const S_EAS_LIB_CONFIG* pLibConfig = NULL; MidiFile::MidiFile() : mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL), mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR), - mStreamType(AudioTrack::MUSIC), mLoop(false), mExit(false), + mStreamType(AudioSystem::MUSIC), mLoop(false), mExit(false), mPaused(false), mRender(false), mTid(-1) { LOGV("constructor"); diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index 009d628..0ad335f 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -55,7 +55,7 @@ static status_t STATE_OPEN = 2; VorbisPlayer::VorbisPlayer() : mAudioBuffer(NULL), mPlayTime(-1), mDuration(-1), mState(STATE_ERROR), - mStreamType(AudioTrack::MUSIC), mLoop(false), mAndroidLoop(false), + mStreamType(AudioSystem::MUSIC), mLoop(false), mAndroidLoop(false), mExit(false), mPaused(false), mRender(false), mRenderTid(-1) { LOGV("constructor\n"); diff --git a/preloaded-classes b/preloaded-classes index 1ace628..61390db 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -340,7 +340,7 @@ android.view.TouchDelegate android.view.VelocityTracker android.view.View android.view.View$AttachInfo -android.view.View$AttachInfo$SoundEffectPlayer +android.view.View$AttachInfo$Callbacks android.view.View$BaseSavedState$1 android.view.View$MeasureSpec android.view.View$ScrollabilityCache diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java index 5ef0fb9..ddf3afe 100644 --- a/services/java/com/android/server/GadgetService.java +++ b/services/java/com/android/server/GadgetService.java @@ -31,9 +31,10 @@ import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.gadget.GadgetManager; -import android.gadget.GadgetInfo; +import android.gadget.GadgetProviderInfo; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; @@ -78,7 +79,7 @@ class GadgetService extends IGadgetService.Stub static class Provider { int uid; - GadgetInfo info; + GadgetProviderInfo info; ArrayList<GadgetId> instances = new ArrayList(); PendingIntent broadcast; @@ -106,7 +107,7 @@ class GadgetService extends IGadgetService.Stub PackageManager mPackageManager; AlarmManager mAlarmManager; ArrayList<Provider> mInstalledProviders = new ArrayList(); - int mNextGadgetId = 1; + int mNextGadgetId = GadgetManager.INVALID_GADGET_ID + 1; ArrayList<GadgetId> mGadgetIds = new ArrayList(); ArrayList<Host> mHosts = new ArrayList(); @@ -128,7 +129,6 @@ class GadgetService extends IGadgetService.Stub // update the provider list. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -148,7 +148,7 @@ class GadgetService extends IGadgetService.Stub int N = mInstalledProviders.size(); pw.println("Providers: (size=" + N + ")"); for (int i=0; i<N; i++) { - GadgetInfo info = mInstalledProviders.get(i).info; + GadgetProviderInfo info = mInstalledProviders.get(i).info; pw.println(" [" + i + "] provder=" + info.provider + " min=(" + info.minWidth + "x" + info.minHeight + ")" + " updatePeriodMillis=" + info.updatePeriodMillis @@ -265,7 +265,7 @@ class GadgetService extends IGadgetService.Stub if (p != null) { p.instances.remove(id); // send the broacast saying that this gadgetId has been deleted - Intent intent = new Intent(GadgetManager.GADGET_DELETED_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_DELETED); intent.setComponent(p.info.provider); intent.putExtra(GadgetManager.EXTRA_GADGET_ID, id.gadgetId); mContext.sendBroadcast(intent); @@ -274,7 +274,7 @@ class GadgetService extends IGadgetService.Stub cancelBroadcasts(p); // send the broacast saying that the provider is not in use any more - intent = new Intent(GadgetManager.GADGET_DISABLED_ACTION); + intent = new Intent(GadgetManager.ACTION_GADGET_DISABLED); intent.setComponent(p.info.provider); mContext.sendBroadcast(intent); } @@ -331,7 +331,7 @@ class GadgetService extends IGadgetService.Stub } } - public GadgetInfo getGadgetInfo(int gadgetId) { + public GadgetProviderInfo getGadgetInfo(int gadgetId) { synchronized (mGadgetIds) { GadgetId id = lookupGadgetIdLocked(gadgetId); if (id != null) { @@ -351,10 +351,10 @@ class GadgetService extends IGadgetService.Stub } } - public List<GadgetInfo> getInstalledProviders() { + public List<GadgetProviderInfo> getInstalledProviders() { synchronized (mGadgetIds) { final int N = mInstalledProviders.size(); - ArrayList<GadgetInfo> result = new ArrayList(N); + ArrayList<GadgetProviderInfo> result = new ArrayList(N); for (int i=0; i<N; i++) { result.add(mInstalledProviders.get(i).info); } @@ -364,7 +364,7 @@ class GadgetService extends IGadgetService.Stub public void updateGadgetIds(int[] gadgetIds, RemoteViews views) { if (gadgetIds == null) { - throw new IllegalArgumentException("bad gadgetIds"); + return; } if (gadgetIds.length == 0) { return; @@ -408,7 +408,7 @@ class GadgetService extends IGadgetService.Stub // the lock is held, but this is a oneway call id.host.callbacks.updateGadget(id.gadgetId, views); } catch (RemoteException e) { - // It failed, remove the callback. No need to prune because + // It failed; remove the callback. No need to prune because // we know that this host is still referenced by this instance. id.host.callbacks = null; } @@ -524,10 +524,7 @@ class GadgetService extends IGadgetService.Stub void getGadgetList() { PackageManager pm = mPackageManager; - // TODO: We have these as different actions. I wonder if it makes more sense to - // have like a GADGET_ACTION, and then subcommands. It's kind of arbitrary that - // we look for GADGET_UPDATE_ACTION and not any of the other gadget actions. - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -538,11 +535,14 @@ class GadgetService extends IGadgetService.Stub } } - void addProviderLocked(ResolveInfo ri) { - Provider p = parseGadgetInfoXml(new ComponentName(ri.activityInfo.packageName, + boolean addProviderLocked(ResolveInfo ri) { + Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name), ri); if (p != null) { mInstalledProviders.add(p); + return true; + } else { + return false; } } @@ -568,16 +568,18 @@ class GadgetService extends IGadgetService.Stub } void sendEnableIntentLocked(Provider p) { - Intent intent = new Intent(GadgetManager.GADGET_ENABLED_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_ENABLED); intent.setComponent(p.info.provider); mContext.sendBroadcast(intent); } void sendUpdateIntentLocked(Provider p, int[] gadgetIds) { - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); - intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); - intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent); + if (gadgetIds != null && gadgetIds.length > 0) { + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); + intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); + intent.setComponent(p.info.provider); + mContext.sendBroadcast(intent); + } } void registerForBroadcastsLocked(Provider p, int[] gadgetIds) { @@ -587,7 +589,7 @@ class GadgetService extends IGadgetService.Stub // PendingIntent.getBroadcast will update the extras. boolean alreadyRegistered = p.broadcast != null; int instancesSize = p.instances.size(); - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); intent.setComponent(p.info.provider); long token = Binder.clearCallingIdentity(); @@ -614,16 +616,16 @@ class GadgetService extends IGadgetService.Stub return gadgetIds; } - private Provider parseGadgetInfoXml(ComponentName component, ResolveInfo ri) { + private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { Provider p = null; ActivityInfo activityInfo = ri.activityInfo; XmlResourceParser parser = null; try { parser = activityInfo.loadXmlMetaData(mPackageManager, - GadgetManager.GADGET_PROVIDER_META_DATA); + GadgetManager.META_DATA_GADGET_PROVIDER); if (parser == null) { - Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for " + Log.w(TAG, "No " + GadgetManager.META_DATA_GADGET_PROVIDER + " meta-data for " + "gadget provider '" + component + '\''); return null; } @@ -644,7 +646,7 @@ class GadgetService extends IGadgetService.Stub } p = new Provider(); - GadgetInfo info = p.info = new GadgetInfo(); + GadgetProviderInfo info = p.info = new GadgetProviderInfo(); info.provider = component; p.uid = activityInfo.applicationInfo.uid; @@ -672,6 +674,7 @@ class GadgetService extends IGadgetService.Stub // of what a client process passes to us should not be fatal for the // system process. Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e); + return null; } finally { if (parser != null) parser.close(); } @@ -979,20 +982,26 @@ class GadgetService extends IGadgetService.Stub if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { synchronized (mGadgetIds) { - addProvidersForPackageLocked(pkgName); - saveStateLocked(); - } - } - else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - synchronized (mGadgetIds) { - updateProvidersForPackageLocked(pkgName); + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package was just upgraded + updateProvidersForPackageLocked(pkgName); + } else { + // The package was just added + addProvidersForPackageLocked(pkgName); + } saveStateLocked(); } } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - synchronized (mGadgetIds) { - removeProvidersForPackageLocked(pkgName); - saveStateLocked(); + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package is being updated. We'll receive a PACKAGE_ADDED shortly. + } else { + synchronized (mGadgetIds) { + removeProvidersForPackageLocked(pkgName); + saveStateLocked(); + } } } } @@ -1002,7 +1011,7 @@ class GadgetService extends IGadgetService.Stub // TODO: If there's a better way of matching an intent filter against the // packages for a given package, use that. void addProvidersForPackageLocked(String pkgName) { - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1021,7 +1030,7 @@ class GadgetService extends IGadgetService.Stub // packages for a given package, use that. void updateProvidersForPackageLocked(String pkgName) { HashSet<String> keep = new HashSet(); - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1031,17 +1040,53 @@ class GadgetService extends IGadgetService.Stub ResolveInfo ri = broadcastReceivers.get(i); ActivityInfo ai = ri.activityInfo; if (pkgName.equals(ai.packageName)) { - Provider p = lookupProviderLocked(new ComponentName(ai.packageName, ai.name)); + ComponentName component = new ComponentName(ai.packageName, ai.name); + Provider p = lookupProviderLocked(component); if (p == null) { - addProviderLocked(ri); + if (addProviderLocked(ri)) { + keep.add(ai.name); + } + } else { + Provider parsed = parseProviderInfoXml(component, ri); + if (parsed != null) { + keep.add(ai.name); + // Use the new GadgetProviderInfo. + GadgetProviderInfo oldInfo = p.info; + p.info = parsed.info; + // If it's enabled + final int M = p.instances.size(); + if (M > 0) { + int[] gadgetIds = getGadgetIds(p); + // Reschedule for the new updatePeriodMillis (don't worry about handling + // it specially if updatePeriodMillis didn't change because we just sent + // an update, and the next one will be updatePeriodMillis from now). + cancelBroadcasts(p); + registerForBroadcastsLocked(p, gadgetIds); + // If it's currently showing, call back with the new GadgetProviderInfo. + for (int j=0; j<M; j++) { + GadgetId id = p.instances.get(j); + if (id.host != null && id.host.callbacks != null) { + try { + id.host.callbacks.providerChanged(id.gadgetId, p.info); + } catch (RemoteException ex) { + // It failed; remove the callback. No need to prune because + // we know that this host is still referenced by this + // instance. + id.host.callbacks = null; + } + } + } + // Now that we've told the host, push out an update. + sendUpdateIntentLocked(p, gadgetIds); + } + } } - keep.add(ai.name); } } // prune the ones we don't want to keep N = mInstalledProviders.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName()) && !keep.contains(p.info.provider.getClassName())) { @@ -1052,7 +1097,7 @@ class GadgetService extends IGadgetService.Stub void removeProvidersForPackageLocked(String pkgName) { int N = mInstalledProviders.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName())) { removeProviderLocked(i, p); @@ -1064,7 +1109,7 @@ class GadgetService extends IGadgetService.Stub // By now, we have removed any gadgets that were in any hosts here, // so we don't need to worry about sending DISABLE broadcasts to them. N = mHosts.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Host host = mHosts.get(i); if (pkgName.equals(host.packageName)) { deleteHostLocked(host); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index ee49365..7588129 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -55,8 +55,10 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -98,6 +100,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_UNBIND_METHOD = 3000; static final int MSG_BIND_METHOD = 3010; + static final long TIME_TO_RECONNECT = 10*1000; + + static final int LOG_IMF_FORCE_RECONNECT_IME = 32000; + final Context mContext; final Handler mHandler; final SettingsObserver mSettingsObserver; @@ -248,6 +254,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IInputMethod mCurMethod; /** + * Time that we last initiated a bind to the input method, to determine + * if we should try to disconnect and reconnect to it. + */ + long mLastBindTime; + + /** * Have we called mCurMethod.bindInput()? */ boolean mBoundToMethod; @@ -486,7 +498,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void systemReady() { - } public List<InputMethodInfo> getInputMethodList() { @@ -571,7 +582,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - private int getShowFlags() { + private int getImeShowFlags() { int flags = 0; if (mShowForced) { flags |= InputMethod.SHOW_FORCED @@ -582,6 +593,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return flags; } + private int getAppShowFlags() { + int flags = 0; + if (mShowForced) { + flags |= InputMethodManager.SHOW_FORCED; + } else if (!mShowExplicitlyRequested) { + flags |= InputMethodManager.SHOW_IMPLICIT; + } + return flags; + } + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( @@ -598,7 +619,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mShowRequested) { if (DEBUG) Log.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(getShowFlags()); + showCurrentInputLocked(getAppShowFlags()); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) @@ -666,14 +687,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return attachNewInputLocked(initial, needResult); } if (mHaveConnection) { - if (mCurMethod != null && !cs.sessionRequested) { - cs.sessionRequested = true; - if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_CREATE_SESSION, mCurMethod, - new MethodCallback(mCurMethod))); + if (mCurMethod != null) { + if (!cs.sessionRequested) { + cs.sessionRequested = true; + if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_CREATE_SESSION, mCurMethod, + new MethodCallback(mCurMethod))); + } + // Return to client, and we will get back with it when + // we have had a session made for it. + return new InputBindResult(null, mCurId, mCurSeq); + } else if (SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // In this case we have connected to the service, but + // don't yet have its interface. If it hasn't been too + // long since we did the connection, we'll return to + // the client and wait to get the service interface so + // we can report back. If it has been too long, we want + // to fall through so we can try a disconnect/reconnect + // to see if we can get back in touch with the service. + return new InputBindResult(null, mCurId, mCurSeq); + } else { + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime, 0); } - return new InputBindResult(null, mCurId, mCurSeq); } } @@ -682,6 +720,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } + if (mHaveConnection) { + mContext.unbindService(this); + mHaveConnection = false; + } + if (mCurToken != null) { try { if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken); @@ -691,16 +734,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurToken = null; } - if (mHaveConnection) { - mContext.unbindService(this); - mHaveConnection = false; - } - clearCurMethod(); mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) { + mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; mCurId = info.getId(); mCurToken = new Binder(); @@ -758,7 +797,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void onSessionCreated(IInputMethod method, IInputMethodSession session) { synchronized (mMethodMap) { - if (mCurMethod == method) { + if (mCurMethod != null && method != null + && mCurMethod.asBinder() == method.asBinder()) { if (mCurClient != null) { mCurClient.curSession = new SessionState(mCurClient, method, session); @@ -781,6 +821,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mCurMethod = null; } + mStatusBar.setIconVisibility(mInputMethodIcon, false); } public void onServiceDisconnected(ComponentName name) { @@ -790,6 +831,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurMethod != null && mCurIntent != null && name.equals(mCurIntent.getComponent())) { clearCurMethod(); + // We consider this to be a new bind attempt, since the system + // should now try to restart the service for us. + mLastBindTime = SystemClock.uptimeMillis(); mShowRequested = mInputShown; mInputShown = false; if (mCurClient != null) { @@ -800,23 +844,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public void updateStatusIcon(int iconId, String iconPackage) { - if (iconId == 0) { - Log.d(TAG, "hide the small icon for the input method"); - mStatusBar.setIconVisibility(mInputMethodIcon, false); - } else { - Log.d(TAG, "show a small icon for the input method"); - - if (iconPackage != null - && iconPackage - .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { - iconPackage = null; + public void updateStatusIcon(IBinder token, String packageName, int iconId) { + long ident = Binder.clearCallingIdentity(); + try { + if (token == null || mCurToken != token) { + Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - - mInputMethodData.iconId = iconId; - mInputMethodData.iconPackage = iconPackage; - mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); - mStatusBar.setIconVisibility(mInputMethodIcon, true); + + synchronized (mMethodMap) { + if (iconId == 0) { + if (DEBUG) Log.d(TAG, "hide the small icon for the input method"); + mStatusBar.setIconVisibility(mInputMethodIcon, false); + } else if (packageName != null) { + if (DEBUG) Log.d(TAG, "show a small icon for the input method"); + mInputMethodData.iconId = iconId; + mInputMethodData.iconPackage = packageName; + mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); + mStatusBar.setIconVisibility(mInputMethodIcon, true); + } + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -860,23 +909,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void showSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring showSoftInput of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring showSoftInput of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be shown"); + showCurrentInputLocked(flags); } - - if (DEBUG) Log.v(TAG, "Client requesting input be shown"); - showCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -891,29 +945,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurMethod != null) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO( - MSG_SHOW_SOFT_INPUT, getShowFlags(), mCurMethod)); + MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod)); mInputShown = true; + } else if (mHaveConnection && SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // The client has asked to have the input method shown, but + // we have been sitting here too long with a connection to the + // service and no interface received, so let's disconnect/connect + // to try to prod things along. + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime,1); + mContext.unbindService(this); + mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE); } } public void hideSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring hideSoftInput of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring hideSoftInput of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); + hideCurrentInputLocked(flags); } - - if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); - hideCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -942,70 +1011,75 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void windowGainedFocus(IInputMethodClient client, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { - synchronized (mMethodMap) { - if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() - + " viewHasFocus=" + viewHasFocus - + " isTextEditor=" + isTextEditor - + " softInputMode=#" + Integer.toHexString(softInputMode) - + " first=" + first + " flags=#" - + Integer.toHexString(windowFlags)); - - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring focus gain of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() + + " viewHasFocus=" + viewHasFocus + + " isTextEditor=" + isTextEditor + + " softInputMode=#" + Integer.toHexString(softInputMode) + + " first=" + first + " flags=#" + + Integer.toHexString(windowFlags)); + + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring focus gain of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } - } - - switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { - if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { - // There is no focus view, and this window will - // be behind any soft input window, so hide the - // soft input window if it is shown. - if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS); + + switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + if (!isTextEditor || (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { + if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { + // There is no focus view, and this window will + // be behind any soft input window, so hide the + // soft input window if it is shown. + if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS); + } + } else if (isTextEditor && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + // There is a focus view, and we are navigating forward + // into the window, so show the input window for the user. + if (DEBUG) Log.v(TAG, "Unspecified window will show input"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); } - } else if (isTextEditor && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - // There is a focus view, and we are navigating forward - // into the window, so show the input window for the user. - if (DEBUG) Log.v(TAG, "Unspecified window will show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: - // Do nothing. - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if (DEBUG) Log.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0); - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + // Do nothing. + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: + if (DEBUG) Log.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(0); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: + if ((softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: + if (DEBUG) Log.v(TAG, "Window asks to always show input"); showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Log.v(TAG, "Window asks to always show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - break; + break; + } } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -1022,7 +1096,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void setInputMethod(IBinder token, String id) { synchronized (mMethodMap) { - if (mCurToken == null) { + if (token == null) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { @@ -1032,19 +1106,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } else if (mCurToken != token) { Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - setInputMethodLocked(id); + long ident = Binder.clearCallingIdentity(); + try { + setInputMethodLocked(id); + } finally { + Binder.restoreCallingIdentity(ident); + } } } public void hideMySoftInput(IBinder token, int flags) { synchronized (mMethodMap) { - if (mCurToken == null || mCurToken != token) { + if (token == null || mCurToken != token) { Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + return; } - hideCurrentInputLocked(flags); + long ident = Binder.clearCallingIdentity(); + try { + hideCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -1209,7 +1295,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- - public void showInputMethodMenu() { + void showInputMethodMenu() { if (DEBUG) Log.v(TAG, "Show switching menu"); hideInputMethodMenu(); @@ -1309,83 +1395,88 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { + long ident = Binder.clearCallingIdentity(); + try { + // Make sure this is a valid input method. + InputMethodInfo imm = mMethodMap.get(id); if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + 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; + + 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); } - // 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)) { + + 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.DEFAULT_INPUT_METHOD, - firstId != null ? firstId : ""); + 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; } - // 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; + + // 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; + } finally { + Binder.restoreCallingIdentity(ident); } - - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); - - // Previous state was disabled. - return false; } } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index bc6fd71..db4daa5 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -132,7 +132,7 @@ public class LocationManagerService extends ILocationManager.Stub private static final int MESSAGE_HEARTBEAT = 1; private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2; private static final int MESSAGE_RELEASE_WAKE_LOCK = 3; - private static final int MESSAGE_SET_NETWORK_LOCATION_PROVIDER = 4; + private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4; // Alarm manager and wakelock variables private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT"; @@ -565,13 +565,21 @@ public class LocationManagerService extends ILocationManager.Stub } } - public void setNetworkLocationProvider(INetworkLocationProvider provider) { - mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER); + public void setInstallCallback(InstallCallback callback) { + mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER); Message m = Message.obtain(mLocationHandler, - MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider); + MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback); mLocationHandler.sendMessageAtFrontOfQueue(m); } + public void setNetworkLocationProvider(INetworkLocationProvider provider) { + mNetworkLocationInterface = provider; + provider.addListener(getPackageNames()); + mNetworkLocationProvider = (LocationProviderImpl)provider; + LocationProviderImpl.addProvider(mNetworkLocationProvider); + updateProviders(); + } + public void setLocationCollector(ILocationCollector collector) { mCollector = collector; if (mGpsLocationProvider != null) { @@ -1598,16 +1606,12 @@ public class LocationManagerService extends ILocationManager.Stub // Update wakelock status so the next alarm is set before releasing wakelock updateWakelockStatus(mScreenOn); releaseWakeLock(); - } else if (msg.what == MESSAGE_SET_NETWORK_LOCATION_PROVIDER) { + } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) { synchronized (LocationManagerService.class) { - Log.d(TAG, "adding network location provider"); - mNetworkLocationInterface = - (INetworkLocationProvider)msg.obj; - mNetworkLocationInterface.addListener(getPackageNames()); - mNetworkLocationProvider = - (LocationProviderImpl)mNetworkLocationInterface; - LocationProviderImpl.addProvider(mNetworkLocationProvider); - updateProviders(); + Log.d(TAG, "installing network location provider"); + INetworkLocationManager.InstallCallback callback = + (INetworkLocationManager.InstallCallback)msg.obj; + callback.installNetworkLocationProvider(mContext, LocationManagerService.this); } } } catch (Exception e) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 221ba46..c490e42 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -3219,12 +3219,15 @@ class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Observer no longer exists."); } } - // There appears to be a subtle deadlock condition if the sendPackageBroadcast call appears - // in the synchronized block above. + // There appears to be a subtle deadlock condition if the sendPackageBroadcast + // call appears in the synchronized block above. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); + if (res.removedInfo.removedPackage != null) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, extras); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 7c111d3..6974b5e 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -1001,11 +1001,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } else { - synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 4, - mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } + // If we're in this case, then this handler is running for a previous + // paired transaction. mBroadcastWakeLock will already have been released + // in sendNotificationLocked. } } }; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index c009fac..eece581 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -87,10 +87,13 @@ public class WifiService extends IWifiManager.Stub { private final LockList mLocks = new LockList(); /** - * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a - * Settings.System value is not present. + * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a + * Settings.Gservices value is not present. This timeout value is chosen as + * the approximate point at which the battery drain caused by Wi-Fi + * being enabled but not active exceeds the battery drain caused by + * re-establishing a connection to the mobile data network. */ - private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */ + private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ private static final String WAKELOCK_TAG = "WifiService"; @@ -101,7 +104,7 @@ public class WifiService extends IWifiManager.Stub { * provides a bit of extra margin. * <p> * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. - * This is the default value if a Settings.System value is not present. + * This is the default value if a Settings.Secure value is not present. */ private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; @@ -1364,7 +1367,7 @@ public class WifiService extends IWifiManager.Stub { * in the current regulatory domain. This method should be used only * if the correct number of channels cannot be determined automatically * for some reason. If the operation is successful, the new value is - * persisted as a System setting. + * persisted as a Secure setting. * @param numChannels the number of allowed channels. Must be greater than 0 * and less than or equal to 16. * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., @@ -1446,8 +1449,8 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - long idleMillis = Settings.System.getLong(mContext.getContentResolver(), - Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); + long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(), + Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); int stayAwakeConditions = Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 09f5d8f..fed6d12 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -768,27 +768,55 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (willMove && w != null) { final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.mAppToken != null) { - int curIndex = -1; - if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" - + mNextAppTransition + " curTarget animating=" - + curTarget.isAnimating() - + " layer=" + curTarget.mAnimLayer - + " new layer=" + w.mAnimLayer); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { - // If we are currently setting up for an animation, - // hold everything until we can find out what will happen. - mInputMethodTargetWaitingAnim = true; - curIndex = localmWindows.indexOf(curTarget); - } else if (curTarget.isAnimating() && - curTarget.mAnimLayer > w.mAnimLayer) { - // If the window we are currently targeting is involved - // with an animation, and it is on top of the next target - // we will be over, then hold off on moving until - // that is done. - curIndex = localmWindows.indexOf(curTarget); - } - if (curIndex >= 0) { - return curIndex + 1; + + // Now some fun for dealing with window animations that + // modify the Z order. We need to look at all windows below + // the current target that are in this app, finding the highest + // visible one in layering. + AppWindowToken token = curTarget.mAppToken; + WindowState highestTarget = null; + int highestPos = 0; + if (token.animating || token.animation != null) { + int pos = 0; + pos = localmWindows.indexOf(curTarget); + while (pos >= 0) { + WindowState win = (WindowState)localmWindows.get(pos); + if (win.mAppToken != token) { + break; + } + if (!win.mRemoved) { + if (highestTarget == null || win.mAnimLayer > + highestTarget.mAnimLayer) { + highestTarget = win; + highestPos = pos; + } + } + pos--; + } + } + + if (highestTarget != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + + mNextAppTransition + " " + highestTarget + + " animating=" + highestTarget.isAnimating() + + " layer=" + highestTarget.mAnimLayer + + " new layer=" + w.mAnimLayer); + + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + // If we are currently setting up for an animation, + // hold everything until we can find out what will happen. + mInputMethodTargetWaitingAnim = true; + mInputMethodTarget = highestTarget; + return highestPos + 1; + } else if (highestTarget.isAnimating() && + highestTarget.mAnimLayer > w.mAnimLayer) { + // If the window we are currently targeting is involved + // with an animation, and it is on top of the next target + // we will be over, then hold off on moving until + // that is done. + mInputMethodTarget = highestTarget; + return highestPos + 1; + } } } } @@ -891,12 +919,27 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + void logWindowList(String prefix) { + int N = mWindows.size(); + while (N > 0) { + N--; + Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); + } + } + void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; + final int N = dialogs.size(); + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { pos = tmpRemoveWindowLocked(pos, dialogs.get(i)); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Window list w/pos=" + pos); + logWindowList(" "); + } + if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; if (pos < mWindows.size()) { @@ -905,17 +948,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pos++; } } + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos); for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = targetAppToken; pos = reAddWindowLocked(pos, win); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Final window list:"); + logWindowList(" "); + } return; } for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = null; reAddWindowToListInOrderLocked(win); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "No IM target, final list:"); + logWindowList(" "); + } } } @@ -971,9 +1023,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (imWin != null) { + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Moving IM from " + imPos); + logWindowList(" "); + } imPos = tmpRemoveWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving with new pos " + imPos + ":"); + logWindowList(" "); + } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving IM to " + imPos + ":"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); } else { moveInputMethodDialogsLocked(imPos); @@ -984,9 +1048,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // because they aren't currently associated with a focus window. if (imWin != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); imWin.mTargetAppToken = null; reAddWindowToListInOrderLocked(imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List with no IM target:"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(-1);; } else { moveInputMethodDialogsLocked(-1);; @@ -1233,7 +1302,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (wasVisible=win.isVisibleLw()) { + if (wasVisible=win.isWinVisibleLw()) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { @@ -1277,6 +1346,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); + win.mRemoved = true; + + if (mInputMethodTarget == win) { + moveInputMethodWindowsIfNeededLocked(false); + } + mPolicy.removeWindowLw(win); win.removeLocked(); @@ -1501,7 +1576,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (win.isVisibleLw() && + if (win.isWinVisibleLw() && applyAnimationLocked(win, transit, false)) { win.mExiting = true; mKeyWaiter.finishedKey(session, client, true, @@ -1511,13 +1586,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // an exit. win.mExiting = true; } else { + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } } } - if (mInputMethodWindow == win) { - mInputMethodWindow = null; - } outSurface.clear(); } @@ -1620,7 +1695,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { - if (win.mAnimating && win.mAnimationIsEntrance == isEntrance) { + if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; @@ -1666,6 +1741,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); + win.mAnimationIsEntrance = isEntrance; } } else { win.clearAnimation(); @@ -2724,7 +2800,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean added = false; for (int j=0; j<NCW; j++) { WindowState cwin = (WindowState)win.mChildWindows.get(j); - if (cwin.mSubLayer >= 0) { + if (!added && cwin.mSubLayer >= 0) { mWindows.add(index, win); index++; added = true; @@ -4645,8 +4721,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyQ() { super(mContext); PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG"); + mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + "KEEP_SCREEN_ON_FLAG"); mHoldingScreen.setReferenceCounted(false); } @@ -4769,8 +4845,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (holding) { mHoldingScreen.acquire(); } else { - long curTime = SystemClock.uptimeMillis(); - mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT); + mPolicy.screenOnStopped(); mHoldingScreen.release(); } } @@ -5095,6 +5170,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + public boolean performHapticFeedback(IWindow window, int effectId, + boolean always) { + synchronized(mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mPolicy.performHapticFeedback( + windowForClientLocked(this, window), effectId, always); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + void windowAddedLocked() { if (mSurfaceSession == null) { if (localLOGV) Log.v( @@ -5255,9 +5343,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Currently running animation. boolean mAnimating; + boolean mLocalAnimating; Animation mAnimation; boolean mAnimationIsEntrance; boolean mHasTransformation; + boolean mHasLocalTransformation; final Transformation mTransformation = new Transformation(); // This is set after IWindowSession.relayout() has been called at @@ -5297,6 +5387,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // been updated for the new orientation. boolean mOrientationChanging; + // Is this window now (or just being) removed? + boolean mRemoved; + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -5516,6 +5609,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV) Log.v( TAG, "Setting animation in " + this + ": " + anim); mAnimating = false; + mLocalAnimating = false; mAnimation = anim; mAnimation.restrictDuration(MAX_ANIMATION_DURATION); mAnimation.scaleCurrentDuration(mWindowAnimationScale); @@ -5524,6 +5618,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void clearAnimation() { if (mAnimation != null) { mAnimating = true; + mLocalAnimating = false; mAnimation = null; } } @@ -5752,13 +5847,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; - if (!mAnimating) { + mHasLocalTransformation = true; + if (!mLocalAnimating) { if (DEBUG_ANIM) Log.v( TAG, "Starting animation in " + this + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); mAnimation.setStartTime(currentTime); + mLocalAnimating = true; mAnimating = true; } mTransformation.clear(); @@ -5767,9 +5864,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_ANIM) Log.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); - if (mAppToken != null && mAppToken.hasTransformation) { - mTransformation.compose(mAppToken.transformation); - } if (more) { // we're not done! return true; @@ -5780,10 +5874,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; //WindowManagerService.this.dump(); } - if (mAppToken != null && mAppToken.hasTransformation) { + mHasLocalTransformation = false; + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null + && mAppToken.hasTransformation) { + // When our app token is animating, we kind-of pretend like + // we are as well. Note the mLocalAnimating mAnimationIsEntrance + // part of this check means that we will only do this if + // our window is not currently exiting, or it is not + // locally animating itself. The idea being that one that + // is exiting and doing a local animation should be removed + // once that animation is done. mAnimating = true; mHasTransformation = true; - mTransformation.set(mAppToken.transformation); + mTransformation.clear(); return false; } else if (mHasTransformation) { // Little trick to get through the path below to act like @@ -5796,10 +5899,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // If the display is frozen, and there is a pending animation, // clear it and make sure we run the cleanup code. mAnimating = true; + mLocalAnimating = true; mAnimation = null; } - if (!mAnimating) { + if (!mAnimating && !mLocalAnimating) { return false; } @@ -5809,6 +5913,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + (mAppToken != null ? mAppToken.reportedVisible : false)); mAnimating = false; + mLocalAnimating = false; mAnimation = null; mAnimLayer = mLayer; if (mIsImWindow) { @@ -5817,6 +5922,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer); mHasTransformation = false; + mHasLocalTransformation = false; mPolicyVisibility = mPolicyVisibilityAfterAnim; mTransformation.clear(); if (mHasDrawn @@ -5891,10 +5997,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void computeShownFrameLocked() { - final boolean selfTransformation = mHasTransformation; - final boolean attachedTransformation = (mAttachedWindow != null - && mAttachedWindow.mHasTransformation); - if (selfTransformation || attachedTransformation) { + final boolean selfTransformation = mHasLocalTransformation; + Transformation attachedTransformation = + (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) + ? mAttachedWindow.mTransformation : null; + Transformation appTransformation = + (mAppToken != null && mAppToken.hasTransformation) + ? mAppToken.transformation : null; + if (selfTransformation || attachedTransformation != null + || appTransformation != null) { // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; @@ -5905,8 +6016,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (selfTransformation) { tmpMatrix.preConcat(mTransformation.getMatrix()); } - if (attachedTransformation) { - tmpMatrix.preConcat(mAttachedWindow.mTransformation.getMatrix()); + if (attachedTransformation != null) { + tmpMatrix.preConcat(attachedTransformation.getMatrix()); + } + if (appTransformation != null) { + tmpMatrix.preConcat(appTransformation.getMatrix()); } // "convert" it into SurfaceFlinger's format @@ -5940,8 +6054,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); } - if (attachedTransformation) { - mShownAlpha *= mAttachedWindow.mTransformation.getAlpha(); + if (attachedTransformation != null) { + mShownAlpha *= attachedTransformation.getAlpha(); + } + if (appTransformation != null) { + mShownAlpha *= appTransformation.getAlpha(); } } else { //Log.i(TAG, "Not applying alpha transform"); @@ -5965,7 +6082,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * Is this window visible? It is not visible if there is no * surface, or we are in the process of running an exit animation - * that will remove the surface. + * that will remove the surface, or its app token has been hidden. */ public boolean isVisibleLw() { final AppWindowToken atoken = mAppToken; @@ -5975,6 +6092,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } /** + * Is this window visible, ignoring its app token? It is not visible + * if there is no surface, or we are in the process of running an exit animation + * that will remove the surface. + */ + public boolean isWinVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested || atoken.animating) + && !mExiting && !mDestroying; + } + + /** * The same as isVisible(), but follows the current hidden state of * the associated app token, not the pending requested hidden state. */ @@ -6203,6 +6332,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "mShownAlpha=" + mShownAlpha + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha); pw.println(prefix + "mAnimating=" + mAnimating + + " mLocalAnimating=" + mLocalAnimating + " mAnimationIsEntrance=" + mAnimationIsEntrance + " mAnimation=" + mAnimation); pw.println(prefix + "XForm: has=" + mHasTransformation @@ -6213,7 +6343,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " mHasDrawn=" + mHasDrawn); pw.println(prefix + "mExiting=" + mExiting + " mRemoveOnExit=" + mRemoveOnExit - + " mDestroying=" + mDestroying); + + " mDestroying=" + mDestroying + + " mRemoved=" + mRemoved); pw.println(prefix + "mOrientationChanging=" + mOrientationChanging + " mAppFreezing=" + mAppFreezing); } @@ -7959,6 +8090,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo i--; WindowState win = mDestroySurface.get(i); win.mDestroying = false; + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } while (i > 0); mDestroySurface.clear(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index f5efe4b..e391da3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -159,7 +159,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int LOG_AM_BROADCAST_DISCARD_APP = 30025; static final int LOG_AM_CREATE_SERVICE = 30030; static final int LOG_AM_DESTROY_SERVICE = 30031; - + static final int LOG_AM_PROCESS_CRASHED_TOO_MUCH = 30032; + static final int LOG_AM_DROP_PROCESS = 30033; + static final int LOG_AM_SERVICE_CRASHED_TOO_MUCH = 30034; + static final int LOG_AM_SCHEDULE_SERVICE_RESTART = 30035; + static final int LOG_AM_PROVIDER_LOST_PROCESS = 30036; + static final int LOG_BOOT_PROGRESS_AMS_READY = 3040; static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050; @@ -1787,7 +1792,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void startPausingLocked(boolean userLeaving) { + private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { if (mPausingActivity != null) { RuntimeException e = new RuntimeException(); Log.e(TAG, "Trying to pause when pause is already pending for " @@ -1840,9 +1845,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new - // activity has started... - prev.pauseKeyDispatchingLocked(); - + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + if (DEBUG_PAUSE) Log.v(TAG, "Key dispatch not paused for screen off"); + } + // Schedule a pause timeout in case the app doesn't respond. // We don't give it much time because this directly impacts the // responsiveness seen by the user. @@ -2188,7 +2199,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing"); - startPausingLocked(userLeaving); + startPausingLocked(userLeaving, false); return true; } @@ -2605,8 +2616,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleNewIntent(ar, r); sent = true; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending new intent to " + r, e); + Log.w(TAG, "Exception thrown sending new intent to " + r, e); } } if (!sent) { @@ -3456,7 +3466,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Finish needs to pause: " + r); if (DEBUG_USER_LEAVING) Log.v(TAG, "finish() => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, false); } } else if (r.state != ActivityState.PAUSING) { @@ -3590,9 +3600,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleSendResult(r, list); return; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending result to " + r, - e); + Log.w(TAG, "Exception thrown sending result to " + r, e); } } @@ -3778,8 +3786,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.configChangeFlags = 0; if (!mLRUActivities.remove(r)) { - Log.w(TAG, "Activity " + r - + " being finished, but not in LRU list"); + Log.w(TAG, "Activity " + r + " being finished, but not in LRU list"); } return removedFromHistory; @@ -4217,7 +4224,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:"+packageName); + Log.w(TAG, "Invalid packageName:" + packageName); return false; } if (uid == pkgUid || checkComponentPermission( @@ -4270,7 +4277,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:"+packageName); + Log.w(TAG, "Invalid packageName: " + packageName); return; } restartPackageLocked(packageName, pkgUid); @@ -4423,6 +4430,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app == null) { Log.w(TAG, "No pending application record for pid " + pid + " (IApplicationThread " + thread + "); dropping process"); + EventLog.writeEvent(LOG_AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { Process.killProcess(pid); } else { @@ -6579,10 +6587,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (caller != null) { r = getRecordForAppLocked(caller); if (r == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when getting content provider " + name); + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when getting content provider " + name); } } @@ -6739,6 +6747,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); + EventLog.writeEvent(LOG_AM_PROVIDER_LOST_PROCESS, + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; } try { cpr.wait(); @@ -6844,9 +6856,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord dst = (ContentProviderRecord)r.pubProviders.get(src.info.name); if (dst != null) { - dst.provider = src.provider; - dst.app = r; - mProvidersByClass.put(dst.info.name, dst); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) { @@ -6863,6 +6872,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } synchronized (dst) { + dst.provider = src.provider; + dst.app = r; dst.notifyAll(); } updateOomAdjLocked(r); @@ -6993,7 +7004,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause..."); if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, true); } } } @@ -7499,6 +7510,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This process loses! Log.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); + EventLog.writeEvent(LOG_AM_PROCESS_CRASHED_TOO_MUCH, + app.info.processName, app.info.uid); killServicesLocked(app, false); for (int i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); @@ -8429,6 +8442,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (sr.crashCount >= 2) { Log.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); + EventLog.writeEvent(LOG_AM_SERVICE_CRASHED_TOO_MUCH, + sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr, true); } else if (!allowRestart) { bringDownServiceLocked(sr, true); @@ -8447,7 +8462,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr) { - cpr.launchingApp = null; + synchronized (cpr) { + cpr.launchingApp = null; + cpr.notifyAll(); + } mProvidersByClass.remove(cpr.info.name); String names[] = cpr.info.authority.split(";"); @@ -8529,6 +8547,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } + } else { + i = NL; } if (i >= NL) { @@ -8602,7 +8622,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProcessesOnHold.remove(app); if (restart) { - // We have component that still need to be running in the + // We have components that still need to be running in the // process, so re-launch it. mProcessNames.put(app.processName, app.info.uid, app); startProcessLocked(app, "restart", app.processName); @@ -8952,6 +8972,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Log.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); + EventLog.writeEvent(LOG_AM_SCHEDULE_SERVICE_RESTART, + r.shortName, r.restartDelay); Message msg = Message.obtain(); msg.what = SERVICE_ERROR_MSG; @@ -11660,7 +11682,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We can finish this one if we have its icicle saved and // it is not persistent. - if ((r.haveState || !r.stateNotNeeded) + if ((r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped && !r.persistent && !r.finishing) { final int origSize = mLRUActivities.size(); destroyActivityLocked(r, true); diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 001987f..3922f39 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -22,13 +22,24 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import com.android.internal.os.PkgUsageStats; +import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.util.Log; +import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -44,35 +55,257 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; static IUsageStats sService; private Context mContext; - private String mFileName; + // structure used to maintain statistics since the last checkin. final private Map<String, PkgUsageStatsExtended> mStats; + // Lock to update package stats. Methods suffixed by SLOCK should invoked with + // this lock held + final Object mStatsLock; + // Lock to write to file. Methods suffixed by FLOCK should invoked with + // this lock held. + final Object mFileLock; + // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks private String mResumedPkg; + private File mFile; + //private File mBackupFile; + private long mLastWriteRealTime; + private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms + private static final String _PREFIX_DELIMIT="."; + private String mFilePrefix; + private Calendar mCal; + private static final int _MAX_NUM_FILES = 10; + private long mLastTime; private class PkgUsageStatsExtended { int mLaunchCount; long mUsageTime; - long mChgTime; + long mPausedTime; + long mResumedTime; + PkgUsageStatsExtended() { mLaunchCount = 0; mUsageTime = 0; - mChgTime = SystemClock.elapsedRealtime(); } void updateResume() { mLaunchCount ++; - mChgTime = SystemClock.elapsedRealtime(); + mResumedTime = SystemClock.elapsedRealtime(); } void updatePause() { - long currTime = SystemClock.elapsedRealtime(); - mUsageTime += (currTime - mChgTime); - mChgTime = currTime; + mPausedTime = SystemClock.elapsedRealtime(); + mUsageTime += (mPausedTime - mResumedTime); + } + void clear() { + mLaunchCount = 0; + mUsageTime = 0; } } - UsageStatsService(String filename) { - mFileName = filename; + UsageStatsService(String fileName) { mStats = new HashMap<String, PkgUsageStatsExtended>(); + mStatsLock = new Object(); + mFileLock = new Object(); + mFilePrefix = fileName; + mCal = Calendar.getInstance(); + // Update current stats which are binned by date + String uFileName = getCurrentDateStr(mFilePrefix); + mFile = new File(uFileName); + readStatsFromFile(); + mLastWriteRealTime = SystemClock.elapsedRealtime(); + mLastTime = new Date().getTime(); + } + + /* + * Utility method to convert date into string. + */ + private String getCurrentDateStr(String prefix) { + mCal.setTime(new Date()); + StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1; + if (mm < 10) { + sb.append("0"); + } + sb.append(mm); + int dd = mCal.get(Calendar.DAY_OF_MONTH); + if (dd < 10) { + sb.append("0"); + } + sb.append(dd); + sb.append(mCal.get(Calendar.YEAR)); + return sb.toString(); + } + + private Parcel getParcelForFile(File file) throws IOException { + FileInputStream stream = new FileInputStream(file); + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + stream.close(); + return in; + } + + private void readStatsFromFile() { + File newFile = mFile; + synchronized (mFileLock) { + try { + if (newFile.exists()) { + readStatsFLOCK(newFile); + } else { + // Check for file limit before creating a new file + checkFileLimitFLOCK(); + newFile.createNewFile(); + } + } catch (IOException e) { + Log.w(TAG,"Error : " + e + " reading data from file:" + newFile); + } + } + } + + private void readStatsFLOCK(File file) throws IOException { + Parcel in = getParcelForFile(file); + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + PkgUsageStatsExtended pus = new PkgUsageStatsExtended(); + pus.mLaunchCount = in.readInt(); + pus.mUsageTime = in.readLong(); + synchronized (mStatsLock) { + mStats.put(pkgName, pus); + } + } + } + + private ArrayList<String> getUsageStatsFileListFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return null; + } + // Check if there are too many files in the system and delete older files + String fList[] = dir.list(); + if (fList == null) { + return null; + } + File pre = new File(mFilePrefix); + String filePrefix = pre.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + ArrayList<String> fileList = new ArrayList<String>(); + for (String file : fList) { + int index = file.indexOf(filePrefix); + if (index == -1) { + continue; + } + if (file.endsWith(".bak")) { + continue; + } + fileList.add(file); + } + return fileList; + } + + private File getUsageFilesDir() { + if (mFilePrefix == null) { + return null; + } + File pre = new File(mFilePrefix); + return new File(pre.getParent()); } + private void checkFileLimitFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return; + } + // Get all usage stats output files + ArrayList<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + // Strange but we dont have to delete any thing + return; + } + int count = fileList.size(); + if (count <= _MAX_NUM_FILES) { + return; + } + // Sort files + Collections.sort(fileList); + count -= _MAX_NUM_FILES; + // Delete older files + for (int i = 0; i < count; i++) { + String fileName = fileList.get(i); + File file = new File(dir, fileName); + Log.i(TAG, "Deleting file : "+fileName); + file.delete(); + } + } + + private void writeStatsToFile() { + synchronized (mFileLock) { + long currTime = new Date().getTime(); + boolean dayChanged = ((currTime - mLastTime) >= (24*60*60*1000)); + long currRealTime = SystemClock.elapsedRealtime(); + if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) && + (!dayChanged)) { + // wait till the next update + return; + } + // Get the most recent file + String todayStr = getCurrentDateStr(mFilePrefix); + // Copy current file to back up + File backupFile = new File(mFile.getPath() + ".bak"); + mFile.renameTo(backupFile); + try { + checkFileLimitFLOCK(); + mFile.createNewFile(); + // Write mStats to file + writeStatsFLOCK(); + mLastWriteRealTime = currRealTime; + mLastTime = currTime; + if (dayChanged) { + // clear stats + synchronized (mStats) { + mStats.clear(); + } + mFile = new File(todayStr); + } + // Delete the backup file + if (backupFile != null) { + backupFile.delete(); + } + } catch (IOException e) { + Log.w(TAG, "Failed writing stats to file:" + mFile); + if (backupFile != null) { + backupFile.renameTo(mFile); + } + } + } + } + + private void writeStatsFLOCK() throws IOException { + FileOutputStream stream = new FileOutputStream(mFile); + Parcel out = Parcel.obtain(); + writeStatsToParcelFLOCK(out); + stream.write(out.marshall()); + out.recycle(); + stream.flush(); + stream.close(); + } + + private void writeStatsToParcelFLOCK(Parcel out) { + synchronized (mStatsLock) { + Set<String> keys = mStats.keySet(); + for (String key : keys) { + PkgUsageStatsExtended pus = mStats.get(key); + out.writeString(key); + out.writeInt(pus.mLaunchCount); + out.writeLong(pus.mUsageTime); + } + } + } + public void publish(Context context) { mContext = context; ServiceManager.addService(SERVICE_NAME, asBinder()); @@ -99,12 +332,14 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } if (localLOGV) Log.i(TAG, "started component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - pus = new PkgUsageStatsExtended(); - mStats.put(pkgName, pus); + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + pus = new PkgUsageStatsExtended(); + mStats.put(pkgName, pus); + } + pus.updateResume(); } - pus.updateResume(); mResumedPkg = pkgName; } @@ -120,13 +355,17 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } if (localLOGV) Log.i(TAG, "paused component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - // Weird some error here - Log.w(TAG, "No package stats for pkg:"+pkgName); - return; + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + // Weird some error here + Log.w(TAG, "No package stats for pkg:"+pkgName); + return; + } + pus.updatePause(); } - pus.updatePause(); + // Persist data to file + writeStatsToFile(); } public void enforceCallingPermission() { @@ -145,17 +384,19 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return null; } - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - return null; + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + return null; + } + return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); } - return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); } public PkgUsageStats[] getAllPkgUsageStats() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); - synchronized (mStats) { + synchronized (mStatsLock) { Set<String> keys = mStats.keySet(); int size = keys.size(); if (size <= 0) { @@ -172,21 +413,120 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + static byte[] readFully(FileInputStream stream) throws java.io.IOException { + int pos = 0; + int avail = stream.available(); + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + if (amt <= 0) { + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length-pos) { + byte[] newData = new byte[pos+avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) { + List<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + return; + } + final boolean isCheckinRequest = scanArgs(args, "-c"); + Collections.sort(fileList); + File usageFile = new File(mFilePrefix); + String dirName = usageFile.getParent(); + File dir = new File(dirName); + String filePrefix = usageFile.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + String todayStr = getCurrentDateStr(null); + for (String file : fileList) { + File dFile = new File(dir, file); + String dateStr = file.substring(prefixLen); + try { + Parcel in = getParcelForFile(dFile); + collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest); + if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) { + // Delete old file after collecting info only for checkin requests + dFile.delete(); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); + return; + } catch (IOException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); + } + } + } + + private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw, + String date, boolean isCheckinRequest) { StringBuilder sb = new StringBuilder(); - synchronized (mStats) { - Set<String> keys = mStats.keySet(); - for (String key: keys) { - PkgUsageStatsExtended ps = mStats.get(key); - sb.append("pkg="); - sb.append(key); + sb.append("Date:"); + sb.append(date); + boolean first = true; + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + int launchCount = in.readInt(); + long usageTime = in.readLong(); + if (isCheckinRequest) { + if (!first) { + sb.append(","); + } + sb.append(pkgName); + sb.append(","); + sb.append(launchCount); + sb.append(","); + sb.append(usageTime); + sb.append("ms"); + } else { + if (first) { + sb.append("\n"); + } + sb.append("pkg="); + sb.append(pkgName); sb.append(", launchCount="); - sb.append(ps.mLaunchCount); + sb.append(launchCount); sb.append(", usageTime="); - sb.append(ps.mUsageTime+" ms\n"); + sb.append(usageTime); + sb.append(" ms\n"); } + first = false; } pw.write(sb.toString()); } + + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } + } + } + return false; + } + + @Override + /* + * The data persisted to file is parsed and the stats are computed. + */ + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mFileLock) { + collectDumpInfoFLOCK(pw, args); + } + } + } diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index 230456a..36f7b9b 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -44,6 +44,7 @@ <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> + <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> diff --git a/tests/AndroidTests/src/com/android/unit_tests/MenuTest.java b/tests/AndroidTests/src/com/android/unit_tests/MenuTest.java index d184543..c436726 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/MenuTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/MenuTest.java @@ -131,20 +131,7 @@ public class MenuTest extends AndroidTestCase { makeKeyEvent(KeyEvent.KEYCODE_A, KeyEvent.META_SYM_ON))); } - - @LargeTest - public void testIsNotShortcutWithMultipleMetas() throws Exception { - mMenu.setQwertyMode(true); - mMenu.add(0, 0, 0, "test").setShortcut('2', 'a'); - Assert.assertFalse(mMenu.isShortcutKey( - 'a', - makeKeyEvent(KeyEvent.KEYCODE_A, KeyEvent.META_SYM_ON & KeyEvent.META_ALT_ON))); - Assert.assertFalse(mMenu.isShortcutKey( - 'a', makeKeyEvent(KeyEvent.KEYCODE_A, KeyEvent.META_SYM_ON))); - Assert.assertFalse(mMenu.isShortcutKey( - 'a', makeKeyEvent(KeyEvent.KEYCODE_A, KeyEvent.META_SHIFT_ON))); - } - + @SmallTest public void testIsShortcutWithUpperCaseAlpha() throws Exception { mMenu.setQwertyMode(true); diff --git a/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java index 8c1c91f..51e841c 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java @@ -262,7 +262,7 @@ public class TextUtilsTest extends TestCase { Map<String, String> fixes = Maps.newHashMap(); fixes.put("a", "<a@gmail.com>"); fixes.put("a b", "<ab@gmail.com>"); - fixes.put("a@b", "<a@gmail.com>"); + fixes.put("a@b", "<a@b>"); for (Map.Entry<String, String> e : fixes.entrySet()) { assertEquals(e.getValue(), validator.fixText(e.getKey()).toString()); diff --git a/tests/DumpRenderTree/compare_layout_results.py b/tests/DumpRenderTree/compare_layout_results.py index 4383dbb..c4285f1 100644 --- a/tests/DumpRenderTree/compare_layout_results.py +++ b/tests/DumpRenderTree/compare_layout_results.py @@ -6,6 +6,7 @@ results to a file. import optparse import os +import sys def DiffResults(marker, new_results, old_results, diff_results, strip_reason): """ Given two result files, generate diff and @@ -45,7 +46,7 @@ def DiffResults(marker, new_results, old_results, diff_results, strip_reason): diff_file.writelines("- " + line) missing_count += 1 - print marker + " >>> New = " + str(new_count) + " , Missing = " + str(missing_count) + print marker + " >>> added " + str(new_count) + " tests, removed " + str(missing_count) + " tests" diff_file.writelines("\n\n") @@ -55,24 +56,32 @@ def DiffResults(marker, new_results, old_results, diff_results, strip_reason): return def main(options, args): - results_dir = options.results_directory + results_dir = os.path.abspath(options.results_directory) ref_dir = options.ref_directory - if os.path.exists(results_dir + "/layout_tests_diff.txt"): - os.remove(results_dir + "/layout_tests_diff.txt") - files=["passed", "nontext", "crashed"] - for f in files: - DiffResults(f, results_dir + "layout_tests_" + f + ".txt", - ref_dir + "layout_tests_" + f + ".txt", results_dir + "layout_tests_diff.txt", False) + # if ref_dir is null, cannonify ref_dir to the script dir. + if not ref_dir: + script_self = sys.argv[0] + script_dir = os.path.dirname(script_self) + ref_dir = os.path.join(script_dir, "results") + + ref_dir = os.path.abspath(ref_dir) - for f in ["failed"]: - DiffResults(f, results_dir + "layout_tests_" + f + ".txt", - ref_dir + "layout_tests_" + f + ".txt", results_dir + "layout_tests_diff.txt", True) + diff_result = os.path.join(results_dir, "layout_tests_diff.txt") + if os.path.exists(diff_result): + os.remove(diff_result) + + files=["passed", "failed", "nontext", "crashed"] + for f in files: + result_file_name = "layout_tests_" + f + ".txt" + DiffResults(f, os.path.join(results_dir, result_file_name), + os.path.join(ref_dir, result_file_name), diff_result, + f == "failed") if '__main__' == __name__: option_parser = optparse.OptionParser() option_parser.add_option("", "--ref-directory", - default="results/", + default=None, dest="ref_directory", help="directory name under which results are stored.") diff --git a/tests/DumpRenderTree/run_layout_tests.py b/tests/DumpRenderTree/run_layout_tests.py index b4eb685..433271e 100755 --- a/tests/DumpRenderTree/run_layout_tests.py +++ b/tests/DumpRenderTree/run_layout_tests.py @@ -48,6 +48,86 @@ def CountLineNumber(filename): fp.close() return lines +def DumpRenderTreeFinished(adb_cmd): + """ Check if DumpRenderTree finished running tests + + Args: + output: adb_cmd string + """ + + # pull /sdcard/running_test.txt, if the content is "#DONE", it's done + shell_cmd_str = adb_cmd + " shell cat /sdcard/running_test.txt" + adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + return adb_output.strip() == "#DONE" + +def DiffResults(marker, new_results, old_results, diff_results, strip_reason): + """ Given two result files, generate diff and + write to diff_results file. All arguments are absolute paths + to files. + """ + old_file = open(old_results, "r") + new_file = open(new_results, "r") + diff_file = open(diff_results, "a") + + # Read lines from each file + ndict = new_file.readlines() + cdict = old_file.readlines() + + # Write marker to diff file + diff_file.writelines(marker + "\n") + diff_file.writelines("###############\n") + + # Strip reason from result lines + if strip_reason is True: + for i in range(0, len(ndict)): + ndict[i] = ndict[i].split(' ')[0] + "\n" + for i in range(0, len(cdict)): + cdict[i] = cdict[i].split(' ')[0] + "\n" + + # Find results in new_results missing in old_results + new_count=0 + for line in ndict: + if line not in cdict: + diff_file.writelines("+ " + line) + new_count += 1 + + # Find results in old_results missing in new_results + missing_count=0 + for line in cdict: + if line not in ndict: + diff_file.writelines("- " + line) + missing_count += 1 + + logging.info(marker + " >>> " + str(new_count) + " new, " + str(missing_count) + " misses") + + diff_file.writelines("\n\n") + + old_file.close() + new_file.close() + diff_file.close() + return + +def CompareResults(ref_dir, results_dir): + """Compare results in two directories + + Args: + ref_dir: the reference directory having layout results as references + results_dir: the results directory + """ + logging.info("Comparing results to " + ref_dir) + + diff_result = os.path.join(results_dir, "layout_tests_diff.txt") + if os.path.exists(diff_result): + os.remove(diff_result) + + files=["passed", "failed", "nontext", "crashed"] + for f in files: + result_file_name = "layout_tests_" + f + ".txt" + DiffResults(f, os.path.join(results_dir, result_file_name), + os.path.join(ref_dir, result_file_name), diff_result, + f == "failed") + logging.info("Detailed diffs are in " + diff_result) + def main(options, args): """Run the tests. Will call sys.exit when complete. @@ -84,7 +164,7 @@ def main(options, args): sys.exit(1) - logging.info("Starting tests") + logging.info("Running tests") # Count crashed tests. crashed_tests = [] @@ -98,7 +178,7 @@ def main(options, args): # Call LayoutTestsAutoTest::startLayoutTests. shell_cmd_str = adb_cmd + " shell am instrument -e class com.android.dumprendertree.LayoutTestsAutoTest#startLayoutTests -e path \"" + path + "\" -e timeout " + timeout_ms + " -w com.android.dumprendertree/.LayoutTestsAutoRunner" adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] - while adb_output.find('Process crashed') != -1: + while not DumpRenderTreeFinished(adb_cmd): # Get the running_test.txt logging.error("DumpRenderTree crashed, output:\n" + adb_output) @@ -118,8 +198,8 @@ def main(options, args): logging.error("Error happened : " + adb_output) sys.exit(1) - logging.info("Done"); logging.debug(adb_output); + logging.info("Done\n"); # Pull results from /sdcard results_dir = options.results_directory @@ -152,8 +232,21 @@ def main(options, args): nontext_tests = CountLineNumber(results_dir + "/layout_tests_nontext.txt") logging.info(str(nontext_tests) + " no dumpAsText") - logging.info("Results are stored under: " + results_dir) + logging.info("Results are stored under: " + results_dir + "\n") + + # Comparing results to references to find new fixes and regressions. + results_dir = os.path.abspath(options.results_directory) + ref_dir = options.ref_directory + # if ref_dir is null, cannonify ref_dir to the script dir. + if not ref_dir: + script_self = sys.argv[0] + script_dir = os.path.dirname(script_self) + ref_dir = os.path.join(script_dir, "results") + + ref_dir = os.path.abspath(ref_dir) + + CompareResults(ref_dir, results_dir) if '__main__' == __name__: option_parser = optparse.OptionParser() @@ -171,6 +264,11 @@ if '__main__' == __name__: help="pass options to adb, such as -d -e, etc"); option_parser.add_option("", "--results-directory", default="layout-test-results", - help="directory name under which results are stored.") + help="directory which results are stored.") + option_parser.add_option("", "--ref-directory", + default=None, + dest="ref_directory", + help="directory where reference results are stored.") + options, args = option_parser.parse_args(); main(options, args) diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java index c77d98a..86bfad7 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java @@ -68,9 +68,12 @@ class TestRecorder { } } - public void nontext(String layout_file) { + public void nontext(String layout_file, boolean has_results) { try { mBufferedOutputNontextStream.write(layout_file.getBytes()); + if (has_results) { + mBufferedOutputNontextStream.write(" : has expected results".getBytes()); + } mBufferedOutputNontextStream.write('\n'); mBufferedOutputNontextStream.flush(); } catch(Exception e) { @@ -299,6 +302,9 @@ public class HTMLHostActivity extends Activity resetTestStatus(); if (testIndex == mTestList.size()) { + if (!mSingleTestMode) { + updateTestStatus("#DONE"); + } finished(); return; } @@ -385,9 +391,9 @@ public class HTMLHostActivity extends Activity } } - public void nontextCase(String file) { + public void nontextCase(String file, boolean has_expected_results) { Log.v("Layout test:", file + " nontext"); - mResultRecorder.nontext(file); + mResultRecorder.nontext(file, has_expected_results); } public void setCallback(HTMLHostCallbackInterface callback) { @@ -447,8 +453,11 @@ public class HTMLHostActivity extends Activity } File nontext_result = new File(short_file + "-android-results.txt"); - if (nontext_result.exists()) - mResultRecorder.nontext(test_path); + if (nontext_result.exists()) { + // Check if the test has expected results. + File expected = new File(short_file + "-expected.txt"); + nontextCase(test_path, expected.exists()); + } } public void finished() { diff --git a/tests/ImfTest/AndroidManifest.xml b/tests/ImfTest/AndroidManifest.xml index 627ee6d..55e5d38 100755 --- a/tests/ImfTest/AndroidManifest.xml +++ b/tests/ImfTest/AndroidManifest.xml @@ -19,7 +19,7 @@ </intent-filter> </activity> - <activity android:name=".samples.AppAdjustmentBigEditTextNonScrollablePanScan" android:label="Big ET !Scroll Pan/Scan"> + <activity android:name=".samples.BigEditTextActivityNonScrollablePanScan" android:label="Big ET !Scroll Pan/Scan"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> @@ -35,7 +35,7 @@ </intent-filter> </activity> - <activity android:name=".samples.AppAdjustmentBigEditTextNonScrollableResize" android:label="Big ET !Scroll Resize"> + <activity android:name=".samples.BigEditTextActivityNonScrollableResize" android:label="Big ET !Scroll Resize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> @@ -43,7 +43,7 @@ </intent-filter> </activity> - <activity android:name=".samples.AppAdjustmentBigEditTextScrollablePanScan" android:label="Big ET Scroll Pan/Scan"> + <activity android:name=".samples.BigEditTextActivityScrollablePanScan" android:label="Big ET Scroll Pan/Scan"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> @@ -51,7 +51,7 @@ </intent-filter> </activity> - <activity android:name=".samples.AppAdjustmentBigEditTextScrollableResize" android:label="Big ET Scroll Resize"> + <activity android:name=".samples.BigEditTextActivityScrollableResize" android:label="Big ET Scroll Resize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> @@ -59,7 +59,7 @@ </intent-filter> </activity> - <activity android:name=".samples.AppAdjustmentEditTextDialog" android:label="ET Dialog"> + <activity android:name=".samples.EditTextActivityDialog" android:label="ET Dialog"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /> @@ -130,14 +130,6 @@ <category android:name="android.intent.category.IMF_TEST" /> </intent-filter> </activity> - - <activity android:name=".samples.OneEditTextActivityLandscape" android:label="OneEditTextActivityLandscape" android:screenOrientation="landscape"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> <activity android:name=".samples.DialogActivity" android:label="DialogActivity" android:screenOrientation="portrait"> <intent-filter> diff --git a/tests/ImfTest/res/values/config.xml b/tests/ImfTest/res/values/config.xml new file mode 100644 index 0000000..5ae40a3 --- /dev/null +++ b/tests/ImfTest/res/values/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources> + <bool name="def_expect_ime_autopop">false</bool> +</resources> diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java deleted file mode 100644 index 15a29c8..0000000 --- a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.android.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.EditText; -import android.widget.LinearLayout; - -public class AppAdjustmentBigEditTextNonScrollablePanScan extends Activity { - - private LinearLayout mLayout; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT)); - - EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mLayout, false); - - mLayout.addView(editText); - - setContentView(mLayout); - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java deleted file mode 100644 index 0726823..0000000 --- a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.android.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.EditText; -import android.widget.LinearLayout; - -public class AppAdjustmentBigEditTextNonScrollableResize extends Activity { - - private LinearLayout mLayout; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT)); - - EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mLayout, false); - - mLayout.addView(editText); - - setContentView(mLayout); - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java new file mode 100644 index 0000000..9754381 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java @@ -0,0 +1,49 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +public class BigEditTextActivityNonScrollablePanScan extends Activity { + + private View mRootView; + private View mDefaultFocusedView; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + + mRootView = new LinearLayout(this); + ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); + mRootView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + View view = getLayoutInflater().inflate( + R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false); + + ((LinearLayout) mRootView).addView(view); + + mDefaultFocusedView = view.findViewById(R.id.data); + + setContentView(mRootView); + } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java new file mode 100644 index 0000000..701795f --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java @@ -0,0 +1,49 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +public class BigEditTextActivityNonScrollableResize extends Activity { + + private View mRootView; + private View mDefaultFocusedView; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + mRootView = new LinearLayout(this); + ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); + mRootView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + View view = getLayoutInflater().inflate( + R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false); + + ((LinearLayout) mRootView).addView(view); + + mDefaultFocusedView = view.findViewById(R.id.data); + + setContentView(mRootView); + } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java index 50a980b..bb3f767 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollablePanScan.java +++ b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java @@ -4,15 +4,17 @@ import com.android.imftest.R; import android.app.Activity; import android.os.Bundle; +import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; -public class AppAdjustmentBigEditTextScrollablePanScan extends Activity { +public class BigEditTextActivityScrollablePanScan extends Activity { - private ScrollView mScrollView; + private View mRootView; + private View mDefaultFocusedView; private LinearLayout mLayout; @Override @@ -21,9 +23,9 @@ public class AppAdjustmentBigEditTextScrollablePanScan extends Activity { getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - mScrollView = new ScrollView(this); - mScrollView.setFillViewport(true); - mScrollView.setLayoutParams(new ViewGroup.LayoutParams( + mRootView = new ScrollView(this); + ((ScrollView) mRootView).setFillViewport(true); + mRootView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); @@ -33,12 +35,23 @@ public class AppAdjustmentBigEditTextScrollablePanScan extends Activity { ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mScrollView, false); + View view = getLayoutInflater().inflate( + R.layout.full_screen_edit_text, ((ScrollView) mRootView), false); - mLayout.addView(editText); - mScrollView.addView(mLayout); + mLayout.addView(view); - setContentView(mScrollView); + ((ScrollView) mRootView).addView(mLayout); + mDefaultFocusedView = view.findViewById(R.id.data); + + setContentView(mRootView); + } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; } } diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java index a256878..f2cae1c 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollableResize.java +++ b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java @@ -4,15 +4,17 @@ import com.android.imftest.R; import android.app.Activity; import android.os.Bundle; +import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; -public class AppAdjustmentBigEditTextScrollableResize extends Activity { +public class BigEditTextActivityScrollableResize extends Activity { - private ScrollView mScrollView; + private View mRootView; + private View mDefaultFocusedView; private LinearLayout mLayout; @Override @@ -21,9 +23,9 @@ public class AppAdjustmentBigEditTextScrollableResize extends Activity { getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - mScrollView = new ScrollView(this); - mScrollView.setFillViewport(true); - mScrollView.setLayoutParams(new ViewGroup.LayoutParams( + mRootView = new ScrollView(this); + ((ScrollView) mRootView).setFillViewport(true); + mRootView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); @@ -33,12 +35,23 @@ public class AppAdjustmentBigEditTextScrollableResize extends Activity { ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mScrollView, false); + View view = getLayoutInflater().inflate( + R.layout.full_screen_edit_text, ((ScrollView) mRootView), false); - mLayout.addView(editText); - mScrollView.addView(mLayout); + mLayout.addView(view); - setContentView(mScrollView); + ((ScrollView) mRootView).addView(mLayout); + mDefaultFocusedView = view.findViewById(R.id.data); + + setContentView(mRootView); + } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; } } diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java index d74b9dd..51f5045 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java +++ b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java @@ -17,21 +17,30 @@ import com.android.imftest.R; */ public class BottomEditTextActivityPanScan extends Activity { - private LayoutInflater mInflater; + private View mRootView; + private View mDefaultFocusedView; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); + mRootView = new LinearLayout(this); + ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - mInflater = getLayoutInflater(); - - View view = mInflater.inflate(R.layout.one_edit_text_activity, layout, false); - layout.addView(view); + View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false); + mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text); + ((LinearLayout) mRootView).addView(view); - setContentView(layout); + setContentView(mRootView); this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } }
\ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java index 82da29a..eb94b4f 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java +++ b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java @@ -17,21 +17,30 @@ import com.android.imftest.R; */ public class BottomEditTextActivityResize extends Activity { - private LayoutInflater mInflater; + private View mRootView; + private View mDefaultFocusedView; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); + mRootView = new LinearLayout(this); + ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - mInflater = getLayoutInflater(); - - View view = mInflater.inflate(R.layout.one_edit_text_activity, layout, false); - layout.addView(view); + View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false); + mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text); + ((LinearLayout) mRootView).addView(view); - setContentView(layout); + setContentView(mRootView); this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } }
\ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java index 4233811..1191f19 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java +++ b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java @@ -13,6 +13,8 @@ import android.widget.TextView; public class ButtonActivity extends Activity { static boolean mKeyboardIsActive = false; + public static final int BUTTON_ID = 0; + private View mRootView; @Override public void onCreate(Bundle savedInstanceState) @@ -23,6 +25,8 @@ public class ButtonActivity extends Activity final Button myButton = new Button(this); myButton.setClickable(true); myButton.setText("Keyboard UP!"); + myButton.setId(BUTTON_ID); + myButton.setFocusableInTouchMode(true); myButton.setOnClickListener(new View.OnClickListener() { public void onClick (View v) @@ -36,7 +40,8 @@ public class ButtonActivity extends Activity } else { - imm.showSoftInput(null, 0); + myButton.requestFocusFromTouch(); + imm.showSoftInput(v, 0); myButton.setText("Keyboard DOWN!"); } @@ -48,5 +53,10 @@ public class ButtonActivity extends Activity layout.setOrientation(LinearLayout.VERTICAL); layout.addView(myButton); setContentView(layout); + mRootView = layout; + } + + public View getRootView() { + return mRootView; } } diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentEditTextDialog.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java index e82f1d5..bd1e934 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentEditTextDialog.java +++ b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java @@ -14,7 +14,7 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; -public class AppAdjustmentEditTextDialog extends Activity { +public class EditTextActivityDialog extends Activity { private static final int SCROLLABLE_DIALOG_ID = 0; private static final int NONSCROLLABLE_DIALOG_ID = 1; @@ -75,18 +75,18 @@ public class AppAdjustmentEditTextDialog extends Activity { EditText editText; if (scrollable) { - layout = new ScrollView(AppAdjustmentEditTextDialog.this); + layout = new ScrollView(EditTextActivityDialog.this); ((ScrollView) layout).setMinimumHeight(mLayout.getHeight()); ((ScrollView) layout).addView(( - LinearLayout) View.inflate(AppAdjustmentEditTextDialog.this, + LinearLayout) View.inflate(EditTextActivityDialog.this, R.layout.dialog_edit_text_no_scroll, null)); } else { - layout = View.inflate(AppAdjustmentEditTextDialog.this, + layout = View.inflate(EditTextActivityDialog.this, R.layout.dialog_edit_text_no_scroll, null); } - Dialog d = new Dialog(AppAdjustmentEditTextDialog.this); + Dialog d = new Dialog(EditTextActivityDialog.this); d.setTitle(getString(R.string.test_dialog)); d.setCancelable(true); d.setContentView(layout); diff --git a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java deleted file mode 100644 index b596023..0000000 --- a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.android.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; -import android.widget.ScrollView; - -import com.android.internal.R; - -public class EditTextActivityNoScrollPanScan extends Activity -{ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - String string = new String(); - for (int i=0; i<9; i++) - { - final EditText editText = new EditText(this); - editText.setText(string.valueOf(i)); - layout.addView(editText); - } - setContentView(layout); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java index 4cb3af6..54ab57a 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java @@ -20,22 +20,31 @@ import com.android.internal.R; */ public class ManyEditTextActivityNoScrollPanScan extends Activity { - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - String string = new String(); - for (int i=0; i<9; i++) - { - final EditText editText = new EditText(this); - editText.setText(string.valueOf(i)); - layout.addView(editText); - } - setContentView(layout); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + public static final int NUM_EDIT_TEXTS = 9; + + private View mRootView; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + mRootView = new LinearLayout(this); + ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); + + for (int i=0; i<NUM_EDIT_TEXTS; i++) + { + final EditText editText = new EditText(this); + editText.setText(String.valueOf(i)); + editText.setId(i); + ((LinearLayout) mRootView).addView(editText); + } + setContentView(mRootView); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } + + public View getRootView() { + return mRootView; + } + } diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java index bd32828..b228d34 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java @@ -20,26 +20,33 @@ import com.android.internal.R; */ public class ManyEditTextActivityScrollPanScan extends Activity { - private ScrollView mScrollView; - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - mScrollView = new ScrollView(this); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - String string = new String(); - for (int i=0; i<12; i++) - { - final EditText editText = new EditText(this); - editText.setText(string.valueOf(i)); - layout.addView(editText); - } - - mScrollView.addView(layout); - setContentView(mScrollView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } + public static final int NUM_EDIT_TEXTS = 12; + + private View mRootView; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + mRootView = new ScrollView(this); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + for (int i=0; i<NUM_EDIT_TEXTS; i++) + { + final EditText editText = new EditText(this); + editText.setText(String.valueOf(i)); + editText.setId(i); + layout.addView(editText); + } + + ((ScrollView) mRootView).addView(layout); + setContentView(mRootView); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } + + public View getRootView() { + return mRootView; + } }
\ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java index eaaa98b..777fbae 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java @@ -2,6 +2,7 @@ package com.android.imftest.samples; import android.app.Activity; import android.os.Bundle; +import android.view.View; import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.EditText; @@ -12,26 +13,33 @@ import android.widget.ScrollView; */ public class ManyEditTextActivityScrollResize extends Activity { - private ScrollView mScrollView; + public static final int NUM_EDIT_TEXTS = 12; + + private View mRootView; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mScrollView = new ScrollView(this); + mRootView = new ScrollView(this); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); - String string = new String(); - for (int i=0; i<12; i++) + for (int i=0; i<NUM_EDIT_TEXTS; i++) { final EditText editText = new EditText(this); - editText.setText(string.valueOf(i)); + editText.setText(String.valueOf(i)); + editText.setId(i); layout.addView(editText); } - mScrollView.addView(layout); - setContentView(mScrollView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + ((ScrollView) mRootView).addView(layout); + setContentView(mRootView); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } + + public View getRootView() { + return mRootView; + } }
\ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java index 5fef884..88a3447 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java +++ b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java @@ -2,6 +2,7 @@ package com.android.imftest.samples; import android.app.Activity; import android.os.Bundle; +import android.os.Debug; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -20,21 +21,36 @@ import com.android.internal.R; */ public class OneEditTextActivityNotSelected extends Activity { - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - final EditText editText = new EditText(this); - final TextView textView = new TextView(this); - textView.setText("The focus is here."); - layout.addView(editText); - layout.addView(textView); - - setContentView(layout); - textView.requestFocus(); + private View mRootView; + private View mDefaultFocusedView; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + mRootView = new ScrollView(this); + + EditText editText = new EditText(this); + Button button = new Button(this); + button.setText("The focus is here."); + button.setFocusableInTouchMode(true); + button.requestFocus(); + mDefaultFocusedView = button; + layout.addView(button); + layout.addView(editText); + + ((ScrollView) mRootView).addView(layout); + setContentView(mRootView); } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } } diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java index 2fd19e8..1b80263 100644 --- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java +++ b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java @@ -20,18 +20,32 @@ import com.android.internal.R; */ public class OneEditTextActivitySelected extends Activity { - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - final EditText editText = new EditText(this); - layout.addView(editText); - - setContentView(layout); - editText.requestFocus(); + private View mRootView; + private View mDefaultFocusedView; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + mRootView = new ScrollView(this); + + EditText editText = new EditText(this); + editText.requestFocus(); + mDefaultFocusedView = editText; + layout.addView(editText); + + ((ScrollView) mRootView).addView(layout); + setContentView(mRootView); } + + public View getRootView() { + return mRootView; + } + + public View getDefaultFocusedView() { + return mDefaultFocusedView; + } } diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk new file mode 100755 index 0000000..0f1924c --- /dev/null +++ b/tests/ImfTest/tests/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := android.test.runner + +LOCAL_PACKAGE_NAME := ImfTestTests + +LOCAL_INSTRUMENTATION_FOR := ImfTest + +include $(BUILD_PACKAGE) diff --git a/tests/ImfTest/tests/AndroidManifest.xml b/tests/ImfTest/tests/AndroidManifest.xml new file mode 100755 index 0000000..122d202 --- /dev/null +++ b/tests/ImfTest/tests/AndroidManifest.xml @@ -0,0 +1,34 @@ +<?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.imftest.tests"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <!-- + This declares that this app uses the instrumentation test runner targeting + the package of com.android.imftest. To run the tests use the command: + "adb shell am instrument -w com.android.imftest.tests/android.test.InstrumentationTestRunner" + --> + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.imftest" + android:label="imf tests"/> + +</manifest> diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java new file mode 100755 index 0000000..a1c5fd2 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java @@ -0,0 +1,49 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BigEditTextActivityNonScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityNonScrollablePanScan> { + + public final String TAG = "BigEditTextActivityNonScrollablePanScanTests"; + + public BigEditTextActivityNonScrollablePanScanTests() { + super(BigEditTextActivityNonScrollablePanScan.class); + } + + @LargeTest + public void testAppAdjustmentPanScan() { + // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getRootView(); + View servedView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java new file mode 100755 index 0000000..2e0b0eb --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java @@ -0,0 +1,48 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BigEditTextActivityNonScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityNonScrollableResize> { + + public final String TAG = "BigEditTextActivityNonScrollableResizeTests"; + + public BigEditTextActivityNonScrollableResizeTests() { + super(BigEditTextActivityNonScrollableResize.class); + } + + @LargeTest + public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getRootView(); + View servedView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java new file mode 100755 index 0000000..d3eefb5 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java @@ -0,0 +1,48 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BigEditTextActivityScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityScrollablePanScan> { + + public final String TAG = "BigEditTextActivityScrollablePanScanTests"; + + public BigEditTextActivityScrollablePanScanTests() { + super(BigEditTextActivityScrollablePanScan.class); + } + + @LargeTest + public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getRootView(); + View servedView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java new file mode 100755 index 0000000..5c40e6d --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java @@ -0,0 +1,49 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BigEditTextActivityScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityScrollableResize> { + + public final String TAG = "BigEditTextActivityScrollableResizeTests"; + + public BigEditTextActivityScrollableResizeTests() { + super(BigEditTextActivityScrollableResize.class); + } + + @LargeTest + public void testAppAdjustmentPanScan() { + // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BigEditTextActivityScrollableResize) mTargetActivity).getRootView(); + View servedView = ((BigEditTextActivityScrollableResize) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java new file mode 100755 index 0000000..9a93133 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java @@ -0,0 +1,49 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BottomEditTextActivityPanScanTests extends ImfBaseTestCase<BottomEditTextActivityPanScan> { + + public final String TAG = "BottomEditTextActivityPanScanTests"; + + public BottomEditTextActivityPanScanTests() { + super(BottomEditTextActivityPanScan.class); + } + + @LargeTest + public void testAppAdjustmentPanScan() { + // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BottomEditTextActivityPanScan) mTargetActivity).getRootView(); + View servedView = ((BottomEditTextActivityPanScan) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java new file mode 100755 index 0000000..9a69fd5 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java @@ -0,0 +1,49 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.imftest.R; + + +public class BottomEditTextActivityResizeTests extends ImfBaseTestCase<BottomEditTextActivityResize> { + + public final String TAG = "BottomEditTextActivityResizeTests"; + + public BottomEditTextActivityResizeTests() { + super(BottomEditTextActivityResize.class); + } + + @LargeTest + public void testAppAdjustmentResize() { + // Give the IME 2 seconds to appear. + pause(2000); + + View rootView = ((BottomEditTextActivityResize) mTargetActivity).getRootView(); + View servedView = ((BottomEditTextActivityResize) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java new file mode 100755 index 0000000..ae900c3 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java @@ -0,0 +1,58 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; +import android.widget.Button; + + +public class ButtonActivityTest extends ImfBaseTestCase<ButtonActivity> { + + final public String TAG = "ButtonActivityTest"; + + public ButtonActivityTest() { + super(ButtonActivity.class); + } + + @LargeTest + public void testButtonActivatesIme() { + + final Button button = (Button) mTargetActivity.findViewById(ButtonActivity.BUTTON_ID); + + // Push button + // Bring the target EditText into focus. + mTargetActivity.runOnUiThread(new Runnable() { + public void run() { + button.requestFocus(); + } + }); + + sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); + + // Give it a couple seconds + pause(2000); + + // We should have initialized imm.mServedView and imm.mCurrentTextBoxAttribute + assertTrue(mImm.isActive()); + // imm.mServedInputConnection should be null since Button doesn't override onCreateInputConnection(). + assertFalse(mImm.isAcceptingText()); + + destructiveCheckImeInitialState(mTargetActivity.getRootView(), button); + + } +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java new file mode 100755 index 0000000..61dc611 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java @@ -0,0 +1,135 @@ +/* + * 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.imftest.samples; + +import android.app.Activity; +import android.os.SystemClock; +import android.test.InstrumentationTestCase; +import android.view.KeyEvent; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +import com.android.imftest.R; + +public abstract class ImfBaseTestCase<T extends Activity> extends InstrumentationTestCase { + + /* + * The amount of time we are willing to wait for the IME to appear after a user action + * before we give up and fail the test. + */ + public final long WAIT_FOR_IME = 5000; + + /* + * Unfortunately there is now way for us to know how tall the IME is, + * so we have to hard code a minimum and maximum value. + */ + public final int IME_MIN_HEIGHT = 150; + public final int IME_MAX_HEIGHT = 300; + + public final String TARGET_PACKAGE_NAME = "com.android.imftest"; + protected InputMethodManager mImm; + protected T mTargetActivity; + protected boolean mExpectAutoPop; + private Class<T> mTargetActivityClass; + + public ImfBaseTestCase(Class<T> activityClass) { + mTargetActivityClass = activityClass; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + mTargetActivity = launchActivity(TARGET_PACKAGE_NAME, mTargetActivityClass, null); + mExpectAutoPop = mTargetActivity.getResources().getBoolean(R.bool.def_expect_ime_autopop); + mImm = InputMethodManager.getInstance(mTargetActivity); + } + + // Utility test methods + public void verifyEditTextAdjustment(final View editText, int rootViewHeight) { + + int[] origLocation = new int[2]; + int[] newLocation = new int[2]; + + // Tell the keyboard to go away. + mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + + // Bring the target EditText into focus. + mTargetActivity.runOnUiThread(new Runnable() { + public void run() { + editText.requestFocus(); + } + }); + + // Get the original location of the EditText. + editText.getLocationOnScreen(origLocation); + + // Tap the EditText to bring up the IME. + sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); + + // Wait until the EditText pops above the IME or until we hit the timeout. + editText.getLocationOnScreen(newLocation); + long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME; + while (newLocation[1] > rootViewHeight - IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) { + editText.getLocationOnScreen(newLocation); + pause(100); + } + + assertTrue(newLocation[1] <= rootViewHeight - IME_MIN_HEIGHT); + + // Tell the keyboard to go away. + mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } + + public void destructiveCheckImeInitialState(View rootView, View servedView) { + if (mExpectAutoPop && (mTargetActivity.getWindow().getAttributes(). + softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { + assertTrue(destructiveCheckImeUp(rootView, servedView)); + } else { + assertFalse(destructiveCheckImeUp(rootView, servedView)); + } + } + + public boolean destructiveCheckImeUp(View rootView, View servedView) { + int origHeight; + int newHeight; + + origHeight = rootView.getHeight(); + + // Tell the keyboard to go away. + mImm.hideSoftInputFromWindow(servedView.getWindowToken(), 0); + + // Give it five seconds to adjust + newHeight = rootView.getHeight(); + long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME; + while (Math.abs(newHeight - origHeight) < IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) { + newHeight = rootView.getHeight(); + } + + return (Math.abs(origHeight - newHeight) >= IME_MIN_HEIGHT); + } + + void pause(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + } + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java new file mode 100755 index 0000000..278efb1 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java @@ -0,0 +1,40 @@ +/* + * 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.imftest.samples; + +import android.app.Activity; +import android.widget.EditText; + + +public abstract class ManyEditTextActivityBaseTestCase<T extends Activity> extends ImfBaseTestCase<T> { + + public ManyEditTextActivityBaseTestCase(Class<T> activityClass){ + super(activityClass); + } + + public abstract void testAllEditTextsAdjust(); + + public void verifyAllEditTextAdjustment(int numEditTexts, int rootViewHeight) { + + for (int i = 0; i < numEditTexts; i++) { + final EditText lastEditText = (EditText) mTargetActivity.findViewById(i); + verifyEditTextAdjustment(lastEditText, rootViewHeight); + } + + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java new file mode 100755 index 0000000..4f8d14e --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java @@ -0,0 +1,36 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; + + +public class ManyEditTextActivityNoScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityNoScrollPanScan> { + + public final String TAG = "ManyEditTextActivityNoScrollPanScanTests"; + + public ManyEditTextActivityNoScrollPanScanTests() { + super(ManyEditTextActivityNoScrollPanScan.class); + } + + + @LargeTest + public void testAllEditTextsAdjust() { + verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, + mTargetActivity.getRootView().getMeasuredHeight()); + } +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java new file mode 100755 index 0000000..7f98f7f --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java @@ -0,0 +1,37 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; + + +public class ManyEditTextActivityScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollPanScan> { + + public final String TAG = "ManyEditTextActivityScrollPanScanTests"; + + + public ManyEditTextActivityScrollPanScanTests() { + super(ManyEditTextActivityScrollPanScan.class); + } + + @LargeTest + public void testAllEditTextsAdjust() { + verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, + mTargetActivity.getRootView().getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java new file mode 100755 index 0000000..68dae87 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java @@ -0,0 +1,37 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; + + +public class ManyEditTextActivityScrollResizeTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollResize> { + + public final String TAG = "ManyEditTextActivityScrollResizeTests"; + + + public ManyEditTextActivityScrollResizeTests() { + super(ManyEditTextActivityScrollResize.class); + } + + @LargeTest + public void testAllEditTextsAdjust() { + verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, + mTargetActivity.getRootView().getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java new file mode 100755 index 0000000..ed5b0c9 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java @@ -0,0 +1,50 @@ +/* + * 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.imftest.samples; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + + +public class OneEditTextActivityNotSelectedTests extends ImfBaseTestCase<OneEditTextActivityNotSelected> { + + public final String TAG = "OneEditTextActivityNotSelectedTests"; + + public OneEditTextActivityNotSelectedTests() { + super(OneEditTextActivityNotSelected.class); + } + + @LargeTest + public void testSoftKeyboardNoAutoPop() { + + // Give the IME 2 seconds to appear. + pause(2000); + + assertFalse(mImm.isAcceptingText()); + + View rootView = ((OneEditTextActivityNotSelected) mTargetActivity).getRootView(); + View servedView = ((OneEditTextActivityNotSelected) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java new file mode 100755 index 0000000..42fcd66 --- /dev/null +++ b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java @@ -0,0 +1,53 @@ +/* + * 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.imftest.samples; + +import com.android.imftest.R; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; +import android.view.View; + + +public class OneEditTextActivitySelectedTests extends ImfBaseTestCase<OneEditTextActivitySelected> { + + public final String TAG = "OneEditTextActivitySelectedTests"; + + public OneEditTextActivitySelectedTests() { + super(OneEditTextActivitySelected.class); + } + + @LargeTest + public void testSoftKeyboardAutoPop() { + + // Give the IME 2 seconds to appear. + pause(2000); + + assertTrue(mImm.isAcceptingText()); + + View rootView = ((OneEditTextActivitySelected) mTargetActivity).getRootView(); + View servedView = ((OneEditTextActivitySelected) mTargetActivity).getDefaultFocusedView(); + + assertNotNull(rootView); + assertNotNull(servedView); + + destructiveCheckImeInitialState(rootView, servedView); + + verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); + } + +} diff --git a/tests/gadgets/GadgetHostTest/AndroidManifest.xml b/tests/gadgets/GadgetHostTest/AndroidManifest.xml index cac2776..52e314f 100644 --- a/tests/gadgets/GadgetHostTest/AndroidManifest.xml +++ b/tests/gadgets/GadgetHostTest/AndroidManifest.xml @@ -17,12 +17,20 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <receiver android:name="TestGadgetProvider" android:label="@string/oh_hai" - android:icon="@drawable/oh_hai_icon"> + + <!-- BEGIN_INCLUDE(GadgetProvider) --> + <receiver android:name="TestGadgetProvider" + android:label="@string/oh_hai" + android:icon="@drawable/oh_hai_icon" + > <intent-filter> <action android:name="android.gadget.action.GADGET_UPDATE" /> </intent-filter> - <meta-data android:name="android.gadget.provider" android:resource="@xml/gadget_info" /> + <meta-data android:name="android.gadget.provider" + android:resource="@xml/gadget_info" + /> </receiver> + <!-- END_INCLUDE(GadgetProvider) --> + </application> </manifest> diff --git a/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml b/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml index 353df30..e0c4222 100644 --- a/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml +++ b/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml @@ -1,8 +1,10 @@ +<!-- BEGIN_INCLUDE(GadgetProviderInfo) --> <gadget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" android:minHeight="30dp" - android:updatePeriodMillis="3000" + android:updatePeriodMillis="86400000" android:initialLayout="@layout/test_gadget" android:configure="com.android.tests.gadgethost.TestGadgetConfigure" > </gadget-provider> +<!-- END_INCLUDE(GadgetProviderInfo) --> diff --git a/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java b/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java index d3dcf41..0bd8926 100644 --- a/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java +++ b/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java @@ -23,7 +23,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.gadget.GadgetHost; import android.gadget.GadgetHostView; -import android.gadget.GadgetInfo; +import android.gadget.GadgetProviderInfo; import android.gadget.GadgetManager; import android.os.Bundle; import android.util.Log; @@ -73,14 +73,13 @@ public class GadgetHostActivity extends Activity }; void discoverGadget(int requestCode) { - Intent intent = new Intent(GadgetManager.GADGET_PICK_ACTION); - intent.putExtra(GadgetManager.EXTRA_HOST_ID, HOST_ID); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_PICK); intent.putExtra(GadgetManager.EXTRA_GADGET_ID, mHost.allocateGadgetId()); startActivityForResult(intent, requestCode); } void configureGadget(int requestCode, int gadgetId, ComponentName configure) { - Intent intent = new Intent(GadgetManager.GADGET_CONFIGURE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_CONFIGURE); intent.setComponent(configure); intent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId); SharedPreferences.Editor prefs = getPreferences(0).edit(); @@ -89,11 +88,13 @@ public class GadgetHostActivity extends Activity startActivityForResult(intent, requestCode); } - void handleGadgetPickResult(int resultCode, Intent data) { - Bundle extras = data.getExtras(); + void handleGadgetPickResult(int resultCode, Intent intent) { + // BEGIN_INCLUDE(getExtra_EXTRA_GADGET_ID) + Bundle extras = intent.getExtras(); int gadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID); + // END_INCLUDE(getExtra_EXTRA_GADGET_ID) if (resultCode == RESULT_OK) { - GadgetInfo gadget = mGadgetManager.getGadgetInfo(gadgetId); + GadgetProviderInfo gadget = mGadgetManager.getGadgetInfo(gadgetId); if (gadget.configure != null) { // configure the gadget if we should @@ -115,14 +116,14 @@ public class GadgetHostActivity extends Activity return; } if (resultCode == RESULT_OK) { - GadgetInfo gadget = mGadgetManager.getGadgetInfo(gadgetId); + GadgetProviderInfo gadget = mGadgetManager.getGadgetInfo(gadgetId); addGadgetView(gadgetId, gadget); } else { mHost.deleteGadgetId(gadgetId); } } - void addGadgetView(int gadgetId, GadgetInfo gadget) { + void addGadgetView(int gadgetId, GadgetProviderInfo gadget) { // Inflate the gadget's RemoteViews GadgetHostView view = mHost.createView(this, gadgetId, gadget); @@ -188,11 +189,10 @@ public class GadgetHostActivity extends Activity } GadgetHost mHost = new GadgetHost(this, HOST_ID) { - protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { + protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetProviderInfo gadget) { return new MyGadgetView(gadgetId); } }; - } diff --git a/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java b/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java index 7614c9e..370a50b 100644 --- a/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java +++ b/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java @@ -37,16 +37,18 @@ public class TestGadgetProvider extends BroadcastReceiver { String action = intent.getAction(); Log.d(TAG, "intent=" + intent); - if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) { + if (GadgetManager.ACTION_GADGET_ENABLED.equals(action)) { Log.d(TAG, "ENABLED"); } - else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_DISABLED.equals(action)) { Log.d(TAG, "DISABLED"); } - else if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_UPDATE.equals(action)) { Log.d(TAG, "UPDATE"); + // BEGIN_INCLUDE(getExtra_EXTRA_GADGET_IDS) Bundle extras = intent.getExtras(); int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); + // END_INCLUDE(getExtra_EXTRA_GADGET_IDS) SharedPreferences prefs = context.getSharedPreferences( TestGadgetProvider.PREFS_NAME, 0); diff --git a/tests/gadgets/GadgetProviderTest/res/xml/gadget_info.xml b/tests/gadgets/GadgetProviderTest/res/xml/gadget_info.xml index 0b8ca2e..0fc7812 100644 --- a/tests/gadgets/GadgetProviderTest/res/xml/gadget_info.xml +++ b/tests/gadgets/GadgetProviderTest/res/xml/gadget_info.xml @@ -1,7 +1,7 @@ <gadget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" android:minHeight="30dp" - android:updatePeriodMillis="3000" + android:updatePeriodMillis="60000" android:initialLayout="@layout/test_gadget" > </gadget-provider> diff --git a/tests/gadgets/GadgetProviderTest/src/com/android/tests/gadgetprovider/TestGadgetProvider.java b/tests/gadgets/GadgetProviderTest/src/com/android/tests/gadgetprovider/TestGadgetProvider.java index 8622bc7..b81575f 100644 --- a/tests/gadgets/GadgetProviderTest/src/com/android/tests/gadgetprovider/TestGadgetProvider.java +++ b/tests/gadgets/GadgetProviderTest/src/com/android/tests/gadgetprovider/TestGadgetProvider.java @@ -34,13 +34,13 @@ public class TestGadgetProvider extends BroadcastReceiver { String action = intent.getAction(); Log.d(TAG, "intent=" + intent); - if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) { + if (GadgetManager.ACTION_GADGET_ENABLED.equals(action)) { Log.d(TAG, "ENABLED"); } - else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_DISABLED.equals(action)) { Log.d(TAG, "DISABLED"); } - else if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) { + else if (GadgetManager.ACTION_GADGET_UPDATE.equals(action)) { Log.d(TAG, "UPDATE"); Bundle extras = intent.getExtras(); int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 39200a1..82fe365 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -789,6 +789,12 @@ public final class Bridge implements ILayoutBridge { } @SuppressWarnings("unused") + public boolean performHapticFeedback(IWindow window, int effectId, boolean always) { + // pass for now. + return false; + } + + @SuppressWarnings("unused") public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { // pass for now. return null; diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 0e70f8b..f0009be 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -1142,6 +1142,7 @@ public class WifiStateTracker extends NetworkStateTracker { // Stop DHCP if (mDhcpTarget != null) { mDhcpTarget.setCancelCallback(true); + mDhcpTarget.removeMessages(EVENT_DHCP_START); } if (!NetworkUtils.stopDhcp(mInterfaceName)) { Log.e(TAG, "Could not stop DHCP"); |