diff options
373 files changed, 9315 insertions, 5898 deletions
diff --git a/api/current.xml b/api/current.xml index a8193d7..8af429d 100644 --- a/api/current.xml +++ b/api/current.xml @@ -2539,6 +2539,17 @@ visibility="public" > </field> +<field name="borderlessButtonStyle" + type="int" + transient="false" + volatile="false" + value="16843580" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="bottom" type="int" transient="false" @@ -3562,6 +3573,28 @@ visibility="public" > </field> +<field name="dividerHorizontal" + type="int" + transient="false" + volatile="false" + value="16843581" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="dividerPadding" + type="int" + transient="false" + volatile="false" + value="16843579" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="dividerVertical" type="int" transient="false" @@ -8391,6 +8424,17 @@ visibility="public" > </field> +<field name="showDividers" + type="int" + transient="false" + volatile="false" + value="16843578" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="showSilent" type="int" transient="false" @@ -15520,6 +15564,17 @@ visibility="public" > </field> +<field name="Widget_Holo_Button_Borderless" + type="int" + transient="false" + volatile="false" + value="16974047" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="Widget_Holo_Button_Inset" type="int" transient="false" @@ -19042,6 +19097,19 @@ <parameter name="items" type="android.animation.Animator..."> </parameter> </method> +<method name="playSequentially" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="items" type="java.util.List<android.animation.Animator>"> +</parameter> +</method> <method name="playTogether" return="void" abstract="false" @@ -19055,6 +19123,19 @@ <parameter name="items" type="android.animation.Animator..."> </parameter> </method> +<method name="playTogether" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="items" type="java.util.Collection<android.animation.Animator>"> +</parameter> +</method> <method name="setDuration" return="android.animation.AnimatorSet" abstract="false" @@ -20723,6 +20804,19 @@ visibility="public" > </constructor> +<method name="addOnMenuVisibilityListener" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener"> +</parameter> +</method> <method name="addTab" return="void" abstract="true" @@ -20972,6 +21066,19 @@ visibility="public" > </method> +<method name="removeOnMenuVisibilityListener" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener"> +</parameter> +</method> <method name="removeTab" return="void" abstract="true" @@ -21105,7 +21212,7 @@ > <parameter name="adapter" type="android.widget.SpinnerAdapter"> </parameter> -<parameter name="callback" type="android.app.ActionBar.NavigationCallback"> +<parameter name="callback" type="android.app.ActionBar.OnNavigationListener"> </parameter> </method> <method name="setDropdownNavigationMode" @@ -21120,7 +21227,7 @@ > <parameter name="adapter" type="android.widget.SpinnerAdapter"> </parameter> -<parameter name="callback" type="android.app.ActionBar.NavigationCallback"> +<parameter name="callback" type="android.app.ActionBar.OnNavigationListener"> </parameter> <parameter name="defaultSelectedPosition" type="int"> </parameter> @@ -21137,7 +21244,7 @@ > <parameter name="adapter" type="android.widget.SpinnerAdapter"> </parameter> -<parameter name="callback" type="android.app.ActionBar.NavigationCallback"> +<parameter name="callback" type="android.app.ActionBar.OnNavigationListener"> </parameter> </method> <method name="setNavigationMode" @@ -21449,7 +21556,28 @@ > </field> </class> -<interface name="ActionBar.NavigationCallback" +<interface name="ActionBar.OnMenuVisibilityListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onMenuVisibilityChanged" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isVisible" type="boolean"> +</parameter> +</method> +</interface> +<interface name="ActionBar.OnNavigationListener" abstract="true" static="true" final="false" @@ -23155,6 +23283,17 @@ <parameter name="exitAnim" type="int"> </parameter> </method> +<method name="recreate" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="registerForContextMenu" return="void" abstract="false" @@ -89679,6 +89818,17 @@ visibility="public" > </method> +<method name="getPreferredPreviewSizeForVideo" + return="android.hardware.Camera.Size" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getPreviewFormat" return="int" abstract="false" @@ -89868,6 +90018,17 @@ visibility="public" > </method> +<method name="getSupportedVideoSizes" + return="java.util.List<android.hardware.Camera.Size>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedWhiteBalance" return="java.util.List<java.lang.String>" abstract="false" @@ -160973,6 +161134,468 @@ > </field> </interface> +<class name="Ptp" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="Ptp" + type="android.provider.Ptp" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<field name="AUTHORITY" + type="java.lang.String" + transient="false" + volatile="false" + value=""ptp"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="Ptp.Device" + extends="java.lang.Object" + abstract="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.provider.BaseColumns"> +</implements> +<constructor name="Ptp.Device" + type="android.provider.Ptp.Device" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="getContentUri" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +</method> +<field name="CONTENT_URI" + type="android.net.Uri" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MANUFACTURER" + type="java.lang.String" + transient="false" + volatile="false" + value=""manufacturer"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MODEL" + type="java.lang.String" + transient="false" + volatile="false" + value=""model"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="Ptp.Object" + extends="java.lang.Object" + abstract="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.provider.BaseColumns"> +</implements> +<constructor name="Ptp.Object" + type="android.provider.Ptp.Object" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="getContentUri" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +<parameter name="objectID" type="long"> +</parameter> +</method> +<method name="getContentUriForImport" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +<parameter name="objectID" type="long"> +</parameter> +<parameter name="destPath" type="java.lang.String"> +</parameter> +</method> +<method name="getContentUriForObjectChildren" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +<parameter name="objectID" type="long"> +</parameter> +</method> +<method name="getContentUriForStorageChildren" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +<parameter name="storageID" type="long"> +</parameter> +</method> +<field name="ASSOCIATION_DESC" + type="java.lang.String" + transient="false" + volatile="false" + value=""association_desc"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ASSOCIATION_TYPE" + type="java.lang.String" + transient="false" + volatile="false" + value=""association_type"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DATE_CREATED" + type="java.lang.String" + transient="false" + volatile="false" + value=""date_created"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DATE_MODIFIED" + type="java.lang.String" + transient="false" + volatile="false" + value=""date_modified"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT" + type="java.lang.String" + transient="false" + volatile="false" + value=""format"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="IMAGE_DEPTH" + type="java.lang.String" + transient="false" + volatile="false" + value=""image_depth"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="IMAGE_HEIGHT" + type="java.lang.String" + transient="false" + volatile="false" + value=""image_height"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="IMAGE_WIDTH" + type="java.lang.String" + transient="false" + volatile="false" + value=""image_width"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYWORDS" + type="java.lang.String" + transient="false" + volatile="false" + value=""keywords"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="NAME" + type="java.lang.String" + transient="false" + volatile="false" + value=""name"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PARENT" + type="java.lang.String" + transient="false" + volatile="false" + value=""parent"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PROTECTION_STATUS" + type="java.lang.String" + transient="false" + volatile="false" + value=""protection_status"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SEQUENCE_NUMBER" + type="java.lang.String" + transient="false" + volatile="false" + value=""sequence_number"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SIZE" + type="java.lang.String" + transient="false" + volatile="false" + value=""size"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STORAGE_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""storage_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="THUMB" + type="java.lang.String" + transient="false" + volatile="false" + value=""thumb"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="THUMB_FORMAT" + type="java.lang.String" + transient="false" + volatile="false" + value=""thumb_format"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="THUMB_HEIGHT" + type="java.lang.String" + transient="false" + volatile="false" + value=""thumb_height"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="THUMB_SIZE" + type="java.lang.String" + transient="false" + volatile="false" + value=""thumb_size"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="THUMB_WIDTH" + type="java.lang.String" + transient="false" + volatile="false" + value=""thumb_width"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="Ptp.Storage" + extends="java.lang.Object" + abstract="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.provider.BaseColumns"> +</implements> +<constructor name="Ptp.Storage" + type="android.provider.Ptp.Storage" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="getContentUri" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +</method> +<method name="getContentUri" + return="android.net.Uri" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceID" type="int"> +</parameter> +<parameter name="storageID" type="long"> +</parameter> +</method> +<field name="DESCRIPTION" + type="java.lang.String" + transient="false" + volatile="false" + value=""description"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="IDENTIFIER" + type="java.lang.String" + transient="false" + volatile="false" + value=""identifier"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="SearchRecentSuggestions" extends="java.lang.Object" abstract="false" @@ -185610,7 +186233,7 @@ > <parameter name="cap" type="android.text.method.TextKeyListener.Capitalize"> </parameter> -<parameter name="autotext" type="boolean"> +<parameter name="autoText" type="boolean"> </parameter> </constructor> <method name="getInputType" @@ -185634,11 +186257,22 @@ deprecated="not deprecated" visibility="public" > -<parameter name="autotext" type="boolean"> +<parameter name="autoText" type="boolean"> </parameter> <parameter name="cap" type="android.text.method.TextKeyListener.Capitalize"> </parameter> </method> +<method name="getInstanceForFullKeyboard" + return="android.text.method.QwertyKeyListener" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="markAsReplaced" return="void" abstract="false" @@ -194772,6 +195406,17 @@ visibility="public" > </method> +<method name="getLocalState" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getResult" return="boolean" abstract="false" @@ -196664,7 +197309,7 @@ value="0" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -204845,6 +205490,21 @@ visibility="public" > </method> +<method name="combineMeasuredStates" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="curState" type="int"> +</parameter> +<parameter name="newState" type="int"> +</parameter> +</method> <method name="computeHorizontalScrollExtent" return="int" abstract="false" @@ -205772,6 +206432,28 @@ visibility="public" > </method> +<method name="getMeasuredHeightAndState" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMeasuredState" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getMeasuredWidth" return="int" abstract="false" @@ -205783,6 +206465,17 @@ visibility="public" > </method> +<method name="getMeasuredWidthAndState" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getNextFocusDownId" return="int" abstract="false" @@ -207688,6 +208381,23 @@ <parameter name="measureSpec" type="int"> </parameter> </method> +<method name="resolveSizeAndState" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="size" type="int"> +</parameter> +<parameter name="measureSpec" type="int"> +</parameter> +<parameter name="childMeasuredState" type="int"> +</parameter> +</method> <method name="restoreHierarchyState" return="void" abstract="false" @@ -208773,6 +209483,8 @@ </parameter> <parameter name="myWindowOnly" type="boolean"> </parameter> +<parameter name="myLocalState" type="java.lang.Object"> +</parameter> </method> <method name="unscheduleDrawable" return="void" @@ -209145,6 +209857,50 @@ visibility="public" > </field> +<field name="MEASURED_HEIGHT_STATE_SHIFT" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MEASURED_SIZE_MASK" + type="int" + transient="false" + volatile="false" + value="16777215" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MEASURED_STATE_MASK" + type="int" + transient="false" + volatile="false" + value="-16777216" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MEASURED_STATE_TOO_SMALL" + type="int" + transient="false" + volatile="false" + value="16777216" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="NO_ID" type="int" transient="false" @@ -236799,6 +237555,17 @@ visibility="public" > </method> +<method name="getShowDividers" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getWeightSum" return="float" abstract="false" @@ -236879,6 +237646,19 @@ <parameter name="i" type="int"> </parameter> </method> +<method name="setDividerDrawable" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="divider" type="android.graphics.drawable.Drawable"> +</parameter> +</method> <method name="setGravity" return="void" abstract="false" @@ -236931,6 +237711,19 @@ <parameter name="orientation" type="int"> </parameter> </method> +<method name="setShowDividers" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="showDividers" type="int"> +</parameter> +</method> <method name="setVerticalGravity" return="void" abstract="false" @@ -236968,6 +237761,50 @@ visibility="public" > </field> +<field name="SHOW_DIVIDER_BEGINNING" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SHOW_DIVIDER_END" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SHOW_DIVIDER_MIDDLE" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SHOW_DIVIDER_NONE" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="VERTICAL" type="int" transient="false" @@ -238734,7 +239571,35 @@ > <parameter name="context" type="android.content.Context"> </parameter> -<parameter name="interpolator" type="android.graphics.Interpolator"> +<parameter name="interpolator" type="android.view.animation.Interpolator"> +</parameter> +</constructor> +<constructor name="OverScroller" + type="android.widget.OverScroller" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="interpolator" type="android.view.animation.Interpolator"> +</parameter> +<parameter name="bounceCoefficientX" type="float"> +</parameter> +<parameter name="bounceCoefficientY" type="float"> +</parameter> +</constructor> +<constructor name="OverScroller" + type="android.widget.OverScroller" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="interpolator" type="android.view.animation.Interpolator"> </parameter> <parameter name="bounceCoefficientX" type="float"> </parameter> @@ -245069,19 +245934,6 @@ deprecated="not deprecated" visibility="public" > -<parameter name="drawable" type="android.graphics.drawable.Drawable"> -</parameter> -</method> -<method name="setDividerDrawable" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> <parameter name="resId" type="int"> </parameter> </method> @@ -245879,6 +246731,17 @@ visibility="public" > </method> +<method name="getCustomSelectionActionModeCallback" + return="android.view.ActionMode.Callback" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDefaultEditable" return="boolean" abstract="false" @@ -246677,6 +247540,19 @@ <parameter name="visible" type="boolean"> </parameter> </method> +<method name="setCustomSelectionActionModeCallback" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="actionModeCallback" type="android.view.ActionMode.Callback"> +</parameter> +</method> <method name="setEditableFactory" return="void" abstract="false" @@ -249356,6 +250232,25 @@ </package> <package name="dalvik.bytecode" > +<class name="OpcodeInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<field name="MAXIMUM_VALUE" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <interface name="Opcodes" abstract="true" static="false" @@ -249711,7 +250606,7 @@ value="236" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250052,7 +250947,7 @@ value="238" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250063,7 +250958,7 @@ value="239" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250360,7 +251255,7 @@ value="244" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250371,7 +251266,7 @@ value="242" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250404,7 +251299,7 @@ value="243" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250415,7 +251310,7 @@ value="232" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250514,7 +251409,7 @@ value="240" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250591,7 +251486,7 @@ value="250" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250602,7 +251497,7 @@ value="251" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250635,7 +251530,7 @@ value="248" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250646,7 +251541,7 @@ value="249" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250723,7 +251618,7 @@ value="247" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250734,7 +251629,7 @@ value="245" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250767,7 +251662,7 @@ value="246" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -250778,7 +251673,7 @@ value="233" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -251526,7 +252421,7 @@ value="234" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -251735,7 +252630,7 @@ value="235" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -251845,7 +252740,7 @@ value="237" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -334572,7 +335467,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="minSize" type="int"> +<parameter name="numElements" type="int"> </parameter> </constructor> <constructor name="ArrayDeque" @@ -334838,7 +335733,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="obj" type="java.lang.Object"> +<parameter name="o" type="java.lang.Object"> </parameter> </method> <method name="removeLast" @@ -334862,7 +335757,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="obj" type="java.lang.Object"> +<parameter name="o" type="java.lang.Object"> </parameter> </method> <method name="size" @@ -339620,6 +340515,19 @@ > <implements name="java.util.Queue"> </implements> +<method name="add" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="e" type="E"> +</parameter> +</method> <method name="addFirst" return="void" abstract="true" @@ -339646,6 +340554,19 @@ <parameter name="e" type="E"> </parameter> </method> +<method name="contains" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="o" type="java.lang.Object"> +</parameter> +</method> <method name="descendingIterator" return="java.util.Iterator<E>" abstract="true" @@ -339657,6 +340578,17 @@ visibility="public" > </method> +<method name="element" + return="E" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getFirst" return="E" abstract="true" @@ -339679,6 +340611,30 @@ visibility="public" > </method> +<method name="iterator" + return="java.util.Iterator<E>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="offer" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="e" type="E"> +</parameter> +</method> <method name="offerFirst" return="boolean" abstract="true" @@ -339705,6 +340661,17 @@ <parameter name="e" type="E"> </parameter> </method> +<method name="peek" + return="E" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="peekFirst" return="E" abstract="true" @@ -339727,6 +340694,17 @@ visibility="public" > </method> +<method name="poll" + return="E" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="pollFirst" return="E" abstract="true" @@ -339773,6 +340751,30 @@ <parameter name="e" type="E"> </parameter> </method> +<method name="remove" + return="E" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="remove" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="o" type="java.lang.Object"> +</parameter> +</method> <method name="removeFirst" return="E" abstract="true" @@ -339821,6 +340823,17 @@ <parameter name="o" type="java.lang.Object"> </parameter> </method> +<method name="size" + return="int" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> </interface> <class name="Dictionary" extends="java.lang.Object" @@ -343745,11 +344758,24 @@ deprecated="not deprecated" visibility="public" > -<parameter name="endKey" type="K"> +<parameter name="toKey" type="K"> </parameter> <parameter name="inclusive" type="boolean"> </parameter> </method> +<method name="headMap" + return="java.util.SortedMap<K, V>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="toKey" type="K"> +</parameter> +</method> <method name="higherEntry" return="java.util.Map.Entry<K, V>" abstract="true" @@ -343856,13 +344882,28 @@ deprecated="not deprecated" visibility="public" > -<parameter name="startKey" type="K"> +<parameter name="fromKey" type="K"> </parameter> -<parameter name="startInclusive" type="boolean"> +<parameter name="fromInclusive" type="boolean"> </parameter> -<parameter name="endKey" type="K"> +<parameter name="toKey" type="K"> </parameter> -<parameter name="endInclusive" type="boolean"> +<parameter name="toInclusive" type="boolean"> +</parameter> +</method> +<method name="subMap" + return="java.util.SortedMap<K, V>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fromKey" type="K"> +</parameter> +<parameter name="toKey" type="K"> </parameter> </method> <method name="tailMap" @@ -343875,11 +344916,24 @@ deprecated="not deprecated" visibility="public" > -<parameter name="startKey" type="K"> +<parameter name="fromKey" type="K"> </parameter> <parameter name="inclusive" type="boolean"> </parameter> </method> +<method name="tailMap" + return="java.util.SortedMap<K, V>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fromKey" type="K"> +</parameter> +</method> </interface> <interface name="NavigableSet" abstract="true" @@ -343948,9 +345002,22 @@ deprecated="not deprecated" visibility="public" > -<parameter name="end" type="E"> +<parameter name="toElement" type="E"> </parameter> -<parameter name="endInclusive" type="boolean"> +<parameter name="inclusive" type="boolean"> +</parameter> +</method> +<method name="headSet" + return="java.util.SortedSet<E>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="toElement" type="E"> </parameter> </method> <method name="higher" @@ -343966,6 +345033,17 @@ <parameter name="e" type="E"> </parameter> </method> +<method name="iterator" + return="java.util.Iterator<E>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="lower" return="E" abstract="true" @@ -344011,13 +345089,28 @@ deprecated="not deprecated" visibility="public" > -<parameter name="start" type="E"> +<parameter name="fromElement" type="E"> </parameter> -<parameter name="startInclusive" type="boolean"> +<parameter name="fromInclusive" type="boolean"> </parameter> -<parameter name="end" type="E"> +<parameter name="toElement" type="E"> </parameter> -<parameter name="endInclusive" type="boolean"> +<parameter name="toInclusive" type="boolean"> +</parameter> +</method> +<method name="subSet" + return="java.util.SortedSet<E>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fromElement" type="E"> +</parameter> +<parameter name="toElement" type="E"> </parameter> </method> <method name="tailSet" @@ -344030,9 +345123,22 @@ deprecated="not deprecated" visibility="public" > -<parameter name="start" type="E"> +<parameter name="fromElement" type="E"> </parameter> -<parameter name="startInclusive" type="boolean"> +<parameter name="inclusive" type="boolean"> +</parameter> +</method> +<method name="tailSet" + return="java.util.SortedSet<E>" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fromElement" type="E"> </parameter> </method> </interface> @@ -344694,6 +345800,19 @@ > <implements name="java.util.Collection"> </implements> +<method name="add" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="e" type="E"> +</parameter> +</method> <method name="element" return="E" abstract="true" @@ -344715,7 +345834,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="o" type="E"> +<parameter name="e" type="E"> </parameter> </method> <method name="peek" diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 040421a..f62db1c 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -341,9 +341,11 @@ public final class Pm { if (nonLocalized != null) { return nonLocalized.toString(); } - Resources r = getResources(pii); - if (r != null) { - return r.getString(res); + if (res != 0) { + Resources r = getResources(pii); + if (r != null) { + return r.getString(res); + } } return null; } diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index b96391a..6e589e4 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.content.res.Resources.NotFoundException; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; import android.view.animation.AnimationUtils; import org.xmlpull.v1.XmlPullParser; @@ -195,72 +196,95 @@ public class AnimatorInflater { anim = new ValueAnimator(); } TypeEvaluator evaluator = null; - boolean hasFrom = a.hasValue(com.android.internal.R.styleable.Animator_valueFrom); - boolean hasTo = a.hasValue(com.android.internal.R.styleable.Animator_valueTo); - - switch (valueType) { - - case VALUE_TYPE_FLOAT: { - float valueFrom; - float valueTo; - if (hasFrom) { - valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); - if (hasTo) { - valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); - anim.setFloatValues(valueFrom, valueTo); + + int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom; + int valueToIndex = com.android.internal.R.styleable.Animator_valueTo; + + boolean getFloats = (valueType == VALUE_TYPE_FLOAT); + + TypedValue tvFrom = a.peekValue(valueFromIndex); + boolean hasFrom = (tvFrom != null); + int fromType = hasFrom ? tvFrom.type : 0; + TypedValue tvTo = a.peekValue(valueToIndex); + boolean hasTo = (tvTo != null); + int toType = hasTo ? tvTo.type : 0; + + if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) && + (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) || + (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) && + (toType <= TypedValue.TYPE_LAST_COLOR_INT))) { + // special case for colors: ignore valueType and get ints + getFloats = false; + anim.setEvaluator(new RGBEvaluator()); + } + + if (getFloats) { + float valueFrom; + float valueTo; + if (hasFrom) { + if (fromType == TypedValue.TYPE_DIMENSION) { + valueFrom = a.getDimension(valueFromIndex, 0f); + } else { + valueFrom = a.getFloat(valueFromIndex, 0f); + } + if (hasTo) { + if (toType == TypedValue.TYPE_DIMENSION) { + valueTo = a.getDimension(valueToIndex, 0f); } else { - anim.setFloatValues(valueFrom); + valueTo = a.getFloat(valueToIndex, 0f); } + anim.setFloatValues(valueFrom, valueTo); + } else { + anim.setFloatValues(valueFrom); + } + } else { + if (toType == TypedValue.TYPE_DIMENSION) { + valueTo = a.getDimension(valueToIndex, 0f); } else { - valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); - anim.setFloatValues(valueTo); + valueTo = a.getFloat(valueToIndex, 0f); } + anim.setFloatValues(valueTo); } - break; - - case VALUE_TYPE_COLOR: - evaluator = new RGBEvaluator(); - anim.setEvaluator(evaluator); - // fall through to pick up values - case VALUE_TYPE_INT: { - int valueFrom; - int valueTo; - if (hasFrom) { - valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0); - if (hasTo) { - valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); - anim.setIntValues(valueFrom, valueTo); + } else { + int valueFrom; + int valueTo; + if (hasFrom) { + if (fromType == TypedValue.TYPE_DIMENSION) { + valueFrom = (int) a.getDimension(valueFromIndex, 0f); + } else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) && + (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) { + valueFrom = a.getColor(valueFromIndex, 0); + } else { + valueFrom = a.getInt(valueFromIndex, 0); + } + if (hasTo) { + if (toType == TypedValue.TYPE_DIMENSION) { + valueTo = (int) a.getDimension(valueToIndex, 0f); + } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) && + (toType <= TypedValue.TYPE_LAST_COLOR_INT)) { + valueTo = a.getColor(valueToIndex, 0); } else { - anim.setIntValues(valueFrom); + valueTo = a.getInt(valueToIndex, 0); } + anim.setIntValues(valueFrom, valueTo); } else { - valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0); - anim.setIntValues(valueTo); + anim.setIntValues(valueFrom); } - } - break; - - case VALUE_TYPE_CUSTOM: { - // TODO: How to get an 'Object' value? - float valueFrom; - float valueTo; - if (hasFrom) { - valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f); - if (hasTo) { - valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); - anim.setFloatValues(valueFrom, valueTo); + } else { + if (hasTo) { + if (toType == TypedValue.TYPE_DIMENSION) { + valueTo = (int) a.getDimension(valueToIndex, 0f); + } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) && + (toType <= TypedValue.TYPE_LAST_COLOR_INT)) { + valueTo = a.getColor(valueToIndex, 0); } else { - anim.setFloatValues(valueFrom); + valueTo = a.getInt(valueToIndex, 0); } - } else { - valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f); - anim.setFloatValues(valueTo); + anim.setIntValues(valueTo); } } - break; } - anim.setDuration(duration); anim.setStartDelay(startDelay); diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index f5420d1..154e084 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -17,7 +17,9 @@ package android.animation; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.List; /** * This class plays a set of {@link Animator} objects in the specified order. Animations @@ -117,10 +119,29 @@ public final class AnimatorSet extends Animator { } /** + * Sets up this AnimatorSet to play all of the supplied animations at the same time. + * + * @param items The animations that will be started simultaneously. + */ + public void playTogether(Collection<Animator> items) { + if (items != null && items.size() > 0) { + mNeedsSort = true; + Builder builder = null; + for (Animator anim : items) { + if (builder == null) { + builder = play(anim); + } else { + builder.with(anim); + } + } + } + } + + /** * Sets up this AnimatorSet to play each of the supplied animations when the * previous animation ends. * - * @param items The aniamtions that will be started one after another. + * @param items The animations that will be started one after another. */ public void playSequentially(Animator... items) { if (items != null) { @@ -136,6 +157,25 @@ public final class AnimatorSet extends Animator { } /** + * Sets up this AnimatorSet to play each of the supplied animations when the + * previous animation ends. + * + * @param items The animations that will be started one after another. + */ + public void playSequentially(List<Animator> items) { + if (items != null && items.size() > 0) { + mNeedsSort = true; + if (items.size() == 1) { + play(items.get(0)); + } else { + for (int i = 0; i < items.size() - 1; ++i) { + play(items.get(i)).before(items.get(i+1)); + } + } + } + } + + /** * Returns the current list of child Animator objects controlled by this * AnimatorSet. This is a copy of the internal list; modifications to the returned list * will not affect the AnimatorSet, although changes to the underlying Animator objects diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 7a6ad0f..2f69520 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -168,13 +168,13 @@ public abstract class ActionBar { * @param adapter An adapter that will provide views both to display * the current navigation selection and populate views * within the dropdown navigation menu. - * @param callback A NavigationCallback that will receive events when the user + * @param callback A OnNavigationListener that will receive events when the user * selects a navigation item. * @deprecated See setListNavigationCallbacks. */ @Deprecated public abstract void setDropdownNavigationMode(SpinnerAdapter adapter, - NavigationCallback callback); + OnNavigationListener callback); /** * Set the adapter and navigation callback for list navigation mode. @@ -182,17 +182,17 @@ public abstract class ActionBar { * The supplied adapter will provide views for the expanded list as well as * the currently selected item. (These may be displayed differently.) * - * The supplied NavigationCallback will alert the application when the user + * The supplied OnNavigationListener will alert the application when the user * changes the current list selection. * * @param adapter An adapter that will provide views both to display * the current navigation selection and populate views * within the dropdown navigation menu. - * @param callback A NavigationCallback that will receive events when the user + * @param callback An OnNavigationListener that will receive events when the user * selects a navigation item. */ public abstract void setListNavigationCallbacks(SpinnerAdapter adapter, - NavigationCallback callback); + OnNavigationListener callback); /** * Set the action bar into dropdown navigation mode and supply an adapter that will @@ -201,7 +201,7 @@ public abstract class ActionBar { * @param adapter An adapter that will provide views both to display the current * navigation selection and populate views within the dropdown * navigation menu. - * @param callback A NavigationCallback that will receive events when the user + * @param callback A OnNavigationListener that will receive events when the user * selects a navigation item. * @param defaultSelectedPosition Position within the provided adapter that should be * selected from the outset. @@ -209,7 +209,7 @@ public abstract class ActionBar { */ @Deprecated public abstract void setDropdownNavigationMode(SpinnerAdapter adapter, - NavigationCallback callback, int defaultSelectedPosition); + OnNavigationListener callback, int defaultSelectedPosition); /** * Set the selected navigation item in list or tabbed navigation modes. @@ -532,9 +532,24 @@ public abstract class ActionBar { public abstract boolean isShowing(); /** - * Callback interface for ActionBar navigation events. + * Add a listener that will respond to menu visibility change events. + * + * @param listener The new listener to add + */ + public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener); + + /** + * Remove a menu visibility listener. This listener will no longer receive menu + * visibility change events. + * + * @param listener A listener to remove that was previously added + */ + public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener); + + /** + * Listener interface for ActionBar navigation events. */ - public interface NavigationCallback { + public interface OnNavigationListener { /** * This method is called whenever a navigation item in your action bar * is selected. @@ -547,6 +562,21 @@ public abstract class ActionBar { } /** + * Listener for receiving events when action bar menus are shown or hidden. + */ + public interface OnMenuVisibilityListener { + /** + * Called when an action bar menu is shown or hidden. Applications may want to use + * this to tune auto-hiding behavior for the action bar or pause/resume video playback, + * gameplay, or other activity within the main content area. + * + * @param isVisible True if an action bar menu is now visible, false if no action bar + * menus are visible. + */ + public void onMenuVisibilityChanged(boolean isVisible); + } + + /** * A tab in the action bar. * * <p>Tabs manage the hiding and showing of {@link Fragment}s. diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d69a179..a0a6b42 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -42,6 +42,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.text.Selection; @@ -78,6 +79,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * An activity is a single, focused thing that the user can do. Almost all @@ -842,8 +844,6 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { - mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( - com.android.internal.R.styleable.Window_windowNoDisplay, false); if (mLastNonConfigurationInstances != null) { mAllLoaderManagers = mLastNonConfigurationInstances.loaders; } @@ -2362,6 +2362,13 @@ public class Activity extends ContextThemeWrapper * @return The default implementation returns true. */ public boolean onMenuOpened(int featureId, Menu menu) { + if (featureId == Window.FEATURE_ACTION_BAR) { + if (mActionBar != null) { + mActionBar.dispatchMenuVisibilityChanged(true); + } else { + Log.e(TAG, "Tried to open action bar menu with no action bar"); + } + } return true; } @@ -2392,7 +2399,7 @@ public class Activity extends ContextThemeWrapper return true; } return mFragments.dispatchContextItemSelected(item); - + default: return false; } @@ -2417,6 +2424,10 @@ public class Activity extends ContextThemeWrapper case Window.FEATURE_CONTEXT_MENU: onContextMenuClosed(menu); break; + + case Window.FEATURE_ACTION_BAR: + mActionBar.dispatchMenuVisibilityChanged(false); + break; } } @@ -3502,6 +3513,22 @@ public class Activity extends ContextThemeWrapper } /** + * Cause this Activity to be recreated with a new instance. This results + * in essentially the same flow as when the Activity is created due to + * a configuration change -- the current instance will go through its + * lifecycle to {@link #onDestroy} and a new instance then created after it. + */ + public void recreate() { + if (mParent != null) { + throw new IllegalStateException("Can only be called on top-level activity"); + } + if (Looper.myLooper() != mMainThread.getLooper()) { + throw new IllegalStateException("Must be called from main thread"); + } + mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false); + } + + /** * Call this when your activity is done and should be closed. The * ActivityResult is propagated back to whoever launched you via * onActivityResult(). @@ -4255,6 +4282,8 @@ public class Activity extends ContextThemeWrapper final void performCreate(Bundle icicle) { onCreate(icicle); + mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c0714e3..a8f08c2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -224,6 +224,11 @@ public final class ActivityThread { boolean startsNotResumed; boolean isForward; + int pendingConfigChanges; + boolean onlyLocalRequest; + + View mPendingRemoveWindow; + WindowManager mPendingRemoveWindowManager; ActivityClientRecord() { parent = null; @@ -444,19 +449,8 @@ public final class ActivityThread { public final void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) { - ActivityClientRecord r = new ActivityClientRecord(); - - r.token = token; - r.pendingResults = pendingResults; - r.pendingIntents = pendingNewIntents; - r.startsNotResumed = notResumed; - r.createdConfig = config; - - synchronized (mPackages) { - mRelaunchingActivities.add(r); - } - - queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges); + requestRelaunchActivity(token, pendingResults, pendingNewIntents, + configChanges, notResumed, config, true); } public final void scheduleNewIntent(List<Intent> intents, IBinder token) { @@ -981,7 +975,7 @@ public final class ActivityThread { } break; case RELAUNCH_ACTIVITY: { ActivityClientRecord r = (ActivityClientRecord)msg.obj; - handleRelaunchActivity(r, msg.arg1); + handleRelaunchActivity(r); } break; case PAUSE_ACTIVITY: handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2); @@ -2183,6 +2177,19 @@ public final class ActivityThread { return r; } + final void cleanUpPendingRemoveWindows(ActivityClientRecord r) { + if (r.mPendingRemoveWindow != null) { + r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow); + IBinder wtoken = r.mPendingRemoveWindow.getWindowToken(); + if (wtoken != null) { + WindowManagerImpl.getDefault().closeAll(wtoken, + r.activity.getClass().getName(), "Activity"); + } + } + r.mPendingRemoveWindow = null; + r.mPendingRemoveWindowManager = null; + } + final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. @@ -2235,6 +2242,9 @@ public final class ActivityThread { r.hideForNow = true; } + // Get rid of anything left hanging around. + cleanUpPendingRemoveWindows(r); + // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible @@ -2267,11 +2277,14 @@ public final class ActivityThread { } } - r.nextIdle = mNewActivities; - mNewActivities = r; - if (localLOGV) Slog.v( - TAG, "Scheduling idle handler for " + r); - Looper.myQueue().addIdleHandler(new Idler()); + if (!r.onlyLocalRequest) { + r.nextIdle = mNewActivities; + mNewActivities = r; + if (localLOGV) Slog.v( + TAG, "Scheduling idle handler for " + r); + Looper.myQueue().addIdleHandler(new Idler()); + } + r.onlyLocalRequest = false; } else { // If an exception was thrown when trying to resume, then @@ -2728,6 +2741,7 @@ public final class ActivityThread { ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance); if (r != null) { + cleanUpPendingRemoveWindows(r); WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v != null) { @@ -2736,16 +2750,31 @@ public final class ActivityThread { } IBinder wtoken = v.getWindowToken(); if (r.activity.mWindowAdded) { - wm.removeViewImmediate(v); + if (r.onlyLocalRequest) { + // Hold off on removing this until the new activity's + // window is being added. + r.mPendingRemoveWindow = v; + r.mPendingRemoveWindowManager = wm; + } else { + wm.removeViewImmediate(v); + } } - if (wtoken != null) { + if (wtoken != null && r.mPendingRemoveWindow == null) { WindowManagerImpl.getDefault().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); } r.activity.mDecor = null; } - WindowManagerImpl.getDefault().closeAll(token, - r.activity.getClass().getName(), "Activity"); + if (r.mPendingRemoveWindow == null) { + // If we are delaying the removal of the activity window, then + // we can't clean up all windows here. Note that we can't do + // so later either, which means any windows that aren't closed + // by the app will leak. Well we try to warning them a lot + // about leaking windows, because that is a bug, so if they are + // using this recreate facility then they get to live with leaks. + WindowManagerImpl.getDefault().closeAll(token, + r.activity.getClass().getName(), "Activity"); + } // Mocked out contexts won't be participating in the normal // process lifecycle, but if we're running with a proper @@ -2766,17 +2795,70 @@ public final class ActivityThread { } } - private final void handleRelaunchActivity(ActivityClientRecord tmp, int configChanges) { + public final void requestRelaunchActivity(IBinder token, + List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + int configChanges, boolean notResumed, Configuration config, + boolean fromServer) { + ActivityClientRecord target = null; + + synchronized (mPackages) { + for (int i=0; i<mRelaunchingActivities.size(); i++) { + ActivityClientRecord r = mRelaunchingActivities.get(i); + if (r.token == token) { + target = r; + if (pendingResults != null) { + if (r.pendingResults != null) { + r.pendingResults.addAll(pendingResults); + } else { + r.pendingResults = pendingResults; + } + } + if (pendingNewIntents != null) { + if (r.pendingIntents != null) { + r.pendingIntents.addAll(pendingNewIntents); + } else { + r.pendingIntents = pendingNewIntents; + } + } + break; + } + } + + if (target == null) { + target = new ActivityClientRecord(); + target.token = token; + target.pendingResults = pendingResults; + target.pendingIntents = pendingNewIntents; + if (!fromServer) { + ActivityClientRecord existing = mActivities.get(token); + if (existing != null) { + target.startsNotResumed = existing.paused; + } + target.onlyLocalRequest = true; + } + mRelaunchingActivities.add(target); + queueOrSendMessage(H.RELAUNCH_ACTIVITY, target); + } + + if (fromServer) { + target.startsNotResumed = notResumed; + target.onlyLocalRequest = false; + } + if (config != null) { + target.createdConfig = config; + } + target.pendingConfigChanges |= configChanges; + } + } + + private final void handleRelaunchActivity(ActivityClientRecord tmp) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); Configuration changedConfig = null; + int configChanges = 0; - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " - + tmp.token + " with configChanges=0x" - + Integer.toHexString(configChanges)); - // First: make sure we have the most recent configuration and most // recent version of the activity, or skip it if some previous call // had taken a more recent version. @@ -2788,6 +2870,7 @@ public final class ActivityThread { ActivityClientRecord r = mRelaunchingActivities.get(i); if (r.token == token) { tmp = r; + configChanges |= tmp.pendingConfigChanges; mRelaunchingActivities.remove(i); i--; N--; @@ -2799,6 +2882,10 @@ public final class ActivityThread { return; } + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " + + tmp.token + " with configChanges=0x" + + Integer.toHexString(configChanges)); + if (mPendingConfiguration != null) { changedConfig = mPendingConfiguration; mPendingConfiguration = null; @@ -2834,6 +2921,7 @@ public final class ActivityThread { } r.activity.mConfigChangeFlags |= configChanges; + r.onlyLocalRequest = tmp.onlyLocalRequest; Intent currentIntent = r.activity.mIntent; Bundle savedState = null; diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 64a4d7a..f90fc59 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -735,6 +735,9 @@ public class Dialog implements DialogInterface, Window.Callback, * @see Activity#onMenuOpened(int, Menu) */ public boolean onMenuOpened(int featureId, Menu menu) { + if (featureId == Window.FEATURE_ACTION_BAR) { + mActionBar.dispatchMenuVisibilityChanged(true); + } return true; } @@ -749,6 +752,9 @@ public class Dialog implements DialogInterface, Window.Callback, * @see Activity#onPanelClosed(int, Menu) */ public void onPanelClosed(int featureId, Menu menu) { + if (featureId == Window.FEATURE_ACTION_BAR) { + mActionBar.dispatchMenuVisibilityChanged(false); + } } /** diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java index e924c1c..139095f 100644 --- a/core/java/android/app/FragmentBreadCrumbs.java +++ b/core/java/android/app/FragmentBreadCrumbs.java @@ -110,6 +110,7 @@ public class FragmentBreadCrumbs extends ViewGroup int maxHeight = 0; int maxWidth = 0; + int measuredChildState = 0; // Find rightmost and bottom-most child for (int i = 0; i < count; i++) { @@ -118,6 +119,8 @@ public class FragmentBreadCrumbs extends ViewGroup measureChild(child, widthMeasureSpec, heightMeasureSpec); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); + measuredChildState = combineMeasuredStates(measuredChildState, + child.getMeasuredState()); } } @@ -129,8 +132,9 @@ public class FragmentBreadCrumbs extends ViewGroup maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); - setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), - resolveSize(maxHeight, heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, measuredChildState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + measuredChildState<<MEASURED_HEIGHT_STATE_SHIFT)); } @Override diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 0c280f9..4d19b62 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -17,12 +17,6 @@ package android.app; -import static android.app.SuggestionsAdapter.getColumnString; - -import java.util.WeakHashMap; -import java.util.concurrent.atomic.AtomicLong; - -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -30,21 +24,15 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.Cursor; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; -import android.provider.Browser; import android.speech.RecognizerIntent; -import android.text.Editable; import android.text.InputType; import android.text.TextUtils; -import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -55,18 +43,15 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; import android.widget.AutoCompleteTextView; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; +import android.widget.SearchView; import android.widget.TextView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemSelectedListener; + +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicLong; /** * Search dialog. This is controlled by the @@ -74,32 +59,28 @@ import android.widget.AdapterView.OnItemSelectedListener; * * @hide */ -public class SearchDialog extends Dialog implements OnItemClickListener, OnItemSelectedListener { +public class SearchDialog extends Dialog { // Debugging support private static final boolean DBG = false; private static final String LOG_TAG = "SearchDialog"; - private static final boolean DBG_LOG_TIMING = false; private static final String INSTANCE_KEY_COMPONENT = "comp"; private static final String INSTANCE_KEY_APPDATA = "data"; - private static final String INSTANCE_KEY_STORED_APPDATA = "sData"; private static final String INSTANCE_KEY_USER_QUERY = "uQry"; - + // The string used for privateImeOptions to identify to the IME that it should not show // a microphone button since one already exists in the search dialog. private static final String IME_OPTION_NO_MICROPHONE = "nm"; - private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12; private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7; // views & widgets private TextView mBadgeLabel; private ImageView mAppIcon; - private SearchAutoComplete mSearchAutoComplete; - private Button mGoButton; - private ImageButton mVoiceButton; + private AutoCompleteTextView mSearchAutoComplete; private View mSearchPlate; + private SearchView mSearchView; private Drawable mWorkingSpinner; // interaction with searchable application @@ -107,30 +88,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private ComponentName mLaunchComponent; private Bundle mAppSearchData; private Context mActivityContext; - private SearchManager mSearchManager; // For voice searching private final Intent mVoiceWebSearchIntent; private final Intent mVoiceAppSearchIntent; - // support for AutoCompleteTextView suggestions display - private SuggestionsAdapter mSuggestionsAdapter; - - // Whether to rewrite queries when selecting suggestions - private static final boolean REWRITE_QUERIES = true; - // The query entered by the user. This is not changed when selecting a suggestion // that modifies the contents of the text field. But if the user then edits // the suggestion, the resulting string is saved. private String mUserQuery; - // The query passed in when opening the SearchDialog. Used in the browser - // case to determine whether the user has edited the query. - private String mInitialQuery; - - // A weak map of drawables we've gotten from other packages, so we don't load them - // more than once. - private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = - new WeakHashMap<String, Drawable.ConstantState>(); // Last known IME options value for the search edit text. private int mSearchAutoCompleteImeOptions; @@ -160,7 +126,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mSearchManager = searchManager; } /** @@ -196,30 +161,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // get the view elements for local access SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar); searchBar.setSearchDialog(this); - - mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge); - mSearchAutoComplete = (SearchAutoComplete) - findViewById(com.android.internal.R.id.search_src_text); + mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view); + mSearchView.setSubmitButtonEnabled(true); + mSearchView.setOnCloseListener(mOnCloseListener); + mSearchView.setOnQueryChangeListener(mOnQueryChangeListener); + mSearchView.setOnSuggestionSelectionListener(mOnSuggestionSelectionListener); + + // TODO: Move the badge logic to SearchView or move the badge to search_bar.xml + mBadgeLabel = (TextView) mSearchView.findViewById(com.android.internal.R.id.search_badge); + mSearchAutoComplete = (AutoCompleteTextView) + mSearchView.findViewById(com.android.internal.R.id.search_src_text); mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon); - mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); - mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); - mSearchPlate = findViewById(com.android.internal.R.id.search_plate); + mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate); mWorkingSpinner = getContext().getResources(). getDrawable(com.android.internal.R.drawable.search_spinner); mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( null, null, mWorkingSpinner, null); setWorking(false); - // attach listeners - mSearchAutoComplete.addTextChangedListener(mTextWatcher); - mSearchAutoComplete.setOnKeyListener(mTextKeyListener); - mSearchAutoComplete.setOnItemClickListener(this); - mSearchAutoComplete.setOnItemSelectedListener(this); - mGoButton.setOnClickListener(mGoButtonClickListener); - mGoButton.setOnKeyListener(mButtonsKeyListener); - mVoiceButton.setOnClickListener(mVoiceButtonClickListener); - mVoiceButton.setOnKeyListener(mButtonsKeyListener); - // pre-hide all the extraneous elements mBadgeLabel.setVisibility(View.GONE); @@ -256,7 +215,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } - mInitialQuery = initialQuery == null ? "" : initialQuery; // finally, load the user's initial text (which may trigger suggestions) setUserQuery(initialQuery); if (selectInitialQuery) { @@ -296,6 +254,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Recreate the search bar view every time the dialog is shown, to get rid // of any bad state in the AutoCompleteTextView etc createContentView(); + mSearchView.setSearchableInfo(mSearchable); show(); } @@ -326,14 +285,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS getContext().unregisterReceiver(mConfChangeListener); - closeSuggestionsAdapter(); - // dump extra memory we're hanging on to mLaunchComponent = null; mAppSearchData = null; mSearchable = null; mUserQuery = null; - mInitialQuery = null; } /** @@ -349,20 +305,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Closes and gets rid of the suggestions adapter. - */ - private void closeSuggestionsAdapter() { - // remove the adapter from the autocomplete first, to avoid any updates - // when we drop the cursor - mSearchAutoComplete.setAdapter((SuggestionsAdapter)null); - // close any leftover cursor - if (mSuggestionsAdapter != null) { - mSuggestionsAdapter.close(); - } - mSuggestionsAdapter = null; - } - - /** * Save the minimal set of data necessary to recreate the search * * @return A bundle with the state of the dialog, or {@code null} if the search @@ -385,8 +327,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS /** * Restore the state of the dialog from a previously saved bundle. * - * TODO: go through this and make sure that it saves everything that is saved - * * @param savedInstanceState The state of the dialog previously saved by * {@link #onSaveInstanceState()}. */ @@ -404,21 +344,18 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return; } } - + /** * Called after resources have changed, e.g. after screen rotation or locale change. */ public void onConfigurationChanged() { if (mSearchable != null && isShowing()) { // Redraw (resources may have changed) - updateSearchButton(); updateSearchAppIcon(); updateSearchBadge(); - updateQueryHint(); if (isLandscapeMode(getContext())) { mSearchAutoComplete.ensureImeVisible(true); } - mSearchAutoComplete.showDropDownAfterLayout(); } } @@ -434,11 +371,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (mSearchable != null) { mDecor.setVisibility(View.VISIBLE); updateSearchAutoComplete(); - updateSearchButton(); updateSearchAppIcon(); updateSearchBadge(); - updateQueryHint(); - updateVoiceButton(TextUtils.isEmpty(mUserQuery)); // In order to properly configure the input method (if one is being used), we // need to let it know if we'll be providing suggestions. Although it would be @@ -474,68 +408,26 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * Updates the auto-complete text view. */ private void updateSearchAutoComplete() { - // close any existing suggestions adapter - closeSuggestionsAdapter(); - - mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation - mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold()); // we dismiss the entire dialog instead mSearchAutoComplete.setDropDownDismissedOnCompletion(false); - - mSearchAutoComplete.setForceIgnoreOutsideTouch(true); - - // attach the suggestions adapter, if suggestions are available - // The existence of a suggestions authority is the proxy for "suggestions available here" - if (mSearchable.getSuggestAuthority() != null) { - mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable, - mOutsideDrawablesCache); - mSearchAutoComplete.setAdapter(mSuggestionsAdapter); - } - } - - private void updateSearchButton() { - String textLabel = null; - Drawable iconLabel = null; - int textId = mSearchable.getSearchButtonText(); - if (isBrowserSearch()){ - iconLabel = getContext().getResources() - .getDrawable(com.android.internal.R.drawable.ic_btn_search_go); - } else if (textId != 0) { - textLabel = mActivityContext.getResources().getString(textId); - } else { - iconLabel = getContext().getResources(). - getDrawable(com.android.internal.R.drawable.ic_btn_search); - } - mGoButton.setText(textLabel); - mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null); + mSearchAutoComplete.setForceIgnoreOutsideTouch(false); } private void updateSearchAppIcon() { - if (isBrowserSearch()) { - mAppIcon.setImageResource(0); - mAppIcon.setVisibility(View.GONE); - mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL, - mSearchPlate.getPaddingTop(), - mSearchPlate.getPaddingRight(), - mSearchPlate.getPaddingBottom()); - } else { - PackageManager pm = getContext().getPackageManager(); - Drawable icon; - try { - ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); - icon = pm.getApplicationIcon(info.applicationInfo); - if (DBG) Log.d(LOG_TAG, "Using app-specific icon"); - } catch (NameNotFoundException e) { - icon = pm.getDefaultActivityIcon(); - Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon"); - } - mAppIcon.setImageDrawable(icon); - mAppIcon.setVisibility(View.VISIBLE); - mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, - mSearchPlate.getPaddingTop(), - mSearchPlate.getPaddingRight(), - mSearchPlate.getPaddingBottom()); + PackageManager pm = getContext().getPackageManager(); + Drawable icon; + try { + ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); + icon = pm.getApplicationIcon(info.applicationInfo); + if (DBG) + Log.d(LOG_TAG, "Using app-specific icon"); + } catch (NameNotFoundException e) { + icon = pm.getDefaultActivityIcon(); + Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon"); } + mAppIcon.setImageDrawable(icon); + mAppIcon.setVisibility(View.VISIBLE); + mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), mSearchPlate.getPaddingBottom()); } /** @@ -546,7 +438,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS int visibility = View.GONE; Drawable icon = null; CharSequence text = null; - + // optionally show one or the other. if (mSearchable.useBadgeIcon()) { icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId()); @@ -557,71 +449,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS visibility = View.VISIBLE; if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId()); } - + mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); mBadgeLabel.setText(text); mBadgeLabel.setVisibility(visibility); } - /** - * Update the hint in the query text field. - */ - private void updateQueryHint() { - if (isShowing()) { - String hint = null; - if (mSearchable != null) { - int hintId = mSearchable.getHintId(); - if (hintId != 0) { - hint = mActivityContext.getString(hintId); - } - } - mSearchAutoComplete.setHint(hint); - } - } - - /** - * Update the visibility of the voice button. There are actually two voice search modes, - * either of which will activate the button. - * @param empty whether the search query text field is empty. If it is, then the other - * criteria apply to make the voice button visible. Otherwise the voice button will not - * be visible - i.e., if the user has typed a query, remove the voice button. - */ - private void updateVoiceButton(boolean empty) { - int visibility = View.GONE; - if ((mAppSearchData == null || !mAppSearchData.getBoolean( - SearchManager.DISABLE_VOICE_SEARCH, false)) - && mSearchable.getVoiceSearchEnabled() && empty) { - Intent testIntent = null; - if (mSearchable.getVoiceSearchLaunchWebSearch()) { - testIntent = mVoiceWebSearchIntent; - } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { - testIntent = mVoiceAppSearchIntent; - } - if (testIntent != null) { - ResolveInfo ri = getContext().getPackageManager(). - resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY); - if (ri != null) { - visibility = View.VISIBLE; - } - } - } - mVoiceButton.setVisibility(visibility); - } - - /** Called by SuggestionsAdapter when the cursor contents changed. */ - void onDataSetChanged() { - if (mSearchAutoComplete != null && mSuggestionsAdapter != null) { - mSearchAutoComplete.onFilterComplete(mSuggestionsAdapter.getCount()); - } - } - - /** - * Hack to determine whether this is the browser, so we can adjust the UI. - */ - private boolean isBrowserSearch() { - return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/"); - } - /* * Listeners of various types */ @@ -642,7 +475,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Let Dialog handle events outside the window while the pop-up is showing. return super.onTouchEvent(event); } - + private boolean isOutOfBounds(View v, MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); @@ -651,336 +484,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS || (x > (v.getWidth()+slop)) || (y > (v.getHeight()+slop)); } - - /** - * Dialog's OnKeyListener implements various search-specific functionality - * - * @param keyCode This is the keycode of the typed key, and is the same value as - * found in the KeyEvent parameter. - * @param event The complete event record for the typed key - * - * @return Return true if the event was handled here, or false if not. - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (DBG) Log.d(LOG_TAG, "onKeyDown(" + keyCode + "," + event + ")"); - if (mSearchable == null) { - return false; - } - - // if it's an action specified by the searchable activity, launch the - // entered query with the action key - SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); - if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { - launchQuerySearch(keyCode, actionKey.getQueryActionMsg()); - return true; - } - - return super.onKeyDown(keyCode, event); - } - - /** - * Callback to watch the textedit field for empty/non-empty - */ - private TextWatcher mTextWatcher = new TextWatcher() { - - public void beforeTextChanged(CharSequence s, int start, int before, int after) { } - - public void onTextChanged(CharSequence s, int start, - int before, int after) { - if (DBG_LOG_TIMING) { - dbgLogTiming("onTextChanged()"); - } - if (mSearchable == null) { - return; - } - if (!mSearchAutoComplete.isPerformingCompletion()) { - // The user changed the query, remember it. - mUserQuery = s == null ? "" : s.toString(); - } - updateWidgetState(); - // Always want to show the microphone if the context is voice. - // Also show the microphone if this is a browser search and the - // query matches the initial query. - updateVoiceButton(mSearchAutoComplete.isEmpty() - || (isBrowserSearch() && mInitialQuery.equals(mUserQuery)) - || (mAppSearchData != null && mAppSearchData.getBoolean( - SearchManager.CONTEXT_IS_VOICE))); - } - - public void afterTextChanged(Editable s) { - if (mSearchable == null) { - return; - } - if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) { - // The user changed the query, check if it is a URL and if so change the search - // button in the soft keyboard to the 'Go' button. - int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)) - | EditorInfo.IME_ACTION_GO; - if (options != mSearchAutoCompleteImeOptions) { - mSearchAutoCompleteImeOptions = options; - mSearchAutoComplete.setImeOptions(options); - // This call is required to update the soft keyboard UI with latest IME flags. - mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType()); - } - } - } - }; - - /** - * Enable/Disable the go button based on edit text state (any text?) - */ - private void updateWidgetState() { - // enable the button if we have one or more non-space characters - boolean enabled = !mSearchAutoComplete.isEmpty(); - if (isBrowserSearch()) { - // In the browser, we hide the search button when there is no text, - // or if the text matches the initial query. - if (enabled && !mInitialQuery.equals(mUserQuery)) { - mSearchAutoComplete.setBackgroundResource( - com.android.internal.R.drawable.textfield_search); - mGoButton.setVisibility(View.VISIBLE); - // Just to be sure - mGoButton.setEnabled(true); - mGoButton.setFocusable(true); - } else { - mSearchAutoComplete.setBackgroundResource( - com.android.internal.R.drawable.textfield_search_empty); - mGoButton.setVisibility(View.GONE); - } - } else { - // Elsewhere we just disable the button - mGoButton.setEnabled(enabled); - mGoButton.setFocusable(enabled); - } - } - - /** - * React to typing in the GO search button by refocusing to EditText. - * Continue typing the query. - */ - View.OnKeyListener mButtonsKeyListener = new View.OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - // guard against possible race conditions - if (mSearchable == null) { - return false; - } - - if (!event.isSystem() && - (keyCode != KeyEvent.KEYCODE_DPAD_UP) && - (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) && - (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) && - (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) { - // restore focus and give key to EditText ... - if (mSearchAutoComplete.requestFocus()) { - return mSearchAutoComplete.dispatchKeyEvent(event); - } - } - - return false; - } - }; - - /** - * React to a click in the GO button by launching a search. - */ - View.OnClickListener mGoButtonClickListener = new View.OnClickListener() { - public void onClick(View v) { - // guard against possible race conditions - if (mSearchable == null) { - return; - } - launchQuerySearch(); - } - }; - - /** - * React to a click in the voice search button. - */ - View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() { - public void onClick(View v) { - // guard against possible race conditions - if (mSearchable == null) { - return; - } - SearchableInfo searchable = mSearchable; - try { - if (searchable.getVoiceSearchLaunchWebSearch()) { - Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent, - searchable); - getContext().startActivity(webSearchIntent); - } else if (searchable.getVoiceSearchLaunchRecognizer()) { - Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent, - searchable); - getContext().startActivity(appSearchIntent); - } - } catch (ActivityNotFoundException e) { - // Should not happen, since we check the availability of - // voice search before showing the button. But just in case... - Log.w(LOG_TAG, "Could not find voice search activity"); - } - dismiss(); - } - }; - - /** - * Create and return an Intent that can launch the voice search activity for web search. - */ - private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) { - Intent voiceIntent = new Intent(baseIntent); - ComponentName searchActivity = searchable.getSearchActivity(); - voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, - searchActivity == null ? null : searchActivity.flattenToShortString()); - return voiceIntent; - } - - /** - * Create and return an Intent that can launch the voice search activity, perform a specific - * voice transcription, and forward the results to the searchable activity. - * - * @param baseIntent The voice app search intent to start from - * @return A completely-configured intent ready to send to the voice search activity - */ - private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) { - ComponentName searchActivity = searchable.getSearchActivity(); - - // create the necessary intent to set up a search-and-forward operation - // in the voice search system. We have to keep the bundle separate, - // because it becomes immutable once it enters the PendingIntent - Intent queryIntent = new Intent(Intent.ACTION_SEARCH); - queryIntent.setComponent(searchActivity); - PendingIntent pending = PendingIntent.getActivity( - getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT); - - // Now set up the bundle that will be inserted into the pending intent - // when it's time to do the search. We always build it here (even if empty) - // because the voice search activity will always need to insert "QUERY" into - // it anyway. - Bundle queryExtras = new Bundle(); - if (mAppSearchData != null) { - queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData); - } - - // Now build the intent to launch the voice search. Add all necessary - // extras to launch the voice recognizer, and then all the necessary extras - // to forward the results to the searchable activity - Intent voiceIntent = new Intent(baseIntent); - - // Add all of the configuration options supplied by the searchable's metadata - String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; - String prompt = null; - String language = null; - int maxResults = 1; - Resources resources = mActivityContext.getResources(); - if (searchable.getVoiceLanguageModeId() != 0) { - languageModel = resources.getString(searchable.getVoiceLanguageModeId()); - } - if (searchable.getVoicePromptTextId() != 0) { - prompt = resources.getString(searchable.getVoicePromptTextId()); - } - if (searchable.getVoiceLanguageId() != 0) { - language = resources.getString(searchable.getVoiceLanguageId()); - } - if (searchable.getVoiceMaxResults() != 0) { - maxResults = searchable.getVoiceMaxResults(); - } - voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); - voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); - voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); - voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); - voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, - searchActivity == null ? null : searchActivity.flattenToShortString()); - - // Add the values that configure forwarding the results - voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); - voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); - - return voiceIntent; - } - - /** - * Corrects http/https typo errors in the given url string, and if the protocol specifier was - * not present defaults to http. - * - * @param inUrl URL to check and fix - * @return fixed URL string. - */ - private String fixUrl(String inUrl) { - if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) - return inUrl; - - if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) { - if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { - inUrl = inUrl.replaceFirst("/", "//"); - } else { - inUrl = inUrl.replaceFirst(":", "://"); - } - } - - if (inUrl.indexOf("://") == -1) { - inUrl = "http://" + inUrl; - } - - return inUrl; - } - - /** - * React to the user typing "enter" or other hardwired keys while typing in the search box. - * This handles these special keys while the edit box has focus. - */ - View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - // guard against possible race conditions - if (mSearchable == null) { - return false; - } - - if (DBG_LOG_TIMING) dbgLogTiming("doTextKey()"); - if (DBG) { - Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event - + "), selection: " + mSearchAutoComplete.getListSelection()); - } - - // If a suggestion is selected, handle enter, search key, and action keys - // as presses on the selected suggestion - if (mSearchAutoComplete.isPopupShowing() && - mSearchAutoComplete.getListSelection() != ListView.INVALID_POSITION) { - return onSuggestionsKey(v, keyCode, event); - } - - // If there is text in the query box, handle enter, and action keys - // The search key is handled by the dialog's onKeyDown(). - if (!mSearchAutoComplete.isEmpty()) { - if (keyCode == KeyEvent.KEYCODE_ENTER - && event.getAction() == KeyEvent.ACTION_UP) { - v.cancelLongPress(); - - // If this is a url entered by the user & we displayed the 'Go' button which - // the user clicked, launch the url instead of using it as a search query. - if (mSearchable.autoUrlDetect() && - (mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION) - == EditorInfo.IME_ACTION_GO) { - Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString())); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - launchIntent(intent); - } else { - // Launch as a regular search. - launchQuerySearch(); - } - return true; - } - if (event.getAction() == KeyEvent.ACTION_DOWN) { - SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); - if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { - launchQuerySearch(keyCode, actionKey.getQueryActionMsg()); - return true; - } - } - } - return false; - } - }; @Override public void hide() { @@ -997,78 +500,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS super.hide(); } - - /** - * React to the user typing while in the suggestions list. First, check for action - * keys. If not handled, try refocusing regular characters into the EditText. - */ - private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { - // guard against possible race conditions (late arrival after dismiss) - if (mSearchable == null) { - return false; - } - if (mSuggestionsAdapter == null) { - return false; - } - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (DBG_LOG_TIMING) { - dbgLogTiming("onSuggestionsKey()"); - } - - // First, check for enter or search (both of which we'll treat as a "click") - if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) { - int position = mSearchAutoComplete.getListSelection(); - return launchSuggestion(position); - } - - // Next, check for left/right moves, which we use to "return" the user to the edit view - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - // give "focus" to text editor, with cursor at the beginning if - // left key, at end if right key - // TODO: Reverse left/right for right-to-left languages, e.g. Arabic - int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? - 0 : mSearchAutoComplete.length(); - mSearchAutoComplete.setSelection(selPoint); - mSearchAutoComplete.setListSelection(0); - mSearchAutoComplete.clearListSelection(); - mSearchAutoComplete.ensureImeVisible(true); - - return true; - } - - // Next, check for an "up and out" move - if (keyCode == KeyEvent.KEYCODE_DPAD_UP - && 0 == mSearchAutoComplete.getListSelection()) { - restoreUserQuery(); - // let ACTV complete the move - return false; - } - - // Next, check for an "action key" - SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); - if ((actionKey != null) && - ((actionKey.getSuggestActionMsg() != null) || - (actionKey.getSuggestActionMsgColumn() != null))) { - // launch suggestion using action key column - int position = mSearchAutoComplete.getListSelection(); - if (position != ListView.INVALID_POSITION) { - Cursor c = mSuggestionsAdapter.getCursor(); - if (c.moveToPosition(position)) { - final String actionMsg = getActionKeyMessage(c, actionKey); - if (actionMsg != null && (actionMsg.length() > 0)) { - return launchSuggestion(position, keyCode, actionMsg); - } - } - } - } - } - return false; - } /** * Launch a search for the text in the query text field. */ - public void launchQuerySearch() { + public void launchQuerySearch() { launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null); } @@ -1080,50 +516,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @param actionMsg The message for the action key that was pressed, * or <code>null</code> if none. */ - protected void launchQuerySearch(int actionKey, String actionMsg) { + protected void launchQuerySearch(int actionKey, String actionMsg) { String query = mSearchAutoComplete.getText().toString(); String action = Intent.ACTION_SEARCH; - Intent intent = createIntent(action, null, null, query, - actionKey, actionMsg); + Intent intent = createIntent(action, null, null, query, actionKey, actionMsg); launchIntent(intent); } - - /** - * Launches an intent based on a suggestion. - * - * @param position The index of the suggestion to create the intent from. - * @return true if a successful launch, false if could not (e.g. bad position). - */ - protected boolean launchSuggestion(int position) { - return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); - } - - /** - * Launches an intent based on a suggestion. - * - * @param position The index of the suggestion to create the intent from. - * @param actionKey The key code of the action key that was pressed, - * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. - * @param actionMsg The message for the action key that was pressed, - * or <code>null</code> if none. - * @return true if a successful launch, false if could not (e.g. bad position). - */ - protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { - if (mSuggestionsAdapter == null) { - return false; - } - Cursor c = mSuggestionsAdapter.getCursor(); - if ((c != null) && c.moveToPosition(position)) { - - Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); - - // launch the intent - launchIntent(intent); - - return true; - } - return false; - } /** * Launches an intent, including any special intent handling. @@ -1149,21 +547,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * If the intent is to open an HTTP or HTTPS URL, we set - * {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that - * has been opened by us for the same URL will be reused. - */ - private void setBrowserApplicationId(Intent intent) { - Uri data = intent.getData(); - if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null) { - String scheme = data.getScheme(); - if (scheme != null && scheme.startsWith("http")) { - intent.putExtra(Browser.EXTRA_APPLICATION_ID, data.toString()); - } - } - } - - /** * Sets the list item selection in the AutoCompleteTextView's ListView. */ public void setListSelection(int index) { @@ -1171,62 +554,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * When a particular suggestion has been selected, perform the various lookups required - * to use the suggestion. This includes checking the cursor for suggestion-specific data, - * and/or falling back to the XML for defaults; It also creates REST style Uri data when - * the suggestion includes a data id. - * - * @param c The suggestions cursor, moved to the row of the user's selection - * @param actionKey The key code of the action key that was pressed, - * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. - * @param actionMsg The message for the action key that was pressed, - * or <code>null</code> if none. - * @return An intent for the suggestion at the cursor's position. - */ - private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) { - try { - // use specific action if supplied, or default action if supplied, or fixed default - String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); - - if (action == null) { - action = mSearchable.getSuggestIntentAction(); - } - if (action == null) { - action = Intent.ACTION_SEARCH; - } - - // use specific data if supplied, or default data if supplied - String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA); - if (data == null) { - data = mSearchable.getSuggestIntentData(); - } - // then, if an ID was provided, append it. - if (data != null) { - String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); - if (id != null) { - data = data + "/" + Uri.encode(id); - } - } - Uri dataUri = (data == null) ? null : Uri.parse(data); - - String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); - String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - - return createIntent(action, dataUri, extraData, query, actionKey, actionMsg); - } catch (RuntimeException e ) { - int rowNum; - try { // be really paranoid now - rowNum = c.getPosition(); - } catch (RuntimeException e2 ) { - rowNum = -1; - } - Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum + - " returned exception" + e.toString()); - return null; - } - } - - /** * Constructs an intent from the given information and the search dialog state. * * @param action Intent action. @@ -1271,30 +598,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * For a given suggestion and a given cursor row, get the action message. If not provided - * by the specific row/column, also check for a single definition (for the action key). - * - * @param c The cursor providing suggestions - * @param actionKey The actionkey record being examined - * - * @return Returns a string, or null if no action key message for this suggestion - */ - private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) { - String result = null; - // check first in the cursor data, for a suggestion-specific message - final String column = actionKey.getSuggestActionMsgColumn(); - if (column != null) { - result = SuggestionsAdapter.getColumnString(c, column); - } - // If the cursor didn't give us a message, see if there's a single message defined - // for the actionkey (for all suggestions) - if (result == null) { - result = actionKey.getSuggestActionMsg(); - } - return result; - } - - /** * The root element in the search bar layout. This is a custom view just to override * the handling of the back button. */ @@ -1315,21 +618,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Overrides the handling of the back key to move back to the previous sources or dismiss - * the search dialog, instead of dismissing the input method. + * Overrides the handling of the back key to move back to the previous + * sources or dismiss the search dialog, instead of dismissing the input + * method. */ @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { - if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + event + ")"); + if (DBG) + Log.d(LOG_TAG, "onKeyPreIme(" + event + ")"); if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { KeyEvent.DispatcherState state = getKeyDispatcherState(); if (state != null) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && event.getRepeatCount() == 0) { + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { state.startTracking(event, this); return true; - } else if (event.getAction() == KeyEvent.ACTION_UP - && !event.isCanceled() && state.isTracking(event)) { + } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled() + && state.isTracking(event)) { mSearchDialog.onBackPressed(); return true; } @@ -1339,86 +643,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } - /** - * Local subclass for AutoCompleteTextView. - */ - public static class SearchAutoComplete extends AutoCompleteTextView { - - private int mThreshold; - - public SearchAutoComplete(Context context) { - super(context); - mThreshold = getThreshold(); - } - - public SearchAutoComplete(Context context, AttributeSet attrs) { - super(context, attrs); - mThreshold = getThreshold(); - } - - public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mThreshold = getThreshold(); - } - - @Override - public void setThreshold(int threshold) { - super.setThreshold(threshold); - mThreshold = threshold; - } - - /** - * Returns true if the text field is empty, or contains only whitespace. - */ - private boolean isEmpty() { - return TextUtils.getTrimmedLength(getText()) == 0; - } - - /** - * We override this method to avoid replacing the query box text - * when a suggestion is clicked. - */ - @Override - protected void replaceText(CharSequence text) { - } - - /** - * We override this method to avoid an extra onItemClick being called on the - * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} - * when an item is clicked with the trackball. - */ - @Override - public void performCompletion() { - } - - /** - * We override this method to be sure and show the soft keyboard if appropriate when - * the TextView has focus. - */ - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - - if (hasWindowFocus) { - InputMethodManager inputManager = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - inputManager.showSoftInput(this, 0); - // If in landscape mode, then make sure that - // the ime is in front of the dropdown. - if (isLandscapeMode(getContext())) { - ensureImeVisible(true); - } - } - } - - /** - * We override this method so that we can allow a threshold of zero, which ACTV does not. - */ - @Override - public boolean enoughToFilter() { - return mThreshold <= 0 || super.enoughToFilter(); - } - + private boolean isEmpty(AutoCompleteTextView actv) { + return TextUtils.getTrimmedLength(actv.getText()) == 0; } @Override @@ -1435,106 +661,58 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS cancel(); } - /** - * Implements OnItemClickListener - */ - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); - launchSuggestion(position); + private boolean onClosePressed() { + // Dismiss the dialog if close button is pressed when there's no query text + if (isEmpty(mSearchAutoComplete)) { + dismiss(); + return true; + } + + return false; } - /** - * Implements OnItemSelectedListener - */ - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); - // A suggestion has been selected, rewrite the query if possible, - // otherwise the restore the original query. - if (REWRITE_QUERIES) { - rewriteQueryFromSuggestion(position); - } - } - - /** - * Implements OnItemSelectedListener - */ - public void onNothingSelected(AdapterView<?> parent) { - if (DBG) Log.d(LOG_TAG, "onNothingSelected()"); - } - - /** - * Query rewriting. - */ - - private void rewriteQueryFromSuggestion(int position) { - Cursor c = mSuggestionsAdapter.getCursor(); - if (c == null) { - return; - } - if (c.moveToPosition(position)) { - // Get the new query from the suggestion. - CharSequence newQuery = mSuggestionsAdapter.convertToString(c); - if (newQuery != null) { - // The suggestion rewrites the query. - if (DBG) Log.d(LOG_TAG, "Rewriting query to '" + newQuery + "'"); - // Update the text field, without getting new suggestions. - setQuery(newQuery); - } else { - // The suggestion does not rewrite the query, restore the user's query. - if (DBG) Log.d(LOG_TAG, "Suggestion gives no rewrite, restoring user query."); - restoreUserQuery(); - } - } else { - // We got a bad position, restore the user's query. - Log.w(LOG_TAG, "Bad suggestion position: " + position); - restoreUserQuery(); - } - } - - /** - * Restores the query entered by the user if needed. - */ - private void restoreUserQuery() { - if (DBG) Log.d(LOG_TAG, "Restoring query to '" + mUserQuery + "'"); - setQuery(mUserQuery); - } - - /** - * Sets the text in the query box, without updating the suggestions. - */ - private void setQuery(CharSequence query) { - mSearchAutoComplete.setText(query, false); - if (query != null) { - mSearchAutoComplete.setSelection(query.length()); - } - } - - /** - * Sets the text in the query box, updating the suggestions. - */ - private void setUserQuery(String query) { - if (query == null) { - query = ""; - } - mUserQuery = query; - mSearchAutoComplete.setText(query); - mSearchAutoComplete.setSelection(query.length()); - } + private final SearchView.OnCloseListener mOnCloseListener = new SearchView.OnCloseListener() { - /** - * Debugging Support - */ + public boolean onClose() { + return onClosePressed(); + } + }; + + private final SearchView.OnQueryChangeListener mOnQueryChangeListener = + new SearchView.OnQueryChangeListener() { + + public boolean onSubmitQuery(String query) { + dismiss(); + return false; + } + + public boolean onQueryTextChanged(String newText) { + return false; + } + }; + + private final SearchView.OnSuggestionSelectionListener mOnSuggestionSelectionListener = + new SearchView.OnSuggestionSelectionListener() { + + public boolean onSuggestionSelected(int position) { + return false; + } + + public boolean onSuggestionClicked(int position) { + dismiss(); + return false; + } + }; /** - * For debugging only, sample the millisecond clock and log it. - * Uses AtomicLong so we can use in multiple threads + * Sets the text in the query box, updating the suggestions. */ - private AtomicLong mLastLogTime = new AtomicLong(SystemClock.uptimeMillis()); - private void dbgLogTiming(final String caller) { - long millis = SystemClock.uptimeMillis(); - long oldTime = mLastLogTime.getAndSet(millis); - long delta = millis - oldTime; - final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller; - Log.d(LOG_TAG,report); + private void setUserQuery(String query) { + if (query == null) { + query = ""; + } + mUserQuery = query; + mSearchAutoComplete.setText(query); + mSearchAutoComplete.setSelection(query.length()); } } diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java deleted file mode 100644 index 5705bff..0000000 --- a/core/java/android/app/SuggestionsAdapter.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import com.android.internal.R; - -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.ContentResolver.OpenResourceIdResult; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.TextAppearanceSpan; -import android.util.Log; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Filter; -import android.widget.ImageView; -import android.widget.ResourceCursorAdapter; -import android.widget.TextView; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.WeakHashMap; - -/** - * Provides the contents for the suggestion drop-down list.in {@link SearchDialog}. - * - * @hide - */ -class SuggestionsAdapter extends ResourceCursorAdapter { - - private static final boolean DBG = false; - private static final String LOG_TAG = "SuggestionsAdapter"; - private static final int QUERY_LIMIT = 50; - - private SearchManager mSearchManager; - private SearchDialog mSearchDialog; - private SearchableInfo mSearchable; - private Context mProviderContext; - private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache; - private boolean mClosed = false; - - // URL color - private ColorStateList mUrlColor; - - // Cached column indexes, updated when the cursor changes. - private int mText1Col; - private int mText2Col; - private int mText2UrlCol; - private int mIconName1Col; - private int mIconName2Col; - - static final int NONE = -1; - - private final Runnable mStartSpinnerRunnable; - private final Runnable mStopSpinnerRunnable; - - /** - * The amount of time we delay in the filter when the user presses the delete key. - * @see Filter#setDelayer(android.widget.Filter.Delayer). - */ - private static final long DELETE_KEY_POST_DELAY = 500L; - - public SuggestionsAdapter(Context context, SearchDialog searchDialog, - SearchableInfo searchable, - WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) { - super(context, - com.android.internal.R.layout.search_dropdown_item_icons_2line, - null, // no initial cursor - true); // auto-requery - mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); - mSearchDialog = searchDialog; - mSearchable = searchable; - - // set up provider resources (gives us icons, etc.) - Context activityContext = mSearchable.getActivityContext(mContext); - mProviderContext = mSearchable.getProviderContext(mContext, activityContext); - - mOutsideDrawablesCache = outsideDrawablesCache; - - mStartSpinnerRunnable = new Runnable() { - public void run() { - mSearchDialog.setWorking(true); - } - }; - - mStopSpinnerRunnable = new Runnable() { - public void run() { - mSearchDialog.setWorking(false); - } - }; - - // delay 500ms when deleting - getFilter().setDelayer(new Filter.Delayer() { - - private int mPreviousLength = 0; - - public long getPostingDelay(CharSequence constraint) { - if (constraint == null) return 0; - - long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0; - mPreviousLength = constraint.length(); - return delay; - } - }); - } - - /** - * Overridden to always return <code>false</code>, since we cannot be sure that - * suggestion sources return stable IDs. - */ - @Override - public boolean hasStableIds() { - return false; - } - - /** - * Use the search suggestions provider to obtain a live cursor. This will be called - * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions). - * The results will be processed in the UI thread and changeCursor() will be called. - */ - @Override - public Cursor runQueryOnBackgroundThread(CharSequence constraint) { - if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")"); - String query = (constraint == null) ? "" : constraint.toString(); - /** - * for in app search we show the progress spinner until the cursor is returned with - * the results. - */ - Cursor cursor = null; - mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); - try { - cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT); - // trigger fill window so the spinner stays up until the results are copied over and - // closer to being ready - if (cursor != null) { - cursor.getCount(); - return cursor; - } - } catch (RuntimeException e) { - Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); - } - // If cursor is null or an exception was thrown, stop the spinner and return null. - // changeCursor doesn't get called if cursor is null - mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); - return null; - } - - public void close() { - if (DBG) Log.d(LOG_TAG, "close()"); - changeCursor(null); - mClosed = true; - } - - @Override - public void notifyDataSetChanged() { - if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged"); - super.notifyDataSetChanged(); - - mSearchDialog.onDataSetChanged(); - - updateSpinnerState(getCursor()); - } - - @Override - public void notifyDataSetInvalidated() { - if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated"); - super.notifyDataSetInvalidated(); - - updateSpinnerState(getCursor()); - } - - private void updateSpinnerState(Cursor cursor) { - Bundle extras = cursor != null ? cursor.getExtras() : null; - if (DBG) { - Log.d(LOG_TAG, "updateSpinnerState - extra = " - + (extras != null - ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS) - : null)); - } - // Check if the Cursor indicates that the query is not complete and show the spinner - if (extras != null - && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) { - mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); - return; - } - // If cursor is null or is done, stop the spinner - mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); - } - - /** - * Cache columns. - */ - @Override - public void changeCursor(Cursor c) { - if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")"); - - if (mClosed) { - Log.w(LOG_TAG, "Tried to change cursor after adapter was closed."); - if (c != null) c.close(); - return; - } - - try { - super.changeCursor(c); - - if (c != null) { - mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); - mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); - mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL); - mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); - mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); - } - } catch (Exception e) { - Log.e(LOG_TAG, "error changing cursor and caching columns", e); - } - } - - /** - * Tags the view with cached child view look-ups. - */ - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - View v = super.newView(context, cursor, parent); - v.setTag(new ChildViewCache(v)); - return v; - } - - /** - * Cache of the child views of drop-drown list items, to avoid looking up the children - * each time the contents of a list item are changed. - */ - private final static class ChildViewCache { - public final TextView mText1; - public final TextView mText2; - public final ImageView mIcon1; - public final ImageView mIcon2; - - public ChildViewCache(View v) { - mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1); - mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2); - mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1); - mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2); - } - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - ChildViewCache views = (ChildViewCache) view.getTag(); - - if (views.mText1 != null) { - String text1 = getStringOrNull(cursor, mText1Col); - setViewText(views.mText1, text1); - } - if (views.mText2 != null) { - // First check TEXT_2_URL - CharSequence text2 = getStringOrNull(cursor, mText2UrlCol); - if (text2 != null) { - text2 = formatUrl(text2); - } else { - text2 = getStringOrNull(cursor, mText2Col); - } - - // If no second line of text is indicated, allow the first line of text - // to be up to two lines if it wants to be. - if (TextUtils.isEmpty(text2)) { - if (views.mText1 != null) { - views.mText1.setSingleLine(false); - views.mText1.setMaxLines(2); - } - } else { - if (views.mText1 != null) { - views.mText1.setSingleLine(true); - views.mText1.setMaxLines(1); - } - } - setViewText(views.mText2, text2); - } - - if (views.mIcon1 != null) { - setViewDrawable(views.mIcon1, getIcon1(cursor)); - } - if (views.mIcon2 != null) { - setViewDrawable(views.mIcon2, getIcon2(cursor)); - } - } - - private CharSequence formatUrl(CharSequence url) { - if (mUrlColor == null) { - // Lazily get the URL color from the current theme. - TypedValue colorValue = new TypedValue(); - mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true); - mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId); - } - - SpannableString text = new SpannableString(url); - text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null), - 0, url.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - return text; - } - - private void setViewText(TextView v, CharSequence text) { - // Set the text even if it's null, since we need to clear any previous text. - v.setText(text); - - if (TextUtils.isEmpty(text)) { - v.setVisibility(View.GONE); - } else { - v.setVisibility(View.VISIBLE); - } - } - - private Drawable getIcon1(Cursor cursor) { - if (mIconName1Col < 0) { - return null; - } - String value = cursor.getString(mIconName1Col); - Drawable drawable = getDrawableFromResourceValue(value); - if (drawable != null) { - return drawable; - } - return getDefaultIcon1(cursor); - } - - private Drawable getIcon2(Cursor cursor) { - if (mIconName2Col < 0) { - return null; - } - String value = cursor.getString(mIconName2Col); - return getDrawableFromResourceValue(value); - } - - /** - * Sets the drawable in an image view, makes sure the view is only visible if there - * is a drawable. - */ - private void setViewDrawable(ImageView v, Drawable drawable) { - // Set the icon even if the drawable is null, since we need to clear any - // previous icon. - v.setImageDrawable(drawable); - - if (drawable == null) { - v.setVisibility(View.GONE); - } else { - v.setVisibility(View.VISIBLE); - - // This is a hack to get any animated drawables (like a 'working' spinner) - // to animate. You have to setVisible true on an AnimationDrawable to get - // it to start animating, but it must first have been false or else the - // call to setVisible will be ineffective. We need to clear up the story - // about animated drawables in the future, see http://b/1878430. - drawable.setVisible(false, false); - drawable.setVisible(true, false); - } - } - - /** - * Gets the text to show in the query field when a suggestion is selected. - * - * @param cursor The Cursor to read the suggestion data from. The Cursor should already - * be moved to the suggestion that is to be read from. - * @return The text to show, or <code>null</code> if the query should not be - * changed when selecting this suggestion. - */ - @Override - public CharSequence convertToString(Cursor cursor) { - if (cursor == null) { - return null; - } - - String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY); - if (query != null) { - return query; - } - - if (mSearchable.shouldRewriteQueryFromData()) { - String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA); - if (data != null) { - return data; - } - } - - if (mSearchable.shouldRewriteQueryFromText()) { - String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1); - if (text1 != null) { - return text1; - } - } - - return null; - } - - /** - * This method is overridden purely to provide a bit of protection against - * flaky content providers. - * - * @see android.widget.ListAdapter#getView(int, View, ViewGroup) - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) { - try { - return super.getView(position, convertView, parent); - } catch (RuntimeException e) { - Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e); - // Put exception string in item title - View v = newView(mContext, mCursor, parent); - if (v != null) { - ChildViewCache views = (ChildViewCache) v.getTag(); - TextView tv = views.mText1; - tv.setText(e.toString()); - } - return v; - } - } - - /** - * Gets a drawable given a value provided by a suggestion provider. - * - * This value could be just the string value of a resource id - * (e.g., "2130837524"), in which case we will try to retrieve a drawable from - * the provider's resources. If the value is not an integer, it is - * treated as a Uri and opened with - * {@link ContentResolver#openOutputStream(android.net.Uri, String)}. - * - * All resources and URIs are read using the suggestion provider's context. - * - * If the string is not formatted as expected, or no drawable can be found for - * the provided value, this method returns null. - * - * @param drawableId a string like "2130837524", - * "android.resource://com.android.alarmclock/2130837524", - * or "content://contacts/photos/253". - * @return a Drawable, or null if none found - */ - private Drawable getDrawableFromResourceValue(String drawableId) { - if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) { - return null; - } - try { - // First, see if it's just an integer - int resourceId = Integer.parseInt(drawableId); - // It's an int, look for it in the cache - String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE - + "://" + mProviderContext.getPackageName() + "/" + resourceId; - // Must use URI as cache key, since ints are app-specific - Drawable drawable = checkIconCache(drawableUri); - if (drawable != null) { - return drawable; - } - // Not cached, find it by resource ID - drawable = mProviderContext.getResources().getDrawable(resourceId); - // Stick it in the cache, using the URI as key - storeInIconCache(drawableUri, drawable); - return drawable; - } catch (NumberFormatException nfe) { - // It's not an integer, use it as a URI - Drawable drawable = checkIconCache(drawableId); - if (drawable != null) { - return drawable; - } - Uri uri = Uri.parse(drawableId); - drawable = getDrawable(uri); - storeInIconCache(drawableId, drawable); - return drawable; - } catch (Resources.NotFoundException nfe) { - // It was an integer, but it couldn't be found, bail out - Log.w(LOG_TAG, "Icon resource not found: " + drawableId); - return null; - } - } - - /** - * Gets a drawable by URI, without using the cache. - * - * @return A drawable, or {@code null} if the drawable could not be loaded. - */ - private Drawable getDrawable(Uri uri) { - try { - String scheme = uri.getScheme(); - if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { - // Load drawables through Resources, to get the source density information - OpenResourceIdResult r = - mProviderContext.getContentResolver().getResourceId(uri); - try { - return r.r.getDrawable(r.id); - } catch (Resources.NotFoundException ex) { - throw new FileNotFoundException("Resource does not exist: " + uri); - } - } else { - // Let the ContentResolver handle content and file URIs. - InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); - if (stream == null) { - throw new FileNotFoundException("Failed to open " + uri); - } - try { - return Drawable.createFromStream(stream, null); - } finally { - try { - stream.close(); - } catch (IOException ex) { - Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); - } - } - } - } catch (FileNotFoundException fnfe) { - Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage()); - return null; - } - } - - private Drawable checkIconCache(String resourceUri) { - Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri); - if (cached == null) { - return null; - } - if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri); - return cached.newDrawable(); - } - - private void storeInIconCache(String resourceUri, Drawable drawable) { - if (drawable != null) { - mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState()); - } - } - - /** - * Gets the left-hand side icon that will be used for the current suggestion - * if the suggestion contains an icon column but no icon or a broken icon. - * - * @param cursor A cursor positioned at the current suggestion. - * @return A non-null drawable. - */ - private Drawable getDefaultIcon1(Cursor cursor) { - // Check the component that gave us the suggestion - Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity()); - if (drawable != null) { - return drawable; - } - - // Fall back to a default icon - return mContext.getPackageManager().getDefaultActivityIcon(); - } - - /** - * Gets the activity or application icon for an activity. - * Uses the local icon cache for fast repeated lookups. - * - * @param component Name of an activity. - * @return A drawable, or {@code null} if neither the activity nor the application - * has an icon set. - */ - private Drawable getActivityIconWithCache(ComponentName component) { - // First check the icon cache - String componentIconKey = component.flattenToShortString(); - // Using containsKey() since we also store null values. - if (mOutsideDrawablesCache.containsKey(componentIconKey)) { - Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey); - return cached == null ? null : cached.newDrawable(mProviderContext.getResources()); - } - // Then try the activity or application icon - Drawable drawable = getActivityIcon(component); - // Stick it in the cache so we don't do this lookup again. - Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState(); - mOutsideDrawablesCache.put(componentIconKey, toCache); - return drawable; - } - - /** - * Gets the activity or application icon for an activity. - * - * @param component Name of an activity. - * @return A drawable, or {@code null} if neither the acitivy or the application - * have an icon set. - */ - private Drawable getActivityIcon(ComponentName component) { - PackageManager pm = mContext.getPackageManager(); - final ActivityInfo activityInfo; - try { - activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA); - } catch (NameNotFoundException ex) { - Log.w(LOG_TAG, ex.toString()); - return null; - } - int iconId = activityInfo.getIconResource(); - if (iconId == 0) return null; - String pkg = component.getPackageName(); - Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo); - if (drawable == null) { - Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for " - + component.flattenToShortString()); - return null; - } - return drawable; - } - - /** - * Gets the value of a string column by name. - * - * @param cursor Cursor to read the value from. - * @param columnName The name of the column to read. - * @return The value of the given column, or <code>null</null> - * if the cursor does not contain the given column. - */ - public static String getColumnString(Cursor cursor, String columnName) { - int col = cursor.getColumnIndex(columnName); - return getStringOrNull(cursor, col); - } - - private static String getStringOrNull(Cursor cursor, int col) { - if (col == NONE) { - return null; - } - try { - return cursor.getString(col); - } catch (Exception e) { - Log.e(LOG_TAG, - "unexpected error retrieving valid column from cursor, " - + "did the remote process die?", e); - return null; - } - } -} diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index d1ab8d5..abeeb74 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -110,16 +110,23 @@ abstract public class ContentProviderNative extends Binder implements IContentPr IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder, observer, window); - reply.writeNoException(); if (bulkCursor != null) { - reply.writeStrongBinder(bulkCursor.asBinder()); - + final IBinder binder = bulkCursor.asBinder(); if (wantsCursorMetadata) { - reply.writeInt(bulkCursor.count()); - reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex( - bulkCursor.getColumnNames())); + final int count = bulkCursor.count(); + final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex( + bulkCursor.getColumnNames()); + + reply.writeNoException(); + reply.writeStrongBinder(binder); + reply.writeInt(count); + reply.writeInt(index); + } else { + reply.writeNoException(); + reply.writeStrongBinder(binder); } } else { + reply.writeNoException(); reply.writeStrongBinder(null); } diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 01ae1da..9893133 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -16,7 +16,6 @@ package android.content.res; -import android.os.MemoryFile; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -184,14 +183,9 @@ public class AssetFileDescriptor implements Parcelable { @Override public int read() throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - int res = super.read(); - if (res >= 0) mRemaining--; - return res; - } - - return super.read(); + byte[] buffer = new byte[1]; + int result = read(buffer, 0, 1); + return result == -1 ? -1 : buffer[0] & 0xff; } @Override @@ -209,16 +203,7 @@ public class AssetFileDescriptor implements Parcelable { @Override public int read(byte[] buffer) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - int count = buffer.length; - if (count > mRemaining) count = (int)mRemaining; - int res = super.read(buffer, 0, count); - if (res >= 0) mRemaining -= res; - return res; - } - - return super.read(buffer); + return read(buffer, 0, buffer.length); } @Override @@ -231,7 +216,6 @@ public class AssetFileDescriptor implements Parcelable { return res; } - // TODO Auto-generated method stub return super.skip(count); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index f3b2c81..fe4b900 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1021,6 +1021,9 @@ public class Camera { private static final String KEY_ZOOM_SUPPORTED = "zoom-supported"; private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported"; private static final String KEY_FOCUS_DISTANCES = "focus-distances"; + private static final String KEY_VIDEO_SIZE = "video-size"; + private static final String KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO = + "preferred-preview-size-for-video"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -1398,7 +1401,7 @@ public class Camera { /** * Returns the dimensions setting for preview pictures. * - * @return a Size object with the height and width setting + * @return a Size object with the width and height setting * for the preview picture */ public Size getPreviewSize() { @@ -1418,6 +1421,46 @@ public class Camera { } /** + * Gets the supported video frame sizes that can be used by + * MediaRecorder. + * + * If the returned list is not null, the returned list will contain at + * least one Size and one of the sizes in the returned list must be + * passed to MediaRecorder.setVideoSize() for camcorder application if + * camera is used as the video source. In this case, the size of the + * preview can be different from the resolution of the recorded video + * during video recording. + * + * @return a list of Size object if camera has separate preview and + * video output; otherwise, null is returned. + * @see #getPreferredPreviewSizeForVideo() + */ + public List<Size> getSupportedVideoSizes() { + String str = get(KEY_VIDEO_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** + * Returns the preferred or recommended preview size (width and height) + * in pixels for video recording. Camcorder applications should + * set the preview size to a value that is not larger than the + * preferred preview size. In other words, the product of the width + * and height of the preview size should not be larger than that of + * the preferred preview size. In addition, we recommend to choose a + * preview size that has the same aspect ratio as the resolution of + * video to be recorded. + * + * @return the preferred preview size (width and height) in pixels for + * video recording if getSupportedVideoSizes() does not return + * null; otherwise, null is returned. + * @see #getSupportedVideoSizes() + */ + public Size getPreferredPreviewSizeForVideo() { + String pair = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); + return strToSize(pair); + } + + /** * Sets the dimensions for EXIF thumbnail in Jpeg picture. If * applications set both width and height to 0, EXIF will not contain * thumbnail. diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 32ff3b3..8c55bf3 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -220,25 +220,47 @@ public class SensorEvent { * </p> * * <h4>{@link android.hardware.Sensor#TYPE_GRAVITY Sensor.TYPE_GRAVITY}:</h4> - * A three dimensional vector indicating the direction and magnitude of gravity. Units - * are m/s^2. The coordinate system is the same as is used by the acceleration sensor. + * <p>A three dimensional vector indicating the direction and magnitude of gravity. Units + * are m/s^2. The coordinate system is the same as is used by the acceleration sensor.</p> + * <p><b>Note:</b> When the device is at rest, the output of the gravity sensor should be identical + * to that of the accelerometer.</p> * * <h4>{@link android.hardware.Sensor#TYPE_LINEAR_ACCELERATION Sensor.TYPE_LINEAR_ACCELERATION}:</h4> * A three dimensional vector indicating acceleration along each device axis, not including * gravity. All values have units of m/s^2. The coordinate system is the same as is used by the - * acceleration sensor. + * acceleration sensor. + * <p>The output of the accelerometer, gravity and linear-acceleration sensors must obey the + * following relation:</p> + * <p><ul>acceleration = gravity + linear-acceleration</ul></p> * * <h4>{@link android.hardware.Sensor#TYPE_ROTATION_VECTOR Sensor.TYPE_ROTATION_VECTOR}:</h4> - * The rotation vector represents the orientation of the device as a combination of an angle - * and an axis, in which the device has rotated through an angle theta around an axis - * <x, y, z>. The three elements of the rotation vector are - * <x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>, such that the magnitude of the rotation - * vector is equal to sin(theta/2), and the direction of the rotation vector is equal to the - * direction of the axis of rotation. The three elements of the rotation vector are equal to - * the last three components of a unit quaternion - * <cos(theta/2), x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>. Elements of the rotation - * vector are unitless. The x,y, and z axis are defined in the same way as the acceleration - * sensor. + * <p>The rotation vector represents the orientation of the device as a combination of an <i>angle</i> + * and an <i>axis</i>, in which the device has rotated through an angle θ around an axis + * <x, y, z>.</p> + * <p>The three elements of the rotation vector are + * <x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)>, such that the magnitude of the rotation + * vector is equal to sin(θ/2), and the direction of the rotation vector is equal to the + * direction of the axis of rotation.</p> + * </p>The three elements of the rotation vector are equal to + * the last three components of a <b>unit</b> quaternion + * <cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)>.</p> + * <p>Elements of the rotation vector are unitless. + * The x,y, and z axis are defined in the same way as the acceleration + * sensor.</p> + * <ul> + * <p> + * values[0]: x*sin(θ/2) + * </p> + * <p> + * values[1]: y*sin(θ/2) + * </p> + * <p> + * values[2]: z*sin(θ/2) + * </p> + * <p> + * values[3]: cos(θ/2) <i>(optional: only if value.length = 4)</i> + * </p> + * </ul> * * <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION * Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees. diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index c178aee..1b799ae 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1938,13 +1938,18 @@ public class SensorManager * @param R an array of floats in which to store the rotation matrix */ public static void getRotationMatrixFromVector(float[] R, float[] rotationVector) { - float q0 = (float)Math.sqrt(1 - rotationVector[0]*rotationVector[0] - - rotationVector[1]*rotationVector[1] - - rotationVector[2]*rotationVector[2]); + + float q0; float q1 = rotationVector[0]; float q2 = rotationVector[1]; float q3 = rotationVector[2]; + if (rotationVector.length == 4) { + q0 = rotationVector[3]; + } else { + q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3); + } + float sq_q1 = 2 * q1 * q1; float sq_q2 = 2 * q2 * q2; float sq_q3 = 2 * q3 * q3; @@ -1995,10 +2000,12 @@ public class SensorManager * @param Q an array of floats in which to store the computed quaternion */ public static void getQuaternionFromVector(float[] Q, float[] rv) { - float w = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]); - //In this case, the w component of the quaternion is known to be a positive number - - Q[0] = w; + if (rv.length == 4) { + Q[0] = rv[3]; + } else { + //In this case, the w component of the quaternion is known to be a positive number + Q[0] = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]); + } Q[1] = rv[0]; Q[2] = rv[1]; Q[3] = rv[2]; diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 1c295a7..9c3bc9d 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -40,6 +40,7 @@ import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; +import dalvik.bytecode.OpcodeInfo; import dalvik.bytecode.Opcodes; import dalvik.system.VMDebug; @@ -786,7 +787,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * @hide */ public static long countInstancesOfClass(Class cls) { - return VMDebug.countInstancesOfClass(cls); + return VMDebug.countInstancesOfClass(cls, true); } /** @@ -865,7 +866,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * </pre> */ public static class InstructionCount { - private static final int NUM_INSTR = 256; + private static final int NUM_INSTR = OpcodeInfo.MAXIMUM_VALUE + 1; private int[] mCounts; @@ -909,8 +910,11 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo */ public int globalTotal() { int count = 0; - for (int i = 0; i < NUM_INSTR; i++) + + for (int i = 0; i < NUM_INSTR; i++) { count += mCounts[i]; + } + return count; } @@ -921,27 +925,16 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo public int globalMethodInvocations() { int count = 0; - //count += mCounts[Opcodes.OP_EXECUTE_INLINE]; - count += mCounts[Opcodes.OP_INVOKE_VIRTUAL]; - count += mCounts[Opcodes.OP_INVOKE_SUPER]; - count += mCounts[Opcodes.OP_INVOKE_DIRECT]; - count += mCounts[Opcodes.OP_INVOKE_STATIC]; - count += mCounts[Opcodes.OP_INVOKE_INTERFACE]; - count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_RANGE]; - count += mCounts[Opcodes.OP_INVOKE_SUPER_RANGE]; - count += mCounts[Opcodes.OP_INVOKE_DIRECT_RANGE]; - count += mCounts[Opcodes.OP_INVOKE_STATIC_RANGE]; - count += mCounts[Opcodes.OP_INVOKE_INTERFACE_RANGE]; - //count += mCounts[Opcodes.OP_INVOKE_DIRECT_EMPTY]; - count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK]; - count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK_RANGE]; - count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK]; - count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE]; + for (int i = 0; i < NUM_INSTR; i++) { + if (OpcodeInfo.isInvoke(i)) { + count += mCounts[i]; + } + } + return count; } } - /** * A Map of typed debug properties. */ diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 754d073..6414936 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -847,6 +847,13 @@ public final class StrictMode { } }; + // Note: only access this once verifying the thread has a Looper. + private static final ThreadLocal<Handler> threadHandler = new ThreadLocal<Handler>() { + @Override protected Handler initialValue() { + return new Handler(); + } + }; + private static boolean tooManyViolationsThisLoop() { return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP; } @@ -954,7 +961,6 @@ public final class StrictMode { return; } - MessageQueue queue = Looper.myQueue(); final ArrayList<ViolationInfo> records = violationsBeingTimed.get(); if (records.size() >= MAX_OFFENSES_PER_LOOP) { // Not worth measuring. Too many offenses in one loop. @@ -977,9 +983,30 @@ public final class StrictMode { } } - queue.addIdleHandler(new MessageQueue.IdleHandler() { - public boolean queueIdle() { + // We post a runnable to a Handler (== delay 0 ms) for + // measuring the end time of a violation instead of using + // an IdleHandler (as was previously used) because an + // IdleHandler may not run for quite a long period of time + // if an ongoing animation is happening and continually + // posting ASAP (0 ms) animation steps. Animations are + // throttled back to 60fps via SurfaceFlinger/View + // invalidates, _not_ by posting frame updates every 16 + // milliseconds. + threadHandler.get().post(new Runnable() { + public void run() { long loopFinishTime = SystemClock.uptimeMillis(); + + // Note: we do this early, before handling the + // violation below, as handling the violation + // may include PENALTY_DEATH and we don't want + // to keep the red border on. + if (windowManager != null) { + try { + windowManager.showStrictModeViolation(false); + } catch (RemoteException unused) { + } + } + for (int n = 0; n < records.size(); ++n) { ViolationInfo v = records.get(n); v.violationNumThisLoop = n + 1; @@ -988,13 +1015,6 @@ public final class StrictMode { handleViolation(v); } records.clear(); - if (windowManager != null) { - try { - windowManager.showStrictModeViolation(false); - } catch (RemoteException unused) { - } - } - return false; // remove this idle handler from the array } }); } diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java index 481859e..f6d01d3 100644 --- a/core/java/android/preference/PreferenceFrameLayout.java +++ b/core/java/android/preference/PreferenceFrameLayout.java @@ -20,16 +20,22 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; /** * @hide */ public class PreferenceFrameLayout extends FrameLayout { - private static final int DEFAULT_TOP_PADDING = 0; - private static final int DEFAULT_BOTTOM_PADDING = 0; - private final int mTopPadding; - private final int mBottomPadding; + private static final int DEFAULT_BORDER_TOP = 0; + private static final int DEFAULT_BORDER_BOTTOM = 0; + private static final int DEFAULT_BORDER_LEFT = 0; + private static final int DEFAULT_BORDER_RIGHT = 0; + private final int mBorderTop; + private final int mBorderBottom; + private final int mBorderLeft; + private final int mBorderRight; private boolean mPaddingApplied = false; public PreferenceFrameLayout(Context context) { @@ -46,45 +52,98 @@ public class PreferenceFrameLayout extends FrameLayout { com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0); float density = context.getResources().getDisplayMetrics().density; - int defaultTopPadding = (int) (density * DEFAULT_TOP_PADDING + 0.5f); - int defaultBottomPadding = (int) (density * DEFAULT_BOTTOM_PADDING + 0.5f); - - mTopPadding = a.getDimensionPixelSize( - com.android.internal.R.styleable.PreferenceFrameLayout_topPadding, - defaultTopPadding); - mBottomPadding = a.getDimensionPixelSize( - com.android.internal.R.styleable.PreferenceFrameLayout_bottomPadding, - defaultBottomPadding); + int defaultBorderTop = (int) (density * DEFAULT_BORDER_TOP + 0.5f); + int defaultBottomPadding = (int) (density * DEFAULT_BORDER_BOTTOM + 0.5f); + int defaultLeftPadding = (int) (density * DEFAULT_BORDER_LEFT + 0.5f); + int defaultRightPadding = (int) (density * DEFAULT_BORDER_RIGHT + 0.5f); + mBorderTop = a.getDimensionPixelSize( + com.android.internal.R.styleable.PreferenceFrameLayout_borderTop, + defaultBorderTop); + mBorderBottom = a.getDimensionPixelSize( + com.android.internal.R.styleable.PreferenceFrameLayout_borderBottom, + defaultBottomPadding); + mBorderLeft = a.getDimensionPixelSize( + com.android.internal.R.styleable.PreferenceFrameLayout_borderLeft, + defaultLeftPadding); + mBorderRight = a.getDimensionPixelSize( + com.android.internal.R.styleable.PreferenceFrameLayout_borderRight, + defaultRightPadding); a.recycle(); } + /** + * {@inheritDoc} + */ + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + @Override public void addView(View child) { - int topPadding = getPaddingTop(); - int bottomPadding = getPaddingBottom(); + int borderTop = getPaddingTop(); + int borderBottom = getPaddingBottom(); + int borderLeft = getPaddingLeft(); + int borderRight = getPaddingRight(); + + LayoutParams layoutParams = (PreferenceFrameLayout.LayoutParams) child.getLayoutParams(); // Check on the id of the child before adding it. - if (child != null && child.getId() != com.android.internal.R.id.default_preference_layout) { - // Add the padding to the view group after determining if the padding already exists. - if (!mPaddingApplied) { - topPadding += mTopPadding; - bottomPadding += mBottomPadding; - mPaddingApplied = true; - } - } else { + if (layoutParams != null && layoutParams.removeBorders) { if (mPaddingApplied) { - topPadding -= mTopPadding; - bottomPadding -= mBottomPadding; + borderTop -= mBorderTop; + borderBottom -= mBorderBottom; + borderLeft -= mBorderLeft; + borderRight -= mBorderRight; mPaddingApplied = false; } + } else { + // Add the padding to the view group after determining if the + // padding already exists. + if (!mPaddingApplied) { + borderTop += mBorderTop; + borderBottom += mBorderBottom; + borderLeft += mBorderLeft; + borderRight += mBorderRight; + mPaddingApplied = true; + } } + int previousTop = getPaddingTop(); int previousBottom = getPaddingBottom(); - if (previousTop != topPadding || previousBottom != bottomPadding) { - setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding); + int previousLeft = getPaddingLeft(); + int previousRight = getPaddingRight(); + if (previousTop != borderTop || previousBottom != borderBottom + || previousLeft != borderLeft || previousRight != borderRight) { + setPadding(borderLeft, borderTop, borderRight, borderBottom); } + super.addView(child); } + + public static class LayoutParams extends FrameLayout.LayoutParams { + public boolean removeBorders = false; + /** + * {@inheritDoc} + */ + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + + TypedArray a = c.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.PreferenceFrameLayout_Layout); + removeBorders = a.getBoolean( + com.android.internal.R.styleable.PreferenceFrameLayout_Layout_layout_removeBorders, + false); + a.recycle(); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(int width, int height) { + super(width, height); + } + } }
\ No newline at end of file diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 683e603..b2b8c5a 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -51,7 +51,14 @@ public final class Downloads { "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED"; /** - * The permission to directly access the download manager's cache directory + * The permission to access the all the downloads in the manager. + */ + public static final String PERMISSION_ACCESS_ALL = + "android.permission.ACCESS_ALL_DOWNLOADS"; + + /** + * The permission to directly access the download manager's cache + * directory */ public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM"; diff --git a/core/java/android/provider/Ptp.java b/core/java/android/provider/Ptp.java index 2c54370..0f0919e 100644 --- a/core/java/android/provider/Ptp.java +++ b/core/java/android/provider/Ptp.java @@ -20,10 +20,13 @@ import android.content.ContentUris; import android.net.Uri; import android.util.Log; - /** * The PTP provider supports accessing content on PTP devices. - * @hide + * Currently the provider supports: + * - enumerating the storage units, files and directories on PTP devices + * - deleting files and directories on PTP devices + * - importing a file from PTP device into the host device's storage + * and adding it to the media provider */ public final class Ptp { @@ -36,6 +39,8 @@ public final class Ptp /** * Contains list of all PTP devices + * The BaseColumns._ID column contains a hardware specific identifier for the attached + * USB device, and is not guaranteed to be persistent across USB disconnects. */ public static final class Device implements BaseColumns { @@ -59,7 +64,8 @@ public final class Ptp } /** - * Contains list of storage units for an PTP device + * Contains list of storage units for an PTP device. + * The BaseColumns._ID column contains the PTP StorageID for the storage unit. */ public static final class Storage implements BaseColumns { @@ -85,7 +91,10 @@ public final class Ptp } /** - * Contains list of objects on an PTP device + * Contains list of objects on a PTP device. + * The columns in this table correspond directly to the ObjectInfo dataset + * described in the PTP specification (PIMA 15740:2000). + * The BaseColumns._ID column contains the object's PTP ObjectHandle. */ public static final class Object implements BaseColumns { @@ -135,14 +144,14 @@ public final class Ptp public static final String STORAGE_ID = "storage_id"; /** - * The object's format. Can be one of the FORMAT_* symbols below, - * or any of the valid PTP object formats as defined in the PTP specification. + * The object's format. Can be any of the valid PTP object formats + * as defined in the PTP specification. * <P>Type: INTEGER</P> */ public static final String FORMAT = "format"; /** - * The protection status of the object. See the PROTECTION_STATUS_*symbols below. + * The protection status of the object. * <P>Type: INTEGER</P> */ public static final String PROTECTION_STATUS = "protection_status"; @@ -154,8 +163,8 @@ public final class Ptp public static final String SIZE = "size"; /** - * The object's thumbnail format. Can be one of the FORMAT_* symbols below, - * or any of the valid PTP object formats as defined in the PTP specification. + * The object's thumbnail format. Can be any of the valid PTP object formats + * as defined in the PTP specification. * <P>Type: INTEGER</P> */ public static final String THUMB_FORMAT = "thumb_format"; @@ -211,7 +220,6 @@ public final class Ptp /** * The association type for a container object. - * For folders this is typically {@link #ASSOCIATION_TYPE_GENERIC_FOLDER} * <P>Type: INTEGER</P> */ public static final String ASSOCIATION_TYPE = "association_type"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4ea4a16..ed71da2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2576,12 +2576,13 @@ public final class Settings { public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; /** - * A positive value indicates the frequency of SamplingProfiler - * taking snapshots in hertz. Zero value means SamplingProfiler is disabled. + * A positive value indicates how often the SamplingProfiler + * should take snapshots. Zero value means SamplingProfiler + * is disabled. * * @hide */ - public static final String SAMPLING_PROFILER_HZ = "sampling_profiler_hz"; + public static final String SAMPLING_PROFILER_MS = "sampling_profiler_ms"; /** * Settings classname to launch when Settings is clicked from All diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 2f7482c..a6fd2f1 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -663,6 +663,25 @@ class TextLine { } /** + * @param wp + */ + private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) { + final int previousTop = fmi.top; + final int previousAscent = fmi.ascent; + final int previousDescent = fmi.descent; + final int previousBottom = fmi.bottom; + final int previousLeading = fmi.leading; + + wp.getFontMetricsInt(fmi); + + fmi.top = Math.min(fmi.top, previousTop); + fmi.ascent = Math.min(fmi.ascent, previousAscent); + fmi.descent = Math.max(fmi.descent, previousDescent); + fmi.bottom = Math.max(fmi.bottom, previousBottom); + fmi.leading = Math.max(fmi.leading, previousLeading); + } + + /** * Utility function for measuring and rendering text. The text must * not include a tab or emoji. * @@ -703,7 +722,7 @@ class TextLine { } if (fmi != null) { - wp.getFontMetricsInt(fmi); + expandMetricsFromPaint(fmi, wp); } if (c != null) { diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 0d012d6..d724320 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -190,7 +190,9 @@ public class ArrowKeyMovementMethod implements MovementMethod { public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int initialScrollX = -1, initialScrollY = -1; - if (event.getAction() == MotionEvent.ACTION_UP) { + final int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP) { initialScrollX = Touch.getInitialScrollX(widget, buffer); initialScrollY = Touch.getInitialScrollY(widget, buffer); } @@ -198,7 +200,7 @@ public class ArrowKeyMovementMethod implements MovementMethod { boolean handled = Touch.onTouchEvent(widget, buffer, event); if (widget.isFocused() && !widget.didTouchFocusSelect()) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (action == MotionEvent.ACTION_DOWN) { boolean cap = isCap(buffer); if (cap) { int offset = widget.getOffset((int) event.getX(), (int) event.getY()); @@ -211,7 +213,7 @@ public class ArrowKeyMovementMethod implements MovementMethod { // mode once the view detected it needed to scroll. widget.getParent().requestDisallowInterceptTouchEvent(true); } - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + } else if (action == MotionEvent.ACTION_MOVE) { boolean cap = isCap(buffer); if (cap && handled) { @@ -231,7 +233,7 @@ public class ArrowKeyMovementMethod implements MovementMethod { Selection.extendSelection(buffer, offset); return true; } - } else if (event.getAction() == MotionEvent.ACTION_UP) { + } else if (action == MotionEvent.ACTION_UP) { // If we have scrolled, then the up shouldn't move the cursor, // but we do need to make sure the cursor is still visible at // the current scroll offset to avoid the scroll jumping later diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 3308172..09388c0 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -31,27 +31,48 @@ import android.view.View; public class QwertyKeyListener extends BaseKeyListener { private static QwertyKeyListener[] sInstance = new QwertyKeyListener[Capitalize.values().length * 2]; + private static QwertyKeyListener sFullKeyboardInstance; - public QwertyKeyListener(Capitalize cap, boolean autotext) { + private Capitalize mAutoCap; + private boolean mAutoText; + private boolean mFullKeyboard; + + private QwertyKeyListener(Capitalize cap, boolean autoText, boolean fullKeyboard) { mAutoCap = cap; - mAutoText = autotext; + mAutoText = autoText; + mFullKeyboard = fullKeyboard; + } + + public QwertyKeyListener(Capitalize cap, boolean autoText) { + this(cap, autoText, false); } /** * Returns a new or existing instance with the specified capitalization * and correction properties. */ - public static QwertyKeyListener getInstance(boolean autotext, - Capitalize cap) { - int off = cap.ordinal() * 2 + (autotext ? 1 : 0); + public static QwertyKeyListener getInstance(boolean autoText, Capitalize cap) { + int off = cap.ordinal() * 2 + (autoText ? 1 : 0); if (sInstance[off] == null) { - sInstance[off] = new QwertyKeyListener(cap, autotext); + sInstance[off] = new QwertyKeyListener(cap, autoText); } return sInstance[off]; } + /** + * Gets an instance of the listener suitable for use with full keyboards. + * Disables auto-capitalization, auto-text and long-press initiated on-screen + * character pickers. + */ + public static QwertyKeyListener getInstanceForFullKeyboard() { + if (sFullKeyboardInstance == null) { + sFullKeyboardInstance = new QwertyKeyListener(Capitalize.NONE, false, true); + } + return sFullKeyboardInstance; + } + public int getInputType() { return makeTextContentType(mAutoCap, mAutoText); } @@ -85,14 +106,16 @@ public class QwertyKeyListener extends BaseKeyListener { int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content)); - int count = event.getRepeatCount(); - if (count > 0 && selStart == selEnd && selStart > 0) { - char c = content.charAt(selStart - 1); + if (!mFullKeyboard) { + int count = event.getRepeatCount(); + if (count > 0 && selStart == selEnd && selStart > 0) { + char c = content.charAt(selStart - 1); - if (c == i || c == Character.toUpperCase(i) && view != null) { - if (showCharacterPicker(view, content, c, false, count)) { - resetMetaState(content); - return true; + if (c == i || c == Character.toUpperCase(i) && view != null) { + if (showCharacterPicker(view, content, c, false, count)) { + resetMetaState(content); + return true; + } } } } @@ -490,8 +513,5 @@ public class QwertyKeyListener extends BaseKeyListener { private char[] mText; } - - private Capitalize mAutoCap; - private boolean mAutoText; } diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java index 8ad6f50..8312fe1 100644 --- a/core/java/android/text/method/TextKeyListener.java +++ b/core/java/android/text/method/TextKeyListener.java @@ -189,7 +189,12 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { return MultiTapKeyListener.getInstance(mAutoText, mAutoCap); } else if (kind == KeyCharacterMap.FULL || kind == KeyCharacterMap.SPECIAL_FUNCTION) { - return QwertyKeyListener.getInstance(false, Capitalize.NONE); + // We consider special function keyboards full keyboards as a workaround for + // devices that do not have built-in keyboards. Applications may try to inject + // key events using the built-in keyboard device id which may be configured as + // a special function keyboard using a default key map. Ideally, as of Honeycomb, + // these applications should be modified to use KeyCharacterMap.VIRTUAL_KEYBOARD. + return QwertyKeyListener.getInstanceForFullKeyboard(); } return NullKeyListener.getInstance(); diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index 07e87d6..6634f00 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -29,6 +29,7 @@ public class DragEvent implements Parcelable { float mX, mY; ClipDescription mClipDescription; ClipData mClipData; + Object mLocalState; boolean mDragResult; private DragEvent mNext; @@ -139,11 +140,11 @@ public static final int ACTION_DRAG_EXITED = 6; } static DragEvent obtain() { - return DragEvent.obtain(0, 0f, 0f, null, null, false); + return DragEvent.obtain(0, 0f, 0f, null, null, null, false); } /** @hide */ - public static DragEvent obtain(int action, float x, float y, + public static DragEvent obtain(int action, float x, float y, Object localState, ClipDescription description, ClipData data, boolean result) { final DragEvent ev; synchronized (gRecyclerLock) { @@ -167,7 +168,7 @@ public static final int ACTION_DRAG_EXITED = 6; /** @hide */ public static DragEvent obtain(DragEvent source) { - return obtain(source.mAction, source.mX, source.mY, + return obtain(source.mAction, source.mX, source.mY, source.mLocalState, source.mClipDescription, source.mClipData, source.mDragResult); } @@ -218,6 +219,15 @@ public static final int ACTION_DRAG_EXITED = 6; } /** + * Provides the local state object passed as the {@code myLocalState} parameter to + * View.startDrag(). The object will always be null here if the application receiving + * the DragEvent is not the one that started the drag. + */ + public Object getLocalState() { + return mLocalState; + } + + /** * Provides an indication of whether the drag operation concluded successfully. * This method is only available on ACTION_DRAG_ENDED events. * @return {@code true} if the drag operation ended with an accepted drop; {@code false} @@ -249,6 +259,7 @@ public static final int ACTION_DRAG_EXITED = 6; mClipData = null; mClipDescription = null; + mLocalState = null; synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 6f4abef..24a9f87 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -94,13 +94,18 @@ public abstract class HardwareRenderer { */ abstract void setup(int width, int height); + interface HardwareDrawCallbacks { + void onHardwarePreDraw(Canvas canvas); + void onHardwarePostDraw(Canvas canvas); + } + /** * Draws the specified view. * * @param view The view to draw. * @param attachInfo AttachInfo tied to the specified view. */ - abstract void draw(View view, View.AttachInfo attachInfo, int yOffset); + abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks); /** * Creates a new display list that can be used to record batches of @@ -456,7 +461,7 @@ public abstract class HardwareRenderer { } @Override - void draw(View view, View.AttachInfo attachInfo, int yOffset) { + void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { if (canDraw()) { attachInfo.mDrawingTime = SystemClock.uptimeMillis(); attachInfo.mIgnoreDirtyState = true; @@ -473,11 +478,12 @@ public abstract class HardwareRenderer { Canvas canvas = mCanvas; int saveCount = canvas.save(); - canvas.translate(0, -yOffset); + callbacks.onHardwarePreDraw(canvas); try { view.draw(canvas); } finally { + callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); } diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index fbd9eac..97bd8dd 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -31,7 +31,19 @@ import java.lang.Character; public class KeyCharacterMap { /** * The id of the device's primary built in keyboard is always 0. + * + * @deprecated This constant should no longer be used because there is no + * guarantee that a device has a built-in keyboard that can be used for + * typing text. There might not be a built-in keyboard, the built-in keyboard + * might be a {@link #NUMERIC} or {@link #SPECIAL_FUNCTION} keyboard, or there + * might be multiple keyboards installed including external keyboards. + * When interpreting key presses received from the framework, applications should + * use the device id specified in the {@link KeyEvent} received. + * When synthesizing key presses for delivery elsewhere or when translating key presses + * from unknown keyboards, applications should use the special {@link #VIRTUAL_KEYBOARD} + * device id. */ + @Deprecated public static final int BUILT_IN_KEYBOARD = 0; /** diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index b3277e4..03407a3 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1081,7 +1081,15 @@ public class KeyEvent extends InputEvent implements Parcelable { static final boolean DEBUG = false; static final String TAG = "KeyEvent"; - + + private static final int MAX_RECYCLED = 10; + private static final Object gRecyclerLock = new Object(); + private static int gRecyclerUsed; + private static KeyEvent gRecyclerTop; + + private KeyEvent mNext; + private boolean mRecycled; + private int mMetaState; private int mAction; private int mKeyCode; @@ -1160,6 +1168,9 @@ public class KeyEvent extends InputEvent implements Parcelable { } } + private KeyEvent() { + } + /** * Create a new key event. * @@ -1382,6 +1393,67 @@ public class KeyEvent extends InputEvent implements Parcelable { mCharacters = origEvent.mCharacters; } + private static KeyEvent obtain() { + final KeyEvent ev; + synchronized (gRecyclerLock) { + ev = gRecyclerTop; + if (ev == null) { + return new KeyEvent(); + } + gRecyclerTop = ev.mNext; + gRecyclerUsed -= 1; + } + ev.mRecycled = false; + ev.mNext = null; + return ev; + } + + /** + * Obtains a (potentially recycled) key event. + * + * @hide + */ + public static KeyEvent obtain(long downTime, long eventTime, int action, + int code, int repeat, int metaState, + int deviceId, int scancode, int flags, int source, String characters) { + KeyEvent ev = obtain(); + ev.mDownTime = downTime; + ev.mEventTime = eventTime; + ev.mAction = action; + ev.mKeyCode = code; + ev.mRepeatCount = repeat; + ev.mMetaState = metaState; + ev.mDeviceId = deviceId; + ev.mScanCode = scancode; + ev.mFlags = flags; + ev.mSource = source; + ev.mCharacters = characters; + return ev; + } + + /** + * Recycles a key event. + * Key events should only be recycled if they are owned by the system since user + * code expects them to be essentially immutable, "tracking" notwithstanding. + * + * @hide + */ + public final void recycle() { + if (mRecycled) { + throw new RuntimeException(toString() + " recycled twice!"); + } + mRecycled = true; + mCharacters = null; + + synchronized (gRecyclerLock) { + if (gRecyclerUsed < MAX_RECYCLED) { + gRecyclerUsed++; + mNext = gRecyclerTop; + gRecyclerTop = this; + } + } + } + /** * Create a new key event that is the same as the given one, but whose * event time and repeat count are replaced with the given value. diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 195d689..e81aa98 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -314,10 +314,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ static private final int BASE_AVAIL_SAMPLES = 8; - static private final int MAX_RECYCLED = 10; - static private Object gRecyclerLock = new Object(); - static private int gRecyclerUsed = 0; - static private MotionEvent gRecyclerTop = null; + private static final int MAX_RECYCLED = 10; + private static final Object gRecyclerLock = new Object(); + private static int gRecyclerUsed; + private static MotionEvent gRecyclerTop; private long mDownTimeNano; private int mAction; @@ -361,7 +361,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { static private MotionEvent obtain(int pointerCount, int sampleCount) { final MotionEvent ev; synchronized (gRecyclerLock) { - if (gRecyclerTop == null) { + ev = gRecyclerTop; + if (ev == null) { if (pointerCount < BASE_AVAIL_POINTERS) { pointerCount = BASE_AVAIL_POINTERS; } @@ -370,7 +371,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { } return new MotionEvent(pointerCount, sampleCount); } - ev = gRecyclerTop; gRecyclerTop = ev.mNext; gRecyclerUsed -= 1; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index b45aa99..b9a9674 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -282,8 +282,12 @@ public class SurfaceView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getDefaultSize(mRequestedWidth, widthMeasureSpec); - int height = getDefaultSize(mRequestedHeight, heightMeasureSpec); + int width = mRequestedWidth >= 0 + ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0) + : getDefaultSize(0, widthMeasureSpec); + int height = mRequestedHeight >= 0 + ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0) + : getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1d0f185..daf9ac4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -986,6 +986,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public static final int FOCUS_DOWN = 0x00000082; /** + * Bits of {@link #getMeasuredWidthAndState()} and + * {@link #getMeasuredWidthAndState()} that provide the actual measured size. + */ + public static final int MEASURED_SIZE_MASK = 0x00ffffff; + + /** + * Bits of {@link #getMeasuredWidthAndState()} and + * {@link #getMeasuredWidthAndState()} that provide the additional state bits. + */ + public static final int MEASURED_STATE_MASK = 0xff000000; + + /** + * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits + * for functions that combine both width and height into a single int, + * such as {@link #getMeasuredState()} and the childState argument of + * {@link #resolveSizeAndState(int, int, int)}. + */ + public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; + + /** + * Bit of {@link #getMeasuredWidthAndState()} and + * {@link #getMeasuredWidthAndState()} that indicates the measured size + * is smaller that the space the view would like to have. + */ + public static final int MEASURED_STATE_TOO_SMALL = 0x01000000; + + /** * Base View state sets */ // Singles @@ -1463,14 +1490,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * {@hide} */ @ViewDebug.ExportedProperty(category = "measurement") - protected int mMeasuredWidth; + /*package*/ int mMeasuredWidth; /** * Height as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty(category = "measurement") - protected int mMeasuredHeight; + /*package*/ int mMeasuredHeight; /** * The view's identifier. @@ -5212,28 +5239,67 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * The width of this view as measured in the most recent call to measure(). + * Like {@link #getMeasuredWidthAndState()}, but only returns the + * raw width component (that is the result is masked by + * {@link #MEASURED_SIZE_MASK}). + * + * @return The raw measured width of this view. + */ + public final int getMeasuredWidth() { + return mMeasuredWidth & MEASURED_SIZE_MASK; + } + + /** + * Return the full width measurement information for this view as computed + * by the most recent call to {@link #measure}. This result is a bit mask + * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. * This should be used during measurement and layout calculations only. Use * {@link #getWidth()} to see how wide a view is after layout. * - * @return The measured width of this view. + * @return The measured width of this view as a bit mask. */ - public final int getMeasuredWidth() { + public final int getMeasuredWidthAndState() { return mMeasuredWidth; } /** - * The height of this view as measured in the most recent call to measure(). - * This should be used during measurement and layout calculations only. Use - * {@link #getHeight()} to see how tall a view is after layout. + * Like {@link #getMeasuredHeightAndState()}, but only returns the + * raw width component (that is the result is masked by + * {@link #MEASURED_SIZE_MASK}). * - * @return The measured height of this view. + * @return The raw measured height of this view. */ public final int getMeasuredHeight() { + return mMeasuredHeight & MEASURED_SIZE_MASK; + } + + /** + * Return the full height measurement information for this view as computed + * by the most recent call to {@link #measure}. This result is a bit mask + * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. + * This should be used during measurement and layout calculations only. Use + * {@link #getHeight()} to see how wide a view is after layout. + * + * @return The measured width of this view as a bit mask. + */ + public final int getMeasuredHeightAndState() { return mMeasuredHeight; } /** + * Return only the state bits of {@link #getMeasuredWidthAndState()} + * and {@link #getMeasuredHeightAndState()}, combined into one integer. + * The width component is in the regular bits {@link #MEASURED_STATE_MASK} + * and the height component is at the shifted bits + * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}. + */ + public final int getMeasuredState() { + return (mMeasuredWidth&MEASURED_STATE_MASK) + | ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT) + & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); + } + + /** * The transform matrix of this view, which is calculated based on the current * roation, scale, and pivot properties. * @@ -9690,8 +9756,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * measured width and measured height. Failing to do so will trigger an * exception at measurement time.</p> * - * @param measuredWidth the measured width of this view - * @param measuredHeight the measured height of this view + * @param measuredWidth The measured width of this view. May be a complex + * bit mask as defined by {@link #MEASURED_SIZE_MASK} and + * {@link #MEASURED_STATE_TOO_SMALL}. + * @param measuredHeight The measured height of this view. May be a complex + * bit mask as defined by {@link #MEASURED_SIZE_MASK} and + * {@link #MEASURED_STATE_TOO_SMALL}. */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; @@ -9701,14 +9771,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Utility to reconcile a desired size with constraints imposed by a MeasureSpec. - * Will take the desired size, unless a different size is imposed by the constraints. + * Merge two states as returned by {@link #getMeasuredState()}. + * @param curState The current state as returned from a view or the result + * of combining multiple views. + * @param newState The new view state to combine. + * @return Returns a new integer reflecting the combination of the two + * states. + */ + public static int combineMeasuredStates(int curState, int newState) { + return curState | newState; + } + + /** + * Version of {@link #resolveSizeAndState(int, int, int)} + * returning only the {@link #MEASURED_SIZE_MASK} bits of the result. + */ + public static int resolveSize(int size, int measureSpec) { + return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK; + } + + /** + * Utility to reconcile a desired size and state, with constraints imposed + * by a MeasureSpec. Will take the desired size, unless a different size + * is imposed by the constraints. The returned value is a compound integer, + * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and + * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting + * size is smaller than the size the view wants to be. * * @param size How big the view wants to be * @param measureSpec Constraints imposed by the parent - * @return The size this view should be. + * @return Size information bit mask as defined by + * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. */ - public static int resolveSize(int size, int measureSpec) { + public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); @@ -9717,13 +9812,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility result = size; break; case MeasureSpec.AT_MOST: - result = Math.min(size, specSize); + if (specSize < size) { + result = specSize | MEASURED_STATE_TOO_SMALL; + } else { + result = size; + } break; case MeasureSpec.EXACTLY: result = specSize; break; } - return result; + return result | (childMeasuredState&MEASURED_STATE_MASK); } /** @@ -10075,9 +10174,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag * operation is handed over to the OS. * !!! TODO: real docs + * + * @param data !!! TODO + * @param thumbBuilder !!! TODO + * @param myWindowOnly When {@code true}, indicates that the drag operation should be + * restricted to the calling application. In this case only the calling application + * will see any DragEvents related to this drag operation. + * @param myLocalState An arbitrary object that will be passed as part of every DragEvent + * delivered to the calling application during the course of the current drag operation. + * This object is private to the application that called startDrag(), and is not + * visible to other applications. It provides a lightweight way for the application to + * propagate information from the initiator to the recipient of a drag within its own + * application; for example, to help disambiguate between 'copy' and 'move' semantics. + * @return {@code true} if the drag operation was initiated successfully; {@code false} if + * an error prevented the drag from taking place. */ public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder, - boolean myWindowOnly) { + boolean myWindowOnly, Object myLocalState) { if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " local=" + myWindowOnly); } @@ -10111,8 +10224,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility surface.unlockCanvasAndPost(canvas); } + final ViewRoot root = getViewRoot(); + + // Cache the local state object for delivery with DragEvents + root.setLocalDragState(myLocalState); + // repurpose 'thumbSize' for the last touch point - getViewRoot().getLastTouchPoint(thumbSize); + root.getLastTouchPoint(thumbSize); okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token, (float) thumbSize.x, (float) thumbSize.y, diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index bb85894..6b41ce5 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -166,7 +166,7 @@ public class ViewConfiguration { /** * Max distance to overfling for edge effects */ - private static final int OVERFLING_DISTANCE = 4; + private static final int OVERFLING_DISTANCE = 12; private final int mEdgeSlop; private final int mFadingEdgeLength; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index cb7d0e2..a34fe87 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -25,7 +25,9 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; @@ -57,6 +59,8 @@ import android.util.TypedValue; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -79,7 +83,8 @@ import java.util.ArrayList; * {@hide} */ @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) -public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { +public final class ViewRoot extends Handler implements ViewParent, + View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { private static final String TAG = "ViewRoot"; private static final boolean DBG = false; private static final boolean SHOW_FPS = false; @@ -213,12 +218,17 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn int mScrollY; int mCurScrollY; Scroller mScroller; + Bitmap mResizeBitmap; + long mResizeBitmapStartTime; + int mResizeBitmapDuration; + static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); final ViewConfiguration mViewConfiguration; /* Drag/drop */ ClipDescription mDragDescription; View mCurrentDragView; + Object mLocalDragState; final PointF mDragPoint = new PointF(); final PointF mLastTouchPoint = new PointF(); @@ -576,6 +586,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn dirty.inset(-1, -1); } } + if (!mDirty.isEmpty()) { + mAttachInfo.mIgnoreDirtyState = true; + } mDirty.union(dirty); if (!mWillDrawSoon) { scheduleTraversals(); @@ -626,6 +639,13 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn return mAppVisible ? mView.getVisibility() : View.GONE; } + void disposeResizeBitmap() { + if (mResizeBitmap != null) { + mResizeBitmap.recycle(); + mResizeBitmap = null; + } + } + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -734,6 +754,48 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn ensureTouchModeLocally(mAddedTouchMode); } else { if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) { + if (mWidth > 0 && mHeight > 0 && + mSurface != null && mSurface.isValid() && + mAttachInfo.mHardwareRenderer != null && + mAttachInfo.mHardwareRenderer.isEnabled() && + lp != null && !PixelFormat.formatHasAlpha(lp.format)) { + + disposeResizeBitmap(); + + boolean completed = false; + try { + mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight, + Bitmap.Config.ARGB_8888); + mResizeBitmap.setHasAlpha(false); + Canvas canvas = new Canvas(mResizeBitmap); + int yoff; + final boolean scrolling = mScroller != null + && mScroller.computeScrollOffset(); + if (scrolling) { + yoff = mScroller.getCurrY(); + mScroller.abortAnimation(); + } else { + yoff = mScrollY; + } + canvas.translate(0, -yoff); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); + } + canvas.setScreenDensity(mAttachInfo.mScalingRequired + ? DisplayMetrics.DENSITY_DEVICE : 0); + mView.draw(canvas); + mResizeBitmapStartTime = SystemClock.uptimeMillis(); + mResizeBitmapDuration = mView.getResources().getInteger( + com.android.internal.R.integer.config_mediumAnimTime); + completed = true; + } catch (OutOfMemoryError e) { + Log.w(TAG, "Not enough memory for content change anim buffer", e); + } finally { + if (!completed) { + mResizeBitmap = null; + } + } + } mAttachInfo.mContentInsets.set(mPendingContentInsets); host.fitSystemWindows(mAttachInfo.mContentInsets); insetsChanged = true; @@ -775,29 +837,23 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); if (baseSize != 0 && desiredWindowWidth > baseSize) { - int maxHeight = (desiredWindowHeight*2)/3; childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); host.measure(childWidthMeasureSpec, childHeightMeasureSpec); if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getWidth() + "," + host.getHeight() + ")"); - // Note: for now we are not taking into account height, since we - // can't distinguish between places where it would be useful to - // increase the width (text) vs. where it would not (a list). - // Maybe we can just try the next size up, and see if that reduces - // the height? - if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) { - Log.v(TAG, "Good!"); + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { goodMeasure = true; } else { // Didn't fit in that size... try expanding a bit. baseSize = (baseSize+desiredWindowWidth)/2; if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" + baseSize); + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); host.measure(childWidthMeasureSpec, childHeightMeasureSpec); if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getWidth() + "," + host.getHeight() + ")"); - if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) { + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { if (DEBUG_DIALOG) Log.v(TAG, "Good!"); goodMeasure = true; } @@ -863,7 +919,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } boolean windowShouldResize = mLayoutRequested && windowSizeMayChange - && ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight) + && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && frame.width() < desiredWindowWidth && frame.width() != mWidth) || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && @@ -910,8 +966,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } } if (DEBUG_LAYOUT) { - Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" + - host.mMeasuredHeight + ", params=" + params); + Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" + + host.getMeasuredHeight() + ", params=" + params); } relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); @@ -972,6 +1028,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (mScroller != null) { mScroller.abortAnimation(); } + disposeResizeBitmap(); } } catch (RemoteException e) { } @@ -1050,15 +1107,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0); - if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth - || mHeight != host.mMeasuredHeight || contentInsetsChanged) { + if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() + || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth=" - + mWidth + " measuredWidth=" + host.mMeasuredWidth + + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight - + " measuredHeight" + host.mMeasuredHeight + + " measuredHeight" + host.getMeasuredHeight() + " coveredInsetsChanged=" + contentInsetsChanged); // Ask host how big it wants to be @@ -1067,8 +1124,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn // Implementation of weights from WindowManager.LayoutParams // We just grow the dimensions as needed and re-measure if // needs be - int width = host.mMeasuredWidth; - int height = host.mMeasuredHeight; + int width = host.getMeasuredWidth(); + int height = host.getMeasuredHeight(); boolean measureAgain = false; if (lp.horizontalWeight > 0.0f) { @@ -1103,12 +1160,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mScrollMayChange = true; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v( TAG, "Laying out " + host + " to (" + - host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")"); + host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); long startTime = 0L; if (ViewDebug.DEBUG_PROFILE_LAYOUT) { startTime = SystemClock.elapsedRealtime(); } - host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); + host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) { @@ -1310,6 +1367,22 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn return measureSpec; } + int mHardwareYOffset; + int mResizeAlpha; + final Paint mResizePaint = new Paint(); + + public void onHardwarePreDraw(Canvas canvas) { + canvas.translate(0, -mHardwareYOffset); + } + + public void onHardwarePostDraw(Canvas canvas) { + if (mResizeBitmap != null) { + canvas.translate(0, mHardwareYOffset); + mResizePaint.setAlpha(mResizeAlpha); + canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint); + } + } + private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; if (surface == null || !surface.isValid()) { @@ -1334,8 +1407,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } int yoff; - final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); - if (scrolling) { + boolean animating = mScroller != null && mScroller.computeScrollOffset(); + if (animating) { yoff = mScroller.getCurrY(); } else { yoff = mScrollY; @@ -1347,10 +1420,29 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn float appScale = mAttachInfo.mApplicationScale; boolean scalingRequired = mAttachInfo.mScalingRequired; + int resizeAlpha = 0; + if (mResizeBitmap != null) { + long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime; + if (deltaTime < mResizeBitmapDuration) { + float amt = deltaTime/(float)mResizeBitmapDuration; + amt = mResizeInterpolator.getInterpolation(amt); + animating = true; + resizeAlpha = 255 - (int)(amt*255); + } else { + disposeResizeBitmap(); + } + } + Rect dirty = mDirty; if (mSurfaceHolder != null) { // The app owns the surface, we won't draw. dirty.setEmpty(); + if (animating) { + if (mScroller != null) { + mScroller.abortAnimation(); + } + disposeResizeBitmap(); + } return; } @@ -1363,10 +1455,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (!dirty.isEmpty() || mIsAnimating) { mIsAnimating = false; dirty.setEmpty(); - mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff); + mHardwareYOffset = yoff; + mResizeAlpha = resizeAlpha; + mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); } - if (scrolling) { + if (animating) { mFullRedrawNeeded = true; scheduleTraversals(); } @@ -1486,7 +1580,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); } - if (scrolling) { + if (animating) { mFullRedrawNeeded = true; scheduleTraversals(); } @@ -1600,7 +1694,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); - if (!immediate) { + if (!immediate && mResizeBitmap == null) { if (mScroller == null) { mScroller = new Scroller(mView.getContext()); } @@ -2512,6 +2606,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn captureKeyLog("captureDispatchKeyEvent", event); } + // Make sure the fallback event policy sees all keys that will be delivered to the + // view hierarchy. + mFallbackEventHandler.preDispatchKeyEvent(event); + // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { finishKeyEvent(event, sendDone, true); @@ -2585,6 +2683,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } /* drag/drop */ + void setLocalDragState(Object obj) { + mLocalDragState = obj; + } + private void handleDragEvent(DragEvent event) { // From the root, only drag start/end/location are dispatched. entered/exited // are determined and dispatched by the viewgroup hierarchy, who then report @@ -2643,7 +2745,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } } - // Report the drop result if necessary + // Report the drop result when we're done if (what == DragEvent.ACTION_DROP) { try { Log.i(TAG, "Reporting drop result: " + result); @@ -2652,6 +2754,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn Log.e(TAG, "Unable to report drop result"); } } + + // When the drag operation ends, release any local state object + // that may have been in use + if (what == DragEvent.ACTION_DRAG_ENDED) { + setLocalDragState(null); + } } } event.recycle(); @@ -2695,8 +2803,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn //Log.d(TAG, ">>>>>> CALLING relayout"); int relayoutResult = sWindowSession.relayout( mWindow, params, - (int) (mView.mMeasuredWidth * appScale + 0.5f), - (int) (mView.mMeasuredHeight * appScale + 0.5f), + (int) (mView.getMeasuredWidth() * appScale + 0.5f), + (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); @@ -2968,6 +3076,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } else { what = DISPATCH_DRAG_EVENT; } + event.mLocalState = mLocalDragState; // only present when this app called startDrag() Message msg = obtainMessage(what, event); sendMessage(msg); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 5a9cd97..af36d80 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -551,19 +551,14 @@ public interface WindowManagerPolicy { * affect the power state of the device, for example, the power keys. * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. - * @param whenNanos The event time in uptime nanoseconds. - * @param action The key event action. - * @param flags The key event flags. - * @param keyCode The key code. - * @param scanCode The key's scan code. + * @param event The key event. * @param policyFlags The policy flags associated with the key. * @param isScreenOn True if the screen is already on * * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags. */ - public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, - int keyCode, int scanCode, int policyFlags, boolean isScreenOn); + public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn); /** * Called from the input dispatcher thread before a key is dispatched to a window. @@ -574,18 +569,12 @@ public interface WindowManagerPolicy { * * @param win The window that currently has focus. This is where the key * event will normally go. - * @param action The key event action. - * @param flags The key event flags. - * @param keyCode The key code. - * @param scanCode The key's scan code. - * @param metaState bit mask of meta keys that are held. - * @param repeatCount Number of times a key down has repeated. + * @param event The key event. * @param policyFlags The policy flags associated with the key. * @return Returns true if the policy consumed the event and it should * not be further dispatched. */ - public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags); + public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags); /** * Called from the input dispatcher thread when an application did not handle @@ -596,17 +585,11 @@ public interface WindowManagerPolicy { * * @param win The window that currently has focus. This is where the key * event will normally go. - * @param action The key event action. - * @param flags The key event flags. - * @param keyCode The key code. - * @param scanCode The key's scan code. - * @param metaState bit mask of meta keys that are held. - * @param repeatCount Number of times a key down has repeated. + * @param event The key event. * @param policyFlags The policy flags associated with the key. * @return Returns true if the policy consumed the event. */ - public boolean dispatchUnhandledKey(WindowState win, int action, int flags, - int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags); + public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags); /** * Called when layout of the windows is about to start. diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 9568e4f..8f92458 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -571,8 +571,11 @@ class BrowserFrame extends Handler { Iterator iter = mJSInterfaceMap.keySet().iterator(); while (iter.hasNext()) { String interfaceName = (String) iter.next(); - nativeAddJavascriptInterface(nativeFramePointer, - mJSInterfaceMap.get(interfaceName), interfaceName); + Object object = mJSInterfaceMap.get(interfaceName); + if (object != null) { + nativeAddJavascriptInterface(nativeFramePointer, + mJSInterfaceMap.get(interfaceName), interfaceName); + } } } } @@ -595,6 +598,7 @@ class BrowserFrame extends Handler { } public void addJavascriptInterface(Object obj, String interfaceName) { + assert obj != null; if (mJSInterfaceMap == null) { mJSInterfaceMap = new HashMap<String, Object>(); } diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 0e9d9b7..1647540 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -621,6 +621,20 @@ public final class CookieManager { } /** + * Tell the cookie store that this is a good time to flush cookies to flash. + * + * This should be called when the app is paused. Note that this method only + * acts as a hint, and may not have any effect. Flushing is asynchronous. + * + * @hide pending API council approval. + */ + public void flushCookieStore() { + if (useChromiumHttpStack()) { + nativeFlushCookieStore(); + } + } + + /** * Package level api, called from CookieSyncManager * * Get a list of cookies which are updated since a given time. @@ -1078,4 +1092,5 @@ public final class CookieManager { private static native void nativeRemoveSessionCookie(); private static native void nativeSetAcceptCookie(boolean accept); private static native void nativeSetCookie(String url, String value); + private static native void nativeFlushCookieStore(); } diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java index 27043e0..641604e 100644 --- a/core/java/android/webkit/FindActionModeCallback.java +++ b/core/java/android/webkit/FindActionModeCallback.java @@ -43,7 +43,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, private Resources mResources; private boolean mMatchesFound; private int mNumberOfMatches; - private View mTitleBar; private ActionMode mActionMode; FindActionModeCallback(Context context) { @@ -62,8 +61,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, mResources = context.getResources(); } - void setTitleBar(View v) { mTitleBar = v; } - void finish() { mActionMode.finish(); } @@ -174,7 +171,6 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, @Override public void onDestroyActionMode(ActionMode mode) { - if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar); mWebView.notifyFindDialogDismissed(); mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); } diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index 6769563..85bff4f 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -138,6 +138,10 @@ class HTML5VideoViewProxy extends Handler mCurrentProxy.dispatchOnEnded(); else mCurrentProxy.dispatchOnPaused(); + + // Re enable plugin views. + mCurrentProxy.getWebView().getViewManager().showAll(); + isVideoSelfEnded = false; mCurrentProxy = null; mLayout.removeView(mVideoView); @@ -199,6 +203,9 @@ class HTML5VideoViewProxy extends Handler mTimer = new Timer(); mVideoView.start(); client.onShowCustomView(mLayout, mCallback); + // Plugins like Flash will draw over the video so hide + // them while we're playing. + mCurrentProxy.getWebView().getViewManager().hideAll(); } public static boolean isPlaying(HTML5VideoViewProxy proxy) { @@ -599,6 +606,10 @@ class HTML5VideoViewProxy extends Handler return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr); } + /* package */ WebView getWebView() { + return mWebView; + } + private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); private native void nativeOnEnded(int nativePointer); private native void nativeOnPaused(int nativePointer); diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java index cf91902..2f3dc7c 100644 --- a/core/java/android/webkit/SelectActionModeCallback.java +++ b/core/java/android/webkit/SelectActionModeCallback.java @@ -25,15 +25,12 @@ import android.view.View; class SelectActionModeCallback implements ActionMode.Callback { private WebView mWebView; - private View mTitleBar; private ActionMode mActionMode; void setWebView(WebView webView) { mWebView = webView; } - void setTitleBar(View v) { mTitleBar = v; } - void finish() { mActionMode.finish(); } @@ -86,7 +83,6 @@ class SelectActionModeCallback implements ActionMode.Callback { @Override public void onDestroyActionMode(ActionMode mode) { - if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar); mWebView.selectionDone(); } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 911b0b0..9becb6d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -314,6 +314,17 @@ public class WebView extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewGroup.OnHierarchyChangeListener { + private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { + public void onGlobalLayout() { + if (isShown()) { + setGLRectViewport(); + } + } + } + + // The listener to capture global layout change event. + private InnerGlobalLayoutListener mListener = null; + // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing // the screen all-the-time. Good for profiling our drawing code static private final boolean AUTO_REDRAW_HACK = false; @@ -2183,14 +2194,13 @@ public class WebView extends AbsoluteLayout // look at the cursor node, and not the focus node. Also, what is // getFocusNodePath? public void requestFocusNodeHref(Message hrefMsg) { - if (hrefMsg == null || mNativeClass == 0) { + if (hrefMsg == null) { return; } - if (nativeCursorIsAnchor()) { - mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF, - nativeCursorFramePointer(), nativeCursorNodePointer(), - hrefMsg); - } + int contentX = viewToContentX((int) mLastTouchX + mScrollX); + int contentY = viewToContentY((int) mLastTouchY + mScrollY); + mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF, + contentX, contentY, hrefMsg); } /** @@ -2254,16 +2264,6 @@ public class WebView extends AbsoluteLayout * @hide */ public void setEmbeddedTitleBar(View v) { - if (null == v) { - // If one of our callbacks is holding onto the titlebar to replace - // it when its ActionMode ends, remove it. - if (mSelectCallback != null) { - mSelectCallback.setTitleBar(null); - } - if (mFindCallback != null) { - mFindCallback.setTitleBar(null); - } - } if (mTitleBar == v) return; if (mTitleBar != null) { removeView(mTitleBar); @@ -2444,6 +2444,7 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, nativeMoveGeneration(), mUserScroll ? 1 : 0, pos); mLastVisibleRectSent = rect; + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); } Rect globalRect = new Rect(); if (getGlobalVisibleRect(globalRect) @@ -2472,15 +2473,14 @@ public class WebView extends AbsoluteLayout // Sets r to be our visible rectangle in content coordinates private void calcOurContentVisibleRect(Rect r) { calcOurVisibleRect(r); - // since we might overscroll, pin the rect to the bounds of the content - r.left = Math.max(viewToContentX(r.left), 0); + r.left = viewToContentX(r.left); // viewToContentY will remove the total height of the title bar. Add // the visible height back in to account for the fact that if the title // bar is partially visible, the part of the visible rect which is // displaying our content is displaced by that amount. - r.top = Math.max(viewToContentY(r.top + getVisibleTitleHeight()), 0); - r.right = Math.min(viewToContentX(r.right), mContentWidth); - r.bottom = Math.min(viewToContentY(r.bottom), mContentHeight); + r.top = viewToContentY(r.top + getVisibleTitleHeight()); + r.right = viewToContentX(r.right); + r.bottom = viewToContentY(r.bottom); } // Sets r to be our visible rectangle in content coordinates. We use this @@ -2490,15 +2490,14 @@ public class WebView extends AbsoluteLayout private void calcOurContentVisibleRectF(RectF r) { Rect ri = new Rect(0,0,0,0); calcOurVisibleRect(ri); - // pin the rect to the bounds of the content - r.left = Math.max(viewToContentXf(ri.left), 0.0f); + r.left = viewToContentXf(ri.left); // viewToContentY will remove the total height of the title bar. Add // the visible height back in to account for the fact that if the title // bar is partially visible, the part of the visible rect which is // displaying our content is displaced by that amount. - r.top = Math.max(viewToContentYf(ri.top + getVisibleTitleHeight()), 0.0f); - r.right = Math.min(viewToContentXf(ri.right), (float)mContentWidth); - r.bottom = Math.min(viewToContentYf(ri.bottom), (float)mContentHeight); + r.top = viewToContentYf(ri.top + getVisibleTitleHeight()); + r.right = viewToContentXf(ri.right); + r.bottom = viewToContentYf(ri.bottom); } static class ViewSizeData { @@ -2898,11 +2897,6 @@ public class WebView extends AbsoluteLayout setFindIsUp(true); mFindCallback.setWebView(this); View titleBar = mTitleBar; - // We do not want to show the embedded title bar during find or - // select, but keep track of it so that it can be replaced when the - // mode is exited. - setEmbeddedTitleBar(null); - mFindCallback.setTitleBar(titleBar); startActionMode(mFindCallback); if (text == null) { text = mLastFind; @@ -3497,10 +3491,14 @@ public class WebView extends AbsoluteLayout * <li> The Java object that is bound runs in another thread and not in * the thread that it was constructed in.</li> * </ul></p> - * @param obj The class instance to bind to Javascript - * @param interfaceName The name to used to expose the class in Javascript + * @param obj The class instance to bind to Javascript, null instances are + * ignored. + * @param interfaceName The name to used to expose the class in JavaScript. */ public void addJavascriptInterface(Object obj, String interfaceName) { + if (obj == null) { + return; + } WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); arg.mObject = obj; arg.mInterfaceName = interfaceName; @@ -3742,6 +3740,8 @@ public class WebView extends AbsoluteLayout if (mNativeClass != 0 && nativeWordSelection(x, y)) { nativeSetExtendSelection(); mDrawSelectionPointer = false; + mSelectionStarted = true; + mTouchMode = TOUCH_DRAG_MODE; return true; } selectionDone(); @@ -4626,12 +4626,6 @@ public class WebView extends AbsoluteLayout nativeHideCursor(); mSelectCallback = new SelectActionModeCallback(); mSelectCallback.setWebView(this); - View titleBar = mTitleBar; - // We do not want to show the embedded title bar during find or - // select, but keep track of it so that it can be replaced when the - // mode is exited. - setEmbeddedTitleBar(null); - mSelectCallback.setTitleBar(titleBar); startActionMode(mSelectCallback); } @@ -4709,6 +4703,11 @@ public class WebView extends AbsoluteLayout protected void onAttachedToWindow() { super.onAttachedToWindow(); if (hasWindowFocus()) setActive(true); + final ViewTreeObserver treeObserver = getViewTreeObserver(); + if (treeObserver != null && mListener == null) { + mListener = new InnerGlobalLayoutListener(); + treeObserver.addOnGlobalLayoutListener(mListener); + } } @Override @@ -4716,6 +4715,13 @@ public class WebView extends AbsoluteLayout clearHelpers(); mZoomManager.dismissZoomPicker(); if (hasWindowFocus()) setActive(false); + + final ViewTreeObserver treeObserver = getViewTreeObserver(); + if (treeObserver != null && mListener != null) { + treeObserver.removeGlobalOnLayoutListener(mListener); + mListener = null; + } + super.onDetachedFromWindow(); } @@ -4856,13 +4862,19 @@ public class WebView extends AbsoluteLayout } void setGLRectViewport() { - View window = getRootView(); - int[] location = new int[2]; - getLocationInWindow(location); - mGLRectViewport = new Rect(location[0], window.getHeight() - - (location[1] + getHeight()), - location[0] + getWidth(), - window.getHeight() - location[1]); + // Use the getGlobalVisibleRect() to get the intersection among the parents + Rect webViewRect = new Rect(); + boolean visible = getGlobalVisibleRect(webViewRect); + + // Then need to invert the Y axis, just for GL + View rootView = getRootView(); + int rootViewHeight = rootView.getHeight(); + int savedWebViewBottom = webViewRect.bottom; + webViewRect.bottom = rootViewHeight - webViewRect.top; + webViewRect.top = rootViewHeight - savedWebViewBottom; + + // Store the viewport + mGLRectViewport = webViewRect; } /** @@ -4998,10 +5010,10 @@ public class WebView extends AbsoluteLayout startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime); } - private void startScrollingLayer(float gestureX, float gestureY) { + private void startScrollingLayer(float x, float y) { if (mTouchMode != TOUCH_DRAG_LAYER_MODE) { - int contentX = viewToContentX((int) gestureX + mScrollX); - int contentY = viewToContentY((int) gestureY + mScrollY); + int contentX = viewToContentX((int) x + mScrollX); + int contentY = viewToContentY((int) y + mScrollY); mScrollingLayer = nativeScrollableLayer(contentX, contentY); if (mScrollingLayer != 0) { mTouchMode = TOUCH_DRAG_LAYER_MODE; @@ -5034,52 +5046,12 @@ public class WebView extends AbsoluteLayout float y = ev.getY(); long eventTime = ev.getEventTime(); - final ScaleGestureDetector detector = - mZoomManager.getMultiTouchGestureDetector(); - boolean isScrollGesture = false; - // Set to the mid-point of a two-finger gesture used to detect if the - // user has touched a layer. - float gestureX = x; - float gestureY = y; - if (detector == null || !detector.isInProgress()) { - // The gesture for scrolling a layer is two fingers close together. - // FIXME: we may consider giving WebKit an option to handle - // multi-touch events later. - if (ev.getPointerCount() > 1) { - float dx = ev.getX(1) - ev.getX(0); - float dy = ev.getY(1) - ev.getY(0); - float dist = (dx * dx + dy * dy) * - DRAG_LAYER_INVERSE_DENSITY_SQUARED; - // Use the approximate center to determine if the gesture is in - // a layer. - gestureX = ev.getX(0) + (dx * .5f); - gestureY = ev.getY(0) + (dy * .5f); - // Now use a consistent point for tracking movement. - if (ev.getX(0) < ev.getX(1)) { - x = ev.getX(0); - y = ev.getY(0); - } else { - x = ev.getX(1); - y = ev.getY(1); - } - action = ev.getActionMasked(); - if (dist < DRAG_LAYER_FINGER_DISTANCE) { - isScrollGesture = true; - } else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { - // Fingers moved too far apart while dragging, the user - // might be trying to zoom. - mTouchMode = TOUCH_INIT_MODE; - } - } - } - - // If the page disallows zoom, pass multi-touch events to webkit. // mDeferMultitouch is a hack for layout tests, where it is used to // force passing multi-touch events to webkit. // FIXME: always pass multi-touch events to webkit and remove everything // related to mDeferMultitouch. if (ev.getPointerCount() > 1 && - (mDeferMultitouch || (!isScrollGesture && mZoomManager.isZoomScaleFixed()))) { + (mDeferMultitouch || mZoomManager.isZoomScaleFixed())) { if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit"); } @@ -5087,8 +5059,11 @@ public class WebView extends AbsoluteLayout return true; } + final ScaleGestureDetector detector = + mZoomManager.getMultiTouchGestureDetector(); + if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 && - mTouchMode != TOUCH_DRAG_LAYER_MODE && !isScrollGesture) { + mTouchMode != TOUCH_DRAG_LAYER_MODE) { if (!detector.isInProgress() && ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) { // Insert a fake pointer down event in order to start @@ -5378,9 +5353,6 @@ public class WebView extends AbsoluteLayout deltaX = 0; deltaY = 0; - if (isScrollGesture) { - startScrollingLayer(gestureX, gestureY); - } startDrag(); } @@ -5449,6 +5421,7 @@ public class WebView extends AbsoluteLayout mUserScroll = true; } + startScrollingLayer(x, y); doDrag(deltaX, deltaY); // Turn off scrollbars when dragging a layer. @@ -5555,6 +5528,7 @@ public class WebView extends AbsoluteLayout break; } case TOUCH_DRAG_MODE: + case TOUCH_DRAG_LAYER_MODE: mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS); mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS); // if the user waits a while w/o moving before the @@ -5586,7 +5560,6 @@ public class WebView extends AbsoluteLayout invalidate(); // fall through case TOUCH_DRAG_START_MODE: - case TOUCH_DRAG_LAYER_MODE: // TOUCH_DRAG_START_MODE should not happen for the real // device as we almost certain will get a MOVE. But this // is possible on emulator. @@ -5676,8 +5649,10 @@ public class WebView extends AbsoluteLayout deltaY = viewToContentDimension(deltaY); if (nativeScrollLayer(mScrollingLayer, deltaX, deltaY)) { invalidate(); + return; } - return; + // Switch to drag mode and fall through. + mTouchMode = TOUCH_DRAG_MODE; } final int oldX = mScrollX; @@ -6139,10 +6114,12 @@ public class WebView extends AbsoluteLayout } if (mOverscrollDistance < mOverflingDistance) { - if (mScrollX == -mOverscrollDistance || mScrollX == maxX + mOverscrollDistance) { + if ((vx > 0 && mScrollX == -mOverscrollDistance) || + (vx < 0 && mScrollX == maxX + mOverscrollDistance)) { vx = 0; } - if (mScrollY == -mOverscrollDistance || mScrollY == maxY + mOverscrollDistance) { + if ((vy > 0 && mScrollY == -mOverscrollDistance) || + (vy < 0 && mScrollY == maxY + mOverscrollDistance)) { vy = 0; } } @@ -6493,6 +6470,8 @@ public class WebView extends AbsoluteLayout if (measuredHeight > heightSize) { measuredHeight = heightSize; mHeightCanMeasure = false; + } else if (measuredHeight < heightSize) { + measuredHeight |= MEASURED_STATE_TOO_SMALL; } } } else { @@ -6506,6 +6485,9 @@ public class WebView extends AbsoluteLayout mWidthCanMeasure = true; measuredWidth = contentWidth; } else { + if (measuredWidth < contentWidth) { + measuredWidth |= MEASURED_STATE_TOO_SMALL; + } mWidthCanMeasure = false; } @@ -6961,6 +6943,7 @@ public class WebView extends AbsoluteLayout int deltaY = pinLocY((int) (mScrollY + mLastDeferTouchY - y)) - mScrollY; + startScrollingLayer(x, y); doDrag(deltaX, deltaY); if (deltaX != 0) mLastDeferTouchX = x; if (deltaY != 0) mLastDeferTouchY = y; @@ -7595,6 +7578,7 @@ public class WebView extends AbsoluteLayout if (mNativeClass == 0) { return false; } + mInitialHitTestResult = null; mLastCursorTime = time; mLastCursorBounds = nativeGetCursorRingBounds(); boolean keyHandled @@ -7715,6 +7699,10 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0); } + /* package */ ViewManager getViewManager() { + return mViewManager; + } + private native int nativeCacheHitFramePointer(); private native Rect nativeCacheHitNodeBounds(); private native int nativeCacheHitNodePointer(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index b7d20b4..a7a839d 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -500,7 +500,7 @@ final class WebViewCore { /** * Update the layers' content */ - private native int nativeUpdateLayers(); + private native int nativeUpdateLayers(Region invalRegion); private native boolean nativeFocusBoundsChanged(); @@ -553,8 +553,8 @@ final class WebViewCore { private native void nativeMoveMouseIfLatest(int moveGeneration, int framePtr, int x, int y); - private native String nativeRetrieveHref(int framePtr, int nodePtr); - private native String nativeRetrieveAnchorText(int framePtr, int nodePtr); + private native String nativeRetrieveHref(int x, int y); + private native String nativeRetrieveAnchorText(int x, int y); private native void nativeTouchUp(int touchGeneration, int framePtr, int nodePtr, int x, int y); @@ -1914,7 +1914,7 @@ final class WebViewCore { return; } DrawData draw = new DrawData(); - draw.mBaseLayer = nativeUpdateLayers(); + draw.mBaseLayer = nativeUpdateLayers(draw.mInvalRegion); webkitDraw(draw); } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index dd71b3f..0da73a4 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -85,6 +85,16 @@ public abstract class AbsSeekBar extends ProgressBar { * @param thumb Drawable representing the thumb */ public void setThumb(Drawable thumb) { + boolean needUpdate; + // This way, calling setThumb again with the same bitmap will result in + // it recalcuating mThumbOffset (if for example it the bounds of the + // drawable changed) + if (mThumb != null && thumb != mThumb) { + mThumb.setCallback(null); + needUpdate = true; + } else { + needUpdate = false; + } if (thumb != null) { thumb.setCallback(this); @@ -92,9 +102,25 @@ public abstract class AbsSeekBar extends ProgressBar { // such that the thumb will hang halfway off either edge of the // progress bar. mThumbOffset = thumb.getIntrinsicWidth() / 2; + + // If we're updating get the new states + if (needUpdate && + (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth() + || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) { + requestLayout(); + } } mThumb = thumb; invalidate(); + if (needUpdate) { + updateThumbPos(getWidth(), getHeight()); + if (thumb.isStateful()) { + // Note that if the states are different this won't work. + // For now, let's consider that an app bug. + int[] state = getDrawableState(); + thumb.setState(state); + } + } } /** @@ -191,6 +217,10 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { + updateThumbPos(w, h); + } + + private void updateThumbPos(int w, int h) { Drawable d = getCurrentDrawable(); Drawable thumb = mThumb; int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); @@ -281,8 +311,8 @@ public abstract class AbsSeekBar extends ProgressBar { dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; - setMeasuredDimension(resolveSize(dw, widthMeasureSpec), - resolveSize(dh, heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), + resolveSizeAndState(dh, heightMeasureSpec, 0)); } @Override diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java index 2b3b98d..3d79205 100644 --- a/core/java/android/widget/AbsSpinner.java +++ b/core/java/android/widget/AbsSpinner.java @@ -223,8 +223,8 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight()); preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth()); - heightSize = resolveSize(preferredHeight, heightMeasureSpec); - widthSize = resolveSize(preferredWidth, widthMeasureSpec); + heightSize = resolveSizeAndState(preferredHeight, heightMeasureSpec, 0); + widthSize = resolveSizeAndState(preferredWidth, widthMeasureSpec, 0); setMeasuredDimension(widthSize, heightSize); mHeightMeasureSpec = heightMeasureSpec; diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java index 970cbe3..ac82af7 100644 --- a/core/java/android/widget/AbsoluteLayout.java +++ b/core/java/android/widget/AbsoluteLayout.java @@ -88,8 +88,8 @@ public class AbsoluteLayout extends ViewGroup { maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); - setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), - resolveSize(maxHeight, heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), + resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); } /** diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index 695ea6b..162b030 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -648,8 +648,8 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> private void measureChildren() { final int count = getChildCount(); - final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; - final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom; + final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight; + final int childHeight = getMeasuredHeight() - mPaddingTop - mPaddingBottom; for (int i = 0; i < count; i++) { final View child = getChildAt(i); @@ -674,16 +674,28 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop + mPaddingBottom : 0; } else if (heightSpecMode == MeasureSpec.AT_MOST) { - heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop + - mPaddingBottom, heightSpecSize) : 0; + if (haveChildRefSize) { + int height = mReferenceChildHeight + mPaddingTop + mPaddingBottom; + if (height > heightSpecSize) { + heightSpecSize |= MEASURED_STATE_TOO_SMALL; + } else { + heightSpecSize = height; + } + } } if (widthSpecMode == MeasureSpec.UNSPECIFIED) { widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft + mPaddingRight : 0; } else if (heightSpecMode == MeasureSpec.AT_MOST) { - widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft + - mPaddingRight, widthSpecSize) : 0; + if (haveChildRefSize) { + int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight; + if (width > widthSpecSize) { + widthSpecSize |= MEASURED_STATE_TOO_SMALL; + } else { + widthSpecSize = width; + } + } } setMeasuredDimension(widthSpecSize, heightSpecSize); diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index f847bc3..84ebec3 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -146,8 +146,8 @@ public class AnalogClock extends View { float scale = Math.min(hScale, vScale); - setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec), - resolveSize((int) (mDialHeight * scale), heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0), + resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0)); } @Override diff --git a/core/java/android/widget/ButtonGroup.java b/core/java/android/widget/ButtonGroup.java index 6af1c7e..7548ef6 100644 --- a/core/java/android/widget/ButtonGroup.java +++ b/core/java/android/widget/ButtonGroup.java @@ -167,12 +167,14 @@ public class ButtonGroup extends LinearLayout { if (getOrientation() == VERTICAL) { final int dividerSize = mDividerHeight * dividerCount; - setMeasuredDimension(getMeasuredWidth(), - resolveSize(getMeasuredHeight() + dividerSize, heightMeasureSpec)); + setMeasuredDimension(getMeasuredWidthAndState(), + resolveSizeAndState(getMeasuredHeight() + dividerSize, heightMeasureSpec, + getMeasuredHeightAndState())); } else { final int dividerSize = mDividerWidth * dividerCount; - setMeasuredDimension(resolveSize(getMeasuredWidth() + dividerSize, widthMeasureSpec), - getMeasuredHeight()); + setMeasuredDimension(resolveSizeAndState(getMeasuredWidth() + dividerSize, + widthMeasureSpec, getMeasuredWidthAndState()), + getMeasuredHeightAndState()); } } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index bcab7a9..940fec1 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -248,6 +248,7 @@ public class FrameLayout extends ViewGroup { int maxHeight = 0; int maxWidth = 0; + int childState = 0; // Find rightmost and bottommost child for (int i = 0; i < count; i++) { @@ -256,6 +257,7 @@ public class FrameLayout extends ViewGroup { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); + childState = combineMeasuredStates(childState, child.getMeasuredState()); } } @@ -274,8 +276,9 @@ public class FrameLayout extends ViewGroup { maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } - setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), - resolveSize(maxHeight, heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState<<MEASURED_HEIGHT_STATE_SHIFT)); } /** diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 9789658..ce76bee 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -814,7 +814,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList * @return Where the top of the child should be */ private int calculateTop(View child, boolean duringLayout) { - int myHeight = duringLayout ? mMeasuredHeight : getHeight(); + int myHeight = duringLayout ? getMeasuredHeight() : getHeight(); int childHeight = duringLayout ? child.getMeasuredHeight() : child.getHeight(); int childTop = 0; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 4146460..84bc5f2 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -979,6 +979,7 @@ public class GridView extends AbsListView { determineColumns(childWidth); int childHeight = 0; + int childState = 0; mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); final int count = mItemCount; @@ -1001,6 +1002,7 @@ public class GridView extends AbsListView { child.measure(childWidthSpec, childHeightSpec); childHeight = child.getMeasuredHeight(); + childState = combineMeasuredStates(childState, child.getMeasuredState()); if (mRecycler.shouldRecycleViewType(p.viewType)) { mRecycler.addScrapView(child, -1); @@ -1029,6 +1031,15 @@ public class GridView extends AbsListView { heightSize = ourSize; } + if (widthMode == MeasureSpec.AT_MOST && mRequestedNumColumns != AUTO_FIT) { + int ourSize = (mRequestedNumColumns*mColumnWidth) + + ((mRequestedNumColumns-1)*mHorizontalSpacing) + + mListPadding.left + mListPadding.right; + if (ourSize > widthSize) { + widthSize |= MEASURED_STATE_TOO_SMALL; + } + } + setMeasuredDimension(widthSize, heightSize); mWidthMeasureSpec = widthMeasureSpec; } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 555d993..1fe6f4b 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -696,7 +696,7 @@ public class ImageView extends View { / desiredAspect) + ptop + pbottom; if (newHeight <= heightSize) { heightSize = newHeight; - } + } } } } @@ -711,8 +711,8 @@ public class ImageView extends View { w = Math.max(w, getSuggestedMinimumWidth()); h = Math.max(h, getSuggestedMinimumHeight()); - widthSize = resolveSize(w, widthMeasureSpec); - heightSize = resolveSize(h, heightMeasureSpec); + widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); + heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); } setMeasuredDimension(widthSize, heightSize); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 1e5489a..a09e5c6 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -20,6 +20,8 @@ import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -57,6 +59,23 @@ public class LinearLayout extends ViewGroup { public static final int VERTICAL = 1; /** + * Don't show any dividers. + */ + public static final int SHOW_DIVIDER_NONE = 0; + /** + * Show a divider at the beginning of the group. + */ + public static final int SHOW_DIVIDER_BEGINNING = 1; + /** + * Show dividers between each item in the group. + */ + public static final int SHOW_DIVIDER_MIDDLE = 2; + /** + * Show a divider at the end of the group. + */ + public static final int SHOW_DIVIDER_END = 4; + + /** * Whether the children of this layout are baseline aligned. Only applicable * if {@link #mOrientation} is horizontal. */ @@ -119,6 +138,12 @@ public class LinearLayout extends ViewGroup { private static final int INDEX_BOTTOM = 2; private static final int INDEX_FILL = 3; + private Drawable mDivider; + private int mDividerWidth; + private int mDividerHeight; + private int mShowDividers; + private int mDividerPadding; + public LinearLayout(Context context) { super(context); } @@ -155,10 +180,158 @@ public class LinearLayout extends ViewGroup { mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false); + setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider)); + mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE); + mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0); + a.recycle(); } /** + * Set how dividers should be shown between items in this layout + * + * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING}, + * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, + * or {@link #SHOW_DIVIDER_NONE} to show no dividers. + */ + public void setShowDividers(int showDividers) { + if (showDividers != mShowDividers) { + requestLayout(); + } + mShowDividers = showDividers; + } + + /** + * @return A flag set indicating how dividers should be shown around items. + * @see #setShowDividers(int) + */ + public int getShowDividers() { + return mShowDividers; + } + + /** + * Set a drawable to be used as a divider between items. + * @param divider Drawable that will divide each item. + * @see #setShowDividers(int) + */ + public void setDividerDrawable(Drawable divider) { + if (divider == mDivider) { + return; + } + mDivider = divider; + if (divider != null) { + mDividerWidth = divider.getIntrinsicWidth(); + mDividerHeight = divider.getIntrinsicHeight(); + } else { + mDividerWidth = 0; + mDividerHeight = 0; + } + setWillNotDraw(divider == null); + requestLayout(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mDivider == null) { + return; + } + + if (mOrientation == VERTICAL) { + drawDividersVertical(canvas); + } else { + drawDividersHorizontal(canvas); + } + } + + void drawDividersVertical(Canvas canvas) { + final boolean showDividerBeginning = + (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING; + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + final boolean showDividerEnd = + (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END; + + final int count = getVirtualChildCount(); + int top = getPaddingTop(); + boolean firstVisible = true; + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null) { + top += measureNullChild(i); + } else if (child.getVisibility() != GONE) { + if (firstVisible) { + firstVisible = false; + if (showDividerBeginning) { + drawHorizontalDivider(canvas, top); + top += mDividerHeight; + } + } else if (showDividerMiddle) { + drawHorizontalDivider(canvas, top); + top += mDividerHeight; + } + + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + top += lp.topMargin + child.getHeight() + lp.bottomMargin; + } + } + + if (showDividerEnd) { + drawHorizontalDivider(canvas, top); + } + } + + void drawDividersHorizontal(Canvas canvas) { + final boolean showDividerBeginning = + (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING; + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + final boolean showDividerEnd = + (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END; + + final int count = getVirtualChildCount(); + int left = getPaddingLeft(); + boolean firstVisible = true; + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null) { + left += measureNullChild(i); + } else if (child.getVisibility() != GONE) { + if (firstVisible) { + firstVisible = false; + if (showDividerBeginning) { + drawVerticalDivider(canvas, left); + left += mDividerWidth; + } + } else if (showDividerMiddle) { + drawVerticalDivider(canvas, left); + left += mDividerWidth; + } + + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + left += lp.leftMargin + child.getWidth() + lp.rightMargin; + } + } + + if (showDividerEnd) { + drawVerticalDivider(canvas, left); + } + } + + void drawHorizontalDivider(Canvas canvas, int top) { + mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, + getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); + mDivider.draw(canvas); + } + + void drawVerticalDivider(Canvas canvas, int left) { + mDivider.setBounds(left, getPaddingTop() + mDividerPadding, + left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); + mDivider.draw(canvas); + } + + /** * <p>Indicates whether widgets contained within this layout are aligned * on their baseline or not.</p> * @@ -363,6 +536,7 @@ public class LinearLayout extends ViewGroup { void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { mTotalLength = 0; int maxWidth = 0; + int childState = 0; int alternativeMaxWidth = 0; int weightedMaxWidth = 0; boolean allFillParent = true; @@ -380,7 +554,14 @@ public class LinearLayout extends ViewGroup { int largestChildHeight = Integer.MIN_VALUE; + // A divider at the end will change how much space views can consume. + final boolean showDividerBeginning = + (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING; + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + // See how tall everyone is. Also remember max width. + boolean firstVisible = true; for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -394,6 +575,15 @@ public class LinearLayout extends ViewGroup { continue; } + if (firstVisible) { + firstVisible = false; + if (showDividerBeginning) { + mTotalLength += mDividerHeight; + } + } else if (showDividerMiddle) { + mTotalLength += mDividerHeight; + } + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); totalWeight += lp.weight; @@ -469,6 +659,7 @@ public class LinearLayout extends ViewGroup { final int margin = lp.leftMargin + lp.rightMargin; final int measuredWidth = child.getMeasuredWidth() + margin; maxWidth = Math.max(maxWidth, measuredWidth); + childState = combineMeasuredStates(childState, child.getMeasuredState()); allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; if (lp.weight > 0) { @@ -486,6 +677,10 @@ public class LinearLayout extends ViewGroup { i += getChildrenSkipCount(child, i); } + if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) { + mTotalLength += mDividerHeight; + } + if (useLargestChild && heightMode == MeasureSpec.AT_MOST) { mTotalLength = 0; @@ -520,7 +715,8 @@ public class LinearLayout extends ViewGroup { heightSize = Math.max(heightSize, getSuggestedMinimumHeight()); // Reconcile our calculated size with the heightMeasureSpec - heightSize = resolveSize(heightSize, heightMeasureSpec); + int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); + heightSize = heightSizeAndState & MEASURED_SIZE_MASK; // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds @@ -569,6 +765,10 @@ public class LinearLayout extends ViewGroup { MeasureSpec.makeMeasureSpec(share > 0 ? share : 0, MeasureSpec.EXACTLY)); } + + // Child may now not fit in vertical dimension. + childState = combineMeasuredStates(childState, child.getMeasuredState() + & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); } final int margin = lp.leftMargin + lp.rightMargin; @@ -605,7 +805,8 @@ public class LinearLayout extends ViewGroup { // Check against our minimum width maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); - setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize); + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + heightSizeAndState); if (matchWidth) { forceUniformWidth(count, heightMeasureSpec); @@ -649,6 +850,7 @@ public class LinearLayout extends ViewGroup { void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { mTotalLength = 0; int maxHeight = 0; + int childState = 0; int alternativeMaxHeight = 0; int weightedMaxHeight = 0; boolean allFillParent = true; @@ -679,7 +881,14 @@ public class LinearLayout extends ViewGroup { int largestChildWidth = Integer.MIN_VALUE; + // A divider at the end will change how much space views can consume. + final boolean showDividerBeginning = + (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING; + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + // See how wide everyone is. Also remember max height. + boolean firstVisible = true; for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -693,6 +902,15 @@ public class LinearLayout extends ViewGroup { continue; } + if (firstVisible) { + firstVisible = false; + if (showDividerBeginning) { + mTotalLength += mDividerWidth; + } + } else if (showDividerMiddle) { + mTotalLength += mDividerWidth; + } + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); @@ -769,6 +987,7 @@ public class LinearLayout extends ViewGroup { final int margin = lp.topMargin + lp.bottomMargin; final int childHeight = child.getMeasuredHeight() + margin; + childState = combineMeasuredStates(childState, child.getMeasuredState()); if (baselineAligned) { final int childBaseline = child.getBaseline(); @@ -803,6 +1022,10 @@ public class LinearLayout extends ViewGroup { i += getChildrenSkipCount(child, i); } + if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) { + mTotalLength += mDividerWidth; + } + // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, // the most common case if (maxAscent[INDEX_TOP] != -1 || @@ -856,7 +1079,8 @@ public class LinearLayout extends ViewGroup { widthSize = Math.max(widthSize, getSuggestedMinimumWidth()); // Reconcile our calculated size with the widthMeasureSpec - widthSize = resolveSize(widthSize, widthMeasureSpec); + int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); + widthSize = widthSizeAndState & MEASURED_SIZE_MASK; // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds @@ -911,6 +1135,10 @@ public class LinearLayout extends ViewGroup { share > 0 ? share : 0, MeasureSpec.EXACTLY), childHeightMeasureSpec); } + + // Child may now not fit in horizontal dimension. + childState = combineMeasuredStates(childState, + child.getMeasuredState() & MEASURED_STATE_MASK); } if (isExactly) { @@ -980,7 +1208,9 @@ public class LinearLayout extends ViewGroup { // Check against our minimum height maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); - setMeasuredDimension(widthSize, resolveSize(maxHeight, heightMeasureSpec)); + setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), + resolveSizeAndState(maxHeight, heightMeasureSpec, + (childState<<MEASURED_HEIGHT_STATE_SHIFT))); if (matchHeight) { forceUniformHeight(count, widthMeasureSpec); @@ -1127,7 +1357,14 @@ public class LinearLayout extends ViewGroup { } } - + + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + + if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) { + childTop += mDividerHeight; + } + for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child == null) { @@ -1162,12 +1399,15 @@ public class LinearLayout extends ViewGroup { break; } - childTop += lp.topMargin; setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); + if (showDividerMiddle) { + childTop += mDividerHeight; + } + i += getChildrenSkipCount(child, i); } } @@ -1216,7 +1456,14 @@ public class LinearLayout extends ViewGroup { childLeft += ((mRight - mLeft) - mTotalLength) / 2; break; } - } + } + + final boolean showDividerMiddle = + (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE; + + if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) { + childLeft += mDividerWidth; + } for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); @@ -1282,6 +1529,10 @@ public class LinearLayout extends ViewGroup { childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); + if (showDividerMiddle) { + childLeft += mDividerWidth; + } + i += getChildrenSkipCount(child, i); } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index fd4f950..3703846 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1102,6 +1102,7 @@ public class ListView extends AbsListView { int childWidth = 0; int childHeight = 0; + int childState = 0; mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || @@ -1112,6 +1113,7 @@ public class ListView extends AbsListView { childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); + childState = combineMeasuredStates(childState, child.getMeasuredState()); if (recycleOnMeasure() && mRecycler.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { @@ -1122,6 +1124,8 @@ public class ListView extends AbsListView { if (widthMode == MeasureSpec.UNSPECIFIED) { widthSize = mListPadding.left + mListPadding.right + childWidth + getVerticalScrollbarWidth(); + } else { + widthSize |= (childState&MEASURED_STATE_MASK); } if (heightMode == MeasureSpec.UNSPECIFIED) { @@ -1134,7 +1138,7 @@ public class ListView extends AbsListView { heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); } - setMeasuredDimension(widthSize, heightSize); + setMeasuredDimension(widthSize , heightSize); mWidthMeasureSpec = widthMeasureSpec; } @@ -3196,7 +3200,8 @@ public class ListView extends AbsListView { } mDivider = divider; mDividerIsOpaque = divider == null || divider.getOpacity() == PixelFormat.OPAQUE; - requestLayoutIfNecessary(); + requestLayout(); + invalidate(); } /** @@ -3214,7 +3219,8 @@ public class ListView extends AbsListView { */ public void setDividerHeight(int height) { mDividerHeight = height; - requestLayoutIfNecessary(); + requestLayout(); + invalidate(); } /** diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java index cd81e31..b9e59d6 100644 --- a/core/java/android/widget/OverScroller.java +++ b/core/java/android/widget/OverScroller.java @@ -17,9 +17,12 @@ package android.widget; import android.content.Context; -import android.graphics.Interpolator; +import android.hardware.SensorManager; +import android.util.FloatMath; +import android.util.Log; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; /** * This class encapsulates scrolling with the ability to overshoot the bounds @@ -27,65 +30,51 @@ import android.view.animation.AnimationUtils; * {@link android.widget.Scroller} in most cases. */ public class OverScroller { - int mMode; + private int mMode; - private final MagneticOverScroller mScrollerX; - private final MagneticOverScroller mScrollerY; + private final SplineOverScroller mScrollerX; + private final SplineOverScroller mScrollerY; - private float mDeceleration; - private final float mPpi; - private final boolean mFlywheel; + private final Interpolator mInterpolator; - private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9)); - private static float ALPHA = 800; // pixels / seconds - private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance) - private static float END_TENSION = 1.0f - START_TENSION; - private static final int NB_SAMPLES = 100; - private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; - private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; + private final boolean mFlywheel; private static final int DEFAULT_DURATION = 250; private static final int SCROLL_MODE = 0; private static final int FLING_MODE = 1; - static { - float x_min = 0.0f; - float y_min = 0.0f; - for (int i = 0; i < NB_SAMPLES; i++) { - final float alpha = (float) i / NB_SAMPLES; - { - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x; - if (Math.abs(tx - alpha) < 1E-5) break; - if (tx > alpha) x_max = x; - else x_min = x; - } - SPLINE_POSITION[i] = coef + x * x * x; - } + /** + * Creates an OverScroller with a viscous fluid scroll interpolator. + * @param context + */ + public OverScroller(Context context) { + this(context, null); + } - { - float y_max = 1.0f; - float y, dy, coef; - while (true) { - y = y_min + (y_max - y_min) / 2.0f; - coef = 3.0f * y * (1.0f - y); - dy = coef + y * y * y; - if (Math.abs(dy - alpha) < 1E-5) break; - if (dy > alpha) y_max = y; - else y_min = y; - } - SPLINE_TIME[i] = coef * ((1.0f - y) * START_TENSION + y * END_TENSION) + y * y * y; - } - } - SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; + /** + * Creates an OverScroller with default edge bounce coefficients and flywheel enabled. + * @param context The context of this application. + * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will + * be used. + */ + public OverScroller(Context context, Interpolator interpolator) { + this(context, interpolator, SplineOverScroller.DEFAULT_BOUNCE_COEFFICIENT, + SplineOverScroller.DEFAULT_BOUNCE_COEFFICIENT); } - public OverScroller(Context context) { - this(context, null, 0.f, 0.f, true); + /** + * Creates an OverScroller with flywheel enabled. + * @param context The context of this application. + * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will + * be used. + * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the + * velocity which is preserved in the bounce when the horizontal edge is reached. A null value + * means no bounce. + * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. + */ + public OverScroller(Context context, Interpolator interpolator, + float bounceCoefficientX, float bounceCoefficientY) { + this(context, interpolator, bounceCoefficientX, bounceCoefficientY, true); } /** @@ -97,20 +86,21 @@ public class OverScroller { * velocity which is preserved in the bounce when the horizontal edge is reached. A null value * means no bounce. * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. + * @param flywheel If true, successive fling motions will keep on increasing scroll speed. */ public OverScroller(Context context, Interpolator interpolator, float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) { + mInterpolator = interpolator; mFlywheel = flywheel; - mPpi = context.getResources().getDisplayMetrics().density * 160.0f; - mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); - mScrollerX = new MagneticOverScroller(); - mScrollerY = new MagneticOverScroller(); + mScrollerX = new SplineOverScroller(); + mScrollerY = new SplineOverScroller(); + + SplineOverScroller.initFromContext(context); mScrollerX.setBounceCoefficient(bounceCoefficientX); mScrollerY.setBounceCoefficient(bounceCoefficientY); } - /** * The amount of friction applied to flings. The default value * is {@link ViewConfiguration#getScrollFriction}. @@ -119,14 +109,8 @@ public class OverScroller { * friction. */ public final void setFriction(float friction) { - mDeceleration = computeDeceleration(friction); - } - - private float computeDeceleration(float friction) { - return 9.81f // g (m/s^2) - * 39.37f // inch/meter - * mPpi // pixels per inch - * friction; + mScrollerX.setFriction(friction); + mScrollerY.setFriction(friction); } /** @@ -178,7 +162,7 @@ public class OverScroller { public float getCurrVelocity() { float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity; squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity; - return (float) Math.sqrt(squaredNorm); + return FloatMath.sqrt(squaredNorm); } /** @@ -307,7 +291,11 @@ public class OverScroller { if (elapsedTime < duration) { float q = (float) (elapsedTime) / duration; - q = Scroller.viscousFluid(q); + if (mInterpolator == null) { + q = Scroller.viscousFluid(q); + } else { + q = mInterpolator.getInterpolation(q); + } mScrollerX.updateScroll(q); mScrollerY.updateScroll(q); @@ -496,9 +484,9 @@ public class OverScroller { */ public boolean isOverScrolled() { return ((!mScrollerX.mFinished && - mScrollerX.mState != MagneticOverScroller.TO_EDGE) || + mScrollerX.mState != SplineOverScroller.TO_EDGE) || (!mScrollerY.mFinished && - mScrollerY.mState != MagneticOverScroller.TO_EDGE)); + mScrollerY.mState != SplineOverScroller.TO_EDGE)); } /** @@ -536,60 +524,126 @@ public class OverScroller { Math.signum(yvel) == Math.signum(dy); } - class MagneticOverScroller { + static class SplineOverScroller { // Initial position - int mStart; + private int mStart; // Current position - int mCurrentPosition; + private int mCurrentPosition; // Final position - int mFinal; + private int mFinal; // Initial velocity - int mVelocity; + private int mVelocity; // Current velocity - float mCurrVelocity; + private float mCurrVelocity; // Constant current deceleration - float mDeceleration; + private float mDeceleration; // Animation starting time, in system milliseconds - long mStartTime; + private long mStartTime; // Animation duration, in milliseconds - int mDuration; + private int mDuration; // Duration to complete spline component of animation - int mSplineDuration; + private int mSplineDuration; // Distance to travel along spline animation - int mSplineDistance; + private int mSplineDistance; // Whether the animation is currently in progress - boolean mFinished; + private boolean mFinished; - private static final int TO_EDGE = 0; - private static final int TO_BOUNDARY = 1; - private static final int TO_BOUNCE = 2; + // The allowed overshot distance before boundary is reached. + private int mOver; + // Fling friction + private float mFlingFriction = ViewConfiguration.getScrollFriction(); + + // Proportion of velocity preserved at the end of a bounce animation. + private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT; + + // Current state of the animation. private int mState = TO_EDGE; - // The allowed overshot distance before boundary is reached. - private int mOver; + // Constant gravity value, used in the deceleration phase. + private static final float GRAVITY = 2000.0f; + + // A device specific coefficient adjusted to physical values. + private static float PHYSICAL_COEF; + + private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9)); + private static final float INFLEXION = 0.3f; // Tension lines cross at (INFLEXION, 1) + private static final float START_TENSION = 0.7f; + private static final float END_TENSION = 0.8f; + private static final float P1 = START_TENSION * INFLEXION; + private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION); + + private static final int NB_SAMPLES = 100; + private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; + private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; + + private static final int TO_EDGE = 0; + private static final int TO_BOUNDARY = 1; + private static final int TO_BOUNCE = 2; // If the velocity is smaller than this value, no bounce is triggered - // when the edge limits are reached (would result in a zero pixels - // displacement anyway). - private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f; //Float.MAX_VALUE;//140.0f; + // when the edge limits are reached. + private static final float MINIMUM_VELOCITY_FOR_BOUNCE = Float.MAX_VALUE;//140.0f; // Proportion of the velocity that is preserved when the edge is reached. - private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.36f; + private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f; - private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT; + static { + float x_min = 0.0f; + float y_min = 0.0f; + for (int i = 0; i < NB_SAMPLES; i++) { + final float alpha = (float) i / NB_SAMPLES; - MagneticOverScroller() { + float x_max = 1.0f; + float x, tx, coef; + while (true) { + x = x_min + (x_max - x_min) / 2.0f; + coef = 3.0f * x * (1.0f - x); + tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x; + if (Math.abs(tx - alpha) < 1E-5) break; + if (tx > alpha) x_max = x; + else x_min = x; + } + SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x; + + float y_max = 1.0f; + float y, dy; + while (true) { + y = y_min + (y_max - y_min) / 2.0f; + coef = 3.0f * y * (1.0f - y); + dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y; + if (Math.abs(dy - alpha) < 1E-5) break; + if (dy > alpha) y_max = y; + else y_min = y; + } + SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y; + } + SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; + } + + static void initFromContext(Context context) { + final float ppi = context.getResources().getDisplayMetrics().density * 160.0f; + PHYSICAL_COEF = SensorManager.GRAVITY_EARTH // g (m/s^2) + * 39.37f // inch/meter + * ppi + * 0.84f; // look and feel tuning + } + + void setFriction(float friction) { + mFlingFriction = friction; + } + + SplineOverScroller() { mFinished = true; } @@ -600,18 +654,18 @@ public class OverScroller { /* * Get a signed deceleration that will reduce the velocity. */ - float getDeceleration(int velocity) { - return velocity > 0 ? -OverScroller.this.mDeceleration : OverScroller.this.mDeceleration; + static private float getDeceleration(int velocity) { + return velocity > 0 ? -GRAVITY : GRAVITY; } /* * Modifies mDuration to the duration it takes to get from start to newFinal using the * spline interpolation. The previous duration was needed to get to oldFinal. */ - void adjustDuration(int start, int oldFinal, int newFinal) { + private void adjustDuration(int start, int oldFinal, int newFinal) { final int oldDistance = oldFinal - start; final int newDistance = newFinal - start; - final float x = (float) Math.abs((float) newDistance / oldDistance); + final float x = Math.abs((float) newDistance / oldDistance); final int index = (int) (NB_SAMPLES * x); if (index < NB_SAMPLES) { final float x_inf = (float) index / NB_SAMPLES; @@ -619,7 +673,6 @@ public class OverScroller { final float t_inf = SPLINE_TIME[index]; final float t_sup = SPLINE_TIME[index + 1]; final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf); - mDuration *= timeCoef; } } @@ -696,7 +749,7 @@ public class OverScroller { mCurrVelocity = mVelocity = velocity; mDuration = mSplineDuration = 0; mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mStart = start; + mCurrentPosition = mStart = start; if (start > max || start < min) { startAfterEdge(start, min, max, velocity); @@ -707,10 +760,8 @@ public class OverScroller { double totalDistance = 0.0; if (velocity != 0) { - final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA); - // Duration are expressed in milliseconds - mDuration = mSplineDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0))); - totalDistance = (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l)); + mDuration = mSplineDuration = getSplineFlingDuration(velocity); + totalDistance = getSplineFlingDistance(velocity); } mSplineDistance = (int) (totalDistance * Math.signum(velocity)); @@ -728,6 +779,23 @@ public class OverScroller { } } + private double getSplineDeceleration(int velocity) { + return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * PHYSICAL_COEF)); + } + + private double getSplineFlingDistance(int velocity) { + final double l = getSplineDeceleration(velocity); + final double decelMinusOne = DECELERATION_RATE - 1.0; + return mFlingFriction * PHYSICAL_COEF * Math.exp(DECELERATION_RATE / decelMinusOne * l); + } + + /* Returns the duration, expressed in milliseconds */ + private int getSplineFlingDuration(int velocity) { + final double l = getSplineDeceleration(velocity); + final double decelMinusOne = DECELERATION_RATE - 1.0; + return (int) (1000.0 * Math.exp(l / decelMinusOne)); + } + private void fitOnBounceCurve(int start, int end, int velocity) { // Simulate a bounce that started from edge final float durationToApex = - velocity / mDeceleration; @@ -748,6 +816,7 @@ public class OverScroller { private void startAfterEdge(int start, int min, int max, int velocity) { if (start > min && start < max) { + Log.e("OverScroller", "startAfterEdge called from a valid position"); mFinished = true; return; } @@ -759,9 +828,7 @@ public class OverScroller { // Will result in a bounce or a to_boundary depending on velocity. startBounceAfterEdge(start, edge, velocity); } else { - final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA); - final double totalDistance = - (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l)); + final double totalDistance = getSplineFlingDistance(velocity); if (totalDistance > Math.abs(overDistance)) { fling(start, velocity, positive ? min : start, positive ? start : max, mOver); } else { @@ -771,11 +838,13 @@ public class OverScroller { } void notifyEdgeReached(int start, int end, int over) { - mOver = over; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - // We were in fling/scroll mode before: current velocity is such that distance to edge - // is increasing. Ensures that startAfterEdge will not start a new fling. - startAfterEdge(start, end, end, (int) mCurrVelocity); + if (mState == TO_EDGE) { + mOver = over; + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + // We were in fling/scroll mode before: current velocity is such that distance to + // edge is increasing. Ensures that startAfterEdge will not start a new fling. + startAfterEdge(start, end, end, (int) mCurrVelocity); + } } private void onEdgeReached() { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 63fb3e9..85ca5f3 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -891,8 +891,8 @@ public class ProgressBar extends View { dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; - setMeasuredDimension(resolveSize(dw, widthMeasureSpec), - resolveSize(dh, heightMeasureSpec)); + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), + resolveSizeAndState(dh, heightMeasureSpec, 0)); } @Override diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java index 28499d0..9e6ff4b 100644 --- a/core/java/android/widget/RatingBar.java +++ b/core/java/android/widget/RatingBar.java @@ -280,7 +280,8 @@ public class RatingBar extends AbsSeekBar { // TODO: Once ProgressBar's TODOs are gone, this can be done more // cleanly than mSampleTile final int width = mSampleTile.getWidth() * mNumStars; - setMeasuredDimension(resolveSize(width, widthMeasureSpec), mMeasuredHeight); + setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0), + getMeasuredHeight()); } } diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 029aebf..b296b77 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -18,8 +18,6 @@ package android.widget; import static android.widget.SuggestionsAdapter.getColumnString; -import com.android.internal.R; - import android.app.PendingIntent; import android.app.SearchManager; import android.app.SearchableInfo; @@ -29,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.Cursor; @@ -50,6 +49,8 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.R; + import java.util.WeakHashMap; /** @@ -84,7 +85,7 @@ public class SearchView extends LinearLayout { private View mCloseButton; private View mSearchEditFrame; private View mVoiceButton; - private AutoCompleteTextView mQueryTextView; + private SearchAutoComplete mQueryTextView; private boolean mSubmitButtonEnabled; private CharSequence mQueryHint; private boolean mQueryRefinement; @@ -181,7 +182,9 @@ public class SearchView extends LinearLayout { inflater.inflate(R.layout.search_view, this, true); mSearchButton = findViewById(R.id.search_button); - mQueryTextView = (AutoCompleteTextView) findViewById(R.id.search_src_text); + mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text); + mQueryTextView.setSearchView(this); + mSearchEditFrame = findViewById(R.id.search_edit_frame); mSubmitButton = findViewById(R.id.search_go_btn); mCloseButton = findViewById(R.id.search_close_btn); @@ -196,6 +199,7 @@ public class SearchView extends LinearLayout { mQueryTextView.setOnEditorActionListener(mOnEditorActionListener); mQueryTextView.setOnItemClickListener(mOnItemClickListener); mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener); + mQueryTextView.setOnKeyListener(mTextKeyListener); // Inform any listener of focus changes mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() { @@ -558,6 +562,148 @@ public class SearchView extends LinearLayout { return super.onKeyDown(keyCode, event); } + /** + * React to the user typing "enter" or other hardwired keys while typing in + * the search box. This handles these special keys while the edit box has + * focus. + */ + View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + // guard against possible race conditions + if (mSearchable == null) { + return false; + } + + if (DBG) { + Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: " + + mQueryTextView.getListSelection()); + } + + // If a suggestion is selected, handle enter, search key, and action keys + // as presses on the selected suggestion + if (mQueryTextView.isPopupShowing() + && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) { + return onSuggestionsKey(v, keyCode, event); + } + + // If there is text in the query box, handle enter, and action keys + // The search key is handled by the dialog's onKeyDown(). + if (!mQueryTextView.isEmpty()) { + if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { + v.cancelLongPress(); + + // Launch as a regular search. + launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText() + .toString()); + return true; + } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); + if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { + launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView + .getText().toString()); + return true; + } + } + } + return false; + } + }; + + /** + * React to the user typing while in the suggestions list. First, check for + * action keys. If not handled, try refocusing regular characters into the + * EditText. + */ + private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { + // guard against possible race conditions (late arrival after dismiss) + if (mSearchable == null) { + return false; + } + if (mSuggestionsAdapter == null) { + return false; + } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + + // First, check for enter or search (both of which we'll treat as a + // "click") + if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) { + int position = mQueryTextView.getListSelection(); + return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); + } + + // Next, check for left/right moves, which we use to "return" the + // user to the edit view + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + // give "focus" to text editor, with cursor at the beginning if + // left key, at end if right key + // TODO: Reverse left/right for right-to-left languages, e.g. + // Arabic + int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView + .length(); + mQueryTextView.setSelection(selPoint); + mQueryTextView.setListSelection(0); + mQueryTextView.clearListSelection(); + mQueryTextView.ensureImeVisible(true); + + return true; + } + + // Next, check for an "up and out" move + if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) { + // TODO: restoreUserQuery(); + // let ACTV complete the move + return false; + } + + // Next, check for an "action key" + SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); + if ((actionKey != null) + && ((actionKey.getSuggestActionMsg() != null) || (actionKey + .getSuggestActionMsgColumn() != null))) { + // launch suggestion using action key column + int position = mQueryTextView.getListSelection(); + if (position != ListView.INVALID_POSITION) { + Cursor c = mSuggestionsAdapter.getCursor(); + if (c.moveToPosition(position)) { + final String actionMsg = getActionKeyMessage(c, actionKey); + if (actionMsg != null && (actionMsg.length() > 0)) { + return onItemClicked(position, keyCode, actionMsg); + } + } + } + } + } + return false; + } + + /** + * For a given suggestion and a given cursor row, get the action message. If + * not provided by the specific row/column, also check for a single + * definition (for the action key). + * + * @param c The cursor providing suggestions + * @param actionKey The actionkey record being examined + * + * @return Returns a string, or null if no action key message for this + * suggestion + */ + private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) { + String result = null; + // check first in the cursor data, for a suggestion-specific message + final String column = actionKey.getSuggestActionMsgColumn(); + if (column != null) { + result = SuggestionsAdapter.getColumnString(c, column); + } + // If the cursor didn't give us a message, see if there's a single + // message defined + // for the actionkey (for all suggestions) + if (result == null) { + result = actionKey.getSuggestActionMsg(); + } + return result; + } + private void updateQueryHint() { if (mQueryHint != null) { mQueryTextView.setHint(mQueryHint); @@ -710,20 +856,34 @@ public class SearchView extends LinearLayout { } } + private boolean onItemClicked(int position, int actionKey, String actionMsg) { + if (mOnSuggestionListener == null + || !mOnSuggestionListener.onSuggestionClicked(position)) { + launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); + setImeVisibility(false); + dismissSuggestions(); + return true; + } + return false; + } + + private boolean onItemSelected(int position) { + if (mOnSuggestionListener == null + || !mOnSuggestionListener.onSuggestionSelected(position)) { + rewriteQueryFromSuggestion(position); + return true; + } + return false; + } + private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { /** * Implements OnItemClickListener */ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (DBG) - Log.d(LOG_TAG, "onItemClick() position " + position); - if (mOnSuggestionListener == null - || !mOnSuggestionListener.onSuggestionClicked(position)) { - launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); - setImeVisibility(false); - dismissSuggestions(); - } + if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); + onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); } }; @@ -733,14 +893,8 @@ public class SearchView extends LinearLayout { * Implements OnItemSelectedListener */ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - if (DBG) - Log.d(LOG_TAG, "onItemSelected() position " + position); - // A suggestion has been selected, rewrite the query if possible, - // otherwise the restore the original query. - if (mOnSuggestionListener == null - || !mOnSuggestionListener.onSuggestionSelected(position)) { - rewriteQueryFromSuggestion(position); - } + if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); + SearchView.this.onItemSelected(position); } /** @@ -1003,6 +1157,11 @@ public class SearchView extends LinearLayout { } } + static boolean isLandscapeMode(Context context) { + return context.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; + } + /** * Callback to watch the text field for empty/non-empty */ @@ -1018,4 +1177,93 @@ public class SearchView extends LinearLayout { public void afterTextChanged(Editable s) { } }; + + /** + * Local subclass for AutoCompleteTextView. + * @hide + */ + public static class SearchAutoComplete extends AutoCompleteTextView { + + private int mThreshold; + private SearchView mSearchView; + + public SearchAutoComplete(Context context) { + super(context); + mThreshold = getThreshold(); + } + + public SearchAutoComplete(Context context, AttributeSet attrs) { + super(context, attrs); + mThreshold = getThreshold(); + } + + public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mThreshold = getThreshold(); + } + + void setSearchView(SearchView searchView) { + mSearchView = searchView; + } + + @Override + public void setThreshold(int threshold) { + super.setThreshold(threshold); + mThreshold = threshold; + } + + /** + * Returns true if the text field is empty, or contains only whitespace. + */ + private boolean isEmpty() { + return TextUtils.getTrimmedLength(getText()) == 0; + } + + /** + * We override this method to avoid replacing the query box text when a + * suggestion is clicked. + */ + @Override + protected void replaceText(CharSequence text) { + } + + /** + * We override this method to avoid an extra onItemClick being called on + * the drop-down's OnItemClickListener by + * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is + * clicked with the trackball. + */ + @Override + public void performCompletion() { + } + + /** + * We override this method to be sure and show the soft keyboard if + * appropriate when the TextView has focus. + */ + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + + if (hasWindowFocus) { + InputMethodManager inputManager = (InputMethodManager) getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.showSoftInput(this, 0); + // If in landscape mode, then make sure that + // the ime is in front of the dropdown. + if (isLandscapeMode(getContext())) { + ensureImeVisible(true); + } + } + } + + /** + * We override this method so that we can allow a threshold of zero, + * which ACTV does not. + */ + @Override + public boolean enoughToFilter() { + return mThreshold <= 0 || super.enoughToFilter(); + } + } } diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 60422ae..006473e 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -357,7 +357,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { // Position vertically based on gravity setting int childTop = mSpinnerPadding.top - + ((mMeasuredHeight - mSpinnerPadding.bottom - + + ((getMeasuredHeight() - mSpinnerPadding.bottom - mSpinnerPadding.top - child.getMeasuredHeight()) / 2); int childBottom = childTop + child.getMeasuredHeight(); diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index aee48c6..64fb985 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -281,7 +281,7 @@ public class StackView extends AdapterViewAnimator { } private void transformViewAtIndex(int index, View view) { - float maxPerpectiveShift = mMeasuredHeight * PERSPECTIVE_SHIFT_FACTOR; + float maxPerpectiveShift = getMeasuredHeight() * PERSPECTIVE_SHIFT_FACTOR; index = mMaxNumActiveViews - index - 1; if (index == mMaxNumActiveViews - 1) index--; @@ -297,7 +297,7 @@ public class StackView extends AdapterViewAnimator { int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1; float perspectiveTranslation = -stackDirection * r * maxPerpectiveShift; float scaleShiftCorrection = stackDirection * (1 - scale) * - (mMeasuredHeight * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f); + (getMeasuredHeight() * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f); float transY = perspectiveTranslation + scaleShiftCorrection; PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY); @@ -897,8 +897,8 @@ public class StackView extends AdapterViewAnimator { private void measureChildren() { final int count = getChildCount(); - final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; - final int childHeight = Math.round(mMeasuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR)) + final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight; + final int childHeight = Math.round(getMeasuredHeight()*(1-PERSPECTIVE_SHIFT_FACTOR)) - mPaddingTop - mPaddingBottom; for (int i = 0; i < count; i++) { @@ -925,17 +925,33 @@ public class StackView extends AdapterViewAnimator { Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop + mPaddingBottom : 0; } else if (heightSpecMode == MeasureSpec.AT_MOST) { - heightSpecSize = haveChildRefSize ? Math.min( - Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop + - mPaddingBottom, heightSpecSize) : 0; + if (haveChildRefSize) { + int height = Math.round(mReferenceChildHeight * (1 + factor)) + + mPaddingTop + mPaddingBottom; + if (height <= heightSpecSize) { + heightSpecSize = height; + } else { + heightSpecSize |= MEASURED_STATE_TOO_SMALL; + } + } else { + heightSpecSize = 0; + } } if (widthSpecMode == MeasureSpec.UNSPECIFIED) { widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft + mPaddingRight : 0; } else if (heightSpecMode == MeasureSpec.AT_MOST) { - widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft + - mPaddingRight, widthSpecSize) : 0; + if (haveChildRefSize) { + int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight; + if (width <= widthSpecSize) { + widthSpecSize = width; + } else { + widthSpecSize |= MEASURED_STATE_TOO_SMALL; + } + } else { + widthSpecSize = 0; + } } setMeasuredDimension(widthSpecSize, heightSpecSize); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 73a8c66..4223040 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -359,10 +359,9 @@ public class Switch extends CompoundButton { mSwitchHeight = switchHeight; super.onMeasure(widthMeasureSpec, heightMeasureSpec); - final int measuredWidth = getMeasuredWidth(); final int measuredHeight = getMeasuredHeight(); if (measuredHeight < switchHeight) { - setMeasuredDimension(measuredWidth, switchHeight); + setMeasuredDimension(getMeasuredWidthAndState(), switchHeight); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 57af643..ced8e9b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -89,6 +89,7 @@ import android.util.FloatMath; import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; +import android.view.ActionMode.Callback; import android.view.ContextMenu; import android.view.DragEvent; import android.view.Gravity; @@ -220,8 +221,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mTemporaryDetach; private boolean mDispatchTemporaryDetach; - private boolean mEatTouchRelease = false; - private boolean mScrolled = false; + private boolean mDiscardNextActionUp = false; + private boolean mIgnoreActionUpEvent = false; private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); @@ -314,6 +315,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Drawable mSelectHandleCenter; private int mLastDownPositionX, mLastDownPositionY; + private Callback mCustomSelectionActionModeCallback; /* * Kick-start the font cache for the zygote process (to pay the cost of @@ -6869,7 +6871,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (mSelectAllOnFocus) { - Selection.setSelection((Spannable) mText, 0, mText.length()); + selectAll(); } mTouchFocusSelected = true; @@ -7002,26 +7004,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private void onTapUpEvent(int prevStart, int prevEnd) { - final int start = getSelectionStart(); - final int end = getSelectionEnd(); - - if (start == end) { - if (start >= prevStart && start < prevEnd) { - // Restore previous selection - Selection.setSelection((Spannable)mText, prevStart, prevEnd); - return; - } else { - // Tapping outside stops selection mode, if any - stopSelectionActionMode(); - - if (hasInsertionController()) { - getInsertionController().show(); - } - } - } - } - class CommitSelectionReceiver extends ResultReceiver { private final int mPrevStart, mPrevEnd; @@ -7066,7 +7048,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Reset this state; it will be re-set if super.onTouchEvent // causes focus to move to the view. mTouchFocusSelected = false; - mScrolled = false; + mIgnoreActionUpEvent = false; } final boolean superResult = super.onTouchEvent(event); @@ -7076,8 +7058,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * move the selection away from whatever the menu action was * trying to affect. */ - if (mEatTouchRelease && action == MotionEvent.ACTION_UP) { - mEatTouchRelease = false; + if (mDiscardNextActionUp && action == MotionEvent.ACTION_UP) { + mDiscardNextActionUp = false; return superResult; } @@ -7106,7 +7088,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mSelectionModifierCursorController.updatePosition(); } } - if (action == MotionEvent.ACTION_UP && !mScrolled && isFocused()) { + if (action == MotionEvent.ACTION_UP && !mIgnoreActionUpEvent && isFocused()) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -7117,13 +7099,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (!mTextIsSelectable) { - // Selection in read-only text should not bring up the IME. + // Show the IME, except when selecting in read-only text. handled |= imm.showSoftInput(this, 0, csr) && (csr != null); } - // Cannot be done by CommitSelectionReceiver, which might not always be called, - // for instance when dealing with an ExtractEditText. - onTapUpEvent(oldSelStart, oldSelEnd); + stopSelectionActionMode(); + if (hasInsertionController()) { + getInsertionController().show(); + } } } @@ -7181,7 +7164,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void cancelLongPress() { super.cancelLongPress(); - mScrolled = true; + mIgnoreActionUpEvent = true; } @Override @@ -7420,10 +7403,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - if (mText.length() > 0 && hasSelection()) { - if (mText instanceof Editable && mInput != null) { - return true; - } + if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mInput != null) { + return true; } return false; @@ -7543,13 +7524,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return (int) (range & 0x00000000FFFFFFFFL); } + private void selectAll() { + Selection.setSelection((Spannable) mText, 0, mText.length()); + } + private void selectCurrentWord() { if (hasPasswordTransformationMethod()) { // selectCurrentWord is not available on a password field and would return an // arbitrary 10-charater selection around pressed position. Select all instead. // Note that cut/copy menu entries are not available for passwords. // This is however useful to delete or paste to replace the entire content. - Selection.setSelection((Spannable) mText, 0, mText.length()); + selectAll(); return; } @@ -7815,7 +7800,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean performLongClick() { if (super.performLongClick()) { - mEatTouchRelease = true; + mDiscardNextActionUp = true; return true; } @@ -7826,7 +7811,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stopSelectionActionMode(); Selection.setSelection((Spannable)mText, offset); getInsertionController().show(0); - mEatTouchRelease = true; + mDiscardNextActionUp = true; return true; } @@ -7837,7 +7822,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int end = getSelectionEnd(); CharSequence selectedText = mTransformed.subSequence(start, end); ClipData data = ClipData.newPlainText(null, null, selectedText); - startDrag(data, getTextThumbnailBuilder(selectedText), false); + startDrag(data, getTextThumbnailBuilder(selectedText), false, null); mDragSourcePositions = packRangeInLong(start, end); stopSelectionActionMode(); } else { @@ -7845,13 +7830,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener getSelectionController().show(); } performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - mEatTouchRelease = true; + mDiscardNextActionUp = true; return true; } if (startSelectionActionMode()) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - mEatTouchRelease = true; + mDiscardNextActionUp = true; return true; } @@ -7881,16 +7866,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Provides the callback used to start a selection action mode. + * If provided, this ActionMode.Callback will be used to create the ActionMode when text + * selection is initiated in this View. * - * @return A callback instance that will be used to start selection mode, or null if selection - * mode is not available. + * The standard implementation populates the menu with a subset of Select All, Cut, Copy and + * Paste actions, depending on what this View supports. + * + * A custom implementation can add new entries in the default menu in its + * {@link ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The default actions + * can also be removed from the menu using {@link Menu#removeItem(int)} and passing + * {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy} or + * {@link android.R.id#paste} ids as parameters. + * + * Action click events should be handled by the custom implementation of + * {@link ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}. + * + * Note that text selection mode is not started when a TextView receives focus and the + * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in + * that case, to allow for quick replacement. */ - private ActionMode.Callback getActionModeCallback() { - if (canSelectText()) { - return new SelectionActionModeCallback(); - } - return null; + public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) { + mCustomSelectionActionModeCallback = actionModeCallback; + } + + /** + * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null. + * + * @return The current custom selection callback. + */ + public ActionMode.Callback getCustomSelectionActionModeCallback() { + return mCustomSelectionActionModeCallback; } /** @@ -7903,7 +7908,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - ActionMode.Callback actionModeCallback = getActionModeCallback(); + selectCurrentWord(); + ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); if (actionModeCallback != null) { mSelectionActionMode = startActionMode(actionModeCallback); return mSelectionActionMode != null; @@ -7969,6 +7975,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sLastCutOrCopyTime = SystemClock.uptimeMillis(); } + /** + * An ActionMode Callback class that is used to provide actions while in text selection mode. + * + * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending + * on which of these this TextView supports. + */ private class SelectionActionModeCallback implements ActionMode.Callback { @Override @@ -7987,48 +7999,47 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle)); mode.setSubtitle(null); - boolean atLeastOne = false; - if (canSelectText()) { - selectCurrentWord(); - menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll). setAlphabeticShortcut('a'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - atLeastOne = true; } if (canCut()) { menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut). - setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCutDrawable, 0)). + setIcon(styledAttributes.getResourceId( + R.styleable.Theme_actionModeCutDrawable, 0)). setAlphabeticShortcut('x'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - atLeastOne = true; } if (canCopy()) { menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy). - setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCopyDrawable, 0)). + setIcon(styledAttributes.getResourceId( + R.styleable.Theme_actionModeCopyDrawable, 0)). setAlphabeticShortcut('c'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - atLeastOne = true; } if (canPaste()) { menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste). - setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModePasteDrawable, 0)). + setIcon(styledAttributes.getResourceId( + R.styleable.Theme_actionModePasteDrawable, 0)). setAlphabeticShortcut('v'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - atLeastOne = true; } styledAttributes.recycle(); - if (atLeastOne) { + if (mCustomSelectionActionModeCallback != null) { + mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu); + } + + if (menu.hasVisibleItems() || mode.getCustomView() != null) { getSelectionController().show(); return true; } else { @@ -8038,15 +8049,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (mCustomSelectionActionModeCallback != null) { + return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); + } return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (mCustomSelectionActionModeCallback != null && + mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { + return true; + } + final int itemId = item.getItemId(); if (itemId == ID_SELECT_ALL) { - Selection.setSelection((Spannable) mText, 0, mText.length()); + selectAll(); // Update controller positions after selection change. if (hasSelectionController()) { getSelectionController().show(); @@ -8089,6 +8108,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onDestroyActionMode(ActionMode mode) { + if (mCustomSelectionActionModeCallback != null) { + mCustomSelectionActionModeCallback.onDestroyActionMode(mode); + } Selection.setSelection((Spannable) mText, getSelectionStart()); hideSelectionModifierCursorController(); mSelectionActionMode = null; @@ -8763,7 +8785,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Double tap detection long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + if (duration <= ViewConfiguration.getDoubleTapTimeout() && + isPositionOnText(x, y)) { final int deltaX = x - mPreviousTapPositionX; final int deltaY = y - mPreviousTapPositionY; final int distanceSquared = deltaX * deltaX + deltaY * deltaY; @@ -8772,6 +8795,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int slopSquared = doubleTapSlop * doubleTapSlop; if (distanceSquared < slopSquared) { startSelectionActionMode(); + mDiscardNextActionUp = true; } } @@ -8941,66 +8965,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextView.this.requestFocus(); return true; - case DragEvent.ACTION_DRAG_LOCATION: { + case DragEvent.ACTION_DRAG_LOCATION: final int offset = getOffset((int) event.getX(), (int) event.getY()); Selection.setSelection((Spannable)mText, offset); return true; - } - - case DragEvent.ACTION_DROP: { - StringBuilder content = new StringBuilder(""); - ClipData clipData = event.getClipData(); - final int itemCount = clipData.getItemCount(); - for (int i=0; i < itemCount; i++) { - Item item = clipData.getItem(i); - content.append(item.coerceToText(TextView.this.mContext)); - } - - final int offset = getOffset((int) event.getX(), (int) event.getY()); - - if (mDragSourcePositions != -1) { - final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions); - final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions); - if (offset >= dragSourceStart && offset < dragSourceEnd) { - // A drop inside the original selection discards the drop. - return true; - } - } - - final int originalLength = mText.length(); - long minMax = prepareSpacesAroundPaste(offset, offset, content); - int min = extractRangeStartFromLong(minMax); - int max = extractRangeEndFromLong(minMax); - - Selection.setSelection((Spannable) mText, max); - ((Editable) mText).replace(min, max, content); - - if (mDragSourcePositions != -1) { - int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions); - int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions); - if (max <= dragSourceStart) { - // Inserting text before selection has shifted positions - final int shift = mText.length() - originalLength; - dragSourceStart += shift; - dragSourceEnd += shift; - } - - // Delete original selection - ((Editable) mText).delete(dragSourceStart, dragSourceEnd); - - // Make sure we do not leave two adjacent spaces. - if ((dragSourceStart == 0 || - Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) && - (dragSourceStart == mText.length() || - Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) { - final int pos = dragSourceStart == mText.length() ? - dragSourceStart - 1 : dragSourceStart; - ((Editable) mText).delete(pos, pos + 1); - } - } + case DragEvent.ACTION_DROP: + onDrop(event); return true; - } case DragEvent.ACTION_DRAG_ENDED: mDragSourcePositions = -1; @@ -9012,6 +8984,59 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private void onDrop(DragEvent event) { + StringBuilder content = new StringBuilder(""); + ClipData clipData = event.getClipData(); + final int itemCount = clipData.getItemCount(); + for (int i=0; i < itemCount; i++) { + Item item = clipData.getItem(i); + content.append(item.coerceToText(TextView.this.mContext)); + } + + final int offset = getOffset((int) event.getX(), (int) event.getY()); + + if (mDragSourcePositions != -1) { + final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions); + final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions); + if (offset >= dragSourceStart && offset < dragSourceEnd) { + // A drop inside the original selection discards the drop. + return; + } + } + + final int originalLength = mText.length(); + long minMax = prepareSpacesAroundPaste(offset, offset, content); + int min = extractRangeStartFromLong(minMax); + int max = extractRangeEndFromLong(minMax); + + Selection.setSelection((Spannable) mText, max); + ((Editable) mText).replace(min, max, content); + + if (mDragSourcePositions != -1) { + int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions); + int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions); + if (max <= dragSourceStart) { + // Inserting text before selection has shifted positions + final int shift = mText.length() - originalLength; + dragSourceStart += shift; + dragSourceEnd += shift; + } + + // Delete original selection + ((Editable) mText).delete(dragSourceStart, dragSourceEnd); + + // Make sure we do not leave two adjacent spaces. + if ((dragSourceStart == 0 || + Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) && + (dragSourceStart == mText.length() || + Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) { + final int pos = dragSourceStart == mText.length() ? + dragSourceStart - 1 : dragSourceStart; + ((Editable) mText).delete(pos, pos + 1); + } + } + } + /** * @return True if this view supports insertion handles. */ diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 20402a3..447a062 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -70,6 +70,10 @@ public class ActionBarImpl extends ActionBar { private ActionMode mActionMode; + private boolean mLastMenuVisibility; + private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = + new ArrayList<OnMenuVisibilityListener>(); + private static final int CONTEXT_DISPLAY_NORMAL = 0; private static final int CONTEXT_DISPLAY_SPLIT = 1; @@ -120,6 +124,26 @@ public class ActionBarImpl extends ActionBar { CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT; } + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.add(listener); + } + + public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.remove(listener); + } + + public void dispatchMenuVisibilityChanged(boolean isVisible) { + if (isVisible == mLastMenuVisibility) { + return; + } + mLastMenuVisibility = isVisible; + + final int count = mMenuVisibilityListeners.size(); + for (int i = 0; i < count; i++) { + mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); + } + } + @Override public void setTitle(int resId) { setTitle(mContext.getString(resId)); @@ -138,11 +162,11 @@ public class ActionBarImpl extends ActionBar { mActionView.setCallback(null); } - public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) { + public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) { setDropdownNavigationMode(adapter, callback, -1); } - public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback, + public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback, int defaultSelectedPosition) { cleanupTabs(); setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE); @@ -516,7 +540,7 @@ public class ActionBarImpl extends ActionBar { public void onMenuModeChange(MenuBuilder menu) { invalidate(); - mUpperContextView.showOverflowMenu(); + mUpperContextView.openOverflowMenu(); } } @@ -627,7 +651,7 @@ public class ActionBarImpl extends ActionBar { } @Override - public void setListNavigationCallbacks(SpinnerAdapter adapter, NavigationCallback callback) { + public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { mActionView.setDropdownAdapter(adapter); mActionView.setCallback(callback); } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index b2810b1..e384320 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -118,6 +118,12 @@ public class AlertController { private int mCheckedItem = -1; + private int mAlertDialogLayout; + private int mListLayout; + private int mMultiChoiceItemLayout; + private int mSingleChoiceItemLayout; + private int mListItemLayout; + private Handler mHandler; View.OnClickListener mButtonHandler = new View.OnClickListener() { @@ -178,6 +184,27 @@ public class AlertController { mDialogInterface = di; mWindow = window; mHandler = new ButtonHandler(di); + + TypedArray a = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.AlertDialog, + com.android.internal.R.attr.alertDialogStyle, 0); + + mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout, + com.android.internal.R.layout.alert_dialog); + mListLayout = a.getResourceId( + com.android.internal.R.styleable.AlertDialog_listLayout, + com.android.internal.R.layout.select_dialog); + mMultiChoiceItemLayout = a.getResourceId( + com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout, + com.android.internal.R.layout.select_dialog_multichoice); + mSingleChoiceItemLayout = a.getResourceId( + com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout, + com.android.internal.R.layout.select_dialog_singlechoice); + mListItemLayout = a.getResourceId( + com.android.internal.R.styleable.AlertDialog_listItemLayout, + com.android.internal.R.layout.select_dialog_item); + + a.recycle(); } static boolean canTextInput(View v) { @@ -210,7 +237,7 @@ public class AlertController { mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); } - mWindow.setContentView(com.android.internal.R.layout.alert_dialog); + mWindow.setContentView(mAlertDialogLayout); setupView(); } @@ -810,13 +837,13 @@ public class AlertController { private void createListView(final AlertController dialog) { final RecycleListView listView = (RecycleListView) - mInflater.inflate(R.layout.select_dialog, null); + mInflater.inflate(dialog.mListLayout, null); ListAdapter adapter; if (mIsMultiChoice) { if (mCursor == null) { adapter = new ArrayAdapter<CharSequence>( - mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) { + mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); @@ -850,7 +877,7 @@ public class AlertController { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.select_dialog_multichoice, + return mInflater.inflate(dialog.mMultiChoiceItemLayout, parent, false); } @@ -858,7 +885,7 @@ public class AlertController { } } else { int layout = mIsSingleChoice - ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item; + ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout; if (mCursor == null) { adapter = (mAdapter != null) ? mAdapter : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems); diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java index c930c57..bfef275 100644 --- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java +++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java @@ -27,6 +27,7 @@ import java.io.PrintStream; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.ThreadFactory; import android.content.pm.PackageInfo; import android.util.Log; @@ -43,27 +44,40 @@ public class SamplingProfilerIntegration { private static final boolean enabled; private static final Executor snapshotWriter; - private static final int samplingProfilerHz; - - /** Whether or not we've created the snapshots dir. */ - private static boolean dirMade = false; + private static final int samplingProfilerMilliseconds; + private static final int samplingProfilerDepth; /** Whether or not a snapshot is being persisted. */ private static final AtomicBoolean pending = new AtomicBoolean(false); static { - samplingProfilerHz = SystemProperties.getInt("persist.sys.profiler_hz", 0); - // Disabling this for now, as it crashes when enabled server-side. So adding - // a new property ("REALLY") for those wanting to test and fix it. - boolean really = SystemProperties.getInt("persist.sys.profiler_hz_REALLY", 0) > 0; - if (samplingProfilerHz > 0 && really) { - snapshotWriter = Executors.newSingleThreadExecutor(); - enabled = true; - Log.i(TAG, "Profiler is enabled. Sampling Profiler Hz: " + samplingProfilerHz); + samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0); + samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4); + if (samplingProfilerMilliseconds > 0) { + File dir = new File(SNAPSHOT_DIR); + dir.mkdirs(); + // the directory needs to be writable to anybody to allow file writing + dir.setWritable(true, false); + // the directory needs to be executable to anybody to allow file creation + dir.setExecutable(true, false); + if (dir.isDirectory()) { + snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() { + public Thread newThread(Runnable r) { + return new Thread(r, TAG); + } + }); + enabled = true; + Log.i(TAG, "Profiling enabled. Sampling interval ms: " + + samplingProfilerMilliseconds); + } else { + snapshotWriter = null; + enabled = true; + Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR); + } } else { snapshotWriter = null; enabled = false; - Log.i(TAG, "Profiler is disabled."); + Log.i(TAG, "Profiling disabled."); } } @@ -85,8 +99,8 @@ public class SamplingProfilerIntegration { } ThreadGroup group = Thread.currentThread().getThreadGroup(); SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group); - INSTANCE = new SamplingProfiler(4, threadSet); // TODO parameter for depth - INSTANCE.start(1000/samplingProfilerHz); + INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet); + INSTANCE.start(samplingProfilerMilliseconds); } /** @@ -106,25 +120,8 @@ public class SamplingProfilerIntegration { if (pending.compareAndSet(false, true)) { snapshotWriter.execute(new Runnable() { public void run() { - if (!dirMade) { - File dir = new File(SNAPSHOT_DIR); - dir.mkdirs(); - // the directory needs to be writable to anybody - dir.setWritable(true, false); - // the directory needs to be executable to anybody - // don't know why yet, but mode 723 would work, while - // mode 722 throws FileNotFoundExecption at line 151 - dir.setExecutable(true, false); - if (new File(SNAPSHOT_DIR).isDirectory()) { - dirMade = true; - } else { - Log.w(TAG, "Creation of " + SNAPSHOT_DIR + " failed."); - pending.set(false); - return; - } - } try { - writeSnapshot(SNAPSHOT_DIR, processName, packageInfo); + writeSnapshotFile(processName, packageInfo); } finally { pending.set(false); } @@ -140,7 +137,7 @@ public class SamplingProfilerIntegration { if (!enabled) { return; } - writeSnapshot("zygote", null); + writeSnapshotFile("zygote", null); INSTANCE.shutdown(); INSTANCE = null; } @@ -148,7 +145,7 @@ public class SamplingProfilerIntegration { /** * pass in PackageInfo to retrieve various values for snapshot header */ - private static void writeSnapshot(String dir, String processName, PackageInfo packageInfo) { + private static void writeSnapshotFile(String processName, PackageInfo packageInfo) { if (!enabled) { return; } @@ -161,7 +158,7 @@ public class SamplingProfilerIntegration { */ long start = System.currentTimeMillis(); String name = processName.replaceAll(":", "."); - String path = dir + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; + String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; PrintStream out = null; try { out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 0891acc..101dd91 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -95,7 +95,7 @@ public class AsyncChannel { * * msg.arg1 == 0 : STATUS_SUCCESSFUL * 1 : STATUS_BINDING_UNSUCCESSFUL - * msg.arg2 == token parameter + * msg.obj == the AsyncChannel * msg.replyTo == dstMessenger if successful */ public static final int CMD_CHANNEL_HALF_CONNECTED = -1; @@ -136,7 +136,7 @@ public class AsyncChannel { * * msg.arg1 == 0 : STATUS_SUCCESSFUL * : All other values signify failure and the channel state is indeterminate - * msg.arg2 == token parameter + * msg.obj == the AsyncChannel * msg.replyTo = messenger disconnecting or null if it was never connected. */ public static final int CMD_CHANNEL_DISCONNECTED = -5; @@ -179,14 +179,12 @@ public class AsyncChannel { * @param dstPackageName is the destination package name * @param dstClassName is the fully qualified class name (i.e. contains * package name) - * @param token unique id for this connection */ private void connectSrcHandlerToPackage( - Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName, - int token) { + Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { if (DBG) log("connect srcHandler to dst Package & class E"); - mConnection = new AsyncChannelConnection(token); + mConnection = new AsyncChannelConnection(); /* Initialize the source information */ mSrcContext = srcContext; @@ -205,7 +203,7 @@ public class AsyncChannel { intent.setClassName(dstPackageName, dstClassName); boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); if (!result) { - replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token); + replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL); } if (DBG) log("connect srcHandler to dst Package & class X result=" + result); @@ -216,7 +214,7 @@ public class AsyncChannel { * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status - * msg.arg2 = token + * msg.obj = the AsyncChannel * * @param srcContext is the context of the source * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED @@ -224,10 +222,9 @@ public class AsyncChannel { * @param dstPackageName is the destination package name * @param dstClassName is the fully qualified class name (i.e. contains * package name) - * @param token returned in msg.arg2 */ public void connect(Context srcContext, Handler srcHandler, String dstPackageName, - String dstClassName, int token) { + String dstClassName) { if (DBG) log("connect srcHandler to dst Package & class E"); final class ConnectAsync implements Runnable { @@ -235,25 +232,21 @@ public class AsyncChannel { Handler mSrcHdlr; String mDstPackageName; String mDstClassName; - int mToken; ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName, - String dstClassName, int token) { + String dstClassName) { mSrcCtx = srcContext; mSrcHdlr = srcHandler; mDstPackageName = dstPackageName; mDstClassName = dstClassName; - mToken = token; } public void run() { - connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName, - mToken); + connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName); } } - ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName, - token); + ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName); new Thread(ca).start(); if (DBG) log("connect srcHandler to dst Package & class X"); @@ -264,15 +257,14 @@ public class AsyncChannel { * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status - * msg.arg2 = token + * msg.obj = the AsyncChannel * * @param srcContext * @param srcHandler * @param klass is the class to send messages to. - * @param token returned in msg.arg2 */ - public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) { - connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token); + public void connect(Context srcContext, Handler srcHandler, Class<?> klass) { + connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName()); } /** @@ -280,14 +272,13 @@ public class AsyncChannel { * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status - * msg.arg2 = token + * msg.obj = the AsyncChannel * * @param srcContext * @param srcHandler * @param dstMessenger - * @param token returned in msg.arg2 */ - public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) { + public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) { if (DBG) log("connect srcHandler to the dstMessenger E"); // Initialize source fields @@ -301,7 +292,7 @@ public class AsyncChannel { if (DBG) log("tell source we are half connected"); // Tell source we are half connected - replyHalfConnected(STATUS_SUCCESSFUL, token); + replyHalfConnected(STATUS_SUCCESSFUL); if (DBG) log("connect srcHandler to the dstMessenger X"); } @@ -311,16 +302,15 @@ public class AsyncChannel { * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status - * msg.arg2 = token + * msg.obj = the AsyncChannel * * @param srcContext is the context of the source * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED * messages * @param dstHandler is the hander to send messages to. - * @param token returned in msg.arg2 */ - public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) { - connect(srcContext, srcHandler, new Messenger(dstHandler), token); + public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) { + connect(srcContext, srcHandler, new Messenger(dstHandler)); } /** @@ -328,14 +318,13 @@ public class AsyncChannel { * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete. * msg.arg1 = status - * msg.arg2 = token + * msg.obj = the AsyncChannel * * @param srcAsyncService * @param dstMessenger - * @param token returned in msg.arg2 */ - public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) { - connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token); + public void connect(AsyncService srcAsyncService, Messenger dstMessenger) { + connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger); } /** @@ -351,15 +340,14 @@ public class AsyncChannel { /** * Disconnect */ - public void disconnect(int token) { + public void disconnect() { if (mConnection != null) { - mConnection.setToken(token); mSrcContext.unbindService(mConnection); } if (mSrcHandler != null) { Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); msg.arg1 = STATUS_SUCCESSFUL; - msg.arg2 = token; + msg.obj = this; msg.replyTo = mDstMessenger; mSrcHandler.sendMessage(msg); } @@ -727,10 +715,10 @@ public class AsyncChannel { * * @param status to be stored in msg.arg1 */ - private void replyHalfConnected(int status, int token) { + private void replyHalfConnected(int status) { Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED); msg.arg1 = status; - msg.arg2 = token; + msg.obj = this; msg.replyTo = mDstMessenger; mSrcHandler.sendMessage(msg); } @@ -739,28 +727,18 @@ public class AsyncChannel { * ServiceConnection to receive call backs. */ class AsyncChannelConnection implements ServiceConnection { - private int mToken; - - AsyncChannelConnection(int token) { - mToken = token; - } - - /** - * @param token - */ - public void setToken(int token) { - mToken = token; + AsyncChannelConnection() { } public void onServiceConnected(ComponentName className, IBinder service) { mDstMessenger = new Messenger(service); - replyHalfConnected(STATUS_SUCCESSFUL, mToken); + replyHalfConnected(STATUS_SUCCESSFUL); } public void onServiceDisconnected(ComponentName className) { Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); msg.arg1 = STATUS_SUCCESSFUL; - msg.arg2 = mToken; + msg.obj = AsyncChannel.this; msg.replyTo = mDstMessenger; mSrcHandler.sendMessage(msg); } diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java index b54daba..2d067da 100644 --- a/core/java/com/android/internal/view/StandaloneActionMode.java +++ b/core/java/com/android/internal/view/StandaloneActionMode.java @@ -135,6 +135,6 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call public void onMenuModeChange(MenuBuilder menu) { invalidate(); - mContextView.showOverflowMenu(); + mContextView.openOverflowMenu(); } } diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index 621defe..84067d0 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -59,6 +59,22 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } }; + private class OpenOverflowRunnable implements Runnable { + private MenuPopupHelper mPopup; + + public OpenOverflowRunnable(MenuPopupHelper popup) { + mPopup = popup; + } + + public void run() { + mOverflowPopup = mPopup; + mPopup.show(); + mPostedOpenRunnable = null; + } + } + + private OpenOverflowRunnable mPostedOpenRunnable; + public ActionMenuView(Context context) { this(context, null); } @@ -100,6 +116,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo @Override public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); final int screen = newConfig.screenLayout; mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; @@ -115,6 +132,14 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mOverflowPopup != null && mOverflowPopup.isShowing()) { + mOverflowPopup.dismiss(); + } + } + private int getMaxActionButtons() { return getResources().getInteger(com.android.internal.R.integer.max_action_buttons); } @@ -193,30 +218,34 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } public boolean showOverflowMenu() { - if (mOverflowButton != null) { - final MenuPopupHelper popup = - new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true); - // Post this for later; we might still need a layout for the anchor to be right. - post(new Runnable() { - public void run() { - popup.show(); - } - }); - mOverflowPopup = popup; + if (mOverflowButton != null && !isOverflowMenuShowing()) { + mMenu.getCallback().onMenuModeChange(mMenu); return true; } return false; } + public void openOverflowMenu() { + OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true); + mPostedOpenRunnable = new OpenOverflowRunnable(popup); + // Post this for later; we might still need a layout for the anchor to be right. + post(mPostedOpenRunnable); + } + public boolean isOverflowMenuShowing() { - MenuPopupHelper popup = mOverflowPopup; - if (popup != null) { - return popup.isShowing(); - } - return false; + return mOverflowPopup != null && mOverflowPopup.isShowing(); + } + + public boolean isOverflowMenuOpen() { + return mOverflowPopup != null; } public boolean hideOverflowMenu() { + if (mPostedOpenRunnable != null) { + removeCallbacks(mPostedOpenRunnable); + return true; + } + MenuPopupHelper popup = mOverflowPopup; if (popup != null) { popup.dismiss(); @@ -274,9 +303,22 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return true; } - // Change to overflow mode - mMenu.getCallback().onMenuModeChange(mMenu); + showOverflowMenu(); return true; } } + + private class OverflowPopup extends MenuPopupHelper { + public OverflowPopup(Context context, MenuBuilder menu, View anchorView, + boolean overflowOnly) { + super(context, menu, anchorView, overflowOnly); + } + + @Override + public void onDismiss() { + super.onDismiss(); + mMenu.getCallback().onCloseMenu(mMenu, true); + mOverflowPopup = null; + } + } } diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java index 178dcde..d18c9727 100644 --- a/core/java/com/android/internal/view/menu/IconMenuView.java +++ b/core/java/com/android/internal/view/menu/IconMenuView.java @@ -486,7 +486,7 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi // Position the children if (layoutNumRows > 0) { - positionChildren(mMeasuredWidth, mMeasuredHeight); + positionChildren(getMeasuredWidth(), getMeasuredHeight()); } } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 1406e4e..2cb78a5 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -35,7 +35,7 @@ import java.lang.ref.WeakReference; * @hide */ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener, - ViewTreeObserver.OnGlobalLayoutListener { + ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener { private static final String TAG = "MenuPopupHelper"; private Context mContext; @@ -46,12 +46,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On private boolean mOverflowOnly; private ViewTreeObserver mTreeObserver; - private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() { - public void onDismiss() { - mPopup = null; - } - }; - public MenuPopupHelper(Context context, MenuBuilder menu) { this(context, menu, null, false); } @@ -77,7 +71,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On public void show() { mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); mPopup.setOnItemClickListener(this); - mPopup.setOnDismissListener(mDismissListener); + mPopup.setOnDismissListener(this); final MenuAdapter adapter = mOverflowOnly ? mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) : @@ -110,8 +104,12 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On if (isShowing()) { mPopup.dismiss(); } + } + + public void onDismiss() { + mPopup = null; if (mTreeObserver != null) { - mTreeObserver.removeGlobalOnLayoutListener(this); + mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this); mTreeObserver = null; } } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index e93c414..cbf12bf 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -180,6 +180,12 @@ public class ActionBarContextView extends ViewGroup { return false; } + public void openOverflowMenu() { + if (mMenuView != null) { + mMenuView.openOverflowMenu(); + } + } + public boolean hideOverflowMenu() { if (mMenuView != null) { return mMenuView.hideOverflowMenu(); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index f931217..07a65fc 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -22,7 +22,7 @@ import com.android.internal.view.menu.ActionMenuView; import com.android.internal.view.menu.MenuBuilder; import android.app.ActionBar; -import android.app.ActionBar.NavigationCallback; +import android.app.ActionBar.OnNavigationListener; import android.app.Activity; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -114,7 +114,7 @@ public class ActionBarView extends ViewGroup { private ActionMenuItem mLogoNavItem; private SpinnerAdapter mSpinnerAdapter; - private NavigationCallback mCallback; + private OnNavigationListener mCallback; private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { @@ -243,7 +243,7 @@ public class ActionBarView extends ViewGroup { return null; } - public void setCallback(NavigationCallback callback) { + public void setCallback(OnNavigationListener callback) { mCallback = callback; } @@ -269,6 +269,12 @@ public class ActionBarView extends ViewGroup { return false; } + public void openOverflowMenu() { + if (mMenuView != null) { + mMenuView.openOverflowMenu(); + } + } + public void postShowOverflowMenu() { post(new Runnable() { public void run() { @@ -291,6 +297,13 @@ public class ActionBarView extends ViewGroup { return false; } + public boolean isOverflowMenuOpen() { + if (mMenuView != null) { + return mMenuView.isOverflowMenuOpen(); + } + return false; + } + public boolean isOverflowReserved() { return mMenuView != null && mMenuView.isOverflowReserved(); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 695d50a4..b033878 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -579,20 +579,30 @@ static int mainWorkCallback(int fd, int events, void* data) { while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) { jobject inputEventObj = android_view_KeyEvent_fromNative( code->env, keyEvent); - jboolean handled = code->env->CallBooleanMethod(code->clazz, - gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); - checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent"); - code->env->DeleteLocalRef(inputEventObj); + jboolean handled; + if (inputEventObj) { + handled = code->env->CallBooleanMethod(code->clazz, + gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); + checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent"); + code->env->DeleteLocalRef(inputEventObj); + } else { + LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent."); + handled = false; + } code->nativeInputQueue->finishEvent(keyEvent, handled, true); } int seq; while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) { jobject inputEventObj = android_view_KeyEvent_fromNative( code->env, keyEvent); - code->env->CallVoidMethod(code->clazz, - gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq); - checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent"); - code->env->DeleteLocalRef(inputEventObj); + if (inputEventObj) { + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq); + checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent"); + code->env->DeleteLocalRef(inputEventObj); + } else { + LOGE("Failed to obtain key event for preDispatchKeyEvent."); + } } } break; case CMD_FINISH: { @@ -987,7 +997,12 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb NativeCode* code = (NativeCode*)handle; if (code->nativeInputQueue != NULL) { KeyEvent* event = code->nativeInputQueue->createKeyEvent(); - android_view_KeyEvent_toNative(env, eventObj, event); + status_t status = android_view_KeyEvent_toNative(env, eventObj, event); + if (status) { + delete event; + jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); + return; + } code->nativeInputQueue->dispatchEvent(event); } } diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index 7e7583c..4b04b8b 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -30,7 +30,8 @@ namespace android { static struct { jclass clazz; - jmethodID ctor; + jmethodID obtain; + jmethodID recycle; jfieldID mDeviceId; jfieldID mSource; @@ -48,7 +49,8 @@ static struct { // ---------------------------------------------------------------------------- jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { - return env->NewObject(gKeyEventClassInfo.clazz, gKeyEventClassInfo.ctor, + jobject eventObj = env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, + gKeyEventClassInfo.obtain, nanoseconds_to_milliseconds(event->getDownTime()), nanoseconds_to_milliseconds(event->getEventTime()), event->getAction(), @@ -58,10 +60,18 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { event->getDeviceId(), event->getScanCode(), event->getFlags(), - event->getSource()); + event->getSource(), + NULL); + if (env->ExceptionCheck()) { + LOGE("An exception occurred while obtaining a key event."); + LOGE_EX(env); + env->ExceptionClear(); + return NULL; + } + return eventObj; } -void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, +status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, KeyEvent* event) { jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId); jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource); @@ -77,6 +87,18 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, event->initialize(deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, milliseconds_to_nanoseconds(downTime), milliseconds_to_nanoseconds(eventTime)); + return OK; +} + +status_t android_view_KeyEvent_recycle(JNIEnv* env, jobject eventObj) { + env->CallVoidMethod(eventObj, gKeyEventClassInfo.recycle); + if (env->ExceptionCheck()) { + LOGW("An exception occurred while recycling a key event."); + LOGW_EX(env); + env->ExceptionClear(); + return UNKNOWN_ERROR; + } + return OK; } static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) { @@ -87,6 +109,7 @@ static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode return KeyEvent::hasDefaultAction(keyCode); } + // ---------------------------------------------------------------------------- static const JNINativeMethod g_methods[] = { @@ -99,6 +122,10 @@ static const JNINativeMethod g_methods[] = { LOG_FATAL_IF(! var, "Unable to find class " className); \ var = jclass(env->NewGlobalRef(var)); +#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find method" methodName); @@ -109,9 +136,11 @@ static const JNINativeMethod g_methods[] = { int register_android_view_KeyEvent(JNIEnv* env) { FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); - - GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz, - "<init>", "(JJIIIIIIII)V"); + + GET_STATIC_METHOD_ID(gKeyEventClassInfo.obtain, gKeyEventClassInfo.clazz, + "obtain", "(JJIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;"); + GET_METHOD_ID(gKeyEventClassInfo.recycle, gKeyEventClassInfo.clazz, + "recycle", "()V"); GET_FIELD_ID(gKeyEventClassInfo.mDeviceId, gKeyEventClassInfo.clazz, "mDeviceId", "I"); diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h index 0bd410c..586eb2f 100644 --- a/core/jni/android_view_KeyEvent.h +++ b/core/jni/android_view_KeyEvent.h @@ -18,18 +18,28 @@ #define _ANDROID_VIEW_KEYEVENT_H #include "jni.h" +#include <utils/Errors.h> +#include <utils/threads.h> namespace android { class KeyEvent; -/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance. */ +/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance. + * Returns NULL on error. */ extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event); -/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance. */ -extern void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, +/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance. + * Returns non-zero on error. */ +extern status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, KeyEvent* event); +/* Recycles a DVM KeyEvent object. + * Key events should only be recycled if they are owned by the system since user + * code expects them to be essentially immutable, "tracking" notwithstanding. + * Returns non-zero on error. */ +extern status_t android_view_KeyEvent_recycle(JNIEnv* env, jobject eventObj); + } // namespace android #endif // _ANDROID_OS_KEYEVENT_H diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 537ac72..f32f0ff 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -169,7 +169,7 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even return eventObj; } -void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, +status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, MotionEvent* event) { jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId); jint source = env->GetIntField(eventObj, gMotionEventClassInfo.mSource); @@ -184,6 +184,16 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, jint flags = env->GetIntField(eventObj, gMotionEventClassInfo.mFlags); jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers); jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples); + + if (numPointers == 0) { + LOGE("Malformed MotionEvent: mNumPointers was zero"); + return BAD_VALUE; + } + if (numSamples == 0) { + LOGE("Malformed MotionEvent: mNumSamples was zero"); + return BAD_VALUE; + } + jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj, gMotionEventClassInfo.mPointerIdentifiers)); jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, @@ -191,9 +201,6 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj, gMotionEventClassInfo.mEventTimeNanoSamples)); - LOG_FATAL_IF(numPointers == 0, "numPointers was zero"); - LOG_FATAL_IF(numSamples == 0, "numSamples was zero"); - jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL); jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical( @@ -236,22 +243,25 @@ void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, event->addSample(sampleEventTime, samplePointerCoords); } - env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT); env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT); - env->DeleteLocalRef(pointerIdentifierArray); - env->DeleteLocalRef(dataSampleArray); env->DeleteLocalRef(eventTimeNanoSampleArray); + env->DeleteLocalRef(dataSampleArray); + env->DeleteLocalRef(pointerIdentifierArray); + return OK; } -void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { +status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); if (env->ExceptionCheck()) { LOGW("An exception occurred while recycling a motion event."); LOGW_EX(env); env->ExceptionClear(); + return UNKNOWN_ERROR; } + return OK; } static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h index 86e4bde..80dc861 100644 --- a/core/jni/android_view_MotionEvent.h +++ b/core/jni/android_view_MotionEvent.h @@ -18,20 +18,24 @@ #define _ANDROID_VIEW_MOTIONEVENT_H #include "jni.h" +#include <utils/Errors.h> namespace android { class MotionEvent; -/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance. */ +/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance. + * Returns NULL on error. */ extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event); -/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance. */ -extern void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, +/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance. + * Returns non-zero on error. */ +extern status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, MotionEvent* event); -/* Recycles a DVM MotionEvent object. */ -extern void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj); +/* Recycles a DVM MotionEvent object. + * Returns non-zero on error. */ +extern status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj); } // namespace android diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ddc63dd..a2666e2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1013,7 +1013,7 @@ <permission android:name="android.permission.SHUTDOWN" android:label="@string/permlab_shutdown" android:description="@string/permdesc_shutdown" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to tell the activity manager to temporarily stop application switches, putting it into a special mode that diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..8bb4048 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png Binary files differnew file mode 100644 index 0000000..fdd3ee7 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png Binary files differindex 87d9c21..ab6abdc 100644 --- a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png Binary files differindex 720ee78..dbdfc79 100644 --- a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png Binary files differindex 4275da0..4eba040 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png Binary files differindex 3ec9c1f..b186730 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png Binary files differindex 227bde2..06190a1 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png Binary files differindex 6ddfab0..8c16566 100644 --- a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..0ce5d13 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..945516e --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png b/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png Binary files differnew file mode 100644 index 0000000..2993b44 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_longpress_holo.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..33e6dc8 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png Binary files differnew file mode 100644 index 0000000..eb0d90f --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png Binary files differindex 09ca253..74c02c2 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png Binary files differindex 0a7d3a1..345f4f5 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png Binary files differindex 54a1519..40e5db3 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png Binary files differindex 06ca0d4..0cbf6d2 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png Binary files differindex 9015299..bc56916 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png Binary files differindex b355cb3..84adf68 100644 --- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png +++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..4a98e57 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..5cf6bf3 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png Binary files differnew file mode 100644 index 0000000..4aad237 --- /dev/null +++ b/core/res/res/drawable-hdpi/textfield_pressed_holo.9.png diff --git a/core/res/res/drawable-mdpi/scrubber_control_holo.png b/core/res/res/drawable-mdpi/scrubber_control_holo.png Binary files differindex 135b2aa..8457833 100644 --- a/core/res/res/drawable-mdpi/scrubber_control_holo.png +++ b/core/res/res/drawable-mdpi/scrubber_control_holo.png diff --git a/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png Binary files differnew file mode 100644 index 0000000..8582b13 --- /dev/null +++ b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png diff --git a/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png Binary files differnew file mode 100644 index 0000000..6ad876e --- /dev/null +++ b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png Binary files differindex 7b48cf9..baf70cd 100644 --- a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png Binary files differindex 7c84ac9..6f31497 100644 --- a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png +++ b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..8bb4048 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png Binary files differnew file mode 100644 index 0000000..fdd3ee7 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png Binary files differindex c98c951..ab6abdc 100644 --- a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png Binary files differindex 7691f81..dbdfc79 100644 --- a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png Binary files differindex fab86ac..06190a1 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png Binary files differindex 876eb794..8c16566 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..0ce5d13 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..945516e --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png b/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png Binary files differnew file mode 100644 index 0000000..2993b44 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_longpress_holo.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..33e6dc8 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png Binary files differnew file mode 100644 index 0000000..eb0d90f --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png Binary files differindex 2646899..44a5d82 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png Binary files differindex 374d457..6613683 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png Binary files differindex 65c87ba..74c02c2 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png Binary files differindex 724b3fd..345f4f5 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png Binary files differindex 2cc7f62..bc56916 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png Binary files differindex a2d9d8a..84adf68 100644 --- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png +++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..4a98e57 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png Binary files differnew file mode 100644 index 0000000..5cf6bf3 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png Binary files differnew file mode 100644 index 0000000..4aad237 --- /dev/null +++ b/core/res/res/drawable-mdpi/textfield_pressed_holo.9.png diff --git a/core/res/res/drawable/edit_text_holo_dark.xml b/core/res/res/drawable/edit_text_holo_dark.xml index 63ccd1d..29a5150 100644 --- a/core/res/res/drawable/edit_text_holo_dark.xml +++ b/core/res/res/drawable/edit_text_holo_dark.xml @@ -17,7 +17,8 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_dark" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_dark" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_activated_holo_dark" /> + <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_dark" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" /> <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_dark" /> <item android:drawable="@drawable/textfield_disabled_holo_dark" /> diff --git a/core/res/res/drawable/edit_text_holo_light.xml b/core/res/res/drawable/edit_text_holo_light.xml index 324acda..5426916 100644 --- a/core/res/res/drawable/edit_text_holo_light.xml +++ b/core/res/res/drawable/edit_text_holo_light.xml @@ -17,7 +17,8 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_light" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_active_holo_light" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_activated_holo_light" /> + <iten android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_light" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" /> <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_light" /> <item android:drawable="@drawable/textfield_disabled_holo_light" /> diff --git a/core/res/res/drawable/edit_text_multiline_holo_dark.xml b/core/res/res/drawable/edit_text_multiline_holo_dark.xml index 67d2748..d20ea19 100644 --- a/core/res/res/drawable/edit_text_multiline_holo_dark.xml +++ b/core/res/res/drawable/edit_text_multiline_holo_dark.xml @@ -17,7 +17,8 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_dark" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_dark" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_activated_holo_dark" /> + <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_multiline_focused_holo_dark" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_dark" /> <item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_dark" /> <item android:drawable="@drawable/textfield_multiline_disabled_holo_dark" /> diff --git a/core/res/res/drawable/edit_text_multiline_holo_light.xml b/core/res/res/drawable/edit_text_multiline_holo_light.xml index 08b3ec6..41a4eab 100644 --- a/core/res/res/drawable/edit_text_multiline_holo_light.xml +++ b/core/res/res/drawable/edit_text_multiline_holo_light.xml @@ -17,7 +17,8 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_multiline_disabled_holo_light" /> - <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_active_holo_light" /> + <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_multiline_activated_holo_light" /> + <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_multiline_focused_holo_light" /> <item android:state_enabled="true" android:drawable="@drawable/textfield_multiline_default_holo_light" /> <item android:state_focused="true" android:drawable="@drawable/textfield_multiline_disabled_focused_holo_light" /> <item android:drawable="@drawable/textfield_multiline_disabled_holo_light" /> diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml index 90172a5..b117bb8 100644 --- a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml +++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml @@ -15,11 +15,14 @@ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@android:id/background" - android:drawable="@android:drawable/scrubber_track_holo_dark" /> - <item android:id="@android:id/secondaryProgress" - android:drawable="@android:drawable/scrubber_track_holo_dark" /> - <item android:id="@android:id/progress" - android:drawable="@android:drawable/scrubber_track_holo_dark" /> + android:drawable="@android:drawable/scrubber_track_holo_dark" /> + <item android:id="@android:id/secondaryProgress"> + <scale android:scaleWidth="100%" + android:drawable="@android:drawable/scrubber_secondary_holo" /> + </item> + <item android:id="@android:id/progress"> + <scale android:scaleWidth="100%" + android:drawable="@android:drawable/scrubber_primary_holo" /> + </item> </layer-list> diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml index 5fc9697..6cd08ea 100644 --- a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml +++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml @@ -15,11 +15,14 @@ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@android:id/background" - android:drawable="@android:drawable/scrubber_track_holo_light" /> - <item android:id="@android:id/secondaryProgress" - android:drawable="@android:drawable/scrubber_track_holo_light" /> - <item android:id="@android:id/progress" - android:drawable="@android:drawable/scrubber_track_holo_light" /> + android:drawable="@android:drawable/scrubber_track_holo_light" /> + <item android:id="@android:id/secondaryProgress"> + <scale android:scaleWidth="100%" + android:drawable="@android:drawable/scrubber_secondary_holo" /> + </item> + <item android:id="@android:id/progress"> + <scale android:scaleWidth="100%" + android:drawable="@android:drawable/scrubber_primary_holo" /> + </item> </layer-list> diff --git a/core/res/res/layout-xlarge/alert_dialog_holo.xml b/core/res/res/layout-xlarge/alert_dialog_holo.xml index 72b1e31..6790a81 100644 --- a/core/res/res/layout-xlarge/alert_dialog_holo.xml +++ b/core/res/res/layout-xlarge/alert_dialog_holo.xml @@ -74,14 +74,17 @@ <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginLeft="16dip" + android:layout_marginRight="16dip" android:paddingTop="32dip" android:paddingBottom="32dip" - android:paddingLeft="32dip" - android:paddingRight="32dip"> + android:clipToPadding="false"> <TextView android:id="@+id/message" style="?android:attr/textAppearanceMedium" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:paddingLeft="16dip" + android:paddingRight="16dip" /> </ScrollView> </LinearLayout> @@ -93,19 +96,24 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="8dip" - android:paddingBottom="8dip" /> + android:paddingBottom="8dip" + android:paddingLeft="32dip" + android:paddingRight="32dip" /> </FrameLayout> <LinearLayout android:id="@+id/buttonPanel" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="54dip" - android:orientation="vertical" > + android:orientation="vertical" + android:divider="?android:attr/dividerHorizontal" + android:showDividers="beginning" + android:dividerPadding="16dip"> <LinearLayout + style="?android:attr/buttonGroupStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingTop="4dip" android:paddingLeft="2dip" android:paddingRight="2dip" android:measureWithLargestChild="true"> @@ -120,18 +128,21 @@ android:layout_gravity="left" android:layout_weight="1" android:maxLines="2" + style="?android:attr/borderlessButtonStyle" android:layout_height="wrap_content" /> <Button android:id="@+id/button3" android:layout_width="0dip" android:layout_gravity="center_horizontal" android:layout_weight="1" android:maxLines="2" + style="?android:attr/borderlessButtonStyle" android:layout_height="wrap_content" /> <Button android:id="@+id/button2" android:layout_width="0dip" android:layout_gravity="right" android:layout_weight="1" android:maxLines="2" + style="?android:attr/borderlessButtonStyle" android:layout_height="wrap_content" /> <LinearLayout android:id="@+id/rightSpacer" android:layout_width="0dip" diff --git a/core/res/res/layout-xlarge/select_dialog_holo.xml b/core/res/res/layout-xlarge/select_dialog_holo.xml new file mode 100644 index 0000000..7c95693 --- /dev/null +++ b/core/res/res/layout-xlarge/select_dialog_holo.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- + This layout file is used by the AlertDialog when displaying a list of items. + This layout file is inflated and used as the ListView to display the items. + Assign an ID so its state will be saved/restored. +--> +<view class="com.android.internal.app.AlertController$RecycleListView" + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+android:id/select_dialog_listview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="5dip" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:cacheColorHint="@null" + android:divider="?android:attr/listDividerAlertDialog" + android:scrollbars="vertical" + android:overScrollMode="ifContentScrolls" /> diff --git a/core/res/res/layout-xlarge/select_dialog_item_holo.xml b/core/res/res/layout-xlarge/select_dialog_item_holo.xml new file mode 100644 index 0000000..396092e --- /dev/null +++ b/core/res/res/layout-xlarge/select_dialog_item_holo.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- + This layout file is used by the AlertDialog when displaying a list of items. + This layout file is inflated and used as the TextView to display individual + items. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorAlertDialogListItem" + android:gravity="center_vertical" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:ellipsize="marquee" +/> diff --git a/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml b/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml new file mode 100644 index 0000000..8027035 --- /dev/null +++ b/core/res/res/layout-xlarge/select_dialog_multichoice_holo.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorAlertDialogListItem" + android:gravity="center_vertical" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:checkMark="?android:attr/listChoiceIndicatorMultiple" + android:ellipsize="marquee" +/> diff --git a/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml b/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml new file mode 100644 index 0000000..cab519f --- /dev/null +++ b/core/res/res/layout-xlarge/select_dialog_singlechoice_holo.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorAlertDialogListItem" + android:gravity="center_vertical" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:checkMark="?android:attr/listChoiceIndicatorSingle" + android:ellipsize="marquee" +/> diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml index 0f84418..fd488bd 100644 --- a/core/res/res/layout/preference_list_content.xml +++ b/core/res/res/layout/preference_list_content.xml @@ -64,8 +64,6 @@ android:layout_marginRight="@dimen/preference_screen_side_margin" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" - android:paddingLeft="32dip" - android:paddingRight="32dip" android:background="?attr/preferencePanelBackground" android:visibility="gone" /> </LinearLayout> diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml index dbe0df0..69fb73a 100644 --- a/core/res/res/layout/preference_list_fragment.xml +++ b/core/res/res/layout/preference_list_fragment.xml @@ -18,11 +18,11 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/default_preference_layout" android:orientation="vertical" android:layout_height="match_parent" android:layout_width="match_parent" - android:background="@android:color/transparent"> + android:background="@android:color/transparent" + android:layout_removeBorders="true"> <ListView android:id="@android:id/list" android:layout_width="match_parent" @@ -30,6 +30,8 @@ android:layout_weight="1" android:paddingTop="48dip" android:paddingBottom="48dip" + android:paddingLeft="32dip" + android:paddingRight="32dip" android:clipToPadding="false" android:drawSelectorOnTop="false" android:cacheColorHint="@android:color/transparent" diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index 7935e2a..4bc2d1a 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* apps/common/res/layout/SearchBar.xml +/* ** ** Copyright 2007, The Android Open Source Project ** @@ -23,87 +23,36 @@ android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" + android:orientation="horizontal" android:focusable="true" + android:background="?attr/actionModeBackground" android:descendantFocusability="afterDescendants"> - <!-- Outer layout defines the entire search bar at the top of the screen --> - <LinearLayout - android:id="@+id/search_plate" + <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingLeft="12dip" - android:paddingRight="6dip" - android:paddingTop="7dip" - android:paddingBottom="16dip" - android:background="@drawable/search_plate_global" > + android:layout_gravity="center_vertical" + > - <!-- This is actually used for the badge icon *or* the badge label (or neither) --> - <TextView - android:id="@+id/search_badge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="2dip" - android:drawablePadding="0dip" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="?android:attr/textColorPrimaryInverse" /> - - <!-- Inner layout contains the app icon, button(s) and EditText --> - <LinearLayout - android:id="@+id/search_edit_frame" + <ImageView + android:id="@+id/search_app_icon" + android:layout_height="48dip" + android:layout_width="48dip" + android:layout_marginLeft="8dip" + android:layout_marginRight="8dip" + android:layout_gravity="center_vertical" + android:layout_alignParentLeft="true" + /> + <SearchView + android:id="@+id/search_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/search_app_icon" - android:layout_height="36dip" - android:layout_width="36dip" - android:layout_marginRight="7dip" - android:layout_gravity="center_vertical" - /> - - <view class="android.app.SearchDialog$SearchAutoComplete" - android:id="@+id/search_src_text" - android:background="@drawable/textfield_search" - android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1.0" - android:paddingLeft="8dip" - android:paddingRight="6dip" - android:drawablePadding="2dip" - android:singleLine="true" - android:ellipsize="end" - android:inputType="text|textAutoComplete" - android:dropDownWidth="match_parent" - android:dropDownHeight="match_parent" - android:dropDownAnchor="@id/search_plate" - android:dropDownVerticalOffset="-9dip" - android:popupBackground="@android:drawable/search_dropdown_background" - /> - - <!-- This button can switch between text and icon "modes" --> - <Button - android:id="@+id/search_go_btn" - android:background="@drawable/btn_search_dialog" - android:layout_width="wrap_content" - android:layout_height="match_parent" + android:maxWidth="600dip" + android:iconifiedByDefault="false" + android:layout_alignParentRight="true" + android:layout_gravity="center_vertical|right" /> - <ImageButton - android:id="@+id/search_voice_btn" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginLeft="0dip" - android:layout_marginTop="-6.5dip" - android:layout_marginBottom="-7dip" - android:layout_marginRight="-5dip" - android:background="@drawable/btn_search_dialog_voice" - android:src="@android:drawable/ic_btn_speak_now" - /> - </LinearLayout> - - </LinearLayout> + </RelativeLayout> </view> diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml index eb0bb11..0fb824f 100644 --- a/core/res/res/layout/search_view.xml +++ b/core/res/res/layout/search_view.xml @@ -74,7 +74,7 @@ android:src="?android:attr/searchViewSearchIcon" /> - <AutoCompleteTextView + <view class="android.widget.SearchView$SearchAutoComplete" android:id="@+id/search_src_text" android:layout_height="36dip" android:layout_width="0dp" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index fd4b32c..4abcc9f 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"تم إرسال عدد كبير من الرسائل القصيرة SMS. حدّد \"موافق\" للمتابعة، أو \"إلغاء\" لإيقاف الإرسال."</string> <string name="sms_control_yes" msgid="2532062172402615953">"موافق"</string> <string name="sms_control_no" msgid="1715320703137199869">"إلغاء"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"تعيين"</string> <string name="default_permission_group" msgid="2690160991405646128">"افتراضي"</string> <string name="no_permissions" msgid="7283357728219338112">"لا أذونات مطلوبة"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index e01bb9b..03206b7 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Изпращат се голям брой SMS съобщения. Изберете „OK“, за да продължите, или „Отказ“, за да спрете изпращането."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Отказ"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Задаване"</string> <string name="default_permission_group" msgid="2690160991405646128">"По подразбиране"</string> <string name="no_permissions" msgid="7283357728219338112">"Не се изискват разрешения"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index c32c95a..fd788e3 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"S\'estan enviant molts missatges SMS. Seleccioneu \"D\'acord\" per continuar o \"Cancel·la\" per aturar l\'enviament."</string> <string name="sms_control_yes" msgid="2532062172402615953">"D\'acord"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancel·la"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Defineix"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predeterminat"</string> <string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index f65f585..1c233f0 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Bez upozornění smazat všechna data telefonu obnovením továrních dat"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Nastavit globální proxy server zařízení"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Vyberte globální proxy server, který se bude používat, když jsou zásady aktivní. Aktuální globální proxy server nastavuje pouze první správce zařízení."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Nastavit konec platnosti hesla"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ovládání doby, po jejímž uplynutí je nutné změnit heslo pro odemknutí obrazovky"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Domů"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"čtení historie a záložek Prohlížeče"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Je odesílán velký počet zpráv SMS. Vyberte OK, chcete-li pokračovat, nebo Zrušit, chcete-li odesílání ukončit."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Zrušit"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Nastavit"</string> <string name="default_permission_group" msgid="2690160991405646128">"Výchozí"</string> <string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Zobrazit vše"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Úložiště USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB připojeno"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Připojili jste telefon k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB zařízení Android či obráceně, vyberte následující tlačítko."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Připojili jste telefon k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB zařízení Android či obráceně, vyberte následující tlačítko."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Připojili jste se k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače do úložiště USB v zařízení Android či obráceně, stiskněte tlačítko níže."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Připojili jste se k počítači pomocí rozhraní USB. Chcete-li kopírovat soubory z počítače na kartu SD v zařízení Android či obráceně, stiskněte tlačítko níže."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Zapnout úložiště USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Problém s použitím úložiště USB jako velkokapacitního úložiště."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Problém s použitím karty SD jako velkokapacitního úložiště USB."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 8bb762b..5f96369 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Slet telefonens data uden varsel ved at gendanne fabriksindstillinger"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angiv enhedens globale proxy"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angiv enhedens globale proxy, der skal bruges, mens politikken er aktiveret. Kun den første enhedsadministrator angiver den effektive globale proxy."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Indstil udløb for adgangskode"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontroller, hvor lang tid der skal gå, før adgangskoden til skærmlåsen skal ændres."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hjem"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Der sendes et stort antal sms-beskeder. Vælg \"OK\" for at fortsætte eller \"Annuller\" for at stoppe afsendelsen."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Annuller"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Angiv"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string> <string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masselagring"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB er tilsluttet"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har forbundet din telefon til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har forbundet din telefon til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har fået forbindelse til din computer via USB. Vælg knappen nedenfor, hvis du vil kopiere filer mellem din computer og din Androids USB-lager."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har fået forbindelse til din computer via USB. Vælg knappen nedenfor, hvis du ønsker at kopiere filer mellem din computer og din Androids SD-kort."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Slå USB-lagringen til"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Der opstod et problem med at bruge USB-lager til USB-masselager."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Der opstod et problem med at bruge dit SD-kort til USB-masselager."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 87e57ce..8aa24d9 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Auf Werkseinstellungen zurücksetzen und Daten auf dem Telefon ohne Warnung löschen"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Den globalen Proxy des Geräts festlegen"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Den globalen Proxy des Geräts zur Verwendung während der Aktivierung der Richtlinie festlegen. Nur der erste Geräteadministrator kann den gültigen globalen Proxy festlegen."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Ablauf des Passworts festlegen"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Zeitraum bis zur Änderung des Passworts für die Bildschirmsperre festlegen"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Privat"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Browserverlauf und Lesezeichen lesen"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Es werden eine große Anzahl an Kurznachrichten versendet. Wählen Sie \"OK\", um fortzufahren, oder drücken Sie auf \"Abbrechen\", um den Sendevorgang zu beenden."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Abbrechen"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Einstellen"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string> <string name="no_permissions" msgid="7283357728219338112">"Keine Berechtigungen erforderlich"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Alle anzeigen"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-Massenspeicher"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB-Verbindung"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Wählen Sie die Schaltfläche unten aus, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Wählen Sie die Schaltfläche unten aus, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Berühren Sie die Schaltfläche unten, wenn Sie Dateien von Ihrem Computer in den USB-Speicher Ihres Android-Geräts und umgekehrt kopieren möchten."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Sie haben Ihr Telefon über USB mit Ihrem Computer verbunden. Berühren Sie die Schaltfläche unten, wenn Sie Dateien von Ihrem Computer auf die SD-Karte Ihres Android-Geräts und umgekehrt kopieren möchten."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-Speicher aktivieren"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Bei der Verwendung Ihres USB-Speichers als USB-Massenspeicher ist ein Problem aufgetreten."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Bei der Verwendung Ihrer SD-Karte als USB-Massenspeicher ist ein Problem aufgetreten."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index bbd02f2..c74839f 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Διαγραφή των δεδομένων του τηλεφώνου χωρίς προειδοποίηση με επαναφορά των εργοστασιακών δεδομένων"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ρύθμιση του γενικού διακομιστή μεσολάβησης της συσκευής"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ορίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής όταν είναι ενεργοποιημένη η πολιτική. Μόνο ο διαχειριστής της πρώτης συσκευής ορίζει τον ισχύοντα γενικό διακομιστή μεσολάβησης."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Ορισμός λήξης κωδ. πρόσβασης"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ελέγξτε πόσος χρόνος απομένει προτού πρέπει να αλλάξετε τον κωδικό πρόσβασης κλειδώματος της οθόνης"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Οικία"</item> <item msgid="869923650527136615">"Κινητό"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Αποστέλλεται μεγάλος αριθμός μηνυμάτων SMS. Επιλέξτε \"OK\" για συνέχεια, ή \"Ακύρωση\" για διακοπή αποστολής."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Ακύρωση"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Ορισμός"</string> <string name="default_permission_group" msgid="2690160991405646128">"Προεπιλεγμένο"</string> <string name="no_permissions" msgid="7283357728219338112">"Δεν απαιτούνται άδειες"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Εμφάνιση όλων"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Μαζική αποθήκευση USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Το USB είναι συνδεδεμένο"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Συνδέσατε το τηλέφωνό σας στον υπολογιστή μέσω USB. Επιλέξτε το παρακάτω κουμπί αν θέλετε να αντιγράψετε αρχεία μεταξύ του υπολογιστή και του χώρου αποθήκευσης USB του Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Συνδέσατε το τηλέφωνό σας στον υπολογιστή μέσω USB. Επιλέξτε το παρακάτω κουμπί αν θέλετε να αντιγράψετε αρχεία μεταξύ του υπολογιστή και του χώρου αποθήκευσης USB του Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Συνδεθήκατε στον υπολογιστή σας μέσω USB. Αγγίξτε το παρακάτω κουμπί, αν θέλετε να αντιγράψετε αρχεία ανάμεσα στον υπολογιστή σας και τον χώρο αποθήκευσης USB του Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Συνδεθήκατε στον υπολογιστή σας μέσω USB. Αγγίξτε το παρακάτω κουμπί, αν θέλετε να αντιγράψετε αρχεία ανάμεσα στον υπολογιστή σας και την κάρτα SD του Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Ενεργοποίηση αποθηκευτικού χώρου USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Παρουσιάστηκε ένα πρόβλημα στη χρήση του αποθηκευτικού χώρου USB ως χώρο USB μαζικής αποθήκευσης."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Παρουσιάστηκε ένα πρόβλημα στη χρήση της κάρτας SD ως χώρο USB μαζικής αποθήκευσης."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index eab891f..11ae9f4 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -905,6 +905,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"A large number of SMS messages are being sent. Select \"OK\" to continue or \"Cancel\" to stop sending."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancel"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Set"</string> <string name="default_permission_group" msgid="2690160991405646128">"Default"</string> <string name="no_permissions" msgid="7283357728219338112">"No permission required"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 7fc79b1..abce7dd 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -481,10 +481,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Borrar los datos del teléfono sin advertencias al restablecer la configuración original"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Configura el proxy global de dispositivo"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configuración del proxy global de dispositivo que se utilizará mientras se habilita la política. Sólo la primera administración de dispositivo configura el proxy global efectivo."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Establecer la caducidad de la contraseña"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Verifica cuánto tiempo antes debes cambiar la contraseña de la pantalla de bloqueo"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Celular"</item> @@ -867,6 +865,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Se envía una gran cantidad de mensajes SMS. Selecciona \"Aceptar\" para continuar o \"Cancelar\" para detener el envío."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Establecer"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string> <string name="no_permissions" msgid="7283357728219338112">"No se requieren permisos"</string> @@ -874,8 +874,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Almacenamiento masivo USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"conectado al USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y el almacenamiento USB de Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado tu teléfono a tu computadora mediante USB. Selecciona el botón a continuación si deseas copiar los archivos entre tu computadora y la tarjeta SD de Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar el almacenamiento USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Hay un problema para utilizar el almacenamiento USB en el almacenamiento masivo USB."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Hay un problema para utilizar tu tarjeta SD en el almacenamiento masivo USB."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 1b28dca..8c3543c 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Borrado de los datos del teléfono sin avisar restableciendo datos de fábrica"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir el servidor proxy global"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el primer administrador de dispositivos define el servidor proxy global efectivo."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir caducidad contraseña"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Permite controlar cuándo se debe cambiar la contraseña de bloqueo de la pantalla."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Móvil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer información de marcadores y del historial del navegador"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Se ha enviado un número elevado de mensajes SMS. Selecciona \"Aceptar\" para continuar o \"Cancelar\" para interrumpir el envío."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Aceptar"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Establecer"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string> <string name="no_permissions" msgid="7283357728219338112">"No es necesario ningún permiso"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Almacenamiento USB masivo"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Conectado por USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado el teléfono al equipo mediante USB. Selecciona el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono con Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado el teléfono al equipo mediante USB. Selecciona el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono con Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Has conectado el teléfono al equipo mediante USB. Toca el botón situado debajo si deseas copiar archivos entre el equipo y el almacenamiento USB del teléfono Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Has conectado el teléfono al equipo mediante USB. Toca el botón situado debajo si deseas copiar archivos entre el equipo y la tarjeta SD del teléfono Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar almacenamiento USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Se ha producido un problema al utilizar el almacenamiento USB para el almacenamiento masivo USB."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Se ha producido un problema al utilizar la tarjeta SD para el almacenamiento USB masivo."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index cbb5fe3..068bfe0 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"تعداد زیادی پیامک ارسال شده است. برای ادامه، \"تأیید\" را کلیک کرده و برای توقف ارسال، \"لغو\" را کلیک کنید."</string> <string name="sms_control_yes" msgid="2532062172402615953">"تأیید"</string> <string name="sms_control_no" msgid="1715320703137199869">"لغو"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string> <string name="default_permission_group" msgid="2690160991405646128">"پیش فرض"</string> <string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 3390669..bddb852 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Olet lähettämässä suurta määrää tekstiviestejä. Jatka valitsemalla OK tai peruuta lähetys valitsemalla Peruuta."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Peruuta"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Aseta"</string> <string name="default_permission_group" msgid="2690160991405646128">"Oletus"</string> <string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index f618070..700674c 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Effacer les données du téléphone sans avertissement, en restaurant les valeurs d\'usine"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Définir le proxy global du mobile"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Indiquez le proxy global à utiliser pour ce mobile lorsque les règles sont activées. Seul l\'administrateur principal du mobile peut définir le proxy global utilisé."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Définir date exp. mot de passe"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Définir la fréquence de changement du mot de passe de verrouillage d\'écran"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Domicile"</item> <item msgid="869923650527136615">"Mobile"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lire l\'historique et les favoris du navigateur"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Vous êtes sur le point d\'envoyer un grand nombre de messages SMS. Sélectionnez OK pour continuer ou Annuler pour interrompre l\'envoi."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Annuler"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Définir"</string> <string name="default_permission_group" msgid="2690160991405646128">"Par défaut"</string> <string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Tout afficher"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Stockage de masse USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Connecté à l\'aide d\'un câble USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Vous avez connecté votre téléphone à votre ordinateur à l\'aide d\'un câble USB. Sélectionnez le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Vous avez connecté votre téléphone à votre ordinateur à l\'aide d\'un câble USB. Sélectionnez le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Vous êtes connecté à votre ordinateur via un câble USB. Appuyez sur le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la mémoire de stockage USB de votre Android, ou inversement."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Vous êtes connecté à votre ordinateur via un câble USB. Appuyez sur le bouton ci-dessous pour copier des fichiers de votre ordinateur vers la carte SD de votre Android, ou inversement."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activer la mémoire de stockage USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Un problème est survenu lors de l\'utilisation de votre mémoire de stockage USB comme mémoire de stockage de masse."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Un problème est survenu lors de l\'utilisation de votre carte SD comme mémoire de stockage de masse USB."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index bc6d451..a075566 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Šalje se velika količina SMS poruka. Odaberite \"U redu\" za nastavak, ili za prekid slanja odaberite \"Odustani\"."</string> <string name="sms_control_yes" msgid="2532062172402615953">"U redu"</string> <string name="sms_control_no" msgid="1715320703137199869">"Odustani"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Postavi"</string> <string name="default_permission_group" msgid="2690160991405646128">"Zadano"</string> <string name="no_permissions" msgid="7283357728219338112">"Nije potrebno dopuštenje"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 36aea65..3a4bf8d 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Nagyszámú SMS-t kíván elküldeni. A folytatáshoz válassza az \"OK\", a küldés leállításához a \"Mégse\" lehetőséget."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Mégse"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Beállítás"</string> <string name="default_permission_group" msgid="2690160991405646128">"Alapértelmezett"</string> <string name="no_permissions" msgid="7283357728219338112">"Nincs szükség engedélyre"</string> diff --git a/core/res/res/values-id-rID/arrays.xml b/core/res/res/values-in-rID/arrays.xml index 512cace..512cace 100644 --- a/core/res/res/values-id-rID/arrays.xml +++ b/core/res/res/values-in-rID/arrays.xml diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml index 4d4ebb2..4d4ebb2 100644 --- a/core/res/res/values-id-rID/donottranslate-cldr.xml +++ b/core/res/res/values-in-rID/donottranslate-cldr.xml diff --git a/core/res/res/values-id/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml index 7a58a19..7a58a19 100644 --- a/core/res/res/values-id/donottranslate-cldr.xml +++ b/core/res/res/values-in/donottranslate-cldr.xml diff --git a/core/res/res/values-id/strings.xml b/core/res/res/values-in/strings.xml index 2d07b33..aa243e0 100644 --- a/core/res/res/values-id/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Sejumlah besar pesan SMS sedang dikirimkan. Pilih \"OK\" untuk melanjutkan, atau \"Batal\" untuk menghentikan pengiriman."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Batal"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Setel"</string> <string name="default_permission_group" msgid="2690160991405646128">"Bawaan"</string> <string name="no_permissions" msgid="7283357728219338112">"Tidak perlu izin"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 238a748..0b74664 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Cancella i dati del telefono senza preavviso eseguendo un ripristino dati di fabbrica"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Imposta il proxy globale del dispositivo"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale effettivo è impostabile solo dal primo amministratore del dispositivo."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Imposta scadenza password"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Stabilisci la scadenza della password di blocco dello schermo"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Cellulare"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lettura cronologia e segnalibri del browser"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"È in corso l\'invio di numerosi SMS. Seleziona \"OK\" per continuare, oppure \"Annulla\" per interrompere l\'invio."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Annulla"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Imposta"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predefinito"</string> <string name="no_permissions" msgid="7283357728219338112">"Nessuna autorizzazione richiesta"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostra tutto"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Archivio di massa USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB collegata"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Il telefono è stato collegato al computer tramite USB. Seleziona il pulsante sottostante se desideri copiare file tra il computer e l\'archivio USB di Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Il telefono è stato collegato al computer tramite USB. Seleziona il pulsante sottostante se desideri copiare file tra il computer e l\'archivio USB di Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ti sei collegato al computer tramite USB. Tocca il pulsante sotto se desideri copiare file tra il computer e l\'archivio USB di Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ti sei collegato al computer tramite USB. Tocca il pulsante sotto se desideri copiare file tra il computer e la scheda SD di Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Attiva archivio USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Problema di utilizzo dell\'archivio USB come archivio di massa USB."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Problema di utilizzo della scheda SD come archivio di massa USB."</string> diff --git a/core/res/res/values-he-rIL/arrays.xml b/core/res/res/values-iw-rIL/arrays.xml index 87c1231..87c1231 100644 --- a/core/res/res/values-he-rIL/arrays.xml +++ b/core/res/res/values-iw-rIL/arrays.xml diff --git a/core/res/res/values-he/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml index d373a34..d373a34 100644 --- a/core/res/res/values-he/donottranslate-cldr.xml +++ b/core/res/res/values-iw/donottranslate-cldr.xml diff --git a/core/res/res/values-he/strings.xml b/core/res/res/values-iw/strings.xml index c5d945c..25074ea 100644 --- a/core/res/res/values-he/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"מספר גדול של הודעות SMS נשלח. בחר \'אישור\' כדי להמשיך או \'ביטול\' כדי לעצור את השליחה."</string> <string name="sms_control_yes" msgid="2532062172402615953">"אישור"</string> <string name="sms_control_no" msgid="1715320703137199869">"ביטול"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"הגדר"</string> <string name="default_permission_group" msgid="2690160991405646128">"ברירת מחדל"</string> <string name="no_permissions" msgid="7283357728219338112">"לא דרושים אישורים"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 75d3722..e8bb57a 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"警告せずにデータの初期化を実行して端末内のデータを消去します。"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"端末のグローバルプロキシを設定"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ポリシーが有効になっている場合は端末のグローバルプロキシが使用されるように設定します。有効なグローバルプロキシを設定できるのは最初のデバイス管理者だけです。"</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"パスワードの有効期限の設定"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"画面ロックパスワードの変更が必要になるまでの期間を指定します"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"自宅"</item> <item msgid="869923650527136615">"携帯"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">"、 "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ブラウザの履歴とブックマークを読み取る"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"大量のSMSメッセージを送信しようとしています。[OK]で送信、[キャンセル]で中止します。"</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"キャンセル"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"設定"</string> <string name="default_permission_group" msgid="2690160991405646128">"端末既定"</string> <string name="no_permissions" msgid="7283357728219338112">"権限の許可は必要ありません"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"すべて表示"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USBマスストレージ"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB接続"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"携帯端末をUSBでパソコンに接続しています。パソコンとAndroidのUSBストレージの間でファイルをコピーするには、下のボタンを選択します。"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"携帯端末をUSBでパソコンに接続しています。パソコンとAndroidのUSBストレージの間でファイルをコピーするには、下のボタンを選択します。"</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USBでパソコンに接続しています。パソコンとAndroidのUSBストレージ間でファイルをコピーするには下のボタンをタップします。"</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USBでパソコンに接続しています。パソコンとAndroidのSDカード間でファイルをコピーするには下のボタンをタップします。"</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"USBストレージをONにする"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USBストレージをUSBマスストレージとして使用する際に問題が発生しました。"</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"USBをUSBマスストレージとして使用する際に問題が発生しました。"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 1ed8492..ab873be 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"초기화를 수행하여 경고 없이 휴대전화 데이터를 지웁니다."</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"기기 전체 프록시 설정"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 첫 번째 기기 관리자가 설정한 전체 프록시만 유효합니다."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"비밀번호 만료 설정"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"화면 잠금 비밀번호를 변경해야 하는 기간 변경"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"집"</item> <item msgid="869923650527136615">"모바일"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"여러 개의 SMS 메시지를 보내는 중입니다. 계속하려면 \'확인\'을 선택하고 전송을 중지하려면 \'취소\'를 선택하세요."</string> <string name="sms_control_yes" msgid="2532062172402615953">"확인"</string> <string name="sms_control_no" msgid="1715320703137199869">"취소"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"설정"</string> <string name="default_permission_group" msgid="2690160991405646128">"기본값"</string> <string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"모두 표시"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 대용량 저장소"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB 연결됨"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USB를 통해 휴대전화를 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 선택하세요."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USB를 통해 휴대전화를 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 선택하세요."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"USB를 통해 컴퓨터에 연결했습니다. 컴퓨터와 Android의 USB 저장소 간에 파일을 복사하려면 아래의 버튼을 터치하세요."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"USB를 통해 컴퓨터에 연결했습니다. 컴퓨터와 Android의 SD 카드 간에 파일을 복사하려면 아래의 버튼을 터치하세요."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB 저장소 사용"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USB 대용량 저장소로 공유 저장용량을 사용하는 동안 문제가 발생했습니다."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"USB 대용량 저장소로 SD 카드를 사용하는 동안 문제가 발생했습니다."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 41d9fbd..c9059cb 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Siunčiama daug SMS pranešimų. Pasirinkite „Gerai“, jei norite tęsti, arba „Atšaukti“, jei norite sustabdyti siuntimą."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Gerai"</string> <string name="sms_control_no" msgid="1715320703137199869">"Atšaukti"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Nustatyti"</string> <string name="default_permission_group" msgid="2690160991405646128">"Numatytasis"</string> <string name="no_permissions" msgid="7283357728219338112">"Nereikia leidimų"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index d706ed5..ee3def8 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Tiek sūtīts liels īsziņu skaits. Atlasiet Labi, lai turpinātu, vai Atcelt, lai apturētu sūtīšanu."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Labi"</string> <string name="sms_control_no" msgid="1715320703137199869">"Atcelt"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Iestatīt"</string> <string name="default_permission_group" msgid="2690160991405646128">"Noklusējums"</string> <string name="no_permissions" msgid="7283357728219338112">"Atļaujas nav nepieciešamas."</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 96d9c32..a7e1879 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Tilbakestill telefonens data uten advarsel ved å utføre tilbakestilling til fabrikkstandard"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angi enhetens globale mellomtjener"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angir den globale mellomtjeneren på enheten som skal brukes når regelen er aktivert. Kun den opprinnelige administratoren av enheten kan angi den globale mellomtjeneren."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Angi utløpsdato for passordet"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Velg hvor lenge det skal gå før passordet til låseskjermen må byttes"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hjemmenummer"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Et stort antall SMS-meldinger blir sendt. Velg «OK» for å fortsette, eller «Avbryt» for å avbryte sendingen."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Lagre"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string> <string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masselagring"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB koblet til"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og USB-lagring for Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og USB-lagring for Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har koblet telefonen til datamaskinen via USB. Trykk på knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og Android-telefonens USB-lagring."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har koblet telefonen til datamaskinen via USB. Trykk på knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og SD-kortet i Android-telefonen."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Slå på USB-lagring"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Det oppstod et problem under USB-lagring for USB-enheten."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Det oppstod et problem under SD-kortet for USB-enheten."</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 8998fba..3f4c7d4 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"De gegevens van de telefoon zonder waarschuwing wissen door de fabrieksinstellingen te herstellen"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Algemene proxy voor het apparaat instellen"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Stel de algemene proxy voor het apparaat in die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eerste apparaatbeheerder stelt de algemene proxy in."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Verval wachtwoord instellen"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Beheren hoe lang het duurt voordat het wachtwoord voor schermvergrendeling moet worden gewijzigd"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Thuis"</item> <item msgid="869923650527136615">"Mobiel"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"browsergeschiedenis en bladwijzers lezen"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Er wordt een groot aantal SMS-berichten verzonden. Selecteer \'OK\' om door te gaan of \'Annuleren\' om de verzending te stoppen."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Annuleren"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Instellen"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standaard"</string> <string name="no_permissions" msgid="7283357728219338112">"Geen machtigingen vereist"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Alles weergeven"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-massaopslag"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB-verbinding"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"U heeft uw telefoon via USB op uw computer aangesloten. Selecteer de onderstaande knop als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"U heeft uw telefoon via USB op uw computer aangesloten. Selecteer de onderstaande knop als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"U heeft uw telefoon via USB op uw computer aangesloten. Raak de onderstaande knop aan als u bestanden tussen uw computer en de USB-opslag van uw Android wilt kopiëren."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"U heeft uw telefoon via USB op uw computer aangesloten. Raak de onderstaande knop aan als u bestanden tussen uw computer en de SD-kaart van uw Android wilt kopiëren."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-opslag inschakelen"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Er is een probleem bij het gebruik van uw USB-opslag voor USB-massaopslag."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Er is een probleem bij het gebruik van uw SD-kaart voor USB-massaopslag."</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index ec80f83..fcd7275 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Wymazywanie danych z telefonu bez ostrzeżenia, przez przywrócenie danych fabrycznych"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ustaw globalny serwer proxy urządzenia"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ustaw globalny serwer proxy urządzenia do wykorzystywania przy włączonych zasadach. Tylko pierwszy administrator urządzenia ustawia obowiązujący globalny serwer proxy."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Ustaw wygasanie hasła"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrola czasu, po którym należy zmienić hasło blokowania ekranu"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Dom"</item> <item msgid="869923650527136615">"Komórka"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"odczyt historii i zakładek przeglądarki"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Wysyłana jest duża liczba wiadomości SMS. Wybierz „OK”, aby kontynuować, lub „Anuluj”, aby zatrzymać wysyłanie."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Anuluj"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Ustaw"</string> <string name="default_permission_group" msgid="2690160991405646128">"Domyślne"</string> <string name="no_permissions" msgid="7283357728219338112">"Nie są wymagane żadne uprawnienia"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaż wszystko"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Pamięć masowa USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Połączenie przez USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Telefon został połączony z komputerem za pośrednictwem USB. Wybierz poniższy przycisk, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Telefon został połączony z komputerem za pośrednictwem USB. Wybierz poniższy przycisk, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Nawiązano połączenie z komputerem za pośrednictwem USB. Dotknij poniższego przycisku, aby skopiować pliki między komputerem a nośnikiem USB systemu Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Nawiązano połączenie z komputerem za pośrednictwem USB. Dotknij poniższego przycisku, aby skopiować pliki między komputerem a kartą SD systemu Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Włącz nośnik USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Wystąpił problem z użyciem nośnika USB jako pamięci masowej USB."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Wystąpił problem z użyciem karty SD jako nośnika pamięci masowej USB."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 5e9b0a4..47a9ed7 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Apagar os dados do telefone sem avisar, ao efectuar uma reposição de dados de fábrica"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do aparelho"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Definir o proxy global do aparelho a ser utilizado quando a política estiver activada. Só o primeiro administrador do aparelho define o proxy global efectivo."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Def. valid. da palavra-passe"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle com que antecedência é necessário alterar a palavra-passe de bloqueio do ecrã"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Residência"</item> <item msgid="869923650527136615">"Móvel"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Está a ser enviado um grande número de mensagens SMS. Seleccione \"OK\" para continuar ou \"Cancelar\" para parar o envio."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Definir"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predefinido"</string> <string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar tudo"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Ligado através de USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ligou o telemóvel ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ligou o telemóvel ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Ligou ao computador através de USB. Toque no botão abaixo se pretender copiar ficheiros entre o computador e o armazenamento USB do Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Ligou ao computador através de USB. Toque no botão abaixo se pretender copiar ficheiros entre o computador e o cartão SD do Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar armazenamento USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Existe um problema ao utilizar o armazenamento USB para o armazenamento em massa USB."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Existe um problema ao utilizar o cartão SD para armazenamento em massa USB."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index ad8f64a..5aeb79f 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Apaga os dados do telefone sem aviso, executando uma redefinição da configuração original"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do dispositivo"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configura o proxy global do dispositivo para ser usado enquanto a política estiver ativada. Somente o primeiro administrador do dispositivo pode configurar um verdadeiro proxy global."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir validade da senha"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle quanto tempo uma senha de bloqueio de tela deve ficar ativa antes de ser alterada"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Residencial"</item> <item msgid="869923650527136615">"Celular"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Muitas mensagens SMS estão sendo enviadas. Selecione \"OK\" para continuar ou \"Cancelar\" para interromper o envio."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Cancelar"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Definir"</string> <string name="default_permission_group" msgid="2690160991405646128">"Padrão"</string> <string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todas"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento USB em massa"</string> <string name="usb_storage_title" msgid="5901459041398751495">"Conectado por USB"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Você conectou o telefone ao computador via USB. Selecione o botão abaixo se quiser copiar arquivos entre seu computador e o armazenamento USB do Android."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Você conectou o telefone ao computador via USB. Selecione o botão abaixo se quiser copiar arquivos entre seu computador e o armazenamento USB do Android."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Você conectou ao computador via USB. Toque no botão abaixo se quiser copiar arquivos entre o computador e o armazenamento USB do seu Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Você conectou ao computador via USB. Toque no botão abaixo se quiser copiar arquivos entre o computador e o cartão SD do seu Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Ativar o armazenamento USB"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Há um problema com o uso do seu armazenamento USB para armazenamento USB em massa."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Há um problema com o uso do seu cartão SD para armazenamento USB em massa."</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index 527e4e1..a1cbc4a 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -904,6 +904,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Vulais Vus trametter in grond dumber da messadis SMS? Tschernì OK per cuntinuar u Interrumper per annullar la spediziun."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Interrumper"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Definir"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string> <string name="no_permissions" msgid="7283357728219338112">"Naginas permissiuns obligatoricas"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 486591c..f235957 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"În acest moment se trimit multe mesaje SMS. Selectaţi „OK” pentru a continua sau „Anulaţi” pentru a opri trimiterea."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Anulaţi"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Setaţi"</string> <string name="default_permission_group" msgid="2690160991405646128">"Prestabilit"</string> <string name="no_permissions" msgid="7283357728219338112">"Nu se solicită nicio permisiune"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 976a006..4e4bee8 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Уничтожить все данные на телефоне без предупреждения путем сброса настроек"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Глобальный прокси-сервер"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Настройте глобальный прокси-сервер устройства, который будет использоваться при активной политике. Глобальный прокси-сервер должен настроить первый администратор устройства."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Задать время действия пароля"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Задать время действия пароля перед появлением экрана блокировки"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Домашний"</item> <item msgid="869923650527136615">"Мобильный"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"считывать историю и закладки браузера"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Отправляется большое количество SMS-сообщений. Нажмите \"ОК\" для продолжения или \"Отмена\" для прекращения отправки."</string> <string name="sms_control_yes" msgid="2532062172402615953">"ОК"</string> <string name="sms_control_no" msgid="1715320703137199869">"Отмена"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Установить"</string> <string name="default_permission_group" msgid="2690160991405646128">"По умолчанию"</string> <string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Показать все"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"Запоминающее устройство USB"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB-подключение установлено"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Телефон подключен к компьютеру через порт USB. Для копирования файлов между компьютером и USB-накопителем Android нажмите кнопку ниже."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Телефон подключен к компьютеру через порт USB. Для копирования файлов между компьютером и USB-накопителем Android нажмите кнопку ниже."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Телефон подключен к компьютеру через порт USB. Нажмите приведенную ниже кнопку, чтобы скопировать файлы с компьютера на USB-накопитель устройства Android."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Телефон подключен к компьютеру через порт USB. Нажмите приведенную ниже кнопку, чтобы скопировать файлы с компьютера на SD-карту устройства Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Включить USB-накопитель"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"При использовании USB-накопителя в качестве запоминающего устройства USB возникла неполадка."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"При использовании SD-карты в качестве запоминающего устройства USB возникла неполадка."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 7e21ae5..d0413cf 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Odosiela sa veľký počet správ SMS. Ak chcete pokračovať, vyberte OK. Ak chcete odosielanie ukončiť, vyberte Zrušiť."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Zrušiť"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Nastaviť"</string> <string name="default_permission_group" msgid="2690160991405646128">"Predvolené"</string> <string name="no_permissions" msgid="7283357728219338112">"Nevyžadujú sa žiadne oprávnenia."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 68eb899..ddcc6e5 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"V pošiljanju je veliko sporočil SMS. Če želite nadaljevati, izberite »V redu«. Če želite pošiljanje ustaviti, izberite »Prekliči«."</string> <string name="sms_control_yes" msgid="2532062172402615953">"V redu"</string> <string name="sms_control_no" msgid="1715320703137199869">"Prekliči"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Nastavi"</string> <string name="default_permission_group" msgid="2690160991405646128">"Privzeto"</string> <string name="no_permissions" msgid="7283357728219338112">"Ni zahtevanih dovoljenj"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 5cf7223..7287793 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Шаље се велики број SMS порука. Кликните на „Потврди“ да бисте наставили или на „Откажи“ да бисте зауставили слање."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Потврди"</string> <string name="sms_control_no" msgid="1715320703137199869">"Откажи"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Подеси"</string> <string name="default_permission_group" msgid="2690160991405646128">"Подразумевано"</string> <string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index e5de6ad..3ace2e3 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Ta bort data från telefonen utan förvarning genom att återställa standardinställningarna"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Ange lösenordets utgångsdatum"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Se hur långt det är kvar till du måste ändra lösenordet till låsningsskärmen"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hem"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Flera SMS-meddelanden skickas. Tryck på OK om du vill fortsätta eller på Avbryt om du vill avsluta sändningen."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Avbryt"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Ställ in"</string> <string name="default_permission_group" msgid="2690160991405646128">"Standardinställning"</string> <string name="no_permissions" msgid="7283357728219338112">"Inga behörigheter krävs"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Visa alla"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masslagring"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB-ansluten"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har anslutit telefonen till datorn via USB. Välj knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har anslutit telefonen till datorn via USB. Välj knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Du har anslutit telefonen till datorn via USB. Tryck på knappen nedan om du vill kopiera filer mellan datorn och Android-telefonens USB-lagringsenhet."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Du har anslutit telefonen till datorn via USB. Tryck på knappen nedan om du vill kopiera filer mellan datorn och SD-kortet i din Android."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"Aktivera USB-lagring"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"Det gick inte att använda din USB-lagringsenhet för USB-masslagring."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"Det gick inte att använda ditt SD-kort för USB-masslagring."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 0113275..d1adc6a 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"กำลังส่งข้อความ SMS จำนวนมาก เลือก \"ตกลง\" เพื่อทำงานต่อหรือ \"ยกเลิก\" เพื่อหยุดส่ง"</string> <string name="sms_control_yes" msgid="2532062172402615953">"ตกลง"</string> <string name="sms_control_no" msgid="1715320703137199869">"ยกเลิก"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"ตั้งค่า"</string> <string name="default_permission_group" msgid="2690160991405646128">"เริ่มต้น"</string> <string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 0e28a1a..209b4b2 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Pinapadala ang malaking bilang ng mga SMS na mensahe. Piliin ang \"OK\" upang magpatuloy, o \"Kanselahin\" upang itigil ang pagpapadala."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Kanselahin"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Itakda"</string> <string name="default_permission_group" msgid="2690160991405646128">"Default"</string> <string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index a1abbeb..d24df0c 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan silin"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Cihaz genelinde geçerli proxy\'i ayarla"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlayın. Etkin genel proxy\'yi yalnızca ilk cihaz yöneticisi ayarlar."</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"Şifre süre sonu tarihi ayarla"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ekran kilitleme şifresinin ne kadar süre sonra değiştirilmesi gerekeceğini denetleyin."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Ev"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Çok sayıda SMS mesajı gönderiliyor. Devam etmek için \"Tamam\"ı, göndermeyi durdurmak için \"İptal\"i seçin."</string> <string name="sms_control_yes" msgid="2532062172402615953">"Tamam"</string> <string name="sms_control_no" msgid="1715320703137199869">"İptal"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Ayarla"</string> <string name="default_permission_group" msgid="2690160991405646128">"Varsayılan"</string> <string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"Tümünü göster"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB Yığın Depolama"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB bağlandı"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Telefonunuzu USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeyi seçin."</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Telefonunuzu USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeyi seçin."</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"Cihazınızı USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin USB depolama birimi arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeye dokunun."</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"Cihazınızı USB ile bilgisayarınıza bağladınız. Bilgisayarınız ile Android\'inizin SD kartı arasında dosya kopyalamak istiyorsanız aşağıdaki düğmeye dokunun."</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB depolama birimini aç"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"USB depolama biriminizi USB yığın depolama amaçlı kullanmayla ilgili bir sorun var."</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"SD kartınızı USB yığın dep br amaçlı kullanmada sorun var."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index f9eacb9..a029694 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Надсил-ся завелика к-сть SMS повідомл. Натисн. \"OK\", щоб продовж, або \"Скасувати\", щоб припин. надсил."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Скасувати"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Устан."</string> <string name="default_permission_group" msgid="2690160991405646128">"За умовч."</string> <string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index e33b7be..9c61058 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -925,6 +925,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"Một số lượng lớn các tin nhắn SMS đang được gửi. Chọn \"OK\" để tiếp tục hoặc \"Huỷ\" để dừng gửi."</string> <string name="sms_control_yes" msgid="2532062172402615953">"OK"</string> <string name="sms_control_no" msgid="1715320703137199869">"Huỷ"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"Đặt"</string> <string name="default_permission_group" msgid="2690160991405646128">"Mặc định"</string> <string name="no_permissions" msgid="7283357728219338112">"Không yêu cầu quyền"</string> diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml index fef3c13..c67bbb7 100644 --- a/core/res/res/values-xlarge/dimens.xml +++ b/core/res/res/values-xlarge/dimens.xml @@ -37,4 +37,7 @@ <dimen name="thumbnail_width">160dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> <dimen name="thumbnail_height">100dp</dimen> + <!-- The standard size (both width and height) of an application icon that + will be displayed in the app launcher and elsewhere. --> + <dimen name="app_icon_size">64dip</dimen> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 031d488..e67e102 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"恢复出厂设置时,将擦除手机上的数据而不发送警告"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"设置设备全局代理"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"请设置在启用政策的情况下要使用的设备全局代理。只有第一设备管理员才可设置有效的全局代理。"</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"设置密码有效期"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制屏幕锁定密码的使用期限"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"住宅"</item> <item msgid="869923650527136615">"手机"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"正在发送大量短信。选择“确定”继续,或选择“取消”停止发送。"</string> <string name="sms_control_yes" msgid="2532062172402615953">"确定"</string> <string name="sms_control_no" msgid="1715320703137199869">"取消"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"设置"</string> <string name="default_permission_group" msgid="2690160991405646128">"默认"</string> <string name="no_permissions" msgid="7283357728219338112">"不需要任何权限"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"全部显示"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 大容量存储设备"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB 已连接"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已通过 USB 将手机连接至计算机。如果您要在计算机与 Android 手机的 USB 存储设备之间复制文件,请点击下面的按钮。"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已通过 USB 将手机连接至计算机。如果您要在计算机与 Android 手机的 USB 存储设备之间复制文件,请点击下面的按钮。"</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已通过 USB 连接至计算机。如果您要在计算机与 Android 设备的 USB 存储设备之间复制文件,请触摸下面的按钮。"</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已通过 USB 连接至计算机。如果您要在计算机和 Android 设备的 SD 卡之间复制文件,请触摸下面的按钮。"</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"打开 USB 存储设备"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"使用 USB 存储设备作为 USB 大容量存储设备时出现问题。"</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"使用 SD 卡作为 USB 大容量存储设备时出现问题。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index c7cfe8e..bab4dfb 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -482,10 +482,8 @@ <string name="policydesc_wipeData" product="default" msgid="7669895333814222586">"執行重設為原廠設定時,系統會直接清除手機資料而不提出警告"</string> <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"設定裝置全域 Proxy"</string> <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"設定政策啟用時所要使用的裝置全域 Proxy,只有第一個裝置管理員所設定的全域 Proxy 具有效力。"</string> - <!-- no translation found for policylab_expirePassword (2314569545488269564) --> - <skip /> - <!-- no translation found for policydesc_expirePassword (7276906351852798814) --> - <skip /> + <string name="policylab_expirePassword" msgid="2314569545488269564">"設定密碼到期日"</string> + <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制螢幕鎖定密碼的使用期限"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"住家電話"</item> <item msgid="869923650527136615">"行動電話"</item> @@ -668,8 +666,7 @@ <string name="autofill_address_name_separator" msgid="2504700673286691795">" "</string> <!-- no translation found for autofill_address_summary_name_format (3268041054899214945) --> <skip /> - <!-- no translation found for autofill_address_summary_separator (7483307893170324129) --> - <skip /> + <string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string> <!-- no translation found for autofill_address_summary_format (4874459455786827344) --> <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"讀取瀏覽器的記錄與書籤"</string> @@ -869,6 +866,8 @@ <string name="sms_control_message" msgid="1289331457999236205">"即將傳送大量 SMS 簡訊。選取 [確定] 繼續或 [取消] 停止傳送。"</string> <string name="sms_control_yes" msgid="2532062172402615953">"確定"</string> <string name="sms_control_no" msgid="1715320703137199869">"取消"</string> + <!-- no translation found for time_picker_dialog_title (8349362623068819295) --> + <skip /> <string name="date_time_set" msgid="5777075614321087758">"設定"</string> <string name="default_permission_group" msgid="2690160991405646128">"預設值"</string> <string name="no_permissions" msgid="7283357728219338112">"無須許可"</string> @@ -876,8 +875,8 @@ <string name="perms_show_all" msgid="2671791163933091180"><b>"顯示全部"</b></string> <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 大量儲存裝置"</string> <string name="usb_storage_title" msgid="5901459041398751495">"USB 已連接"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"已透過 USB 連接手機與電腦。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請選取下方按鈕。"</string> - <!-- outdated translation 115779324551502062 --> <string name="usb_storage_message" product="default" msgid="4510858346516069238">"已透過 USB 連接手機與電腦。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請選取下方按鈕。"</string> + <string name="usb_storage_message" product="nosdcard" msgid="6631094834151575841">"您已透過 USB 與電腦建立連線。如要在電腦和 Android 的 USB 儲存裝置之間複製檔案,請輕觸下方按鈕。"</string> + <string name="usb_storage_message" product="default" msgid="4510858346516069238">"您已透過 USB 與電腦建立連線。如要在電腦和 Android 的 SD 卡之間複製檔案,請輕觸下方按鈕。"</string> <string name="usb_storage_button_mount" msgid="1052259930369508235">"開啟 USB 儲存裝置"</string> <string name="usb_storage_error_message" product="nosdcard" msgid="3276413764430468454">"使用您的 USB 儲存裝置作為 USB 大量儲存裝置時發生問題。"</string> <string name="usb_storage_error_message" product="default" msgid="120810397713773275">"使用您的 SD 卡作為 USB 大量儲存裝置時發生問題。"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5daa944..78465cf 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -602,9 +602,12 @@ <!-- Theme to use for alert dialogs spawned from this theme. --> <attr name="alertDialogTheme" format="reference" /> - <!-- Drawable to use for vertical dividers. --> + <!-- Drawable to use for generic vertical dividers. --> <attr name="dividerVertical" format="reference" /> + <!-- Drawable to use for generic horizontal dividers. --> + <attr name="dividerHorizontal" format="reference" /> + <!-- Style for button groups --> <attr name="buttonGroupStyle" format="reference" /> @@ -614,6 +617,9 @@ <!-- Background drawable for standalone items that need focus/pressed states. --> <attr name="selectableItemBackground" format="reference" /> + <!-- Style for buttons without an explicit border, often used in groups. --> + <attr name="borderlessButtonStyle" format="reference" /> + <!-- ============================ --> <!-- SearchView styles and assets --> <!-- ============================ --> @@ -1293,6 +1299,11 @@ <attr name="bottomBright" format="reference|color" /> <attr name="bottomMedium" format="reference|color" /> <attr name="centerMedium" format="reference|color" /> + <attr name="layout" /> + <attr name="listLayout" format="reference" /> + <attr name="multiChoiceItemLayout" format="reference" /> + <attr name="singleChoiceItemLayout" format="reference" /> + <attr name="listItemLayout" format="reference" /> </declare-styleable> <!-- Fragment animation class attributes. --> @@ -2178,6 +2189,17 @@ the minimum size of the largest child. If false, all children are measured normally. --> <attr name="measureWithLargestChild" format="boolean" /> + <!-- Drawable to use as a vertical divider between buttons. --> + <attr name="divider" /> + <!-- Setting for which dividers to show. --> + <attr name="showDividers"> + <flag name="none" value="0" /> + <flag name="beginning" value="1" /> + <flag name="middle" value="2" /> + <flag name="end" value="4" /> + </attr> + <!-- Size of padding on either end of a divider. --> + <attr name="dividerPadding" format="dimension" /> </declare-styleable> <declare-styleable name="ListView"> <!-- Reference to an array resource that will populate the ListView. For static content, @@ -2201,9 +2223,17 @@ </declare-styleable> <declare-styleable name="PreferenceFrameLayout"> <!-- Padding to use at the top of the prefs content. --> - <attr name="topPadding" format="dimension" /> + <attr name="borderTop" format="dimension" /> <!-- Padding to use at the bottom of the prefs content. --> - <attr name="bottomPadding" format="dimension" /> + <attr name="borderBottom" format="dimension" /> + <!-- Padding to use at the left of the prefs content. --> + <attr name="borderLeft" format="dimension" /> + <!-- Padding to use at the right of the prefs content. --> + <attr name="borderRight" format="dimension" /> + </declare-styleable> + <declare-styleable name="PreferenceFrameLayout_Layout"> + <!-- Padding to use at the top of the prefs content. --> + <attr name="layout_removeBorders" format="boolean" /> </declare-styleable> <declare-styleable name="MenuView"> <!-- Default appearance of menu item text. --> @@ -3410,21 +3440,18 @@ greater than 0 or infinite. The default value is restart. --> <attr name="repeatMode"/> <!-- Value the animation starts from. --> - <attr name="valueFrom" format="float|integer"/> + <attr name="valueFrom" format="float|integer|color|dimension"/> <!-- Value the animation animates to. --> - <attr name="valueTo" format="float|integer"/> + <attr name="valueTo" format="float|integer|color|dimension"/> <!-- The type of valueFrom and valueTo. --> <attr name="valueType"> - <!-- valueFrom and valueTo are floats. --> + <!-- valueFrom and valueTo are floats. This is the default value is valueType is + unspecified. Note that if either valueFrom or valueTo represent colors + (beginning with "#"), then this attribute is ignored and the color values are + interpreted as integers. --> <enum name="floatType" value="0" /> <!-- valueFrom and valueTo are integers. --> <enum name="intType" value="1" /> - <!-- valueFrom and valueTo are doubles. --> - <enum name="doubleType" value="2" /> - <!-- valueFrom and valueTo are colors. --> - <enum name="colorType" value="3" /> - <!-- valueFrom and valueTo are a custom type. --> - <enum name="customType" value="4" /> </attr> </declare-styleable> @@ -4542,11 +4569,7 @@ <!-- Drawable to use as a background for buttons added to this group. --> <attr name="buttonBackground" format="reference" /> <!-- Setting for which dividers to show. --> - <attr name="showDividers"> - <flag name="beginning" value="1" /> - <flag name="middle" value="2" /> - <flag name="end" value="4" /> - </attr> + <attr name="showDividers" /> </declare-styleable> <declare-styleable name="ActionBar_LayoutParams"> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e1460e1..3fac653 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1392,6 +1392,10 @@ <public type="attr" name="rotation"/> <public type="attr" name="rotationX"/> <public type="attr" name="rotationY"/> + <public type="attr" name="showDividers" /> + <public type="attr" name="dividerPadding" /> + <public type="attr" name="borderlessButtonStyle" /> + <public type="attr" name="dividerHorizontal" /> <public type="anim" name="animator_fade_in" /> <public type="anim" name="animator_fade_out" /> @@ -1555,6 +1559,7 @@ <public type="style" name="Widget.Holo.Light.ActionMode" /> <public type="style" name="Widget.Holo.Light.ActionButton.CloseMode" /> <public type="style" name="Widget.Holo.Light.ActionBar" /> + <public type="style" name="Widget.Holo.Button.Borderless" /> <public type="string" name="selectTextMode" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index eafa9b6..0f5ff05 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1806,7 +1806,7 @@ <!-- Do not translate. WebView User Agent string --> <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>) - AppleWebKit/534.12 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.12</string> + AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.13</string> <!-- Do not translate. WebView User Agent targeted content --> <string name="web_user_agent_target_content" translatable="false">"Mobile "</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index b3c3e0d..b7c11a8 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -50,8 +50,10 @@ </style> <style name="Widget.PreferenceFrameLayout"> - <item name="android:topPadding">0dip</item> - <item name="android:bottomPadding">0dip</item> + <item name="android:borderTop">0dip</item> + <item name="android:borderBottom">0dip</item> + <item name="android:borderLeft">0dip</item> + <item name="android:borderRight">0dip</item> </style> <!-- Base style for animations. This style specifies no animations. --> @@ -1058,7 +1060,6 @@ </style> <style name="TextAppearance.Holo.Medium" parent="TextAppearance.Medium"> - <item name="android:textLineHeight">24dip</item> </style> <style name="TextAppearance.Holo.Small" parent="TextAppearance.Small"> @@ -1302,6 +1303,10 @@ <item name="android:paddingBottom">4dip</item> </style> + <style name="Widget.Holo.Button.Borderless"> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + <style name="Widget.Holo.Button.Small"> <item name="android:background">@android:drawable/btn_default_holo_dark</item> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> @@ -1331,7 +1336,7 @@ <style name="Widget.Holo.ButtonGroup" parent="Widget.ButtonGroup"> <item name="divider">?android:attr/dividerVertical</item> <item name="showDividers">middle</item> - <item name="android:background">@android:drawable/btn_default_holo_dark</item> + <item name="dividerPadding">8dip</item> </style> <style name="Widget.Holo.ButtonGroup.AlertDialog"> @@ -1699,7 +1704,7 @@ <style name="Widget.Holo.Light.ListView.DropDown"> </style> - <style name="Widget.Holo.Light.EditText" parent="Widget.EditText"> + <style name="Widget.Holo.Light.EditText" parent="Widget.Holo.EditText"> </style> <style name="Widget.Holo.Light.ExpandableListView" parent="Widget.ExpandableListView"> @@ -1922,6 +1927,11 @@ <item name="bottomBright">@android:drawable/dialog_bottom_holo_dark</item> <item name="bottomMedium">@android:drawable/dialog_bottom_holo_dark</item> <item name="centerMedium">@android:drawable/dialog_middle_holo_dark</item> + <item name="layout">@android:layout/alert_dialog_holo</item> + <item name="listLayout">@android:layout/select_dialog_holo</item> + <item name="listItemLayout">@android:layout/select_dialog_item_holo</item> + <item name="multiChoiceItemLayout">@android:layout/select_dialog_multichoice_holo</item> + <item name="singleChoiceItemLayout">@android:layout/select_dialog_singlechoice_holo</item> </style> <style name="AlertDialog.Holo.Light"> @@ -1962,7 +1972,9 @@ </style> <style name="Widget.Holo.PreferenceFrameLayout"> - <item name="android:topPadding">48dip</item> - <item name="android:bottomPadding">48dip</item> + <item name="android:borderTop">48dip</item> + <item name="android:borderBottom">48dip</item> + <item name="android:borderLeft">32dip</item> + <item name="android:borderRight">32dip</item> </style> </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index e1040d9..380d63b 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -92,6 +92,7 @@ <item name="groupButtonBackground">@null</item> <item name="selectableItemBackground">@android:drawable/item_background</item> + <item name="borderlessButtonStyle">?android:attr/buttonStyle</item> <item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item> <!-- List attributes --> @@ -258,6 +259,7 @@ <item name="actionBarSize">56dip</item> <item name="dividerVertical">@drawable/divider_vertical_dark</item> + <item name="dividerHorizontal">@drawable/divider_vertical_dark</item> <item name="buttonGroupStyle">@android:style/Widget.ButtonGroup</item> <!-- SearchView attributes --> @@ -561,6 +563,21 @@ <item name="android:windowNoTitle">true</item> </style> + <!-- Default holo light theme for panel windows. This removes all extraneous + window decorations, so you basically have an empty rectangle in which + to place your content. It makes the window floating, with a transparent + background, and turns off dimming behind the window. --> + <style name="Theme.Holo.Light.Panel"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowFrame">@null</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowIsFloating">true</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowNoTitle">true</item> + </style> + <!-- Default theme for input methods, which is used by the {@link android.inputmethodservice.InputMethodService} class. this inherits from Theme.NoTitleBar, but makes the background @@ -575,7 +592,7 @@ </style> <!-- Theme for the search input bar. --> - <style name="Theme.SearchBar" parent="Theme.Panel"> + <style name="Theme.SearchBar" parent="Theme.Holo.Light.Panel"> <item name="windowContentOverlay">@null</item> </style> @@ -723,6 +740,7 @@ <item name="groupButtonBackground">@android:drawable/group_button_background_holo_dark</item> <item name="selectableItemBackground">@android:drawable/item_background_holo_dark</item> + <item name="borderlessButtonStyle">@android:style/Widget.Holo.Button.Borderless</item> <item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item> <!-- List attributes --> @@ -883,6 +901,7 @@ <item name="actionBarSize">56dip</item> <item name="dividerVertical">@drawable/divider_vertical_holo_dark</item> + <item name="dividerHorizontal">@drawable/divider_vertical_holo_dark</item> <item name="buttonGroupStyle">@android:style/Widget.Holo.ButtonGroup</item> <!-- SearchView attributes --> @@ -1046,20 +1065,20 @@ <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item> <!-- Widget styles --> - <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item> + <item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item> <item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.Light.AutoCompleteTextView</item> - <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item> + <item name="checkboxStyle">@android:style/Widget.Holo.Light.CompoundButton.CheckBox</item> <item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item> - <item name="editTextStyle">@android:style/Widget.Holo.EditText</item> - <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item> - <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item> - <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item> - <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item> - <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item> - <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item> - <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item> - <item name="listViewStyle">@android:style/Widget.Holo.ListView</item> - <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item> + <item name="editTextStyle">@android:style/Widget.Holo.Light.EditText</item> + <item name="expandableListViewStyle">@android:style/Widget.Holo.Light.ExpandableListView</item> + <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.Light.ExpandableListView.White</item> + <item name="galleryStyle">@android:style/Widget.Holo.Light.Gallery</item> + <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.Light.GestureOverlayView</item> + <item name="gridViewStyle">@android:style/Widget.Holo.Light.GridView</item> + <item name="imageButtonStyle">@android:style/Widget.Holo.Light.ImageButton</item> + <item name="imageWellStyle">@android:style/Widget.Holo.Light.ImageWell</item> + <item name="listViewStyle">@android:style/Widget.Holo.Light.ListView</item> + <item name="listViewWhiteStyle">@android:style/Widget.Holo.Light.ListView.White</item> <item name="popupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow</item> <item name="progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar</item> <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item> @@ -1069,22 +1088,22 @@ <item name="progressBarStyleInverse">@android:style/Widget.Holo.Light.ProgressBar.Inverse</item> <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.Light.ProgressBar.Small.Inverse</item> <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.Light.ProgressBar.Large.Inverse</item> - <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item> - <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item> - <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item> - <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item> - <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item> - <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item> - <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item> + <item name="seekBarStyle">@android:style/Widget.Holo.Light.SeekBar</item> + <item name="ratingBarStyle">@android:style/Widget.Holo.Light.RatingBar</item> + <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.Light.RatingBar.Indicator</item> + <item name="ratingBarStyleSmall">@android:style/Widget.Holo.Light.RatingBar.Small</item> + <item name="radioButtonStyle">@android:style/Widget.Holo.Light.CompoundButton.RadioButton</item> + <item name="scrollViewStyle">@android:style/Widget.Holo.Light.ScrollView</item> + <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.Light.HorizontalScrollView</item> <item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item> <item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Light.Spinner.DropDown</item> - <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item> - <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item> - <item name="textViewStyle">@android:style/Widget.Holo.TextView</item> - <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item> - <item name="webViewStyle">@android:style/Widget.Holo.WebView</item> - <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item> - <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item> + <item name="starStyle">@android:style/Widget.Holo.Light.CompoundButton.Star</item> + <item name="tabWidgetStyle">@android:style/Widget.Holo.Light.TabWidget</item> + <item name="textViewStyle">@android:style/Widget.Holo.Light.TextView</item> + <item name="webTextViewStyle">@android:style/Widget.Holo.Light.WebTextView</item> + <item name="webViewStyle">@android:style/Widget.Holo.Light.WebView</item> + <item name="dropDownItemStyle">@android:style/Widget.Holo.Light.DropDownItem</item> + <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.Light.DropDownItem.Spinner</item> <item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item> <item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item> <item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item> @@ -1128,6 +1147,7 @@ <item name="actionBarSize">56dip</item> <item name="dividerVertical">@drawable/divider_vertical_holo_light</item> + <item name="dividerHorizontal">@drawable/divider_vertical_holo_light</item> <item name="buttonGroupStyle">@android:style/Widget.Holo.Light.ButtonGroup</item> <!-- SearchView attributes --> diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml index 2b0e4af..5e3252c 100755 --- a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml +++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml @@ -4,7 +4,7 @@ <ssid>opennet</ssid> <security>NONE</security> </accesspoint> - <accesspoint> + <accesspoint> <ssid>GoogleGuest</ssid> <security>NONE</security> </accesspoint> @@ -14,6 +14,16 @@ <password>androidwifi</password> </accesspoint> <accesspoint> + <ssid>securenetstatic</ssid> + <security>PSK</security> + <password>androidwifi</password> + <ip>192.168.14.2</ip> + <gateway>192.168.14.1</gateway> + <networkprefixlength>24</networkprefixlength> + <dns1>192.168.14.1</dns1> + <dns2>192.168.1.9</dns2> + </accesspoint> + <accesspoint> <ssid>botnet</ssid> <security>EAP</security> <eap>PEAP</eap> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java index 863fbe6..21f1bfc 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java @@ -25,11 +25,18 @@ import org.xml.sax.helpers.DefaultHandler; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.AuthAlgorithm; +import android.net.wifi.WifiConfiguration.IpAssignment; import android.net.wifi.WifiConfiguration.KeyMgmt; - +import android.net.wifi.WifiConfiguration.ProxySettings; +import android.net.LinkAddress; +import android.net.LinkProperties; import android.util.Log; + import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; @@ -38,7 +45,8 @@ import java.util.List; * The configurations of an access point is included in tag * <accesspoint></accesspoint>. The supported configuration includes: ssid, * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert, - * in which each is included in the corresponding tags. All access points have to be + * in which each is included in the corresponding tags. Static IP setting is also supported. + * Tags that can be used include: ip, gateway, netmask, dns1, dns2. All access points have to be * enclosed in tags of <resources></resources>. * * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2. @@ -52,6 +60,9 @@ import java.util.List; * <password>abcdefgh</password> * </accesspoint> * </resources> + * + * Note:ssid and security have to be the first two tags + * for static ip setting, tag "ip" should be listed before other fields: dns, gateway, netmask. */ public class AccessPointParserHelper { private static final String KEYSTORE_SPACE = "keystore://"; @@ -93,9 +104,11 @@ public class AccessPointParserHelper { boolean security = false; boolean password = false; boolean ip = false; - boolean subnetmask = false; boolean gateway = false; - boolean dns = false; + boolean networkprefix = false; + boolean netmask = false; + boolean dns1 = false; + boolean dns2 = false; boolean eap = false; boolean phase2 = false; boolean identity = false; @@ -104,6 +117,8 @@ public class AccessPointParserHelper { boolean usercert = false; WifiConfiguration config = null; int securityType = NONE; + LinkProperties mLinkProperties = null; + InetAddress mInetAddr = null; @Override public void startElement(String uri, String localName, String tagName, @@ -138,12 +153,37 @@ public class AccessPointParserHelper { if (tagName.equalsIgnoreCase("usercert")) { usercert = true; } + if (tagName.equalsIgnoreCase("ip")) { + mLinkProperties = new LinkProperties(); + ip = true; + } + if (tagName.equalsIgnoreCase("gateway")) { + gateway = true; + } + if (tagName.equalsIgnoreCase("networkprefixlength")) { + networkprefix = true; + } + if (tagName.equalsIgnoreCase("netmask")) { + netmask = true; + } + if (tagName.equalsIgnoreCase("dns1")) { + dns1 = true; + } + if (tagName.equalsIgnoreCase("dns2")) { + dns2 = true; + } } @Override public void endElement(String uri, String localName, String tagName) throws SAXException { - Log.v(TAG, "endElement: " + tagName); if (tagName.equalsIgnoreCase("accesspoint")) { + if (mLinkProperties != null) { + config.ipAssignment = IpAssignment.STATIC; + config.linkProperties = mLinkProperties; + } else { + config.ipAssignment = IpAssignment.DHCP; + } + config.proxySettings = ProxySettings.NONE; networks.add(config); } } @@ -152,14 +192,11 @@ public class AccessPointParserHelper { public void characters(char ch[], int start, int length) throws SAXException { if (ssid) { config.SSID = new String(ch, start, length); - Log.v(TAG, "ssid: " + config.SSID); ssid = false; } if (security) { String securityStr = (new String(ch, start, length)).toUpperCase(); - Log.v(TAG, "security: " + securityStr); securityType = getSecurityType(securityStr); - Log.v(TAG, "securityType = " + securityType); switch (securityType) { case NONE: config.allowedKeyManagement.set(KeyMgmt.NONE); @@ -175,6 +212,13 @@ public class AccessPointParserHelper { case EAP: config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); + // Initialize other fields. + config.phase2.setValue(""); + config.ca_cert.setValue(""); + config.client_cert.setValue(""); + config.private_key.setValue(""); + config.identity.setValue(""); + config.anonymous_identity.setValue(""); break; default: throw new SAXException(); @@ -187,7 +231,6 @@ public class AccessPointParserHelper { if (len == 0) { throw new SAXException(); } - Log.v(TAG, "passwordStr:" + passwordStr); if (securityType == WEP) { if ((len == 10 || len == 26 || len == 58) && passwordStr.matches("[0-9A-Fa-f]*")) { @@ -242,21 +285,94 @@ public class AccessPointParserHelper { config.client_cert.setValue(KEYSTORE_SPACE); usercert = false; } + if (ip) { + try { + String ipAddr = new String(ch, start, length); + if (!InetAddress.isNumeric(ipAddr)) { + throw new SAXException(); + } + mInetAddr = InetAddress.getByName(ipAddr); + } catch (UnknownHostException e) { + throw new SAXException(); + } + ip = false; + } + if (gateway) { + try { + String gwAddr = new String(ch, start, length); + if (!InetAddress.isNumeric(gwAddr)) { + throw new SAXException(); + } + mLinkProperties.setGateway(InetAddress.getByName(gwAddr)); + } catch (UnknownHostException e) { + throw new SAXException(); + } + gateway = false; + } + if (networkprefix) { + try { + int nwPrefixLength = Integer.parseInt(new String(ch, start, length)); + if ((nwPrefixLength < 0) || (nwPrefixLength > 32)) { + throw new SAXException(); + } + mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, nwPrefixLength)); + } catch (NumberFormatException e) { + throw new SAXException(); + } + networkprefix = false; + } + if (netmask) { + try { + String netMaskStr = new String(ch, start, length); + if (!InetAddress.isNumeric(netMaskStr)) { + throw new SAXException(); + } + InetAddress netMaskAddr = InetAddress.getByName(netMaskStr); + mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, netMaskAddr)); + } catch (UnknownHostException e) { + throw new SAXException(); + } + netmask = false; + } + if (dns1) { + try { + String dnsAddr = new String(ch, start, length); + if (!InetAddress.isNumeric(dnsAddr)) { + throw new SAXException(); + } + mLinkProperties.addDns(InetAddress.getByName(dnsAddr)); + } catch (UnknownHostException e) { + throw new SAXException(); + } + dns1 = false; + } + if (dns2) { + try { + String dnsAddr = new String(ch, start, length); + if (!InetAddress.isNumeric(dnsAddr)) { + throw new SAXException(); + } + mLinkProperties.addDns(InetAddress.getByName(dnsAddr)); + } catch (UnknownHostException e) { + throw new SAXException(); + } + dns2 = false; + } } }; - public AccessPointParserHelper() { - } - /** - * Process the accesspoint.xml file - * @return List of WifiConfiguration - * @throws Exception when parsing the XML file + * Process the InputStream in + * @param in is the InputStream that can be used for XML parsing + * @throws Exception */ - public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception { + public AccessPointParserHelper(InputStream in) throws Exception { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(in, mHandler); + } + + public List<WifiConfiguration> getNetworkConfigurations() throws Exception { return networks; } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index 37b9f52..2888696 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -16,10 +16,8 @@ package com.android.connectivitymanagertest; -import com.android.connectivitymanagertest.R; import android.app.Activity; import android.content.Context; -import android.content.res.Resources; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; @@ -36,7 +34,6 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.State; -import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiInfo; @@ -52,7 +49,7 @@ public class ConnectivityManagerTestActivity extends Activity { public static final String LOG_TAG = "ConnectivityManagerTestActivity"; public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds - public static final int WIFI_SCAN_TIMEOUT = 20 * 1000; + public static final int WIFI_SCAN_TIMEOUT = 50 * 1000; public static final int SHORT_TIMEOUT = 5 * 1000; public static final long LONG_TIMEOUT = 50 * 1000; public static final int SUCCESS = 0; // for Wifi tethering state change @@ -61,6 +58,7 @@ public class ConnectivityManagerTestActivity extends Activity { private static final String ACCESS_POINT_FILE = "accesspoints.xml"; public ConnectivityReceiver mConnectivityReceiver = null; public WifiReceiver mWifiReceiver = null; + private AccessPointParserHelper mParseHelper = null; /* * Track network connectivity information */ @@ -101,7 +99,7 @@ public class ConnectivityManagerTestActivity extends Activity { private class ConnectivityReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent); + log("ConnectivityReceiver: onReceive() is called with " + intent); String action = intent.getAction(); if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { Log.v("ConnectivityReceiver", "onReceive() called with " + intent); @@ -126,9 +124,9 @@ public class ConnectivityManagerTestActivity extends Activity { mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON); mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); - Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString()); + log("mNetworkInfo: " + mNetworkInfo.toString()); if (mOtherNetworkInfo != null) { - Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString()); + log("mOtherNetworkInfo: " + mOtherNetworkInfo.toString()); } recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState()); if (mOtherNetworkInfo != null) { @@ -148,7 +146,7 @@ public class ConnectivityManagerTestActivity extends Activity { } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { mWifiNetworkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString()); + log("mWifiNetworkInfo: " + mWifiNetworkInfo.toString()); if (mWifiNetworkInfo.getState() == State.CONNECTED) { mBssid = intent.getStringExtra(WifiManager.EXTRA_BSSID); } @@ -181,7 +179,7 @@ public class ConnectivityManagerTestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode())); + log("onCreate, inst=" + Integer.toHexString(hashCode())); // Create a simple layout LinearLayout contentView = new LinearLayout(this); @@ -212,7 +210,7 @@ public class ConnectivityManagerTestActivity extends Activity { initializeNetworkStates(); if (mWifiManager.isWifiEnabled()) { - Log.v(LOG_TAG, "Clear Wifi before we start the test."); + log("Clear Wifi before we start the test."); removeConfiguredNetworksAndDisableWifi(); } mWifiRegexs = mCM.getTetherableWifiRegexs(); @@ -220,32 +218,22 @@ public class ConnectivityManagerTestActivity extends Activity { public List<WifiConfiguration> loadNetworkConfigurations() throws Exception { InputStream in = getAssets().open(ACCESS_POINT_FILE); - AccessPointParserHelper parseHelper = new AccessPointParserHelper(); - return parseHelper.processAccessPoint(in); - } - - private void printNetConfig(String[] configuration) { - for (int i = 0; i < configuration.length; i++) { - if (i == 0) { - Log.v(LOG_TAG, "SSID: " + configuration[0]); - } else { - Log.v(LOG_TAG, " " + configuration[i]); - } - } + mParseHelper = new AccessPointParserHelper(in); + return mParseHelper.getNetworkConfigurations(); } // for each network type, initialize network states to UNKNOWN, and no verification flag is set public void initializeNetworkStates() { for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) { connectivityState[networkType] = new NetworkState(); - Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " + + log("Initialize network state for " + networkType + ": " + connectivityState[networkType].toString()); } } // deposit a network state public void recordNetworkState(int networkType, State networkState) { - Log.v(LOG_TAG, "record network state for network " + networkType + + log("record network state for network " + networkType + ", state is " + networkState); connectivityState[networkType].recordState(networkState); } @@ -259,40 +247,40 @@ public class ConnectivityManagerTestActivity extends Activity { // Validate the states recorded public boolean validateNetworkStates(int networkType) { - Log.v(LOG_TAG, "validate network state for " + networkType + ": "); + log("validate network state for " + networkType + ": "); return connectivityState[networkType].validateStateTransition(); } // return result from network state validation public String getTransitionFailureReason(int networkType) { - Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " + + log("get network state transition failure reason for " + networkType + ": " + connectivityState[networkType].toString()); return connectivityState[networkType].getReason(); } private void notifyNetworkConnectivityChange() { synchronized(connectivityObject) { - Log.v(LOG_TAG, "notify network connectivity changed"); + log("notify network connectivity changed"); connectivityObject.notifyAll(); } } private void notifyScanResult() { synchronized (this) { - Log.v(LOG_TAG, "notify that scan results are available"); + log("notify that scan results are available"); this.notify(); } } private void notifyWifiState() { synchronized (wifiObject) { - Log.v(LOG_TAG, "notify wifi state changed"); + log("notify wifi state changed"); wifiObject.notify(); } } private void notifyWifiAPState() { synchronized (this) { - Log.v(LOG_TAG, "notify wifi AP state changed"); + log("notify wifi AP state changed"); this.notify(); } } @@ -306,7 +294,7 @@ public class ConnectivityManagerTestActivity extends Activity { for (Object obj: tethered) { String str = (String)obj; for (String tethRex: mWifiRegexs) { - Log.v(LOG_TAG, "str: " + str +"tethRex: " + tethRex); + log("str: " + str +"tethRex: " + tethRex); if (str.matches(tethRex)) { wifiTethered = true; } @@ -316,7 +304,7 @@ public class ConnectivityManagerTestActivity extends Activity { for (Object obj: errored) { String str = (String)obj; for (String tethRex: mWifiRegexs) { - Log.v(LOG_TAG, "error: str: " + str +"tethRex: " + tethRex); + log("error: str: " + str +"tethRex: " + tethRex); if (str.matches(tethRex)) { wifiErrored = true; } @@ -328,7 +316,7 @@ public class ConnectivityManagerTestActivity extends Activity { } else if (wifiErrored) { mWifiTetherResult = FAILURE; // wifi tethering failed } - Log.v(LOG_TAG, "mWifiTetherResult: " + mWifiTetherResult); + log("mWifiTetherResult: " + mWifiTetherResult); this.notify(); } } @@ -344,12 +332,12 @@ public class ConnectivityManagerTestActivity extends Activity { return false; } else { // the broadcast has been sent out. the state has been changed. - Log.v(LOG_TAG, "networktype: " + networkType + " state: " + + log("networktype: " + networkType + " state: " + mCM.getNetworkInfo(networkType)); return true; } } - Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + + log("Wait for the connectivity state for network: " + networkType + " to be " + expectedState.toString()); synchronized (connectivityObject) { try { @@ -359,7 +347,7 @@ public class ConnectivityManagerTestActivity extends Activity { } if ((mNetworkInfo.getType() != networkType) || (mNetworkInfo.getState() != expectedState)) { - Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() + + log("network state for " + mNetworkInfo.getType() + "is: " + mNetworkInfo.getState()); continue; } @@ -380,7 +368,7 @@ public class ConnectivityManagerTestActivity extends Activity { return true; } } - Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); + log("Wait for wifi state to be: " + expectedState); synchronized (wifiObject) { try { wifiObject.wait(SHORT_TIMEOUT); @@ -388,7 +376,7 @@ public class ConnectivityManagerTestActivity extends Activity { e.printStackTrace(); } if (mWifiState != expectedState) { - Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState()); + log("Wifi state is: " + mWifiState); continue; } return true; @@ -408,7 +396,7 @@ public class ConnectivityManagerTestActivity extends Activity { return true; } } - Log.v(LOG_TAG, "Wait for wifi AP state to be: " + expectedState); + log("Wait for wifi AP state to be: " + expectedState); synchronized (wifiObject) { try { wifiObject.wait(SHORT_TIMEOUT); @@ -416,7 +404,7 @@ public class ConnectivityManagerTestActivity extends Activity { e.printStackTrace(); } if (mWifiManager.getWifiApState() != expectedState) { - Log.v(LOG_TAG, "Wifi state is: " + mWifiManager.getWifiApState()); + log("Wifi state is: " + mWifiManager.getWifiApState()); continue; } return true; @@ -436,7 +424,7 @@ public class ConnectivityManagerTestActivity extends Activity { if ((System.currentTimeMillis() - startTime) > timeout) { return mWifiTetherResult; } - Log.v(LOG_TAG, "Wait for wifi tethering result."); + log("Wait for wifi tethering result."); synchronized (this) { try { this.wait(SHORT_TIMEOUT); @@ -490,64 +478,61 @@ public class ConnectivityManagerTestActivity extends Activity { //If Wifi is not enabled, enable it if (!mWifiManager.isWifiEnabled()) { - Log.v(LOG_TAG, "Wifi is not enabled, enable it"); + log("Wifi is not enabled, enable it"); mWifiManager.setWifiEnabled(true); + // wait for the wifi state change before start scanning. + if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2*SHORT_TIMEOUT)) { + log("wait for WIFI_STATE_ENABLED failed"); + return false; + } } - List<ScanResult> netList = mWifiManager.getScanResults(); - if (netList == null) { - Log.v(LOG_TAG, "scan results are null"); - // if no scan results are available, start active scan - mWifiManager.startScanActive(); - mScanResultIsAvailable = false; - long startTime = System.currentTimeMillis(); - while (!mScanResultIsAvailable) { - if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) { - return false; + boolean foundApInScanResults = false; + for (int retry = 0; retry < 5; retry++) { + List<ScanResult> netList = mWifiManager.getScanResults(); + if (netList != null) { + log("size of scan result list: " + netList.size()); + for (int i = 0; i < netList.size(); i++) { + ScanResult sr= netList.get(i); + if (sr.SSID.equals(ssid)) { + log("found " + ssid + " in the scan result list"); + log("retry: " + retry); + foundApInScanResults = true; + mWifiManager.connectNetwork(config); + break; + } } - // wait for the scan results to be available - synchronized (this) { - // wait for the scan result to be available - try { - this.wait(WAIT_FOR_SCAN_RESULT); - } catch (InterruptedException e) { - e.printStackTrace(); + } + if (foundApInScanResults) { + return true; + } else { + // Start an active scan + mWifiManager.startScanActive(); + mScanResultIsAvailable = false; + long startTime = System.currentTimeMillis(); + while (!mScanResultIsAvailable) { + if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) { + log("wait for scan results timeout"); + return false; } - if ((mWifiManager.getScanResults() == null) || - (mWifiManager.getScanResults().size() <= 0)) { - continue; + // wait for the scan results to be available + synchronized (this) { + // wait for the scan result to be available + try { + this.wait(WAIT_FOR_SCAN_RESULT); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if ((mWifiManager.getScanResults() == null) || + (mWifiManager.getScanResults().size() <= 0)) { + continue; + } + mScanResultIsAvailable = true; } - mScanResultIsAvailable = true; } } } - - netList = mWifiManager.getScanResults(); - - for (int i = 0; i < netList.size(); i++) { - ScanResult sr= netList.get(i); - if (sr.SSID.equals(ssid)) { - Log.v(LOG_TAG, "found " + ssid + " in the scan result list"); - int networkId = mWifiManager.addNetwork(config); - // Connect to network by disabling others. - mWifiManager.enableNetwork(networkId, true); - mWifiManager.saveConfiguration(); - List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks(); - for (WifiConfiguration netConfig : wifiNetworks) { - Log.v(LOG_TAG, netConfig.toString()); - } - - mWifiManager.reconnect(); - break; - } - } - - List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks(); - if (netConfList.size() <= 0) { - Log.v(LOG_TAG, ssid + " is not available"); - return false; - } - return true; + return false; } /* @@ -555,27 +540,13 @@ public class ConnectivityManagerTestActivity extends Activity { */ public boolean disconnectAP() { if (mWifiManager.isWifiEnabled()) { - //remove the current network Id - WifiInfo curWifi = mWifiManager.getConnectionInfo(); - if (curWifi == null) { - return false; - } - int curNetworkId = curWifi.getNetworkId(); - mWifiManager.removeNetwork(curNetworkId); - mWifiManager.saveConfiguration(); - - // remove other saved networks - List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks(); - if (netConfList != null) { - Log.v(LOG_TAG, "remove configured network ids"); - for (int i = 0; i < netConfList.size(); i++) { - WifiConfiguration conf = new WifiConfiguration(); - conf = netConfList.get(i); - mWifiManager.removeNetwork(conf.networkId); - } + // remove saved networks + List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration wifiConfig: wifiConfigList) { + log("remove wifi configuration: " + wifiConfig.toString()); + mWifiManager.forgetNetwork(wifiConfig.networkId); } } - mWifiManager.saveConfiguration(); return true; } /** @@ -599,7 +570,7 @@ public class ConnectivityManagerTestActivity extends Activity { } // Wait for the actions to be completed try { - Thread.sleep(5*1000); + Thread.sleep(SHORT_TIMEOUT); } catch (InterruptedException e) {} return true; } @@ -632,7 +603,7 @@ public class ConnectivityManagerTestActivity extends Activity { if (mWifiReceiver != null) { unregisterReceiver(mWifiReceiver); } - Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode())); + log("onDestroy, inst=" + Integer.toHexString(hashCode())); } @Override @@ -674,4 +645,8 @@ public class ConnectivityManagerTestActivity extends Activity { } return super.onKeyDown(keyCode, event); } + + private void log(String message) { + Log.v(LOG_TAG, message); + } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java index 69eb5db..9c72102 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java @@ -17,26 +17,33 @@ package com.android.connectivitymanagertest.functional; import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; -import com.android.connectivitymanagertest.NetworkState; +import com.android.connectivitymanagertest.ConnectivityManagerTestRunner; import android.R; import android.app.Activity; +import android.content.ContentResolver; import android.content.Intent; import android.content.Context; import android.content.res.Resources; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.net.wifi.WifiConfiguration.Status; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.ConnectivityManager; +import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.State; +import android.provider.Settings; import android.test.suitebuilder.annotation.LargeTest; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Test Wi-Fi connection with different configuration @@ -48,18 +55,25 @@ import java.util.List; public class WifiConnectionTest extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { private static final String TAG = "WifiConnectionTest"; - private static final boolean DEBUG = true; - private static final String PKG_NAME = "com.android.connectivitymanagertests"; + private static final boolean DEBUG = false; private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); private ConnectivityManagerTestActivity mAct; + private ConnectivityManagerTestRunner mRunner; + private WifiManager mWifiManager = null; + private Set<WifiConfiguration> enabledNetworks = null; public WifiConnectionTest() { - super(PKG_NAME, ConnectivityManagerTestActivity.class); + super(ConnectivityManagerTestActivity.class); } @Override public void setUp() throws Exception { super.setUp(); + log("before we launch the test activity, we preserve all the configured networks."); + mRunner = ((ConnectivityManagerTestRunner)getInstrumentation()); + mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE); + enabledNetworks = getEnabledNetworks(mWifiManager.getConfiguredNetworks()); + mAct = getActivity(); networks = mAct.loadNetworkConfigurations(); if (DEBUG) { @@ -68,30 +82,61 @@ public class WifiConnectionTest // enable Wifi and verify wpa_supplicant is started assertTrue("enable Wifi failed", mAct.enableWifi()); - try { - Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); - } catch (Exception e) { - fail("interrupted while waiting for WPA_SUPPLICANT to start"); - } + sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT, + "interrupted while waiting for WPA_SUPPLICANT to start"); WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo(); assertNotNull(mConnection); assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant()); } private void printNetworkConfigurations() { - Log.v(TAG, "==== print network configurations parsed from XML file ===="); - Log.v(TAG, "number of access points: " + networks.size()); + log("==== print network configurations parsed from XML file ===="); + log("number of access points: " + networks.size()); for (WifiConfiguration config : networks) { - Log.v(TAG, config.toString()); + log(config.toString()); } } @Override public void tearDown() throws Exception { + log("tearDown()"); mAct.removeConfiguredNetworksAndDisableWifi(); + reEnableNetworks(enabledNetworks); super.tearDown(); } + private Set<WifiConfiguration> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) { + Set<WifiConfiguration> networks = new HashSet<WifiConfiguration>(); + for (WifiConfiguration wifiConfig : configuredNetworks) { + if (wifiConfig.status == Status.ENABLED || wifiConfig.status == Status.CURRENT) { + networks.add(wifiConfig); + log("remembering enabled network " + wifiConfig.SSID + + " status is " + wifiConfig.status); + } + } + return networks; + } + + private void reEnableNetworks(Set<WifiConfiguration> enabledNetworks) { + if (!mWifiManager.isWifiEnabled()) { + log("reEnableNetworks: enable Wifi"); + mWifiManager.setWifiEnabled(true); + sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT, + "interruped while waiting for wifi to be enabled"); + } + + for (WifiConfiguration config : enabledNetworks) { + if (DEBUG) { + log("recover wifi configuration: " + config.toString()); + } + config.SSID = "\"" + config.SSID + "\""; + config.networkId = -1; + mWifiManager.connectNetwork(config); + sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT, + "interruped while connecting to " + config.SSID); + } + } + /** * Connect to the provided Wi-Fi network * @param config is the network configuration @@ -103,32 +148,40 @@ public class WifiConnectionTest mAct.connectToWifiWithConfiguration(config)); // step 2: verify Wifi state and network state; - assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.SHORT_TIMEOUT)); assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + State.CONNECTED, 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT)); // step 3: verify the current connected network is the given SSID + assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo()); if (DEBUG) { - Log.v(TAG, "config.SSID = " + config.SSID); - Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" + + log("config.SSID = " + config.SSID); + log("mAct.mWifiManager.getConnectionInfo.getSSID()" + mAct.mWifiManager.getConnectionInfo().getSSID()); } assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID())); + } - // Maintain the connection for 50 seconds before switching + private void sleep(long sometime, String errorMsg) { try { - Thread.sleep(50*1000); - } catch (Exception e) { - fail("interrupted while waiting for WPA_SUPPLICANT to start"); + Thread.sleep(sometime); + } catch (InterruptedException e) { + fail(errorMsg); } } + private void log(String message) { + Log.v(TAG, message); + } + @LargeTest public void testWifiConnections() { for (int i = 0; i < networks.size(); i++) { + String ssid = networks.get(i).SSID; + log("-- START Wi-Fi connection test to : " + ssid + " --"); connectToWifi(networks.get(i)); - mAct.removeConfiguredNetworksAndDisableWifi(); + sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT, + "interruped while waiting for wifi disabled."); + log("-- END Wi-Fi connection test to " + ssid + " -- "); } } } diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index c8ad60d..b878aa5 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -732,4 +732,20 @@ public class UriTest extends TestCase { assertEquals(1, names.size()); assertEquals("foo", names.iterator().next()); } + + /** + * Query parameters may omit the '='. http://b/3124097 + */ + public void testGetQueryParametersEmptyValue() { + assertEquals(Arrays.asList(""), + Uri.parse("http://foo/path?abc").getQueryParameters("abc")); + assertEquals(Arrays.asList(""), + Uri.parse("http://foo/path?foo=bar&abc").getQueryParameters("abc")); + assertEquals(Arrays.asList(""), + Uri.parse("http://foo/path?abcd=abc&abc").getQueryParameters("abc")); + assertEquals(Arrays.asList("a", "", ""), + Uri.parse("http://foo/path?abc=a&abc=&abc").getQueryParameters("abc")); + assertEquals(Arrays.asList("a", "", ""), + Uri.parse("http://foo/path?abc=a&abc=&abc=").getQueryParameters("abc")); + } } diff --git a/data/keyboards/Logitech_USB_Receiver.kl b/data/keyboards/Vendor_046d_Product_c532.kl index 23a8f54..741c2e1 100644 --- a/data/keyboards/Logitech_USB_Receiver.kl +++ b/data/keyboards/Vendor_046d_Product_c532.kl @@ -13,7 +13,7 @@ # limitations under the License. # -# Logitech Revue keyboard +# Logitech Revue Wireless keyboard # # Notes: # - The GRAVE key is emulated by the keyboard. @@ -105,7 +105,7 @@ key 108 DPAD_DOWN key 109 PAGE_DOWN key 110 NUMPAD_ENTER key 111 FORWARD_DEL -key 113 VALUME_MUTE +key 113 VOLUME_MUTE key 114 VOLUME_DOWN key 115 VOLUME_UP key 119 MEDIA_PAUSE diff --git a/data/keyboards/Apple_Wireless_Keyboard.kl b/data/keyboards/Vendor_05ac_Product_0239.kl index 9262a03..5234d58 100644 --- a/data/keyboards/Apple_Wireless_Keyboard.kl +++ b/data/keyboards/Vendor_05ac_Product_0239.kl @@ -101,7 +101,7 @@ key 108 DPAD_DOWN key 109 PAGE_DOWN key 110 NUMPAD_ENTER key 111 FORWARD_DEL -key 113 VALUME_MUTE +key 113 VOLUME_MUTE key 114 VOLUME_DOWN key 115 VOLUME_UP # key 120 switch applications diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Vendor_22b8_Product_093d.kl index 87b3c32..87b3c32 100644 --- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl +++ b/data/keyboards/Vendor_22b8_Product_093d.kl diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk index 5c2a75d..56c287a 100644 --- a/data/keyboards/common.mk +++ b/data/keyboards/common.mk @@ -16,16 +16,20 @@ # Used by Android.mk and keyboards.mk. keylayouts := \ - Apple_Wireless_Keyboard.kl \ - AVRCP.kl \ Generic.kl \ - Logitech_USB_Receiver.kl \ - Motorola_Bluetooth_Wireless_Keyboard.kl \ + AVRCP.kl \ qwerty.kl \ - qwerty2.kl + Vendor_046d_Product_c532.kl \ + Vendor_05ac_Product_0239.kl \ + Vendor_22b8_Product_093d.kl keycharmaps := \ Generic.kcm \ + Virtual.kcm \ qwerty.kcm \ - qwerty2.kcm \ - Virtual.kcm + qwerty2.kcm + +keyconfigs := \ + qwerty.idc \ + qwerty2.idc + diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk index b32e436..564f41c 100644 --- a/data/keyboards/keyboards.mk +++ b/data/keyboards/keyboards.mk @@ -22,4 +22,8 @@ PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\ PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\ frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file)) -PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) +PRODUCT_COPY_FILES += $(foreach file,$(keyconfigs),\ + frameworks/base/data/keyboards/$(file):system/usr/idc/$(file)) + +PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) $(keyconfigs) + diff --git a/data/keyboards/qwerty.idc b/data/keyboards/qwerty.idc new file mode 100644 index 0000000..129b0bc --- /dev/null +++ b/data/keyboards/qwerty.idc @@ -0,0 +1,23 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Emulator keyboard configuration file #1. +# + +keyboard.layout = qwerty +keyboard.characterMap = qwerty +keyboard.orientationAware = 1 +keyboard.builtIn = 1 + diff --git a/data/keyboards/qwerty.kcm b/data/keyboards/qwerty.kcm index f31333e..f3e1524 100644 --- a/data/keyboards/qwerty.kcm +++ b/data/keyboards/qwerty.kcm @@ -259,9 +259,9 @@ key COMMA { label: ',' number: ',' base: ',' - shift, capslock: ';' + shift: ';' alt: ';' - shift+alt, capslock+alt: '|' + shift+alt: '|' } key PERIOD { diff --git a/data/keyboards/qwerty2.idc b/data/keyboards/qwerty2.idc new file mode 100644 index 0000000..1a795c6 --- /dev/null +++ b/data/keyboards/qwerty2.idc @@ -0,0 +1,23 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Emulator keyboard configuration file #2. +# + +keyboard.layout = qwerty +keyboard.characterMap = qwerty2 +keyboard.orientationAware = 1 +keyboard.builtIn = 1 + diff --git a/data/keyboards/qwerty2.kl b/data/keyboards/qwerty2.kl deleted file mode 100644 index 863a258..0000000 --- a/data/keyboards/qwerty2.kl +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# Emulator keyboard layout #2. -# - -key 399 GRAVE -key 2 1 -key 3 2 -key 4 3 -key 5 4 -key 6 5 -key 7 6 -key 8 7 -key 9 8 -key 10 9 -key 11 0 -key 158 BACK WAKE_DROPPED -key 230 SOFT_RIGHT WAKE -key 60 SOFT_RIGHT WAKE -key 107 ENDCALL WAKE_DROPPED -key 62 ENDCALL WAKE_DROPPED -key 229 MENU WAKE_DROPPED -key 139 MENU WAKE_DROPPED -key 59 MENU WAKE_DROPPED -key 127 SEARCH WAKE_DROPPED -key 217 SEARCH WAKE_DROPPED -key 228 POUND -key 227 STAR -key 231 CALL WAKE_DROPPED -key 61 CALL WAKE_DROPPED -key 232 DPAD_CENTER WAKE_DROPPED -key 108 DPAD_DOWN WAKE_DROPPED -key 103 DPAD_UP WAKE_DROPPED -key 102 HOME WAKE -key 105 DPAD_LEFT WAKE_DROPPED -key 106 DPAD_RIGHT WAKE_DROPPED -key 115 VOLUME_UP WAKE -key 114 VOLUME_DOWN WAKE -key 116 POWER WAKE -key 212 CAMERA - -key 16 Q -key 17 W -key 18 E -key 19 R -key 20 T -key 21 Y -key 22 U -key 23 I -key 24 O -key 25 P -key 26 LEFT_BRACKET -key 27 RIGHT_BRACKET -key 43 BACKSLASH - -key 30 A -key 31 S -key 32 D -key 33 F -key 34 G -key 35 H -key 36 J -key 37 K -key 38 L -key 39 SEMICOLON -key 40 APOSTROPHE -key 14 DEL - -key 44 Z -key 45 X -key 46 C -key 47 V -key 48 B -key 49 N -key 50 M -key 51 COMMA -key 52 PERIOD -key 53 SLASH -key 28 ENTER - -key 56 ALT_LEFT -key 100 ALT_RIGHT -key 42 SHIFT_LEFT -key 54 SHIFT_RIGHT -key 15 TAB -key 57 SPACE -key 150 EXPLORER -key 155 ENVELOPE - -key 12 MINUS -key 13 EQUALS -key 215 AT - -# On an AT keyboard: ESC, F10 -key 1 BACK WAKE_DROPPED -key 68 MENU WAKE_DROPPED diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index 6c02031..623ee22 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -7,7 +7,9 @@ page.title=Data Backup <h2>Quickview</h2> <ul> - <li>Back up your data to the cloud in case the user looses it</li> + <li>Back up the user's data to the cloud in case the user loses it</li> + <li>If the user upgrades to a new Android-powered device, your app can restore the user's +data onto the new device</li> <li>Easily back up SharedPreferences and private files with BackupAgentHelper</li> <li>Requires API Level 8</li> </ul> @@ -389,7 +391,7 @@ conceptually a set of key-value pairs.</p> <p>To add an entity to your backup data set, you must:</p> <ol> <li>Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int) -writeEntityheader()}, passing a unique string key for the data you're about to write and the data +writeEntityHeader()}, passing a unique string key for the data you're about to write and the data size.</li> <li>Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int) writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write @@ -403,8 +405,8 @@ single entity:</p> ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data -outWriter.writeString(playerName); -outWriter.writeInt(playerScore); +outWriter.writeUTF(mPlayerName); +outWriter.writeInt(mPlayerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; @@ -422,10 +424,10 @@ android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,Pa onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then {@code oldState} will be empty during the next callback. - <p>Again, the following example saves a representation of the data using the file's -last-modified timestamp:</p> + <p>The following example saves a representation of the current data into {@code newState} using +the file's last-modified timestamp:</p> <pre> -FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); +FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); @@ -493,7 +495,8 @@ onBackup()} is called after the device is restored.</dd> <p>In your implementation of {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) -onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} to iterate +onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} on the +{@code data} to iterate through all entities in the data set. For each entity found, do the following:</p> <ol> @@ -517,6 +520,54 @@ android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,Pa onBackup()}. </ol> +<p>For example, here's how you can restore the data backed up by the example in the previous +section:</p> + +<pre> +@Override +public void onRestore(BackupDataInput data, int appVersionCode, + ParcelFileDescriptor newState) throws IOException { + // There should be only one entity, but the safest + // way to consume it is using a while loop + while (data.readNextHeader()) { + String key = data.getKey(); + int dataSize = data.getDataSize(); + + // If the key is ours (for saving top score). Note this key was used when + // we wrote the backup entity header + if (TOPSCORE_BACKUP_KEY.equals(key)) { + // Create an input stream for the BackupDataInput + byte[] dataBuf = new byte[dataSize]; + data.readEntityData(dataBuf, 0, dataSize); + ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); + DataInputStream in = new DataInputStream(baStream); + + // Read the player name and score from the backup data + mPlayerName = in.readUTF(); + mPlayerScore = in.readInt(); + + // Record the score on the device (to a file or something) + recordScore(mPlayerName, mPlayerScore); + } else { + // We don't know this entity key. Skip it. (Shouldn't happen.) + data.skipEntityData(); + } + } + + // Finally, write to the state blob (newState) that describes the restored data + FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); + DataOutputStream out = new DataOutputStream(outstream); + out.writeUTF(mPlayerName); + out.writeInt(mPlayerScore); +} +</pre> + +<p>In this example, the {@code appVersionCode} parameter passed to {@link +android.app.backup.BackupAgent#onRestore onRestore()} is not used. However, you might want to use +it if you've chosen to perform backup when the user's version of the application has actually moved +backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see +the section about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</p> + <div class="special"> <p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code @@ -592,7 +643,8 @@ public class MyPrefsBackupAgent extends BackupAgentHelper { static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent - void onCreate() { + @Override + public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd index c3d3482..44d75c1 100644 --- a/docs/html/guide/topics/ui/actionbar.jd +++ b/docs/html/guide/topics/ui/actionbar.jd @@ -455,7 +455,7 @@ Action Bar.</p> <ol> <li>Create a {@link android.widget.SpinnerAdapter} that provides the list of selectable items for the list and the layout to use when drawing each item in the list.</li> - <li>Implement {@link android.app.ActionBar.NavigationCallback} to define the behavior when the + <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the user selects an item from the list.</li> <li>Turn on navigation mode for the Action Bar with {@link android.app.ActionBar#setNavigationMode setNavigationMode()}. For example: @@ -472,17 +472,17 @@ android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}. actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback); </pre> <p>This method takes your {@link android.widget.SpinnerAdapter} and {@link -android.app.ActionBar.NavigationCallback}. More about these next.</p> +android.app.ActionBar.OnNavigationListener}. More about these next.</p> </li> </ol> <p>That's the basic setup. The {@link android.widget.SpinnerAdapter} and {@link -android.app.ActionBar.NavigationCallback} is where most of the work is done. There are many ways +android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many ways you can implement these to define the functionality for your drop-down navigation. Implementing various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this document—you should refer to the class refrence for more information about implementing it or extending an existing implementation. However, below is a simple example for a {@link -android.widget.SpinnerAdapter} and {@link android.app.ActionBar.NavigationCallback} to get you +android.widget.SpinnerAdapter} and {@link android.app.ActionBar.OnNavigationListener} to get you started.</p> @@ -520,24 +520,24 @@ defined as a resource looks like this:</p> </pre> -<h3 id="NavigationCallback">Example: simple NavigationCallback</h3> +<h3 id="OnNavigationListener">Example: simple OnNavigationListener</h3> -<p>Your implementation of {@link android.app.ActionBar.NavigationCallback} is where you handle +<p>Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle fragment changes or other modifications to your activity when the user selects an item from the drop-down list. There's only one callback method to implement: {@link -android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}.</p> +android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.</p> <p>The {@link -android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()} +android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()} method receives the position of the item in the list and an item ID provided by the {@link android.widget.SpinnerAdapter}.</p> <p>Here's an example that instantiates an anonymous implementation of {@link -android.app.ActionBar.NavigationCallback}, which inserts a {@link android.app.Fragment} into the +android.app.ActionBar.OnNavigationListener}, which inserts a {@link android.app.Fragment} into the layout container identified by {@code R.id.fragment_container}:</p> <pre> -mNavigationCallback = new NavigationCallback() { +mOnNavigationListener = new OnNavigationListener() { // Get the same strings provided for the drop-down's ArrayAdapter String[] strings = getResources().getStringArray(R.array.action_list); @@ -556,7 +556,7 @@ mNavigationCallback = new NavigationCallback() { }; </pre> -<p>This instance of {@link android.app.ActionBar.NavigationCallback} can be given to {@link +<p>This instance of {@link android.app.ActionBar.OnNavigationListener} can be given to {@link android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from above.</p> diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd index cef057e..7416764 100644 --- a/docs/html/resources/dashboard/platform-versions.jd +++ b/docs/html/resources/dashboard/platform-versions.jd @@ -52,9 +52,8 @@ Android Market within a 14-day period ending on the data collection date noted b <div class="dashboard-panel"> <img alt="" height="250" width="460" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:7.9,15.0,0.1,40.8,36.2&chl= -Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, -6fad0c" /> +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:6.3,10.6,0.1,39.6,43.4&chl +=Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" /> <table> <tr> @@ -62,13 +61,13 @@ Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, <th>API Level</th> <th>Distribution</th> </tr> -<tr><td>Android 1.5</td><td>3</td><td>7.9%</td></tr> -<tr><td>Android 1.6</td><td>4</td><td>15.0%</td></tr> -<tr><td>Android 2.1</td><td>7</td><td>40.8%</td></tr> -<tr><td>Android 2.2</td><td>8</td><td>36.2%</td></tr> +<tr><td>Android 1.5</td><td>3</td><td>6.3%</td></tr> +<tr><td>Android 1.6</td><td>4</td><td>10.6%</td></tr> +<tr><td>Android 2.1</td><td>7</td><td>39.6%</td></tr> +<tr><td>Android 2.2</td><td>8</td><td>43.4%</td></tr> </table> -<p><em>Data collected during two weeks ending on November 1, 2010</em></p> +<p><em>Data collected during two weeks ending on December 1, 2010</em></p> <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p> </div><!-- end dashboard-panel --> @@ -97,17 +96,16 @@ Android Market within a 14-day period ending on the date indicated on the x-axis <img alt="" height="250" width="660" style="padding:5px;background:#fff" src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100& -chxl=0%3A%7C2010/05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/ -01%7C10/15%7C2010/11/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25 -%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.9,99.3,100.3,100.8,99.7,99. -8,99.8,99.7,99.8,99.9,99.9,99.9,99.9|61.6,63.1,72.7,76.1,78.4,80.9,84.3,86.5,87.9,89.2,90.2,91.1,92. -0|32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77.0|0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3, -27.8,32.1,33.4,34.5,36.2&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6, -5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202. -2,131d02,3,7,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1| -Android%202.2&chco=add274,94d134,73ad18,507d08" /> - -<p><em>Last historical dataset collected during two weeks ending on November 1, 2010</em></p> +chxl=0:|2010/06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|10/01|10/15|11/01|11/15|2010/12/01|1:|0 +%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12& +chxtc=0,5&chd=t:100.3,100.8,99.7,99.8,99.8,99.7,99.8,99.9,99.9,99.9,99.9,99.9,99.9|72.7,76.1,78.4,80 +.9,84.3,86.5,87.9,89.2,90.2,91.1,92.0,92.7,93.6|45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77 +.0,79.0,83.0|0.8,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4,34.5,36.2,38.3,43.4&chm=tAndroid%201.5,7caa36,0 +,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b +,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202.2,131d02,3,5,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl= +Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=add274,94d134,73ad18,507d08" /> + +<p><em>Last historical dataset collected during two weeks ending on December 1, 2010</em></p> </div><!-- end dashboard-panel --> diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd index 0f36345..2c8e4c2 100644 --- a/docs/html/sdk/ndk/index.jd +++ b/docs/html/sdk/ndk/index.jd @@ -67,73 +67,71 @@ padding: .25em 1em; width="9px" /> Android NDK, Revision 5</a> <em>(November 2010)</em> <div class="toggleme"> - <dl> - <dt>NDK r5 notes:</dt> - - <dd> - <p>The r5 release of the NDK includes many new APIs, many of which are introduced to - support native game development and applications that require similar requirements. Most - notably, native activities are now supported, which allow you to write an application - entirely with native code. For detailed information describing the changes in this - release, read the CHANGES.HTML document included in the downloaded NDK package.</p> - </dd> - </dl> - + <p>This release of the NDK includes many new APIs, most of which are introduced to + support the development of games and similar applications that make extensive use + of native code. Using the APIs, developers have direct native access to events, audio, + graphics and window management, assets, and storage. Developers can also implement the + Android application lifecycle in native code with help from the new + {@link android.app.NativeActivity} class. For detailed information describing the changes in this + release, read the CHANGES.HTML document included in the downloaded NDK package. + </p> <dl> <dt>General notes:</dt> - <dd> <ul> - - <li>A new toolchain (based on GCC 4.4.3), which generates better code, and can also now -be used as a standalone cross-compiler, for people who want to build their stuff with -<code>./configure && make</code>. See -docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided, -but the 4.2.1 binaries were removed.</li> - - <li>Support for prebuilt static and shared libraries (docs/PREBUILTS.html), module -exports and imports to make sharing and reuse of third-party modules much easier -(docs/IMPORT-MODULE.html explains why).</li> - - <li>A C++ STL implementation (based on STLport) is now provided as a helper module. It -can be used either as a static or shared library (details and usage exemple under -sources/android/stlport/README). <strong>Note:</strong> For now, C++ Exceptions and RTTI are still -not supported.</li> - - <li>Improvements to the <code>cpufeatures</code> helper library to deal with buggy -kernel that incorrectly report they run on an ARMv7 CPU (while the device really is an ARMv6). We -recommend developers that use it to simply rebuild their applications to benefit from it, then -upload to Market.</li> - - <li>Adds support for native activities, which allows you to write completely native - applications.</li> - - <li>Adds an EGL library that lets you create and manage OpenGL ES textures and - services.</li> + <li>Adds support for native activities, which allows you to implement the + Android application lifecycle in native code.</li> <li>Adds native support for the following: <ul> + <li>Input subsystem (such as the keyboard and touch screen)</li> + <li>Access to sensor data (accelerometer, compass, gyroscope, etc).</li> + + <li>Event loop APIs to wait for things such as input and sensor events.</li> + <li>Window and surface subsystem</li> <li>Audio APIs based on the OpenSL ES standard that support playback and recording as well as control over platform audio effects</li> - <li>Event loop APIs to wait for things such as input and sensor events</li> - - <li>Access to assets packaged in the <code>.apk</code></li> - - <li>Access to sensor data (accelerometer, compass, gyroscope, etc.)</li> + <li>Access to assets packaged in an <code>.apk</code> file.</li> + </ul> </li> - <li>New sample applications, <code>native-plasma</code> and - <code>native-activity</code>, to demonstrate how to write a native activity.</li> - - <li>Plus many bugfixes and other small improvements; see docs/CHANGES.html for a more -detailed list of changes.</li> + <li>Includes a new toolchain (based on GCC 4.4.3), which generates better code, and can also now + be used as a standalone cross-compiler, for people who want to build their stuff with + <code>./configure && make</code>. See + docs/STANDALONE-TOOLCHAIN.html for the details. The binaries for GCC 4.4.0 are still provided, + but the 4.2.1 binaries were removed.</li> + + <li>Adds support for prebuilt static and shared libraries (docs/PREBUILTS.html) and module + exports and imports to make sharing and reuse of third-party modules much easier + (docs/IMPORT-MODULE.html explains why).</li> + + <li>Provides a default C++ STL implementation (based on STLport) as a helper module. It can be used either + as a static or shared library (details and usage examples are in sources/android/stlport/README). Prebuilt + binaries for STLport (static or shared) and GNU libstdc++ (static only) are also provided if you choose to + compile against those libraries instead of the default C++ STL implementation. + C++ Exceptions and RTTI are not supported in the default STL implementation. For more information, see + docs/CPLUSPLUS-SUPPORT.HTML.</li> + + <li>Includes improvements to the <code>cpufeatures</code> helper library that improves reporting + of the CPU type (some devices previously reported ARMv7 CPU when the device really was an ARMv6). We + recommend developers that use this library to rebuild their applications then + upload to Market to benefit from the improvements.</li> + + <li>Adds an EGL library that lets you create and manage OpenGL ES textures and + services.</li> + + <li>Adds new sample applications, <code>native-plasma</code> and <code>native-activity</code>, + to demonstrate how to write a native activity.</li> + + <li>Includes many bugfixes and other small improvements; see docs/CHANGES.html for a more + detailed list of changes.</li> </ul> </dd> </dl> @@ -296,14 +294,13 @@ detailed list of changes.</li> <h2 id="installing">Installing the NDK</h2> <p>Installing the NDK on your development computer is straightforward and involves extracting the - NDK from its download package. Unlike previous releases, there is no need to run a host-setup - script.</p> + NDK from its download package.</p> <p>Before you get started make sure that you have downloaded the latest <a href= "{@docRoot}sdk/index.html">Android SDK</a> and upgraded your applications and environment as - needed. The NDK will not work with older versions of the Android SDK. Also, take a moment to - review the <a href="{@docRoot}sdk/ndk/reqs.html">System and Software Requirements</a> for the - NDK, if you haven't already.</p> + needed. The NDK is compatible with older platform versions but not older versions of the SDK tools. + Also, take a moment to review the <a href="{@docRoot}sdk/ndk/reqs.html">System and Software Requirements</a> + for the NDK, if you haven't already.</p> <p>To install the NDK, follow these steps:</p> @@ -318,7 +315,7 @@ detailed list of changes.</li> <code><ndk></code>.</li> </ol> - <p>You are now ready start working with the NDK.</p> + <p>You are now ready to start working with the NDK.</p> <h2 id="gettingstarted">Getting Started with the NDK</h2> @@ -342,8 +339,7 @@ detailed list of changes.</li> <li>Build your native code by running the 'ndk-build' script from your project's directory. It is located in the top-level NDK directory: - <pre class="no-pretty-print"> -cd <project> + <pre class="no-pretty-print">cd <project> <ndk>/ndk-build </pre> @@ -360,220 +356,10 @@ cd <project> <h2 id="samples">Sample Applications</h2> - <p>The NDK includes sample applications that illustrate how to use native code in your Android - applications:</p> - - <ul> - <li><code>hello-jni</code> — a simple application that loads a string from a native - method implemented in a shared library and then displays it in the application UI.</li> - - <li><code>two-libs</code> — a simple application that loads a shared library dynamically - and calls a native method provided by the library. In this case, the method is implemented in a - static library imported by the shared library.</li> - - <li><code>san-angeles</code> — a simple application that renders 3D graphics through the - native OpenGL ES APIs, while managing activity lifecycle with a {@link - android.opengl.GLSurfaceView} object.</li> - - <li><code>hello-gl2</code> — a simple application that renders a triangle using OpenGL ES - 2.0 vertex and fragment shaders.</li> - - <li><code>hello-neon</code> — a simple application that shows how to use the - <code>cpufeatures</code> library to check CPU capabilities at runtime, then use NEON intrinsics - if supported by the CPU. Specifically, the application implements two versions of a tiny - benchmark for a FIR filter loop, a C version and a NEON-optimized version for devices that - support it.</li> - - <li><code>bitmap-plasma</code> — a simple application that demonstrates how to access the - pixel buffers of Android {@link android.graphics.Bitmap} objects from native code, and uses - this to generate an old-school "plasma" effect.</li> - - <li><code>native-activity</code> — a simple application that demonstrates how to use the - native-app-glue static library to create a native activity</li> - - <li><code>native-plasma</code> — a version of bitmap-plasma implemented with a native - activity.</li> - </ul> - - <p>For each sample, the NDK includes the corresponding C source code and the necessary Android.mk - and Application.mk files. There are located under <code><ndk>/samples/<name>/</code> - and their source code can be found under <code><ndk>/samples/<name>/jni/</code>.</p> - - <p>You can build the shared libraries for the sample apps by going into - <code><ndk>/samples/<name>/</code> then calling the <code>ndk-build</code> command. - The generated shared libraries will be located under - <code><ndk>/samples/<name>/libs/armeabi/</code> for (ARMv5TE machine code) and/or - <code><ndk>/samples/<name>/libs/armeabi-v7a/</code> for (ARMv7 machine code).</p> - - <p>Next, build the sample Android applications that use the shared libraries:</p> - - <ul> - <li>If you are developing in Eclipse with ADT, use the New Project Wizard to create a new - Android project for each sample, using the "Import from Existing Source" option and importing - the source from <code><ndk>/apps/<app_name>/project/</code>. Then, set up an AVD, - if necessary, and build/run the application in the emulator. For more information about - creating a new Android project in Eclipse, see <a href= - "{@docRoot}guide/developing/eclipse-adt.html">Developing in Eclipse</a>.</li> - - <li>If you are developing with Ant, use the <code>android</code> tool to create the build file - for each of the sample projects at <code><ndk>/apps/<app_name>/project/</code>. - Then set up an AVD, if necessary, build your project in the usual way, and run it in the - emulator. For more information, see <a href= - "{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</li> - </ul> - - <h3 id="hello-jni">Exploring the hello-jni Sample</h3> - - <p>The hello-jni sample is a simple demonstration on how to use JNI from an Android application. - The HelloJni activity receives a string from a simple C function and displays it in a - TextView.</p> - - <p>The main components of the sample include:</p> - - <ul> - <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code> - file, a <code>src/</code> and <code>res</code> directories, and a main activity)</li> - - <li>A <code>jni/</code> directory that includes the implemented source file for the native code - as well as the Android.mk file</li> - - <li>A <code>tests/</code> directory that contains unit test code.</li> - </ul> - - <ol> - <li>Create a new project in Eclipse from the existing sample source or use the - <code>android</code> tool to update the project so it generates a build.xml file that you can - use to build the sample. - - <ul> - <li>In Eclipse: - - <ol type="a"> - <li>Click <strong>File > New Android Project...</strong></li> + <p>The NDK includes sample Android applications that illustrate how to use native code in your + Android applications. For more information, see <a href= + "{@docRoot}sdk/ndk/overview.html#samples">Sample Applications</a>.</p> - <li>Select the <strong>Create project from existing source</strong> radio button.</li> - - <li>Select any API level above Android 1.5.</li> - - <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select - the <code><ndk-root>/samples/hello-jni</code> directory.</li> - - <li>Click <strong>Finish</strong>.</li> - </ol> - </li> - - <li>On the command line: - - <ol type="a"> - <li>Change to the <code><ndk-root>/samples/hello-jni</code> directory.</li> - - <li>Run the following command to generate a build.xml file: - <pre class="no-pretty-print"> -android update project -p . -s -</pre> - </li> - </ol> - </li> - </ul> - </li> - - <li>Compile the native code using the <code>ndk-build</code> command. - <pre class="no-pretty-print"> -cd <ndk-root>/samples/hello-jni -<ndk_root>/ndk-build -</pre> - </li> - - <li>Build and install the application as you would a normal Android application. If you are - using Eclipse, run the application to build and install it on a device. If you are using Ant, - run the following commands from the project directory: - <pre class="no-pretty-print"> -ant debug -adb install bin/HelloJni-debug.apk -</pre> - </li> - </ol> - - <p>When you run the application on the device, the string <code>Hello JNI</code> should appear on - your device. You can explore the rest of the samples that are located in the - <code><ndk-root>/samples</code> directory for more examples on how to use the JNI.</p> - - <h3 id="native-activity">Exploring the native-activity Sample Application</h3> - - <p>The native-activity sample provided with the Android NDK demonstrates how to use the - android_native_app_glue static library. This static library makes creating a native activity - easier by providing you with an implementation that handles your callbacks in another thread, so - you do not have to worry about them blocking your main UI thread. The main parts of the sample - are described below:</p> - - <ul> - <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code> - file, a <code>src/</code> and <code>res</code> directories). The AndroidManifest.xml declares - that the application is native and specifies the .so file of the native activity. See {@link - android.app.NativeActivity} for the source or see the - <code><ndk_root>/platforms/samples/native-activity/AndroidManifest.xml</code> file.</li> - - <li>A <code>jni/</code> directory contains the native activity, main.c, which uses the - <code>android_native_app_glue.h</code> interface to implement the activity. The Android.mk that - describes the native module to the build system also exists here.</li> - </ul> - - <p>To build this sample application:</p> - - <ol> - <li>Create a new project in Eclipse from the existing sample source or use the - <code>android</code> tool to update the project so it generates a build.xml file that you can - use to build the sample. - - <ul> - <li>In Eclipse: - - <ol type="a"> - <li>Click <strong>File > New Android Project...</strong></li> - - <li>Select the <strong>Create project from existing source</strong> radio button.</li> - - <li>Select any API level above Android 2.3.</li> - - <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select - the <code><ndk-root>/samples/native-activity</code> directory.</li> - - <li>Click <strong>Finish</strong>.</li> - </ol> - </li> - - <li>On the command line: - - <ol type="a"> - <li>Change to the <code><ndk-root>/samples/native-activity</code> directory.</li> - - <li>Run the following command to generate a build.xml file: - <pre class="no-pretty-print"> -android update project -p . -s -</pre> - </li> - </ol> - </li> - </ul> - </li> - - <li>Compile the native code using the <code>ndk-build</code> command. - <pre class="no-pretty-print"> -cd <ndk-root>/platforms/samples/android-9/samples/native-activity -<ndk_root>/ndk-build -</pre> - </li> - - <li>Build and install the application as you would a normal Android application. If you are - using Eclipse, run the application to build and install it on a device. If you are using Ant, - run the following commands in the project directory, then run the application on the device: - <pre class="no-pretty-print"> -ant debug -adb install bin/NativeActivity-debug.apk -</pre> - </li> - </ol> - <h2 id="forum">Discussion Forum and Mailing List</h2> <p>If you have questions about the NDK or would like to read or contribute to discussions about diff --git a/docs/html/sdk/ndk/overview.jd b/docs/html/sdk/ndk/overview.jd index a7ec5d4..f6d148a 100644 --- a/docs/html/sdk/ndk/overview.jd +++ b/docs/html/sdk/ndk/overview.jd @@ -7,10 +7,8 @@ page.title=What is the NDK? <ol> <li><a href="#choosing">When to Develop in Native Code</a></li> - <li> <a href="#contents">Contents of the NDK</a> - <ol> <li><a href="#tools">Development tools</a></li> @@ -19,9 +17,7 @@ page.title=What is the NDK? <li><a href="#samples">Sample applications</a></li> </ol> </li> - <li><a href="#reqs">System and Software Requirements</a></li> - </ol> </div> </div> @@ -102,9 +98,8 @@ page.title=What is the NDK? later.</li> <li> - <p>Write a native activity, which allows you to potentially create an application completely in native - code, because you can implement the lifecycle callbacks natively. The Android SDK provides - the {@link android.app.NativeActivity} class, which is a convenience class that notifies your + <p>Write a native activity, which allows you to implement the lifecycle callbacks in native + code. The Android SDK provides the {@link android.app.NativeActivity} class, which is a convenience class that notifies your native code of any activity lifecycle callbacks (<code>onCreate()</code>, <code>onPause()</code>, <code>onResume()</code>, etc). You can implement the callbacks in your native code to handle these events when they occur. Applications that use native activities must be run on Android @@ -142,6 +137,10 @@ page.title=What is the NDK? <li>libjnigraphics (Pixel buffer access) header (for Android 2.2 and above).</li> <li>A Minimal set of headers for C++ support</li> + + <li>OpenSL ES native audio libraries</li> + + <li>Android native application APIS</li> </ul> <p>The NDK also provides a build system that lets you work efficiently with your sources, without @@ -163,25 +162,18 @@ page.title=What is the NDK? the <code><ndk>/docs/</code> directory. Included are these files:</p> <ul> - <li>INSTALL.HTML — describes how to install the NDK and configure it for your host + <li> + INSTALL.HTML — describes how to install the NDK and configure it for your host system</li> <li>OVERVIEW.HTML — provides an overview of the NDK capabilities and usage</li> - + <li>ANDROID-MK.HTML — describes the use of the Android.mk file, which defines the native sources you want to compile</li> - + <li>APPLICATION-MK.HTML — describes the use of the Application.mk file, which describes - the native sources required by your Android application</li> - - <li>HOWTO.HTML — information about common tasks associated with NDK development.</li> - - <li>SYSTEM-ISSUES.HTML — known issues in the Android system images that you should be - aware of, if you are developing using the NDK.</li> - - <li>STABLE-APIS.HTML — a complete list of the stable APIs exposed by headers in the - NDK.</li> - + the native sources required by your Android application</li> + <li>CPLUSPLUS-SUPPORT.HTML — describes the C++ support provided in the Android NDK</li> <li>CPU-ARCH-ABIS.HTML — a description of supported CPU architectures and how to target them.</li> @@ -193,6 +185,32 @@ page.title=What is the NDK? instructions.</li> <li>CHANGES.HTML — a complete list of changes to the NDK across all releases.</li> + + <li>DEVELOPMENT.HTML — describes how to modify the NDK and generate release packages for it</li> + + <li>HOWTO.HTML — information about common tasks associated with NDK development</li> + + <li>IMPORT-MODULE.HTML — describes how to share and reuse modules</li> + + <li>LICENSES.HTML — information about the various open source licenses that govern the Android NDK</li> + + <li>NATIVE-ACTIVITY.HTML — describes how to implement native activities</li> + + <li>NDK-BUILD.HTML — describes the usage of the ndk-build script</li> + + <li>NDK-GDB.HTML — describes how to use the native code debugger</li> + + <li>PREBUILTS.HTML — information about how shared and static prebuilt libraries work </li> + + <li>STANDALONE-TOOLCHAIN.HTML — describes how to use Android NDK toolchain as a standalone + compiler (still in beta).</li> + + <li>SYSTEM-ISSUES.HTML — known issues in the Android system images that you should be + aware of, if you are developing using the NDK.</li> + + <li>STABLE-APIS.HTML — a complete list of the stable APIs exposed by headers in the + NDK.</li> + </ul> <p>Additionally, the package includes detailed information about the "bionic" C library provided @@ -206,9 +224,218 @@ page.title=What is the NDK? <h3 id="samples">Sample applications</h3> - <p>The NDK includes sample Android applications that illustrate how to use native code in your - Android applications. For more information, see <a href= - "{@docRoot}sdk/ndk/installing.html#samples">Sample Applications</a>.</p> +<p>The NDK includes sample applications that illustrate how to use native code in your Android + applications:</p> + + <ul> + <li><code>hello-jni</code> — a simple application that loads a string from a native + method implemented in a shared library and then displays it in the application UI.</li> + + <li><code>two-libs</code> — a simple application that loads a shared library dynamically + and calls a native method provided by the library. In this case, the method is implemented in a + static library imported by the shared library.</li> + + <li><code>san-angeles</code> — a simple application that renders 3D graphics through the + native OpenGL ES APIs, while managing activity lifecycle with a {@link + android.opengl.GLSurfaceView} object.</li> + + <li><code>hello-gl2</code> — a simple application that renders a triangle using OpenGL ES + 2.0 vertex and fragment shaders.</li> + + <li><code>hello-neon</code> — a simple application that shows how to use the + <code>cpufeatures</code> library to check CPU capabilities at runtime, then use NEON intrinsics + if supported by the CPU. Specifically, the application implements two versions of a tiny + benchmark for a FIR filter loop, a C version and a NEON-optimized version for devices that + support it.</li> + + <li><code>bitmap-plasma</code> — a simple application that demonstrates how to access the + pixel buffers of Android {@link android.graphics.Bitmap} objects from native code, and uses + this to generate an old-school "plasma" effect.</li> + + <li><code>native-activity</code> — a simple application that demonstrates how to use the + native-app-glue static library to create a native activity</li> + + <li><code>native-plasma</code> — a version of bitmap-plasma implemented with a native + activity.</li> + </ul> + + <p>For each sample, the NDK includes the corresponding C source code and the necessary Android.mk + and Application.mk files. There are located under <code><ndk>/samples/<name>/</code> + and their source code can be found under <code><ndk>/samples/<name>/jni/</code>.</p> + + <p>You can build the shared libraries for the sample apps by going into + <code><ndk>/samples/<name>/</code> then calling the <code>ndk-build</code> command. + The generated shared libraries will be located under + <code><ndk>/samples/<name>/libs/armeabi/</code> for (ARMv5TE machine code) and/or + <code><ndk>/samples/<name>/libs/armeabi-v7a/</code> for (ARMv7 machine code).</p> + + <p>Next, build the sample Android applications that use the shared libraries:</p> + + <ul> + <li>If you are developing in Eclipse with ADT, use the New Project Wizard to create a new + Android project for each sample, using the "Import from Existing Source" option and importing + the source from <code><ndk>/apps/<app_name>/project/</code>. Then, set up an AVD, + if necessary, and build/run the application in the emulator. For more information about + creating a new Android project in Eclipse, see <a href= + "{@docRoot}guide/developing/eclipse-adt.html">Developing in Eclipse</a>.</li> + + <li>If you are developing with Ant, use the <code>android</code> tool to create the build file + for each of the sample projects at <code><ndk>/apps/<app_name>/project/</code>. + Then set up an AVD, if necessary, build your project in the usual way, and run it in the + emulator. For more information, see <a href= + "{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</li> + </ul> + + <h4 id="hello-jni">Exploring the hello-jni Sample</h4> + + <p>The hello-jni sample is a simple demonstration on how to use JNI from an Android application. + The HelloJni activity receives a string from a simple C function and displays it in a + TextView.</p> + + <p>The main components of the sample include:</p> + + <ul> + <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code> + file, a <code>src/</code> and <code>res</code> directories, and a main activity)</li> + + <li>A <code>jni/</code> directory that includes the implemented source file for the native code + as well as the Android.mk file</li> + + <li>A <code>tests/</code> directory that contains unit test code.</li> + </ul> + + <ol> + <li>Create a new project in Eclipse from the existing sample source or use the + <code>android</code> tool to update the project so it generates a build.xml file that you can + use to build the sample. + + <ul> + <li>In Eclipse: + + <ol type="a"> + <li>Click <strong>File > New Android Project...</strong></li> + + <li>Select the <strong>Create project from existing source</strong> radio button.</li> + + <li>Select any API level above Android 1.5.</li> + + <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select + the <code><ndk-root>/samples/hello-jni</code> directory.</li> + + <li>Click <strong>Finish</strong>.</li> + </ol> + </li> + + <li>On the command line: + + <ol type="a"> + <li>Change to the <code><ndk-root>/samples/hello-jni</code> directory.</li> + + <li>Run the following command to generate a build.xml file: + <pre class="no-pretty-print">android update project -p . -s</pre> + </li> + </ol> + </li> + </ul> + </li> + + <li>Compile the native code using the <code>ndk-build</code> command. + <pre class="no-pretty-print"> +cd <ndk-root>/samples/hello-jni +<ndk_root>/ndk-build +</pre> + </li> + + <li>Build and install the application as you would a normal Android application. If you are + using Eclipse, run the application to build and install it on a device. If you are using Ant, + run the following commands from the project directory: + <pre class="no-pretty-print"> +ant debug +adb install bin/HelloJni-debug.apk +</pre> + </li> + </ol> + + <p>When you run the application on the device, the string <code>Hello JNI</code> should appear on + your device. You can explore the rest of the samples that are located in the + <code><ndk-root>/samples</code> directory for more examples on how to use the JNI.</p> + + <h4 id="native-activity">Exploring the native-activity Sample Application</h4> + + <p>The native-activity sample provided with the Android NDK demonstrates how to use the + android_native_app_glue static library. This static library makes creating a native activity + easier by providing you with an implementation that handles your callbacks in another thread, so + you do not have to worry about them blocking your main UI thread. The main parts of the sample + are described below:</p> + + <ul> + <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code> + file, a <code>src/</code> and <code>res</code> directories). The AndroidManifest.xml declares + that the application is native and specifies the .so file of the native activity. See {@link + android.app.NativeActivity} for the source or see the + <code><ndk_root>/platforms/samples/native-activity/AndroidManifest.xml</code> file.</li> + + <li>A <code>jni/</code> directory contains the native activity, main.c, which uses the + <code>android_native_app_glue.h</code> interface to implement the activity. The Android.mk that + describes the native module to the build system also exists here.</li> + </ul> + + <p>To build this sample application:</p> + + <ol> + <li>Create a new project in Eclipse from the existing sample source or use the + <code>android</code> tool to update the project so it generates a build.xml file that you can + use to build the sample. + + <ul> + <li>In Eclipse: + + <ol type="a"> + <li>Click <strong>File > New Android Project...</strong></li> + + <li>Select the <strong>Create project from existing source</strong> radio button.</li> + + <li>Select any API level above Android 2.3.</li> + + <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select + the <code><ndk-root>/samples/native-activity</code> directory.</li> + + <li>Click <strong>Finish</strong>.</li> + </ol> + </li> + + <li>On the command line: + + <ol type="a"> + <li>Change to the <code><ndk-root>/samples/native-activity</code> directory.</li> + + <li>Run the following command to generate a build.xml file: + <pre class="no-pretty-print"> +android update project -p . -s +</pre> + </li> + </ol> + </li> + </ul> + </li> + + <li>Compile the native code using the <code>ndk-build</code> command. + <pre class="no-pretty-print"> +cd <ndk-root>/platforms/samples/android-9/samples/native-activity +<ndk_root>/ndk-build +</pre> + </li> + + <li>Build and install the application as you would a normal Android application. If you are + using Eclipse, run the application to build and install it on a device. If you are using Ant, + run the following commands in the project directory, then run the application on the device: + <pre class="no-pretty-print"> +ant debug +adb install bin/NativeActivity-debug.apk +</pre> + </li> + </ol> + <h2 id="reqs">System and Software Requirements</h2> @@ -313,7 +540,7 @@ page.title=What is the NDK? to users whose devices are capable of supporting your application. For example: <pre style="margin:1em;"> <manifest> - ... + ... <!-- Declare that the application uses the OpenGL ES 2.0 API and is designed to run only on devices that support OpenGL ES 2.0 or higher. --> <uses-feature android:glEsVersion="0x00020000" /> @@ -331,4 +558,4 @@ page.title=What is the NDK? containing the library can be deployed only to devices running Android 2.2 (API level 8) or higher. To ensure compatibility, make sure that your application declares <code><uses-sdk android:minSdkVersion="8" /></code> attribute value in its manifest.</li> - </ul>
\ No newline at end of file + </ul> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 62fbfb4..e3bb6eb 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1818,6 +1818,7 @@ public class Paint { nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds); } + @Override protected void finalize() throws Throwable { finalizer(mNativePaint); } diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index 6364d58..431aaa47 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -79,6 +79,14 @@ public: // the camera only has a single output, and does not have // separate output for video frames and preview frame. void getSupportedVideoSizes(Vector<Size> &sizes) const; + // Retrieve the preferred preview size (width and height) in pixels + // for video recording. The given width and height must be one of + // supported preview sizes returned from getSupportedPreviewSizes(). + // Must not be called if getSupportedVideoSizes() returns an empty + // Vector of Size. If getSupportedVideoSizes() returns an empty + // Vector of Size, the width and height returned from this method + // is invalid, and is "-1x-1". + void getPreferredPreviewSizeForVideo(int *width, int *height) const; void setPreviewFrameRate(int fps); int getPreviewFrameRate() const; @@ -319,6 +327,21 @@ public: // frameworks/base/include/camera/Camera.h. // Example: "176x144,1280x720". Read only. static const char KEY_SUPPORTED_VIDEO_SIZES[]; + + // Preferred preview frame size in pixels for video recording. + // The width and height must be one of the supported sizes retrieved + // via KEY_SUPPORTED_PREVIEW_SIZES. This key can be used only when + // getSupportedVideoSizes() does not return an empty Vector of Size. + // Camcorder applications are recommended to set the preview size + // to a value that is not larger than the preferred preview size. + // In other words, the product of the width and height of the + // preview size should not be larger than that of the preferred + // preview size. In addition, we recommend to choos a preview size + // that has the same aspect ratio as the resolution of video to be + // recorded. + // Example value: "800x600". Read only. + static const char KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[]; + // The image format for video frames. See CAMERA_MSG_VIDEO_FRAME in // frameworks/base/include/camera/Camera.h. // Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read only. diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index f4dc536..6c6c297 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -18,14 +18,19 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H -#include <android/input.h> +#include <ui/Input.h> #include <ui/Keyboard.h> +#include <ui/KeyLayoutMap.h> +#include <ui/KeyCharacterMap.h> +#include <ui/VirtualKeyMap.h> #include <utils/String8.h> #include <utils/threads.h> #include <utils/Log.h> #include <utils/threads.h> #include <utils/List.h> #include <utils/Errors.h> +#include <utils/PropertyMap.h> +#include <utils/Vector.h> #include <linux/input.h> @@ -58,8 +63,6 @@ struct pollfd; namespace android { -class KeyLayoutMap; - /* * A raw event as retrieved from the EventHub. */ @@ -156,6 +159,8 @@ public: virtual String8 getDeviceName(int32_t deviceId) const = 0; + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const = 0; @@ -191,6 +196,9 @@ public: virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; + virtual void getVirtualKeyDefinitions(int32_t deviceId, + Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; + virtual void dump(String8& dump) = 0; }; @@ -205,6 +213,8 @@ public: virtual String8 getDeviceName(int32_t deviceId) const; + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const; @@ -225,6 +235,9 @@ public: virtual bool hasLed(int32_t deviceId, int32_t led) const; virtual void setLedState(int32_t deviceId, int32_t led, bool on); + virtual void getVirtualKeyDefinitions(int32_t deviceId, + Vector<VirtualKeyDefinition>& outVirtualKeys) const; + virtual void dump(String8& dump); protected: @@ -233,75 +246,80 @@ protected: private: bool openPlatformInput(void); - int openDevice(const char *device); - int closeDevice(const char *device); + int openDevice(const char *devicePath); + int closeDevice(const char *devicePath); int scanDir(const char *dirname); int readNotify(int nfd); status_t mError; - struct device_t { - const int32_t id; - const String8 path; - String8 name; - uint32_t classes; - uint8_t* keyBitmask; - KeyLayoutMap* layoutMap; - KeyMapInfo keyMapInfo; - int fd; - device_t* next; - - device_t(int32_t _id, const char* _path, const char* name); - ~device_t(); + struct Device { + Device* next; + + int fd; + const int32_t id; + const String8 path; + const InputDeviceIdentifier identifier; + + uint32_t classes; + uint8_t* keyBitmask; + String8 configurationFile; + PropertyMap* configuration; + VirtualKeyMap* virtualKeyMap; + KeyMap keyMap; + + Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier); + ~Device(); + + void close(); }; - device_t* getDeviceLocked(int32_t deviceId) const; - bool hasKeycodeLocked(device_t* device, int keycode) const; + Device* getDeviceLocked(int32_t deviceId) const; + bool hasKeycodeLocked(Device* device, int keycode) const; - int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const; - int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const; - int32_t getSwitchStateLocked(device_t* device, int32_t sw) const; - bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const; + int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const; + int32_t getSwitchStateLocked(Device* device, int32_t sw) const; + bool markSupportedKeyCodesLocked(Device* device, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; - void configureKeyMap(device_t* device); - void setKeyboardProperties(device_t* device, bool firstKeyboard); - void clearKeyboardProperties(device_t* device, bool firstKeyboard); + void loadConfiguration(Device* device); + status_t loadVirtualKeyMap(Device* device); + status_t loadKeyMap(Device* device); + void setKeyboardProperties(Device* device, bool builtInKeyboard); + void clearKeyboardProperties(Device* device, bool builtInKeyboard); // Protect all internal state. - mutable Mutex mLock; - - bool mHaveFirstKeyboard; - int32_t mFirstKeyboardId; // the API is that the built-in keyboard is id 0, so map it - - struct device_ent { - device_t* device; - uint32_t seq; - }; - device_ent *mDevicesById; - int mNumDevicesById; - - device_t *mOpeningDevices; - device_t *mClosingDevices; - - device_t **mDevices; - struct pollfd *mFDs; - int mFDCount; + mutable Mutex mLock; + + // The actual id of the built-in keyboard, or -1 if none. + // EventHub remaps the built-in keyboard to id 0 externally as required by the API. + int32_t mBuiltInKeyboardId; + + int32_t mNextDeviceId; + + // Parallel arrays of fds and devices. + // First index is reserved for inotify. + Vector<struct pollfd> mFds; + Vector<Device*> mDevices; + + Device *mOpeningDevices; + Device *mClosingDevices; - bool mOpened; - bool mNeedToSendFinishedDeviceScan; - List<String8> mExcludedDevices; + bool mOpened; + bool mNeedToSendFinishedDeviceScan; + List<String8> mExcludedDevices; // device ids that report particular switches. #ifdef EV_SW - int32_t mSwitches[SW_MAX + 1]; + int32_t mSwitches[SW_MAX + 1]; #endif static const int INPUT_BUFFER_SIZE = 64; struct input_event mInputBufferData[INPUT_BUFFER_SIZE]; - int32_t mInputBufferIndex; - int32_t mInputBufferCount; - int32_t mInputDeviceIndex; + size_t mInputBufferIndex; + size_t mInputBufferCount; + size_t mInputFdIndex; }; }; // namespace android diff --git a/include/ui/Input.h b/include/ui/Input.h index 1355bab..27f65bc 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -497,6 +497,54 @@ private: KeyedVector<int32_t, MotionRange> mMotionRanges; }; +/* + * Identifies a device. + */ +struct InputDeviceIdentifier { + inline InputDeviceIdentifier() : + bus(0), vendor(0), product(0), version(0) { + } + + String8 name; + String8 location; + String8 uniqueId; + uint16_t bus; + uint16_t vendor; + uint16_t product; + uint16_t version; +}; + +/* Types of input device configuration files. */ +enum InputDeviceConfigurationFileType { + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ +}; + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The device identifier is used to construct several default configuration file + * names to try based on the device name, vendor, product, and version. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type); + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The name is case-sensitive and is used to construct the filename to resolve. + * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type); } // namespace android diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index d0812de..b621680 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -291,9 +291,7 @@ public: * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event * should be dispatched to applications. */ - virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId, - int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode, - uint32_t& policyFlags) = 0; + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; /* Intercepts a generic touch, trackball or other event before queueing it. * The policy can use this method as an opportunity to perform power management functions @@ -894,9 +892,6 @@ private: // Input channels that will receive a copy of all input events. Vector<sp<InputChannel> > mMonitoringChannels; - // Preallocated key event object used for policy inquiries. - KeyEvent mReusableKeyEvent; - // Event injection and synchronization. Condition mInjectionResultAvailableCondition; bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index f3a2dd2..8ec5421 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -35,34 +35,6 @@ namespace android { class InputDevice; class InputMapper; -/* Describes a virtual key. */ -struct VirtualKeyDefinition { - int32_t scanCode; - - // configured position data, specified in display coords - int32_t centerX; - int32_t centerY; - int32_t width; - int32_t height; -}; - - -/* Specifies input device calibration settings. */ -class InputDeviceCalibration { -public: - InputDeviceCalibration(); - - void clear(); - void addProperty(const String8& key, const String8& value); - - bool tryGetProperty(const String8& key, String8& outValue) const; - bool tryGetProperty(const String8& key, int32_t& outValue) const; - bool tryGetProperty(const String8& key, float& outValue) const; - -private: - KeyedVector<String8, String8> mProperties; -}; - /* * Input reader policy interface. @@ -103,14 +75,6 @@ public: */ virtual bool filterJumpyTouchEvents() = 0; - /* Gets the configured virtual key definitions for an input device. */ - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0; - - /* Gets the calibration for an input device. */ - virtual void getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration) = 0; - /* Gets the excluded device names for the platform. */ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0; }; @@ -314,8 +278,8 @@ public: int32_t getMetaState(); - inline const InputDeviceCalibration& getCalibration() { - return mCalibration; + inline const PropertyMap& getConfiguration() { + return mConfiguration; } private: @@ -330,7 +294,7 @@ private: typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); - InputDeviceCalibration mCalibration; + PropertyMap mConfiguration; }; @@ -389,13 +353,13 @@ private: class KeyboardInputMapper : public InputMapper { public: - KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources, - int32_t keyboardType); + KeyboardInputMapper(InputDevice* device, uint32_t sources, int32_t keyboardType); virtual ~KeyboardInputMapper(); virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); + virtual void configure(); virtual void reset(); virtual void process(const RawEvent* rawEvent); @@ -414,10 +378,15 @@ private: int32_t scanCode; }; - int32_t mAssociatedDisplayId; uint32_t mSources; int32_t mKeyboardType; + // Immutable configuration parameters. + struct Parameters { + int32_t associatedDisplayId; + bool orientationAware; + } mParameters; + struct LockedState { Vector<KeyDown> keyDowns; // keys that are down int32_t metaState; @@ -435,6 +404,9 @@ private: void initializeLocked(); void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led); + void configureParameters(); + void dumpParameters(String8& dump); + bool isKeyboardOrGamepadKey(int32_t scanCode); void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, @@ -450,12 +422,13 @@ private: class TrackballInputMapper : public InputMapper { public: - TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId); + TrackballInputMapper(InputDevice* device); virtual ~TrackballInputMapper(); virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); + virtual void configure(); virtual void reset(); virtual void process(const RawEvent* rawEvent); @@ -467,7 +440,11 @@ private: Mutex mLock; - int32_t mAssociatedDisplayId; + // Immutable configuration parameters. + struct Parameters { + int32_t associatedDisplayId; + bool orientationAware; + } mParameters; struct Accumulator { enum { @@ -499,13 +476,16 @@ private: void initializeLocked(); + void configureParameters(); + void dumpParameters(String8& dump); + void sync(nsecs_t when); }; class TouchInputMapper : public InputMapper { public: - TouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + TouchInputMapper(InputDevice* device); virtual ~TouchInputMapper(); virtual uint32_t getSources(); @@ -591,10 +571,17 @@ protected: } }; - int32_t mAssociatedDisplayId; - // Immutable configuration parameters. struct Parameters { + enum DeviceType { + DEVICE_TYPE_TOUCH_SCREEN, + DEVICE_TYPE_TOUCH_PAD, + }; + + DeviceType deviceType; + int32_t associatedDisplayId; + bool orientationAware; + bool useBadTouchFilter; bool useJumpyTouchFilter; bool useAveragingTouchFilter; @@ -641,7 +628,7 @@ protected: bool haveToolSizeAreaBias; float toolSizeAreaBias; bool haveToolSizeIsSummed; - int32_t toolSizeIsSummed; + bool toolSizeIsSummed; // Pressure enum PressureCalibration { @@ -846,7 +833,7 @@ private: class SingleTouchInputMapper : public TouchInputMapper { public: - SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + SingleTouchInputMapper(InputDevice* device); virtual ~SingleTouchInputMapper(); virtual void reset(); @@ -892,7 +879,7 @@ private: class MultiTouchInputMapper : public TouchInputMapper { public: - MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + MultiTouchInputMapper(InputDevice* device); virtual ~MultiTouchInputMapper(); virtual void reset(); diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h index 3b477c7..50296e2 100644 --- a/include/ui/Keyboard.h +++ b/include/ui/Keyboard.h @@ -20,6 +20,7 @@ #include <ui/Input.h> #include <utils/Errors.h> #include <utils/String8.h> +#include <utils/PropertyMap.h> namespace android { @@ -32,26 +33,58 @@ enum { DEVICE_ID_VIRTUAL_KEYBOARD = -1, }; -struct KeyMapInfo { - String8 keyMapName; +class KeyLayoutMap; +class KeyCharacterMap; + +/** + * Loads the key layout map and key character map for a keyboard device. + */ +class KeyMap { +public: String8 keyLayoutFile; + KeyLayoutMap* keyLayoutMap; + String8 keyCharacterMapFile; - bool isDefaultKeyMap; + KeyCharacterMap* keyCharacterMap; + + KeyMap(); + ~KeyMap(); + + status_t load(const InputDeviceIdentifier& deviceIdenfier, + const PropertyMap* deviceConfiguration); - KeyMapInfo() : isDefaultKeyMap(false) { + inline bool haveKeyLayout() const { + return !keyLayoutFile.isEmpty(); } + + inline bool haveKeyCharacterMap() const { + return !keyCharacterMapFile.isEmpty(); + } + + inline bool isComplete() const { + return haveKeyLayout() && haveKeyCharacterMap(); + } + +private: + bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name); + String8 getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type); }; /** - * Resolves the key map to use for a particular keyboard device. + * Returns true if the keyboard is eligible for use as a built-in keyboard. */ -extern status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo); +extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap); /** * Sets keyboard system properties. */ -extern void setKeyboardProperties(int32_t deviceId, const String8& deviceName, - const KeyMapInfo& keyMapInfo); +extern void setKeyboardProperties(int32_t deviceId, const InputDeviceIdentifier& deviceIdentifier, + const String8& keyLayoutFile, const String8& keyCharacterMapFile); /** * Clears keyboard system properties. diff --git a/include/ui/VirtualKeyMap.h b/include/ui/VirtualKeyMap.h new file mode 100644 index 0000000..7813d9d --- /dev/null +++ b/include/ui/VirtualKeyMap.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_VIRTUAL_KEY_MAP_H +#define _UI_VIRTUAL_KEY_MAP_H + +#include <stdint.h> + +#include <ui/Input.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> +#include <utils/String8.h> +#include <utils/Unicode.h> + +namespace android { + +/* Describes a virtual key. */ +struct VirtualKeyDefinition { + int32_t scanCode; + + // configured position data, specified in display coords + int32_t centerX; + int32_t centerY; + int32_t width; + int32_t height; +}; + + +/** + * Describes a collection of virtual keys on a touch screen in terms of + * virtual scan codes and hit rectangles. + */ +class VirtualKeyMap { +public: + ~VirtualKeyMap(); + + static status_t load(const String8& filename, VirtualKeyMap** outMap); + + inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { + return mVirtualKeys; + } + +private: + class Parser { + VirtualKeyMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(VirtualKeyMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + bool consumeFieldDelimiterAndSkipWhitespace(); + bool parseNextIntField(int32_t* outValue); + }; + + Vector<VirtualKeyDefinition> mVirtualKeys; + + VirtualKeyMap(); +}; + +} // namespace android + +#endif // _UI_KEY_CHARACTER_MAP_H diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h new file mode 100644 index 0000000..a54f819 --- /dev/null +++ b/include/utils/PropertyMap.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_PROPERTY_MAP_H +#define _UTILS_PROPERTY_MAP_H + +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> + +namespace android { + +/* + * Provides a mechanism for passing around string-based property key / value pairs + * and loading them from property files. + * + * The property files have the following simple structure: + * + * # Comment + * key = value + * + * Keys and values are any sequence of printable ASCII characters. + * The '=' separates the key from the value. + * The key and value may not contain whitespace. + * + * The '\' character is reserved for escape sequences and is not currently supported. + * The '"" character is reserved for quoting and is not currently supported. + * Files that contain the '\' or '"' character will fail to parse. + * + * The file must not contain duplicate keys. + * + * TODO Support escape sequences and quoted values when needed. + */ +class PropertyMap { +public: + /* Creates an empty property map. */ + PropertyMap(); + ~PropertyMap(); + + /* Clears the property map. */ + void clear(); + + /* Adds a property. + * Replaces the property with the same key if it is already present. + */ + void addProperty(const String8& key, const String8& value); + + /* Returns true if the property map contains the specified key. */ + bool hasProperty(const String8& key) const; + + /* Gets the value of a property and parses it. + * Returns true and sets outValue if the key was found and its value was parsed successfully. + * Otherwise returns false and does not modify outValue. (Also logs a warning.) + */ + bool tryGetProperty(const String8& key, String8& outValue) const; + bool tryGetProperty(const String8& key, bool& outValue) const; + bool tryGetProperty(const String8& key, int32_t& outValue) const; + bool tryGetProperty(const String8& key, float& outValue) const; + + /* Loads a property map from a file. */ + static status_t load(const String8& filename, PropertyMap** outMap); + +private: + class Parser { + PropertyMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(PropertyMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseType(); + status_t parseKey(); + status_t parseKeyProperty(); + status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseCharacterLiteral(char16_t* outCharacter); + }; + + KeyedVector<String8, String8> mProperties; +}; + +} // namespace android + +#endif // _UTILS_PROPERTY_MAP_H diff --git a/include/utils/String8.h b/include/utils/String8.h index 6abfb06..6b49ff5 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -47,7 +47,12 @@ public: explicit String8(const char32_t* o); explicit String8(const char32_t* o, size_t numChars); ~String8(); - + + static inline const String8 empty(); + + static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2))); + static String8 formatV(const char* fmt, va_list args); + inline const char* string() const; inline size_t size() const; inline size_t length() const; @@ -229,6 +234,10 @@ inline int strictly_order_type(const String8& lhs, const String8& rhs) return compare_type(lhs, rhs) < 0; } +inline const String8 String8::empty() { + return String8(); +} + inline const char* String8::string() const { return mString; diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h index 21e58e6..c7db5fb 100644 --- a/include/utils/Tokenizer.h +++ b/include/utils/Tokenizer.h @@ -28,7 +28,7 @@ namespace android { * A simple tokenizer for loading and parsing ASCII text files line by line. */ class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length); + Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length); public: ~Tokenizer(); @@ -110,7 +110,7 @@ private: String8 mFilename; FileMap* mFileMap; - const char* mBuffer; + char* mBuffer; size_t mLength; const char* mCurrent; diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp index 45b1b9a..e9a5f8c 100644 --- a/libs/camera/CameraParameters.cpp +++ b/libs/camera/CameraParameters.cpp @@ -75,6 +75,7 @@ const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances"; const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format"; const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size"; const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values"; +const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video"; const char CameraParameters::TRUE[] = "true"; const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity"; @@ -333,6 +334,14 @@ void CameraParameters::getPreviewSize(int *width, int *height) const parse_pair(p, width, height, 'x'); } +void CameraParameters::getPreferredPreviewSizeForVideo(int *width, int *height) const +{ + *width = *height = -1; + const char *p = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const { const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES); diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 7ca289d..ebffd34 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -42,6 +42,9 @@ Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQua mXDivs = new int32_t[mXCount]; mYDivs = new int32_t[mYCount]; + PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, vertices = %d", + xCount, yCount, emptyQuads, verticesCount); + glGenBuffers(1, &meshBuffer); } @@ -208,7 +211,15 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, uint32_t& quadCount) { - if (((mColorKey >> quadCount++) & 0x1) == 1) { + uint32_t oldQuadCount = quadCount; + + // Degenerate quads are an artifact of our implementation and should not + // be taken into account when checking for transparent quads + if (x2 - x1 > 0.999f && y2 - y1 > 0.999f) { + quadCount++; + } + + if (((mColorKey >> oldQuadCount) & 0x1) == 1) { return; } diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp index 91b1c32..b86bbc5 100644 --- a/libs/hwui/SkiaColorFilter.cpp +++ b/libs/hwui/SkiaColorFilter.cpp @@ -36,6 +36,11 @@ SkiaColorFilter::~SkiaColorFilter() { SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector): SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) { + // Skia uses the range [0..255] for the addition vector, but we need + // the [0..1] range to apply the vector in GLSL + for (int i = 0; i < 4; i++) { + mVector[i] /= 255.0f; + } } SkiaColorMatrixFilter::~SkiaColorMatrixFilter() { diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 5948e04..d0e041a 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Keyboard.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ + VirtualKeyMap.cpp # For the host # ===================================================== diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index f468217..8f4bac6 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -33,6 +33,8 @@ #include <assert.h> #include <ui/KeyLayoutMap.h> +#include <ui/KeyCharacterMap.h> +#include <ui/VirtualKeyMap.h> #include <string.h> #include <stdint.h> @@ -56,10 +58,6 @@ /* this macro computes the number of bytes needed to represent a bit array of the specified size */ #define sizeof_bit_array(bits) ((bits + 7) / 8) -#define ID_MASK 0x0000ffff -#define SEQ_MASK 0x7fff0000 -#define SEQ_SHIFT 16 - #ifndef ABS_MT_TOUCH_MAJOR #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #endif @@ -72,6 +70,9 @@ #define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ #endif +// Fd at index 0 is always reserved for inotify +#define FIRST_ACTUAL_DEVICE_INDEX 1 + #define INDENT " " #define INDENT2 " " #define INDENT3 " " @@ -79,7 +80,7 @@ namespace android { static const char *WAKE_LOCK_ID = "KeyEvents"; -static const char *device_path = "/dev/input"; +static const char *DEVICE_PATH = "/dev/input"; /* return the larger integer */ static inline int max(int v1, int v2) @@ -91,72 +92,89 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } -EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name) - : id(_id), path(_path), name(name), classes(0) - , keyBitmask(NULL), layoutMap(NULL), fd(-1), next(NULL) { +// --- EventHub::Device --- + +EventHub::Device::Device(int fd, int32_t id, const String8& path, + const InputDeviceIdentifier& identifier) : + next(NULL), + fd(fd), id(id), path(path), identifier(identifier), + classes(0), keyBitmask(NULL), configuration(NULL), virtualKeyMap(NULL) { } -EventHub::device_t::~device_t() { - delete [] keyBitmask; - delete layoutMap; +EventHub::Device::~Device() { + close(); + delete[] keyBitmask; + delete configuration; + delete virtualKeyMap; } -EventHub::EventHub(void) - : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(-1) - , mDevicesById(0), mNumDevicesById(0) - , mOpeningDevices(0), mClosingDevices(0) - , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false) - , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0) -{ +void EventHub::Device::close() { + if (fd >= 0) { + ::close(fd); + fd = -1; + } +} + + +// --- EventHub --- + +EventHub::EventHub(void) : + mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1), + mOpeningDevices(0), mClosingDevices(0), + mOpened(false), mNeedToSendFinishedDeviceScan(false), + mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); #ifdef EV_SW memset(mSwitches, 0, sizeof(mSwitches)); #endif } -/* - * Clean up. - */ -EventHub::~EventHub(void) -{ +EventHub::~EventHub(void) { release_wake_lock(WAKE_LOCK_ID); // we should free stuff here... } -status_t EventHub::errorCheck() const -{ +status_t EventHub::errorCheck() const { return mError; } -String8 EventHub::getDeviceName(int32_t deviceId) const -{ +String8 EventHub::getDeviceName(int32_t deviceId) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device == NULL) return String8(); - return device->name; + return device->identifier.name; } -uint32_t EventHub::getDeviceClasses(int32_t deviceId) const -{ +uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device == NULL) return 0; return device->classes; } +void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->configuration) { + *outConfiguration = *device->configuration; + } else { + outConfiguration->clear(); + } +} + status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const { outAxisInfo->clear(); AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device == NULL) return -1; struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { LOGW("Error reading absolute controller %d for device %s fd %d\n", - axis, device->name.string(), device->fd); + axis, device->identifier.name.string(), device->fd); return -errno; } @@ -174,7 +192,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device != NULL) { return getScanCodeStateLocked(device, scanCode); } @@ -182,7 +200,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const { +int32_t EventHub::getScanCodeStateLocked(Device* device, int32_t scanCode) const { uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); if (ioctl(device->fd, @@ -195,20 +213,20 @@ int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) con int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device != NULL) { return getKeyCodeStateLocked(device, keyCode); } return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const { - if (!device->layoutMap) { +int32_t EventHub::getKeyCodeStateLocked(Device* device, int32_t keyCode) const { + if (!device->keyMap.haveKeyLayout()) { return AKEY_STATE_UNKNOWN; } Vector<int32_t> scanCodes; - device->layoutMap->findScanCodes(keyCode, &scanCodes); + device->keyMap.keyLayoutMap->findScanCodes(keyCode, &scanCodes); uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); @@ -236,7 +254,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { if (sw >= 0 && sw <= SW_MAX) { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device != NULL) { return getSwitchStateLocked(device, sw); } @@ -245,7 +263,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const { +int32_t EventHub::getSwitchStateLocked(Device* device, int32_t sw) const { uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; memset(sw_bitmask, 0, sizeof(sw_bitmask)); if (ioctl(device->fd, @@ -259,16 +277,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device != NULL) { return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags); } return false; } -bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, +bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - if (device->layoutMap == NULL || device->keyBitmask == NULL) { + if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) { return false; } @@ -276,7 +294,7 @@ bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { scanCodes.clear(); - status_t err = device->layoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes); + status_t err = device->keyMap.keyLayoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes); if (! err) { // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver @@ -295,20 +313,20 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (device && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags); if (err == NO_ERROR) { return NO_ERROR; } } - if (mHaveFirstKeyboard) { - device = getDeviceLocked(mFirstKeyboardId); + if (mBuiltInKeyboardId != -1) { + device = getDeviceLocked(mBuiltInKeyboardId); - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (device && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags); if (err == NO_ERROR) { return NO_ERROR; } @@ -330,7 +348,7 @@ void EventHub::addExcludedDevice(const char* deviceName) bool EventHub::hasLed(int32_t deviceId, int32_t led) const { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device) { uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)]; memset(bitmask, 0, sizeof(bitmask)); @@ -345,7 +363,7 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); + Device* device = getDeviceLocked(deviceId); if (device) { struct input_event ev; ev.time.tv_sec = 0; @@ -361,21 +379,33 @@ void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { } } -EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const -{ - if (deviceId == 0) deviceId = mFirstKeyboardId; - int32_t id = deviceId & ID_MASK; - if (id >= mNumDevicesById || id < 0) return NULL; - device_t* dev = mDevicesById[id].device; - if (dev == NULL) return NULL; - if (dev->id == deviceId) { - return dev; +void EventHub::getVirtualKeyDefinitions(int32_t deviceId, + Vector<VirtualKeyDefinition>& outVirtualKeys) const { + outVirtualKeys.clear(); + + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->virtualKeyMap) { + outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys()); + } +} + +EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { + if (deviceId == 0) { + deviceId = mBuiltInKeyboardId; + } + + size_t numDevices = mDevices.size(); + for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < numDevices; i++) { + Device* device = mDevices[i]; + if (device->id == deviceId) { + return device; + } } return NULL; } -bool EventHub::getEvent(RawEvent* outEvent) -{ +bool EventHub::getEvent(RawEvent* outEvent) { outEvent->deviceId = 0; outEvent->type = 0; outEvent->scanCode = 0; @@ -396,11 +426,11 @@ bool EventHub::getEvent(RawEvent* outEvent) for (;;) { // Report any devices that had last been added/removed. if (mClosingDevices != NULL) { - device_t* device = mClosingDevices; - LOGV("Reporting device closed: id=0x%x, name=%s\n", + Device* device = mClosingDevices; + LOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; - if (device->id == mFirstKeyboardId) { + if (device->id == mBuiltInKeyboardId) { outEvent->deviceId = 0; } else { outEvent->deviceId = device->id; @@ -413,11 +443,11 @@ bool EventHub::getEvent(RawEvent* outEvent) } if (mOpeningDevices != NULL) { - device_t* device = mOpeningDevices; - LOGV("Reporting device opened: id=0x%x, name=%s\n", + Device* device = mOpeningDevices; + LOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; - if (device->id == mFirstKeyboardId) { + if (device->id == mBuiltInKeyboardId) { outEvent->deviceId = 0; } else { outEvent->deviceId = device->id; @@ -440,11 +470,11 @@ bool EventHub::getEvent(RawEvent* outEvent) // Consume buffered input events, if any. if (mInputBufferIndex < mInputBufferCount) { const struct input_event& iev = mInputBufferData[mInputBufferIndex++]; - const device_t* device = mDevices[mInputDeviceIndex]; + const Device* device = mDevices[mInputFdIndex]; LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); - if (device->id == mFirstKeyboardId) { + if (device->id == mBuiltInKeyboardId) { outEvent->deviceId = 0; } else { outEvent->deviceId = device->id; @@ -454,8 +484,8 @@ bool EventHub::getEvent(RawEvent* outEvent) outEvent->flags = 0; if (iev.type == EV_KEY) { outEvent->keyCode = AKEYCODE_UNKNOWN; - if (device->layoutMap) { - status_t err = device->layoutMap->map(iev.code, + if (device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->map(iev.code, &outEvent->keyCode, &outEvent->flags); LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", iev.code, outEvent->keyCode, outEvent->flags, err); @@ -475,13 +505,13 @@ bool EventHub::getEvent(RawEvent* outEvent) // Finish reading all events from devices identified in previous poll(). // This code assumes that mInputDeviceIndex is initially 0 and that the // revents member of pollfd is initialized to 0 when the device is first added. - // Since mFDs[0] is used for inotify, we process regular events starting at index 1. - mInputDeviceIndex += 1; - if (mInputDeviceIndex >= mFDCount) { + // Since mFds[0] is used for inotify, we process regular events starting at index 1. + mInputFdIndex += 1; + if (mInputFdIndex >= mFds.size()) { break; } - const struct pollfd& pfd = mFDs[mInputDeviceIndex]; + const struct pollfd& pfd = mFds[mInputFdIndex]; if (pfd.revents & POLLIN) { int32_t readSize = read(pfd.fd, mInputBufferData, sizeof(struct input_event) * INPUT_BUFFER_SIZE); @@ -492,7 +522,7 @@ bool EventHub::getEvent(RawEvent* outEvent) } else if ((readSize % sizeof(struct input_event)) != 0) { LOGE("could not get event (wrong size: %d)", readSize); } else { - mInputBufferCount = readSize / sizeof(struct input_event); + mInputBufferCount = size_t(readSize) / sizeof(struct input_event); mInputBufferIndex = 0; } } @@ -501,14 +531,14 @@ bool EventHub::getEvent(RawEvent* outEvent) #if HAVE_INOTIFY // readNotify() will modify mFDs and mFDCount, so this must be done after // processing all other events. - if(mFDs[0].revents & POLLIN) { - readNotify(mFDs[0].fd); - mFDs[0].revents = 0; + if(mFds[0].revents & POLLIN) { + readNotify(mFds[0].fd); + mFds.editItemAt(0).revents = 0; continue; // report added or removed devices immediately } #endif - mInputDeviceIndex = 0; + mInputFdIndex = 0; // Poll for events. Mind the wake lock dance! // We hold a wake lock at all times except during poll(). This works due to some @@ -520,7 +550,7 @@ bool EventHub::getEvent(RawEvent* outEvent) // pending or currently being processed. release_wake_lock(WAKE_LOCK_ID); - int pollResult = poll(mFDs, mFDCount, -1); + int pollResult = poll(mFds.editArray(), mFds.size(), -1); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); @@ -536,36 +566,37 @@ bool EventHub::getEvent(RawEvent* outEvent) /* * Open the platform-specific input device. */ -bool EventHub::openPlatformInput(void) -{ +bool EventHub::openPlatformInput(void) { /* * Open platform-specific input device(s). */ - int res; + int res, fd; - mFDCount = 1; - mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); - mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); - mFDs[0].events = POLLIN; - mFDs[0].revents = 0; - mDevices[0] = NULL; #ifdef HAVE_INOTIFY - mFDs[0].fd = inotify_init(); - res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); + fd = inotify_init(); + res = inotify_add_watch(fd, DEVICE_PATH, IN_DELETE | IN_CREATE); if(res < 0) { - LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); + LOGE("could not add watch for %s, %s\n", DEVICE_PATH, strerror(errno)); } #else /* * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. * We allocate space for it and set it to something invalid. */ - mFDs[0].fd = -1; + fd = -1; #endif - res = scanDir(device_path); + // Reserve fd index 0 for inotify. + struct pollfd pollfd; + pollfd.fd = fd; + pollfd.events = POLLIN; + pollfd.revents = 0; + mFds.push(pollfd); + mDevices.push(NULL); + + res = scanDir(DEVICE_PATH); if(res < 0) { - LOGE("scan dir failed for %s\n", device_path); + LOGE("scan dir failed for %s\n", DEVICE_PATH); } return true; @@ -593,128 +624,104 @@ static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE }; -int EventHub::openDevice(const char *deviceName) { - int version; - int fd; - struct pollfd *new_mFDs; - device_t **new_devices; - char **new_device_names; - char name[80]; - char location[80]; - char idstr[80]; - struct input_id id; +int EventHub::openDevice(const char *devicePath) { + char buffer[80]; - LOGV("Opening device: %s", deviceName); + LOGV("Opening device: %s", devicePath); AutoMutex _l(mLock); - fd = open(deviceName, O_RDWR); + int fd = open(devicePath, O_RDWR); if(fd < 0) { - LOGE("could not open %s, %s\n", deviceName, strerror(errno)); + LOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } - if(ioctl(fd, EVIOCGVERSION, &version)) { - LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - if(ioctl(fd, EVIOCGID, &id)) { - LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - name[sizeof(name) - 1] = '\0'; - location[sizeof(location) - 1] = '\0'; - idstr[sizeof(idstr) - 1] = '\0'; - if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); - name[0] = '\0'; + InputDeviceIdentifier identifier; + + // Get device name. + if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.name.setTo(buffer); } - // check to see if the device is on our excluded list + // Check to see if the device is on our excluded list List<String8>::iterator iter = mExcludedDevices.begin(); List<String8>::iterator end = mExcludedDevices.end(); for ( ; iter != end; iter++) { const char* test = *iter; - if (strcmp(name, test) == 0) { - LOGI("ignoring event id %s driver %s\n", deviceName, test); + if (identifier.name == test) { + LOGI("ignoring event id %s driver %s\n", devicePath, test); close(fd); return -1; } } - if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); - location[0] = '\0'; - } - if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); - idstr[0] = '\0'; - } - - if (fcntl(fd, F_SETFL, O_NONBLOCK)) { - LOGE("Error %d making device file descriptor non-blocking.", errno); + // Get device driver version. + int driverVersion; + if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { + LOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; } - int devid = 0; - while (devid < mNumDevicesById) { - if (mDevicesById[devid].device == NULL) { - break; - } - devid++; + // Get device identifier. + struct input_id inputId; + if(ioctl(fd, EVIOCGID, &inputId)) { + LOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); + close(fd); + return -1; } - if (devid >= mNumDevicesById) { - device_ent* new_devids = (device_ent*)realloc(mDevicesById, - sizeof(mDevicesById[0]) * (devid + 1)); - if (new_devids == NULL) { - LOGE("out of memory"); - return -1; - } - mDevicesById = new_devids; - mNumDevicesById = devid+1; - mDevicesById[devid].device = NULL; - mDevicesById[devid].seq = 0; + identifier.bus = inputId.bustype; + identifier.product = inputId.product; + identifier.vendor = inputId.vendor; + identifier.version = inputId.version; + + // Get device physical location. + if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.location.setTo(buffer); } - mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; - if (mDevicesById[devid].seq == 0) { - mDevicesById[devid].seq = 1<<SEQ_SHIFT; + // Get device unique id. + if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.uniqueId.setTo(buffer); } - new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); - new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); - if (new_mFDs == NULL || new_devices == NULL) { - LOGE("out of memory"); + // Make file descriptor non-blocking for use with poll(). + if (fcntl(fd, F_SETFL, O_NONBLOCK)) { + LOGE("Error %d making device file descriptor non-blocking.", errno); + close(fd); return -1; } - mFDs = new_mFDs; - mDevices = new_devices; + + // Allocate device. (The device object takes ownership of the fd at this point.) + int32_t deviceId = mNextDeviceId++; + Device* device = new Device(fd, deviceId, String8(devicePath), identifier); #if 0 - LOGI("add device %d: %s\n", mFDCount, deviceName); - LOGI(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", - id.bustype, id.vendor, id.product, id.version); - LOGI(" name: \"%s\"\n", name); - LOGI(" location: \"%s\"\n" - " id: \"%s\"\n", location, idstr); - LOGI(" version: %d.%d.%d\n", - version >> 16, (version >> 8) & 0xff, version & 0xff); + LOGI("add device %d: %s\n", deviceId, devicePath); + LOGI(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + identifier.bus, identifier.vendor, identifier.product, identifier.version); + LOGI(" name: \"%s\"\n", identifier.name.string()); + LOGI(" location: \"%s\"\n", identifier.location.string()); + LOGI(" unique id: \"%s\"\n", identifier.uniqueId.string()); + LOGI(" driver: v%d.%d.%d\n", + driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); #endif - device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); - if (device == NULL) { - LOGE("out of memory"); - return -1; - } - - device->fd = fd; - mFDs[mFDCount].fd = fd; - mFDs[mFDCount].events = POLLIN; - mFDs[mFDCount].revents = 0; + // Load the configuration file for the device. + loadConfiguration(device); // Figure out the kinds of events the device reports. @@ -784,7 +791,7 @@ int EventHub::openDevice(const char *deviceName) { bool hasSwitches = false; if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { for (int i=0; i<EV_SW; i++) { - //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); + //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); if (test_bit(i, sw_bitmask)) { hasSwitches = true; if (mSwitches[i] == 0) { @@ -798,38 +805,29 @@ int EventHub::openDevice(const char *deviceName) { } #endif - if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { - // a more descriptive name - device->name = name; - - // Configure the keymap for the device. + if ((device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) { + // Load the virtual keys for the touch screen, if any. + // We do this now so that we can make sure to load the keymap if necessary. + status_t status = loadVirtualKeyMap(device); + if (!status) { + device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + } + } - configureKeyMap(device); + if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { + // Load the keymap for the device. + status_t status = loadKeyMap(device); - // Tell the world about the devname (the descriptive name) - if (!mHaveFirstKeyboard && !device->keyMapInfo.isDefaultKeyMap && strstr(name, "-keypad")) { - // the built-in keyboard has a well-known device ID of 0, - // this device better not go away. - mHaveFirstKeyboard = true; - mFirstKeyboardId = device->id; - setKeyboardProperties(device, true); - } else { - // ensure mFirstKeyboardId is set to -something-. - if (mFirstKeyboardId == -1) { - mFirstKeyboardId = device->id; - setKeyboardProperties(device, true); - } - } + // Set system properties for the keyboard. setKeyboardProperties(device, false); - // Load the keylayout. - if (!device->keyMapInfo.keyLayoutFile.isEmpty()) { - status_t status = KeyLayoutMap::load(device->keyMapInfo.keyLayoutFile, - &device->layoutMap); - if (status) { - LOGE("Error %d loading key layout file '%s'.", status, - device->keyMapInfo.keyLayoutFile.string()); - } + // Register the keyboard as a built-in keyboard if it is eligible. + if (!status + && mBuiltInKeyboardId == -1 + && isEligibleBuiltInKeyboard(device->identifier, + device->configuration, &device->keyMap)) { + mBuiltInKeyboardId = device->id; + setKeyboardProperties(device, true); } // 'Q' key support = cheap test of whether this is an alpha-capable kbd @@ -853,58 +851,87 @@ int EventHub::openDevice(const char *deviceName) { break; } } - - LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n", - device->id, name, - device->keyMapInfo.keyLayoutFile.string(), - device->keyMapInfo.keyCharacterMapFile.string()); } // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { - LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid); - close(fd); + LOGV("Dropping device: id=%d, path='%s', name='%s'", + deviceId, devicePath, device->identifier.name.string()); delete device; return -1; } - LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); - - LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", - deviceName, device, mFDCount, devid, device->classes); + LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " + "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s", + deviceId, fd, devicePath, device->identifier.name.string(), + device->classes, + device->configurationFile.string(), + device->keyMap.keyLayoutFile.string(), + device->keyMap.keyCharacterMapFile.string(), + toString(mBuiltInKeyboardId == deviceId)); + + struct pollfd pollfd; + pollfd.fd = fd; + pollfd.events = POLLIN; + pollfd.revents = 0; + mFds.push(pollfd); + mDevices.push(device); - mDevicesById[devid].device = device; device->next = mOpeningDevices; mOpeningDevices = device; - mDevices[mFDCount] = device; - - mFDCount++; return 0; } -void EventHub::configureKeyMap(device_t* device) { - android::resolveKeyMap(device->name, device->keyMapInfo); +void EventHub::loadConfiguration(Device* device) { + device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( + device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); + if (device->configurationFile.isEmpty()) { + LOGD("No input device configuration file found for device '%s'.", + device->identifier.name.string()); + } else { + status_t status = PropertyMap::load(device->configurationFile, + &device->configuration); + if (status) { + LOGE("Error loading input device configuration file for device '%s'. " + "Using default configuration.", + device->identifier.name.string()); + } + } +} + +status_t EventHub::loadVirtualKeyMap(Device* device) { + // The virtual key map is supplied by the kernel as a system board property file. + String8 path; + path.append("/sys/board_properties/virtualkeys."); + path.append(device->identifier.name); + if (access(path.string(), R_OK)) { + return NAME_NOT_FOUND; + } + return VirtualKeyMap::load(path, &device->virtualKeyMap); +} + +status_t EventHub::loadKeyMap(Device* device) { + return device->keyMap.load(device->identifier, device->configuration); } -void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) { - int32_t id = firstKeyboard ? 0 : device->id; - android::setKeyboardProperties(id, device->name, device->keyMapInfo); +void EventHub::setKeyboardProperties(Device* device, bool builtInKeyboard) { + int32_t id = builtInKeyboard ? 0 : device->id; + android::setKeyboardProperties(id, device->identifier, + device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile); } -void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) { - int32_t id = firstKeyboard ? 0 : device->id; +void EventHub::clearKeyboardProperties(Device* device, bool builtInKeyboard) { + int32_t id = builtInKeyboard ? 0 : device->id; android::clearKeyboardProperties(id); } -bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const -{ - if (device->keyBitmask == NULL || device->layoutMap == NULL) { +bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { + if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) { return false; } Vector<int32_t> scanCodes; - device->layoutMap->findScanCodes(keycode, &scanCodes); + device->keyMap.keyLayoutMap->findScanCodes(keycode, &scanCodes); const size_t N = scanCodes.size(); for (size_t i=0; i<N && i<=KEY_MAX; i++) { int32_t sc = scanCodes.itemAt(i); @@ -916,29 +943,15 @@ bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const return false; } -int EventHub::closeDevice(const char *deviceName) { +int EventHub::closeDevice(const char *devicePath) { AutoMutex _l(mLock); - int i; - for(i = 1; i < mFDCount; i++) { - if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { - //LOGD("remove device %d: %s\n", i, deviceName); - device_t* device = mDevices[i]; - - LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - device->path.string(), device->name.string(), device->id, - mNumDevicesById, mFDCount, mFDs[i].fd, device->classes); - - // Clear this device's entry. - int index = (device->id&ID_MASK); - mDevicesById[index].device = NULL; - - // Close the file descriptor and compact the fd array. - close(mFDs[i].fd); - int count = mFDCount - i - 1; - memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); - memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); - mFDCount--; + for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) { + Device* device = mDevices[i]; + if (device->path == devicePath) { + LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", + device->path.string(), device->identifier.name.string(), device->id, + device->fd, device->classes); #ifdef EV_SW for (int j=0; j<EV_SW; j++) { @@ -947,21 +960,25 @@ int EventHub::closeDevice(const char *deviceName) { } } #endif - - device->next = mClosingDevices; - mClosingDevices = device; - if (device->id == mFirstKeyboardId) { + if (device->id == mBuiltInKeyboardId) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.string(), mFirstKeyboardId); - mFirstKeyboardId = -1; + device->path.string(), mBuiltInKeyboardId); + mBuiltInKeyboardId = -1; clearKeyboardProperties(device, true); } clearKeyboardProperties(device, false); + + mFds.removeAt(i); + mDevices.removeAt(i); + device->close(); + + device->next = mClosingDevices; + mClosingDevices = device; return 0; } } - LOGE("remove device: %s not found\n", deviceName); + LOGE("remove device: %s not found\n", devicePath); return -1; } @@ -985,7 +1002,7 @@ int EventHub::readNotify(int nfd) { } //printf("got %d bytes of event information\n", res); - strcpy(devname, device_path); + strcpy(devname, DEVICE_PATH); filename = devname + strlen(devname); *filename++ = '/'; @@ -1009,7 +1026,6 @@ int EventHub::readNotify(int nfd) { return 0; } - int EventHub::scanDir(const char *dirname) { char devname[PATH_MAX]; @@ -1040,30 +1056,34 @@ void EventHub::dump(String8& dump) { { // acquire lock AutoMutex _l(mLock); - dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard)); - dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId); + dump.appendFormat(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId); dump.append(INDENT "Devices:\n"); - for (int i = 0; i < mNumDevicesById; i++) { - const device_t* device = mDevicesById[i].device; + for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) { + const Device* device = mDevices[i]; if (device) { - if (mFirstKeyboardId == device->id) { - dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n", - device->id, device->name.string()); + if (mBuiltInKeyboardId == device->id) { + dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", + device->id, device->identifier.name.string()); } else { - dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string()); + dump.appendFormat(INDENT2 "%d: %s\n", device->id, + device->identifier.name.string()); } dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); - dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n", - toString(device->keyMapInfo.isDefaultKeyMap)); - dump.appendFormat(INDENT3 "KeyMapName: %s\n", - device->keyMapInfo.keyMapName.string()); + dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string()); + dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string()); + dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " + "product=0x%04x, version=0x%04x\n", + device->identifier.bus, device->identifier.vendor, + device->identifier.product, device->identifier.version); dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", - device->keyMapInfo.keyLayoutFile.string()); + device->keyMap.keyLayoutFile.string()); dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n", - device->keyMapInfo.keyCharacterMapFile.string()); + device->keyMap.keyCharacterMapFile.string()); + dump.appendFormat(INDENT3 "ConfigurationFile: %s\n", + device->configurationFile.string()); } } } // release lock diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 944a79b..b8d59e6 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -7,11 +7,117 @@ //#define LOG_NDEBUG 0 +#define DEBUG_PROBE 0 + +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + #include <ui/Input.h> namespace android { -// class InputEvent +static const char* CONFIGURATION_FILE_DIR[] = { + "idc/", + "keylayout/", + "keychars/", +}; + +static const char* CONFIGURATION_FILE_EXTENSION[] = { + ".idc", + ".kl", + ".kcm", +}; + +static bool isValidNameChar(char ch) { + return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); +} + +static void appendInputDeviceConfigurationFileRelativePath(String8& path, + const String8& name, InputDeviceConfigurationFileType type) { + path.append(CONFIGURATION_FILE_DIR[type]); + for (size_t i = 0; i < name.length(); i++) { + char ch = name[i]; + if (!isValidNameChar(ch)) { + ch = '_'; + } + path.append(&ch, 1); + } + path.append(CONFIGURATION_FILE_EXTENSION[type]); +} + +String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type) { + if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { + if (deviceIdentifier.version != 0) { + // Try vendor product version. + String8 versionPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x_Version_%04x", + deviceIdentifier.vendor, deviceIdentifier.product, + deviceIdentifier.version), + type)); + if (!versionPath.isEmpty()) { + return versionPath; + } + } + + // Try vendor product. + String8 productPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x", + deviceIdentifier.vendor, deviceIdentifier.product), + type)); + if (!productPath.isEmpty()) { + return productPath; + } + } + + // Try device name. + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); +} + +String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type) { + // Search system repository. + String8 path; + path.setTo(getenv("ANDROID_ROOT")); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system provided input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Search user repository. + // TODO Should only look here if not in safe mode. + path.setTo(getenv("ANDROID_DATA")); + path.append("/system/devices/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system user input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Not found. +#if DEBUG_PROBE + LOGD("Probe failed to find input device configuration file: name='%s', type=%d", + name.string(), type); +#endif + return String8(); +} + + +// --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source) { mDeviceId = deviceId; @@ -23,7 +129,7 @@ void InputEvent::initialize(const InputEvent& from) { mSource = from.mSource; } -// class KeyEvent +// --- KeyEvent --- bool KeyEvent::hasDefaultAction(int32_t keyCode) { switch (keyCode) { @@ -131,7 +237,7 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } -// class MotionEvent +// --- MotionEvent --- void MotionEvent::initialize( int32_t deviceId, @@ -178,7 +284,7 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } -// class InputDeviceInfo +// --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { initialize(-1, String8("uninitialized device info")); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index db7d448..f1223f1 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -315,7 +315,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, // Throttle it! #if DEBUG_THROTTLING LOGD("Throttling - Delaying motion event for " - "device 0x%x, source 0x%08x by up to %0.3fms.", + "device %d, source 0x%08x by up to %0.3fms.", deviceId, source, (nextTime - currentTime) * 0.000001); #endif if (nextTime < *nextWakeupTime) { @@ -704,7 +704,7 @@ bool InputDispatcher::dispatchKeyLocked( void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " "repeatCount=%d, downTime=%lld", prefix, @@ -767,7 +767,7 @@ bool InputDispatcher::dispatchMotionLocked( void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, " "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", prefix, @@ -2072,7 +2072,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { #if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " + LOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, " "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", eventTime, deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, downTime); @@ -2081,9 +2081,22 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou return; } + if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { + policyFlags |= POLICY_FLAG_VIRTUAL; + flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + } + policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); + + KeyEvent event; + event.initialize(deviceId, source, action, flags, keyCode, scanCode, + metaState, 0, downTime, eventTime); + + mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); + + if (policyFlags & POLICY_FLAG_WOKE_HERE) { + flags |= AKEY_EVENT_FLAG_WOKE_HERE; + } bool needWake; { // acquire lock @@ -2107,7 +2120,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) { #if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, " "xPrecision=%f, yPrecision=%f, downTime=%lld", eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags, @@ -2289,17 +2302,22 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, return INPUT_EVENT_INJECTION_FAILED; } - nsecs_t eventTime = keyEvent->getEventTime(); - int32_t deviceId = keyEvent->getDeviceId(); int32_t flags = keyEvent->getFlags(); - int32_t keyCode = keyEvent->getKeyCode(); - int32_t scanCode = keyEvent->getScanCode(); - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); + if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { + policyFlags |= POLICY_FLAG_VIRTUAL; + } + + mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags); + + if (policyFlags & POLICY_FLAG_WOKE_HERE) { + flags |= AKEY_EVENT_FLAG_WOKE_HERE; + } mLock.lock(); - injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(), - policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(), + injectedEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), + keyEvent->getDeviceId(), keyEvent->getSource(), + policyFlags, action, flags, + keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), keyEvent->getDownTime()); break; } @@ -2999,12 +3017,14 @@ void InputDispatcher::doNotifyANRLockedInterruptible( void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; - initializeKeyEvent(&mReusableKeyEvent, entry); + + KeyEvent event; + initializeKeyEvent(&event, entry); mLock.unlock(); bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel, - & mReusableKeyEvent, entry->policyFlags); + &event, entry->policyFlags); mLock.lock(); @@ -3025,12 +3045,13 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( && dispatchEntry->hasForegroundTarget() && dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); - initializeKeyEvent(&mReusableKeyEvent, keyEntry); + KeyEvent event; + initializeKeyEvent(&event, keyEntry); mLock.unlock(); mPolicy->dispatchUnhandledKey(connection->inputChannel, - & mReusableKeyEvent, keyEntry->policyFlags); + &event, keyEntry->policyFlags); mLock.lock(); } diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index daff2d0..9cc96ad 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -25,6 +25,7 @@ #include <cutils/log.h> #include <ui/InputReader.h> #include <ui/Keyboard.h> +#include <ui/VirtualKeyMap.h> #include <stddef.h> #include <stdlib.h> @@ -98,64 +99,6 @@ static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { } -// --- InputDeviceCalibration --- - -InputDeviceCalibration::InputDeviceCalibration() { -} - -void InputDeviceCalibration::clear() { - mProperties.clear(); -} - -void InputDeviceCalibration::addProperty(const String8& key, const String8& value) { - mProperties.add(key, value); -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const { - ssize_t index = mProperties.indexOfKey(key); - if (index < 0) { - return false; - } - - outValue = mProperties.valueAt(index); - return true; -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - int value = strtol(stringValue.string(), & end, 10); - if (*end != '\0') { - LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - float value = strtof(stringValue.string(), & end); - if (*end != '\0') { - LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - - // --- InputReader --- InputReader::InputReader(const sp<EventHubInterface>& eventHub, @@ -179,7 +122,7 @@ void InputReader::loopOnce() { mEventHub->getEvent(& rawEvent); #if DEBUG_RAW_EVENTS - LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", + LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d", rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, rawEvent.value); #endif @@ -215,9 +158,9 @@ void InputReader::addDevice(int32_t deviceId) { device->configure(); if (device->isIgnored()) { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string()); + LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string()); } else { - LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(), + LOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(), device->getSources()); } @@ -259,10 +202,10 @@ void InputReader::removeDevice(int32_t deviceId) { } if (device->isIgnored()) { - LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", + LOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), device->getName().string()); } else { - LOGI("Device removed: id=0x%x, name=%s, sources=%08x", + LOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(), device->getName().string(), device->getSources()); } @@ -274,8 +217,6 @@ void InputReader::removeDevice(int32_t deviceId) { InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) { InputDevice* device = new InputDevice(this, deviceId, name); - const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices - // Switch-like devices. if (classes & INPUT_DEVICE_CLASS_SWITCH) { device->addMapper(new SwitchInputMapper(device)); @@ -295,20 +236,19 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui } if (keyboardSources != 0) { - device->addMapper(new KeyboardInputMapper(device, - associatedDisplayId, keyboardSources, keyboardType)); + device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType)); } // Trackball-like devices. if (classes & INPUT_DEVICE_CLASS_TRACKBALL) { - device->addMapper(new TrackballInputMapper(device, associatedDisplayId)); + device->addMapper(new TrackballInputMapper(device)); } // Touchscreen-like devices. if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) { - device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId)); + device->addMapper(new MultiTouchInputMapper(device)); } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) { - device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId)); + device->addMapper(new SingleTouchInputMapper(device)); } return device; @@ -596,7 +536,7 @@ void InputDevice::dump(String8& dump) { InputDeviceInfo deviceInfo; getDeviceInfo(& deviceInfo); - dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(), + dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(), deviceInfo.getName().string()); dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); @@ -626,7 +566,7 @@ void InputDevice::addMapper(InputMapper* mapper) { void InputDevice::configure() { if (! isIgnored()) { - mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration); + mContext->getEventHub()->getConfiguration(mId, &mConfiguration); } mSources = 0; @@ -792,9 +732,9 @@ int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCod // --- KeyboardInputMapper --- -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t sources, int32_t keyboardType) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), + InputMapper(device), mSources(sources), mKeyboardType(keyboardType) { initializeLocked(); } @@ -832,7 +772,7 @@ void KeyboardInputMapper::dump(String8& dump) { { // acquire lock AutoMutex _l(mLock); dump.append(INDENT2 "Keyboard Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); + dumpParameters(dump); dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType); dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size()); dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState); @@ -840,6 +780,30 @@ void KeyboardInputMapper::dump(String8& dump) { } // release lock } + +void KeyboardInputMapper::configure() { + InputMapper::configure(); + + // Configure basic parameters. + configureParameters(); +} + +void KeyboardInputMapper::configureParameters() { + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"), + mParameters.orientationAware); + + mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1; +} + +void KeyboardInputMapper::dumpParameters(String8& dump) { + dump.append(INDENT3 "Parameters:\n"); + dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n", + mParameters.associatedDisplayId); + dump.appendFormat(INDENT4 "OrientationAware: %s\n", + toString(mParameters.orientationAware)); +} + void KeyboardInputMapper::reset() { for (;;) { int32_t keyCode, scanCode; @@ -896,9 +860,10 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, if (down) { // Rotate key codes according to orientation if needed. // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. - if (mAssociatedDisplayId >= 0) { + if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) { int32_t orientation; - if (!getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, + NULL, NULL, & orientation)) { orientation = InputReaderPolicyInterface::ROTATION_0; } @@ -1011,8 +976,8 @@ void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& // --- TrackballInputMapper --- -TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { +TrackballInputMapper::TrackballInputMapper(InputDevice* device) : + InputMapper(device) { mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; @@ -1039,7 +1004,7 @@ void TrackballInputMapper::dump(String8& dump) { { // acquire lock AutoMutex _l(mLock); dump.append(INDENT2 "Trackball Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); + dumpParameters(dump); dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision); dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision); dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down)); @@ -1047,6 +1012,29 @@ void TrackballInputMapper::dump(String8& dump) { } // release lock } +void TrackballInputMapper::configure() { + InputMapper::configure(); + + // Configure basic parameters. + configureParameters(); +} + +void TrackballInputMapper::configureParameters() { + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("trackball.orientationAware"), + mParameters.orientationAware); + + mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1; +} + +void TrackballInputMapper::dumpParameters(String8& dump) { + dump.append(INDENT3 "Parameters:\n"); + dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n", + mParameters.associatedDisplayId); + dump.appendFormat(INDENT4 "OrientationAware: %s\n", + toString(mParameters.orientationAware)); +} + void TrackballInputMapper::initializeLocked() { mAccumulator.clear(); @@ -1155,11 +1143,13 @@ void TrackballInputMapper::sync(nsecs_t when) { pointerCoords.toolMinor = 0; pointerCoords.orientation = 0; - if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) { + if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0 + && (x != 0.0f || y != 0.0f)) { // Rotate motion based on display orientation if needed. // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. int32_t orientation; - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, + NULL, NULL, & orientation)) { orientation = InputReaderPolicyInterface::ROTATION_0; } @@ -1205,8 +1195,8 @@ int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scan // --- TouchInputMapper --- -TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { +TouchInputMapper::TouchInputMapper(InputDevice* device) : + InputMapper(device) { mLocked.surfaceOrientation = -1; mLocked.surfaceWidth = -1; mLocked.surfaceHeight = -1; @@ -1218,7 +1208,15 @@ TouchInputMapper::~TouchInputMapper() { } uint32_t TouchInputMapper::getSources() { - return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD; + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + return AINPUT_SOURCE_TOUCHSCREEN; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + return AINPUT_SOURCE_TOUCHPAD; + default: + assert(false); + return AINPUT_SOURCE_UNKNOWN; + } } void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { @@ -1269,7 +1267,6 @@ void TouchInputMapper::dump(String8& dump) { { // acquire lock AutoMutex _l(mLock); dump.append(INDENT2 "Touch Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); dumpParameters(dump); dumpVirtualKeysLocked(dump); dumpRawAxes(dump); @@ -1339,14 +1336,50 @@ void TouchInputMapper::configureParameters() { mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); + + String8 deviceTypeString; + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { + if (deviceTypeString == "touchPad") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString != "touchScreen") { + LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + } + } + bool isTouchScreen = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + + mParameters.orientationAware = isTouchScreen; + getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); + + mParameters.associatedDisplayId = mParameters.orientationAware || isTouchScreen ? 0 : -1; } void TouchInputMapper::dumpParameters(String8& dump) { - dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n", + dump.append(INDENT3 "Parameters:\n"); + + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + dump.append(INDENT4 "DeviceType: touchScreen\n"); + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + dump.append(INDENT4 "DeviceType: touchPad\n"); + break; + default: + assert(false); + } + + dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n", + mParameters.associatedDisplayId); + dump.appendFormat(INDENT4 "OrientationAware: %s\n", + toString(mParameters.orientationAware)); + + dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n", toString(mParameters.useBadTouchFilter)); - dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n", + dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n", toString(mParameters.useAveragingTouchFilter)); - dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n", + dump.appendFormat(INDENT4 "UseJumpyTouchFilter: %s\n", toString(mParameters.useJumpyTouchFilter)); } @@ -1384,17 +1417,20 @@ void TouchInputMapper::dumpRawAxes(String8& dump) { bool TouchInputMapper::configureSurfaceLocked() { // Update orientation and dimensions if needed. - int32_t orientation; - int32_t width, height; - if (mAssociatedDisplayId >= 0) { + int32_t orientation = InputReaderPolicyInterface::ROTATION_0; + int32_t width = mRawAxes.x.getRange(); + int32_t height = mRawAxes.y.getRange(); + + if (mParameters.associatedDisplayId >= 0) { + bool wantSize = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + bool wantOrientation = mParameters.orientationAware; + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { + if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId, + wantSize ? &width : NULL, wantSize ? &height : NULL, + wantOrientation ? &orientation : NULL)) { return false; } - } else { - orientation = InputReaderPolicyInterface::ROTATION_0; - width = mRawAxes.x.getRange(); - height = mRawAxes.y.getRange(); } bool orientationChanged = mLocked.surfaceOrientation != orientation; @@ -1404,7 +1440,7 @@ bool TouchInputMapper::configureSurfaceLocked() { bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; if (sizeChanged) { - LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d", + LOGI("Device reconfigured: id=%d, name='%s', display size is now %dx%d", getDeviceId(), getDeviceName().string(), width, height); mLocked.surfaceWidth = width; @@ -1616,9 +1652,8 @@ void TouchInputMapper::dumpSurfaceLocked(String8& dump) { void TouchInputMapper::configureVirtualKeysLocked() { assert(mRawAxes.x.valid && mRawAxes.y.valid); - // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock. Vector<VirtualKeyDefinition> virtualKeyDefinitions; - getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); + getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); mLocked.virtualKeys.clear(); @@ -1686,7 +1721,7 @@ void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) { } void TouchInputMapper::parseCalibration() { - const InputDeviceCalibration& in = getDevice()->getCalibration(); + const PropertyMap& in = getDevice()->getConfiguration(); Calibration& out = mCalibration; // Position @@ -1972,8 +2007,8 @@ void TouchInputMapper::dumpCalibration(String8& dump) { } if (mCalibration.haveToolSizeIsSummed) { - dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n", - mCalibration.toolSizeIsSummed); + dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %s\n", + toString(mCalibration.toolSizeIsSummed)); } // Pressure @@ -3157,8 +3192,8 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode // --- SingleTouchInputMapper --- -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - TouchInputMapper(device, associatedDisplayId) { +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : + TouchInputMapper(device) { initialize(); } @@ -3286,8 +3321,8 @@ void SingleTouchInputMapper::configureRawAxes() { // --- MultiTouchInputMapper --- -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - TouchInputMapper(device, associatedDisplayId) { +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : + TouchInputMapper(device) { initialize(); } diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp index de76e25..6faa600 100644 --- a/libs/ui/Keyboard.cpp +++ b/libs/ui/Keyboard.cpp @@ -22,102 +22,173 @@ #include <ui/Keyboard.h> #include <ui/KeycodeLabels.h> +#include <ui/KeyLayoutMap.h> +#include <ui/KeyCharacterMap.h> #include <utils/Errors.h> #include <utils/Log.h> #include <cutils/properties.h> namespace android { -static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) { - if (keyMapInfo.keyMapName.isEmpty()) { - keyMapInfo.keyMapName.setTo(keyMapName); - keyMapInfo.isDefaultKeyMap = defaultKeyMap; - } +// --- KeyMap --- + +KeyMap::KeyMap() : + keyLayoutMap(NULL), keyCharacterMap(NULL) { +} + +KeyMap::~KeyMap() { + delete keyLayoutMap; + delete keyCharacterMap; } -static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) { - const char* root = getenv("ANDROID_ROOT"); +status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, + const PropertyMap* deviceConfiguration) { + // Use the configured key layout if available. + if (deviceConfiguration) { + String8 keyLayoutName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), + keyLayoutName)) { + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " + "it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } - // TODO Consider also looking somewhere in a writeable partition like /data for a - // custom keymap supplied by the user for this device. - bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty(); - if (!haveKeyLayout) { - keyMapInfo.keyLayoutFile.setTo(root); - keyMapInfo.keyLayoutFile.append("/usr/keylayout/"); - keyMapInfo.keyLayoutFile.append(keyMapName); - keyMapInfo.keyLayoutFile.append(".kl"); - if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) { - keyMapInfo.keyLayoutFile.clear(); - } else { - haveKeyLayout = true; + String8 keyCharacterMapName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), + keyCharacterMapName)) { + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard character " + "map '%s' but it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } } - } - bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty(); - if (!haveKeyCharacterMap) { - keyMapInfo.keyCharacterMapFile.setTo(root); - keyMapInfo.keyCharacterMapFile.append("/usr/keychars/"); - keyMapInfo.keyCharacterMapFile.append(keyMapName); - keyMapInfo.keyCharacterMapFile.append(".kcm"); - if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) { - keyMapInfo.keyCharacterMapFile.clear(); - } else { - haveKeyCharacterMap = true; + if (isComplete()) { + return OK; } } - if (haveKeyLayout || haveKeyCharacterMap) { - selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap); + // Try searching by device identifier. + if (probeKeyMap(deviceIdenfifier, String8::empty())) { + return OK; + } + + // Fall back on the Generic key map. + // TODO Apply some additional heuristics here to figure out what kind of + // generic key map to use (US English, etc.) for typical external keyboards. + if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + return OK; + } + + // Try the Virtual key map as a last resort. + if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + return OK; } - return haveKeyLayout && haveKeyCharacterMap; + + // Give up! + LOGE("Could not determine key map for device '%s' and no default key maps were found!", + deviceIdenfifier.name.string()); + return NAME_NOT_FOUND; } -status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) { - // As an initial key map name, try using the device name. - String8 keyMapName(deviceName); - char* p = keyMapName.lockBuffer(keyMapName.size()); - while (*p) { - if (*p == ' ') *p = '_'; - p++; +bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& keyMapName) { + if (!haveKeyLayout()) { + loadKeyLayout(deviceIdentifier, keyMapName); } - keyMapName.unlockBuffer(); + if (!haveKeyCharacterMap()) { + loadKeyCharacterMap(deviceIdentifier, keyMapName); + } + return isComplete(); +} - if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK; +status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } - // TODO Consider allowing the user to configure a specific key map somehow. + KeyLayoutMap* map; + status_t status = KeyLayoutMap::load(path, &map); + if (status) { + return status; + } - // Try the Generic key map. - // TODO Apply some additional heuristics here to figure out what kind of - // generic key map to use (US English, etc.). - keyMapName.setTo("Generic"); - if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK; + keyLayoutFile.setTo(path); + keyLayoutMap = map; + return OK; +} - // Give up! - keyMapName.setTo("unknown"); - selectKeyMap(outKeyMapInfo, keyMapName, true); - LOGE("Could not determine key map for device '%s'.", deviceName.string()); - return NAME_NOT_FOUND; +status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + KeyCharacterMap* map; + status_t status = KeyCharacterMap::load(path, &map); + if (status) { + return status; + } + + keyCharacterMapFile.setTo(path); + keyCharacterMap = map; + return OK; } -void setKeyboardProperties(int32_t deviceId, const String8& deviceName, - const KeyMapInfo& keyMapInfo) { +String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type) { + return name.isEmpty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + + +// --- Global functions --- + +bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { + if (!keyMap->haveKeyCharacterMap() + || keyMap->keyCharacterMap->getKeyboardType() + == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + return false; + } + + if (deviceConfiguration) { + bool builtIn = false; + if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) + && builtIn) { + return true; + } + } + + return strstr(deviceIdentifier.name.string(), "-keypad"); +} + +void setKeyboardProperties(int32_t deviceId, + const InputDeviceIdentifier& deviceIdentifier, + const String8& keyLayoutFile, const String8& keyCharacterMapFile) { char propName[PROPERTY_KEY_MAX]; snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); - property_set(propName, deviceName.string()); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId); - property_set(propName, keyMapInfo.keyMapName.string()); + property_set(propName, deviceIdentifier.name.string()); snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); - property_set(propName, keyMapInfo.keyLayoutFile.string()); + property_set(propName, keyLayoutFile.string()); snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); - property_set(propName, keyMapInfo.keyCharacterMapFile.string()); + property_set(propName, keyCharacterMapFile.string()); } void clearKeyboardProperties(int32_t deviceId) { char propName[PROPERTY_KEY_MAX]; snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); property_set(propName, ""); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId); - property_set(propName, ""); snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); property_set(propName, ""); snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); @@ -125,31 +196,24 @@ void clearKeyboardProperties(int32_t deviceId) { } status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) { - char propName[PROPERTY_KEY_MAX]; - char fn[PROPERTY_VALUE_MAX]; - snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); - if (property_get(propName, fn, "") > 0) { - outKeyCharacterMapFile.setTo(fn); - return OK; - } - - const char* root = getenv("ANDROID_ROOT"); - char path[PATH_MAX]; - if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) { - snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root); - if (!access(path, R_OK)) { - outKeyCharacterMapFile.setTo(path); + if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) { + char propName[PROPERTY_KEY_MAX]; + char fn[PROPERTY_VALUE_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + if (property_get(propName, fn, "") > 0) { + outKeyCharacterMapFile.setTo(fn); return OK; } } - snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root); - if (!access(path, R_OK)) { - outKeyCharacterMapFile.setTo(path); + // Default to Virtual since the keyboard does not appear to be installed. + outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"), + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (!outKeyCharacterMapFile.isEmpty()) { return OK; } - LOGE("Can't find any key character map files (also tried %s)", path); + LOGE("Can't find any key character map files including the Virtual key map!"); return NAME_NOT_FOUND; } diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/ui/VirtualKeyMap.cpp new file mode 100644 index 0000000..e756cdd --- /dev/null +++ b/libs/ui/VirtualKeyMap.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VirtualKeyMap" + +#include <stdlib.h> +#include <string.h> +#include <ui/VirtualKeyMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; + + +// --- VirtualKeyMap --- + +VirtualKeyMap::VirtualKeyMap() { +} + +VirtualKeyMap::~VirtualKeyMap() { +} + +status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening virtual key map file %s.", status, filename.string()); + } else { + VirtualKeyMap* map = new VirtualKeyMap(); + if (!map) { + LOGE("Error allocating virtual key map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- VirtualKeyMap::Parser --- + +VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +VirtualKeyMap::Parser::~Parser() { +} + +status_t VirtualKeyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + // Multiple keys can appear on one line or they can be broken up across multiple lines. + do { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + if (token != "0x01") { + LOGE("%s: Unknown virtual key type, expected 0x01.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + VirtualKeyDefinition defn; + bool success = parseNextIntField(&defn.scanCode) + && parseNextIntField(&defn.centerX) + && parseNextIntField(&defn.centerY) + && parseNextIntField(&defn.width) + && parseNextIntField(&defn.height); + if (!success) { + LOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + LOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " + "width=%d, height=%d", + defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); +#endif + mMap->mVirtualKeys.push(defn); + } while (consumeFieldDelimiterAndSkipWhitespace()); + + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + + return NO_ERROR; +} + +bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->peekChar() == ':') { + mTokenizer->nextChar(); + mTokenizer->skipDelimiters(WHITESPACE); + return true; + } + return false; +} + +bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { + if (!consumeFieldDelimiterAndSkipWhitespace()) { + return false; + } + + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + char* end; + *outValue = strtol(token.string(), &end, 0); + if (token.isEmpty() || *end != '\0') { + LOGE("Expected an integer, got '%s'.", token.string()); + return false; + } + return true; +} + +} // namespace android diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp index f352dbf..68f9037 100644 --- a/libs/ui/tests/InputDispatcher_test.cpp +++ b/libs/ui/tests/InputDispatcher_test.cpp @@ -54,9 +54,7 @@ private: return 60; } - virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId, - int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode, - uint32_t& policyFlags) { + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) { } virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp index ded0225..d6c2cbd 100644 --- a/libs/ui/tests/InputReader_test.cpp +++ b/libs/ui/tests/InputReader_test.cpp @@ -42,8 +42,6 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { KeyedVector<int32_t, DisplayInfo> mDisplayInfos; bool mFilterTouchEvents; bool mFilterJumpyTouchEvents; - KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions; - KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations; Vector<String8> mExcludedDeviceNames; protected: @@ -76,29 +74,6 @@ public: mFilterJumpyTouchEvents = enabled; } - void addInputDeviceCalibration(const String8& deviceName, - const InputDeviceCalibration& calibration) { - mInputDeviceCalibrations.add(deviceName, calibration); - } - - void addInputDeviceCalibrationProperty(const String8& deviceName, - const String8& key, const String8& value) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index < 0) { - index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration()); - } - mInputDeviceCalibrations.editValueAt(index).addProperty(key, value); - } - - void addVirtualKeyDefinition(const String8& deviceName, - const VirtualKeyDefinition& definition) { - if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) { - mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>()); - } - - mVirtualKeyDefinitions.editValueFor(deviceName).push(definition); - } - void addExcludedDeviceName(const String8& deviceName) { mExcludedDeviceNames.push(deviceName); } @@ -131,22 +106,6 @@ private: return mFilterJumpyTouchEvents; } - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) { - ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName); - if (index >= 0) { - outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index)); - } - } - - virtual void getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index >= 0) { - outCalibration = mInputDeviceCalibrations.valueAt(index); - } - } - virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { outExcludedDeviceNames.appendVector(mExcludedDeviceNames); } @@ -371,12 +330,14 @@ class FakeEventHub : public EventHubInterface { struct Device { String8 name; uint32_t classes; + PropertyMap configuration; KeyedVector<int, RawAbsoluteAxisInfo> axes; KeyedVector<int32_t, int32_t> keyCodeStates; KeyedVector<int32_t, int32_t> scanCodeStates; KeyedVector<int32_t, int32_t> switchStates; KeyedVector<int32_t, KeyInfo> keys; KeyedVector<int32_t, bool> leds; + Vector<VirtualKeyDefinition> virtualKeys; Device(const String8& name, uint32_t classes) : name(name), classes(classes) { @@ -415,6 +376,11 @@ public: enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0); } + void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) { + Device* device = getDevice(deviceId); + device->configuration.addProperty(key, value); + } + void addAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat, int fuzz) { Device* device = getDevice(deviceId); @@ -465,6 +431,11 @@ public: return mExcludedDevices; } + void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) { + Device* device = getDevice(deviceId); + device->virtualKeys.push(definition); + } + void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type, int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) { RawEvent event; @@ -499,6 +470,13 @@ private: return device ? device->name : String8("unknown"); } + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { + Device* device = getDevice(deviceId); + if (device) { + *outConfiguration = device->configuration; + } + } + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const { Device* device = getDevice(deviceId); @@ -613,6 +591,16 @@ private: } } + virtual void getVirtualKeyDefinitions(int32_t deviceId, + Vector<VirtualKeyDefinition>& outVirtualKeys) const { + outVirtualKeys.clear(); + + Device* device = getDevice(deviceId); + if (device) { + outVirtualKeys.appendVector(device->virtualKeys); + } + } + virtual void dump(String8& dump) { } }; @@ -1208,9 +1196,7 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { // Configuration. - InputDeviceCalibration calibration; - calibration.addProperty(String8("key"), String8("value")); - mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration); + mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value")); FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD); mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); @@ -1231,8 +1217,8 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe mDevice->configure(); String8 propertyValue; - ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue)) - << "Device should have read calibration during configuration phase."; + ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue)) + << "Device should have read configuration during configuration phase."; ASSERT_STREQ("value", propertyValue.string()); ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled()); @@ -1329,9 +1315,8 @@ protected: mFakeEventHub.clear(); } - void prepareCalibration(const char* key, const char* value) { - mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME), - String8(key), String8(value)); + void addConfigurationProperty(const char* key, const char* value) { + mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value)); } void addMapperAndConfigure(InputMapper* mapper) { @@ -1448,7 +1433,7 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, TEST_F(KeyboardInputMapperTest, GetSources) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1456,7 +1441,7 @@ TEST_F(KeyboardInputMapperTest, GetSources) { } TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1493,7 +1478,7 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { } TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1513,7 +1498,7 @@ TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) } TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1558,7 +1543,7 @@ TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) { } TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1597,11 +1582,14 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); } -TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, +TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); + mFakePolicy->setDisplayInfo(DISPLAY_ID, + DISPLAY_WIDTH, DISPLAY_HEIGHT, + InputReaderPolicyInterface::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1612,9 +1600,10 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotate KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); } -TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID, +TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addConfigurationProperty("keyboard.orientationAware", "1"); addMapperAndConfigure(mapper); mFakePolicy->setDisplayInfo(DISPLAY_ID, @@ -1689,7 +1678,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) } TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1701,7 +1690,7 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { } TEST_F(KeyboardInputMapperTest, GetScanCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1713,7 +1702,7 @@ TEST_F(KeyboardInputMapperTest, GetScanCodeState) { } TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1731,7 +1720,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/); mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); @@ -1830,14 +1819,14 @@ void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper, } TEST_F(TrackballInputMapperTest, GetSources) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources()); } TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); InputDeviceInfo info; @@ -1850,7 +1839,7 @@ TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) { } TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -1898,7 +1887,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaS } TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); FakeInputDispatcher::NotifyMotionArgs args; @@ -1922,7 +1911,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { } TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); FakeInputDispatcher::NotifyMotionArgs args; @@ -1943,7 +1932,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { } TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); FakeInputDispatcher::NotifyMotionArgs args; @@ -1978,7 +1967,7 @@ TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) } TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); FakeInputDispatcher::NotifyMotionArgs args; @@ -1998,7 +1987,7 @@ TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeBu } TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); FakeInputDispatcher::NotifyMotionArgs args; @@ -2016,10 +2005,13 @@ TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); } -TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); +TEST_F(TrackballInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) { + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); addMapperAndConfigure(mapper); + mFakePolicy->setDisplayInfo(DISPLAY_ID, + DISPLAY_WIDTH, DISPLAY_HEIGHT, + InputReaderPolicyInterface::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); @@ -2030,8 +2022,9 @@ TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotat ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); } -TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID); +TEST_F(TrackballInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) { + TrackballInputMapper* mapper = new TrackballInputMapper(mDevice); + addConfigurationProperty("trackball.orientationAware", "1"); addMapperAndConfigure(mapper); mFakePolicy->setDisplayInfo(DISPLAY_ID, @@ -2152,8 +2145,8 @@ void TouchInputMapperTest::prepareDisplay(int32_t orientation) { } void TouchInputMapperTest::prepareVirtualKeys() { - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]); - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]); + mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]); + mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]); mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE); } @@ -2232,24 +2225,26 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { } -TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1); +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchPad_ReturnsTouchPad) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareAxes(POSITION); + addConfigurationProperty("touch.displayType", "touchPad"); addMapperAndConfigure(mapper); ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); } -TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchScreen_ReturnsTouchScreen) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareAxes(POSITION); + addConfigurationProperty("touch.displayType", "touchScreen"); addMapperAndConfigure(mapper); ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources()); } TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2276,7 +2271,7 @@ TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { } TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2303,7 +2298,7 @@ TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { } TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2319,7 +2314,7 @@ TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) { // Note: Ideally we should send cancels but the implementation is more straightforward // with up and this will only happen if a device is forcibly removed. - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2352,7 +2347,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) { } TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2378,7 +2373,7 @@ TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens } TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2427,7 +2422,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor } TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2541,7 +2536,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB } TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2609,7 +2604,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMoves } TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -2691,8 +2686,30 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); } -TEST_F(SingleTouchInputMapperTest, Process_Rotation) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); +TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + prepareAxes(POSITION); + addConfigurationProperty("touch.orientationAware", "0"); + addMapperAndConfigure(mapper); + + FakeInputDispatcher::NotifyMotionArgs args; + + // Rotation 90. + prepareDisplay(InputReaderPolicyInterface::ROTATION_90); + processDown(mapper, toRawX(50), toRawY(75)); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); + ASSERT_NEAR(50, args.pointerCoords[0].x, 1); + ASSERT_NEAR(75, args.pointerCoords[0].y, 1); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareAxes(POSITION); addMapperAndConfigure(mapper); @@ -2752,7 +2769,7 @@ TEST_F(SingleTouchInputMapperTest, Process_Rotation) { } TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | PRESSURE | TOOL); addMapperAndConfigure(mapper); @@ -2884,7 +2901,7 @@ void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) { TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); @@ -3135,7 +3152,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackin } TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | ID); prepareVirtualKeys(); @@ -3295,7 +3312,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId } TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR); addMapperAndConfigure(mapper); @@ -3340,11 +3357,11 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL | MINOR); - prepareCalibration("touch.touchSize.calibration", "geometric"); - prepareCalibration("touch.toolSize.calibration", "geometric"); + addConfigurationProperty("touch.touchSize.calibration", "geometric"); + addConfigurationProperty("touch.toolSize.calibration", "geometric"); addMapperAndConfigure(mapper); // These calculations are based on the input device calibration documentation. @@ -3381,17 +3398,17 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) } TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "linear"); - prepareCalibration("touch.toolSize.linearScale", "10"); - prepareCalibration("touch.toolSize.linearBias", "160"); - prepareCalibration("touch.toolSize.isSummed", "1"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); + addConfigurationProperty("touch.touchSize.calibration", "pressure"); + addConfigurationProperty("touch.toolSize.calibration", "linear"); + addConfigurationProperty("touch.toolSize.linearScale", "10"); + addConfigurationProperty("touch.toolSize.linearBias", "160"); + addConfigurationProperty("touch.toolSize.isSummed", "1"); + addConfigurationProperty("touch.pressure.calibration", "amplitude"); + addConfigurationProperty("touch.pressure.source", "touch"); + addConfigurationProperty("touch.pressure.scale", "0.01"); addMapperAndConfigure(mapper); // These calculations are based on the input device calibration documentation. @@ -3437,18 +3454,18 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinear } TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(InputReaderPolicyInterface::ROTATION_0); prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "area"); - prepareCalibration("touch.toolSize.areaScale", "22"); - prepareCalibration("touch.toolSize.areaBias", "1"); - prepareCalibration("touch.toolSize.linearScale", "9.2"); - prepareCalibration("touch.toolSize.linearBias", "3"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); + addConfigurationProperty("touch.touchSize.calibration", "pressure"); + addConfigurationProperty("touch.toolSize.calibration", "area"); + addConfigurationProperty("touch.toolSize.areaScale", "22"); + addConfigurationProperty("touch.toolSize.areaBias", "1"); + addConfigurationProperty("touch.toolSize.linearScale", "9.2"); + addConfigurationProperty("touch.toolSize.linearBias", "3"); + addConfigurationProperty("touch.pressure.calibration", "amplitude"); + addConfigurationProperty("touch.pressure.source", "touch"); + addConfigurationProperty("touch.pressure.scale", "0.01"); addMapperAndConfigure(mapper); // These calculations are based on the input device calibration documentation. diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 9c01aea..8bd833b 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -28,6 +28,7 @@ commonSources:= \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index e1ba9b2..f1f8bda 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -63,16 +63,18 @@ FileMap::~FileMap(void) free(mFileName); } #ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { + if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { + if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } - CloseHandle(mFileMapping); + if (mFileMapping != INVALID_HANDLE_VALUE) { + CloseHandle(mFileMapping); + } CloseHandle(mFileHandle); #endif } diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp new file mode 100644 index 0000000..fd7edec --- /dev/null +++ b/libs/utils/PropertyMap.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PropertyMap" + +#include <stdlib.h> +#include <string.h> + +#include <utils/PropertyMap.h> +#include <utils/Log.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + + +// --- PropertyMap --- + +PropertyMap::PropertyMap() { +} + +PropertyMap::~PropertyMap() { +} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), & end, 10); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), & end); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + LOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +PropertyMap::Parser::~Parser() { +} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + LOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + LOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + LOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index e531a2a..0bc5aff 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -195,6 +195,24 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + void String8::clear() { SharedBuffer::bufferFromData(mString)->release(); mString = getEmptyString(); diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp index 9251973..b3445b7 100644 --- a/libs/utils/Tokenizer.cpp +++ b/libs/utils/Tokenizer.cpp @@ -35,16 +35,16 @@ static inline bool isDelimiter(char ch, const char* delimiters) { return strchr(delimiters, ch) != NULL; } - -Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, - const char* buffer, size_t length) : - mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length), - mCurrent(buffer), mLineNumber(1) { +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), + mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { } Tokenizer::~Tokenizer() { if (mFileMap) { mFileMap->release(); + } else { + delete[] mBuffer; } } @@ -63,22 +63,33 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); } else { size_t length = size_t(stat.st_size); + FileMap* fileMap = new FileMap(); - if (!fileMap->create(NULL, fd, 0, length, true)) { - result = NO_MEMORY; - LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno)); - } else { + char* buffer; + if (fileMap->create(NULL, fd, 0, length, true)) { fileMap->advise(FileMap::SEQUENTIAL); - - *outTokenizer = new Tokenizer(filename, fileMap, - static_cast<const char*>(fileMap->getDataPtr()), length); - if (!*outTokenizer) { - result = NO_MEMORY; - LOGE("Error allocating tokenizer for file=%s.", filename.string()); + buffer = static_cast<char*>(fileMap->getDataPtr()); + } else { + fileMap->release(); + fileMap = NULL; + + // Fall back to reading into a buffer since we can't mmap files in sysfs. + // The length we obtained from stat is wrong too (it will always be 4096) + // so we must trust that read will read the entire file. + buffer = new char[length]; + ssize_t nrd = read(fd, buffer, length); + if (nrd < 0) { + result = -errno; + LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + delete[] buffer; + buffer = NULL; + } else { + length = size_t(nrd); } } - if (result) { - fileMap->release(); + + if (!result) { + *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); } } close(fd); diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 0e689e4..beac04c 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -194,6 +194,7 @@ public class MediaFile { addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp"); addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST); + addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST); addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST); addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST); addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl"); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 69b872b..e17a640 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -992,7 +992,12 @@ public class MediaScanner private boolean inScanDirectory(String path, String[] directories) { for (int i = 0; i < directories.length; i++) { - if (path.startsWith(directories[i])) { + String directory = directories[i]; + if (mExternalStoragePath != null && directory.equals(mMediaStoragePath)) { + // database paths use external storage prefix + directory = mExternalStoragePath; + } + if (path.startsWith(directory)) { return true; } } diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index 0387989..139a6ea 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -33,6 +33,7 @@ import android.provider.MediaStore.MediaColumns; import android.util.Log; import java.io.File; +import java.util.HashMap; /** * {@hide} @@ -48,6 +49,14 @@ public class MtpDatabase { private final String mMediaStoragePath; private final String mExternalStoragePath; + // cached property groups for single properties + private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty + = new HashMap<Integer, MtpPropertyGroup>(); + + // cached property groups for all properties for a given format + private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat + = new HashMap<Integer, MtpPropertyGroup>(); + // true if the database has been modified in the current MTP session private boolean mDatabaseModified; @@ -386,6 +395,43 @@ public class MtpDatabase { MtpConstants.PROPERTY_DESCRIPTION, }; + static final int[] ALL_PROPERTIES = { + // NOTE must match FILE_PROPERTIES above + MtpConstants.PROPERTY_STORAGE_ID, + MtpConstants.PROPERTY_OBJECT_FORMAT, + MtpConstants.PROPERTY_PROTECTION_STATUS, + MtpConstants.PROPERTY_OBJECT_SIZE, + MtpConstants.PROPERTY_OBJECT_FILE_NAME, + MtpConstants.PROPERTY_DATE_MODIFIED, + MtpConstants.PROPERTY_PARENT_OBJECT, + MtpConstants.PROPERTY_PERSISTENT_UID, + MtpConstants.PROPERTY_NAME, + MtpConstants.PROPERTY_DISPLAY_NAME, + MtpConstants.PROPERTY_DATE_ADDED, + + // image specific properties + MtpConstants.PROPERTY_DESCRIPTION, + + // audio specific properties + MtpConstants.PROPERTY_ARTIST, + MtpConstants.PROPERTY_ALBUM_NAME, + MtpConstants.PROPERTY_ALBUM_ARTIST, + MtpConstants.PROPERTY_TRACK, + MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE, + MtpConstants.PROPERTY_DURATION, + MtpConstants.PROPERTY_GENRE, + MtpConstants.PROPERTY_COMPOSER, + + // video specific properties + MtpConstants.PROPERTY_ARTIST, + MtpConstants.PROPERTY_ALBUM_NAME, + MtpConstants.PROPERTY_DURATION, + MtpConstants.PROPERTY_DESCRIPTION, + + // image specific properties + MtpConstants.PROPERTY_DESCRIPTION, + }; + private int[] getSupportedObjectProperties(int format) { switch (format) { case MtpConstants.FORMAT_MP3: @@ -403,6 +449,8 @@ public class MtpDatabase { case MtpConstants.FORMAT_PNG: case MtpConstants.FORMAT_BMP: return IMAGE_PROPERTIES; + case 0: + return ALL_PROPERTIES; default: return FILE_PROPERTIES; } @@ -415,324 +463,32 @@ public class MtpDatabase { }; } - private String queryString(int id, String column) { - Cursor c = null; - try { - // for now we are only reading properties from the "objects" table - c = mMediaProvider.query(mObjectsUri, - new String [] { Files.FileColumns._ID, column }, - ID_WHERE, new String[] { Integer.toString(id) }, null); - if (c != null && c.moveToNext()) { - return c.getString(1); - } else { - return ""; - } - } catch (Exception e) { - return null; - } finally { - if (c != null) { - c.close(); - } - } - } - - private String queryAudio(int id, String column) { - Cursor c = null; - try { - c = mMediaProvider.query(Audio.Media.getContentUri(mVolumeName), - new String [] { Files.FileColumns._ID, column }, - ID_WHERE, new String[] { Integer.toString(id) }, null); - if (c != null && c.moveToNext()) { - return c.getString(1); - } else { - return ""; - } - } catch (Exception e) { - return null; - } finally { - if (c != null) { - c.close(); - } - } - } - private String queryGenre(int id) { - Cursor c = null; - try { - Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id); - c = mMediaProvider.query(uri, - new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME }, - null, null, null); - if (c != null && c.moveToNext()) { - return c.getString(1); - } else { - return ""; - } - } catch (Exception e) { - Log.e(TAG, "queryGenre exception", e); - return null; - } finally { - if (c != null) { - c.close(); - } - } - } - - private Long queryLong(int id, String column) { - Cursor c = null; - try { - // for now we are only reading properties from the "objects" table - c = mMediaProvider.query(mObjectsUri, - new String [] { Files.FileColumns._ID, column }, - ID_WHERE, new String[] { Integer.toString(id) }, null); - if (c != null && c.moveToNext()) { - return new Long(c.getLong(1)); - } - } catch (Exception e) { - } finally { - if (c != null) { - c.close(); - } - } - return null; - } - - private String nameFromPath(String path) { - // extract name from full path - int start = 0; - int lastSlash = path.lastIndexOf('/'); - if (lastSlash >= 0) { - start = lastSlash + 1; - } - int end = path.length(); - if (end - start > 255) { - end = start + 255; - } - return path.substring(start, end); - } - - private MtpPropertyList getObjectPropertyList(int handle, int format, int property, + private MtpPropertyList getObjectPropertyList(long handle, int format, long property, int groupCode, int depth) { // FIXME - implement group support - // For now we only support a single property at a time if (groupCode != 0) { return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED); } - if (depth > 1) { - return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED); - } - String column = null; - int type = MtpConstants.TYPE_UNDEFINED; - - switch (property) { - case MtpConstants.PROPERTY_STORAGE_ID: - // no query needed until we support multiple storage units - // for now it is always mStorageID - type = MtpConstants.TYPE_UINT32; - break; - case MtpConstants.PROPERTY_OBJECT_FORMAT: - column = Files.FileColumns.FORMAT; - type = MtpConstants.TYPE_UINT16; - break; - case MtpConstants.PROPERTY_PROTECTION_STATUS: - // protection status is always 0 - type = MtpConstants.TYPE_UINT16; - break; - case MtpConstants.PROPERTY_OBJECT_SIZE: - column = Files.FileColumns.SIZE; - type = MtpConstants.TYPE_UINT64; - break; - case MtpConstants.PROPERTY_OBJECT_FILE_NAME: - column = Files.FileColumns.DATA; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_NAME: - column = MediaColumns.TITLE; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_DATE_MODIFIED: - column = Files.FileColumns.DATE_MODIFIED; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_DATE_ADDED: - column = Files.FileColumns.DATE_ADDED; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: - column = Audio.AudioColumns.YEAR; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_PARENT_OBJECT: - column = Files.FileColumns.PARENT; - type = MtpConstants.TYPE_UINT32; - break; - case MtpConstants.PROPERTY_PERSISTENT_UID: - // PUID is concatenation of storageID and object handle - type = MtpConstants.TYPE_UINT128; - break; - case MtpConstants.PROPERTY_DURATION: - column = Audio.AudioColumns.DURATION; - type = MtpConstants.TYPE_UINT32; - break; - case MtpConstants.PROPERTY_TRACK: - column = Audio.AudioColumns.TRACK; - type = MtpConstants.TYPE_UINT16; - break; - case MtpConstants.PROPERTY_DISPLAY_NAME: - column = MediaColumns.DISPLAY_NAME; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_ARTIST: - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_ALBUM_NAME: - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_ALBUM_ARTIST: - column = Audio.AudioColumns.ALBUM_ARTIST; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_GENRE: - // genre requires a special query - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_COMPOSER: - column = Audio.AudioColumns.COMPOSER; - type = MtpConstants.TYPE_STR; - break; - case MtpConstants.PROPERTY_DESCRIPTION: - column = Images.ImageColumns.DESCRIPTION; - type = MtpConstants.TYPE_STR; - break; - default: - return new MtpPropertyList(0, MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED); - } - - Cursor c = null; - try { - if (column != null) { - c = mMediaProvider.query(mObjectsUri, - new String [] { Files.FileColumns._ID, column }, - // depth 0: single record, depth 1: immediate children - (depth == 0 ? ID_WHERE : PARENT_WHERE), - new String[] { Integer.toString(handle) }, null); - if (c == null) { - return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); - } - } else if (depth == 1) { - c = mMediaProvider.query(mObjectsUri, - new String [] { Files.FileColumns._ID }, - PARENT_WHERE, new String[] { Integer.toString(handle) }, null); - if (c == null) { - return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); - } - } - - int count = (c == null ? 1 : c.getCount()); - MtpPropertyList result = new MtpPropertyList(count, MtpConstants.RESPONSE_OK); - - for (int index = 0; index < count; index++) { - if (c != null) { - c.moveToNext(); - } - if (depth == 1) { - handle = (int)c.getLong(0); - } - - switch (property) { - // handle special cases here - case MtpConstants.PROPERTY_STORAGE_ID: - result.setProperty(index, handle, property, MtpConstants.TYPE_UINT32, - mStorageID); - break; - case MtpConstants.PROPERTY_PROTECTION_STATUS: - // protection status is always 0 - result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16, 0); - break; - case MtpConstants.PROPERTY_OBJECT_FILE_NAME: - // special case - need to extract file name from full path - String value = c.getString(1); - if (value != null) { - result.setProperty(index, handle, property, nameFromPath(value)); - } else { - result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); - } - break; - case MtpConstants.PROPERTY_NAME: - // first try title - String name = c.getString(1); - // then try name - if (name == null) { - name = queryString(handle, Audio.PlaylistsColumns.NAME); - } - // if title and name fail, extract name from full path - if (name == null) { - name = queryString(handle, Files.FileColumns.DATA); - if (name != null) { - name = nameFromPath(name); - } - } - if (name != null) { - result.setProperty(index, handle, property, name); - } else { - result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); - } - break; - case MtpConstants.PROPERTY_DATE_MODIFIED: - case MtpConstants.PROPERTY_DATE_ADDED: - // convert from seconds to DateTime - result.setProperty(index, handle, property, format_date_time(c.getInt(1))); - break; - case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: - // release date is stored internally as just the year - int year = c.getInt(1); - String dateTime = Integer.toString(year) + "0101T000000"; - result.setProperty(index, handle, property, dateTime); - break; - case MtpConstants.PROPERTY_PERSISTENT_UID: - // PUID is concatenation of storageID and object handle - long puid = mStorageID; - puid <<= 32; - puid += handle; - result.setProperty(index, handle, property, MtpConstants.TYPE_UINT128, puid); - break; - case MtpConstants.PROPERTY_TRACK: - result.setProperty(index, handle, property, MtpConstants.TYPE_UINT16, - c.getInt(1) % 1000); - break; - case MtpConstants.PROPERTY_ARTIST: - result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ARTIST)); - break; - case MtpConstants.PROPERTY_ALBUM_NAME: - result.setProperty(index, handle, property, queryAudio(handle, Audio.AudioColumns.ALBUM)); - break; - case MtpConstants.PROPERTY_GENRE: - String genre = queryGenre(handle); - if (genre != null) { - result.setProperty(index, handle, property, genre); - } else { - result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); - } - break; - default: - if (type == MtpConstants.TYPE_STR) { - result.setProperty(index, handle, property, c.getString(1)); - } else { - result.setProperty(index, handle, property, type, c.getLong(1)); - } - } + MtpPropertyGroup propertyGroup; + if (property == 0xFFFFFFFFL) { + propertyGroup = mPropertyGroupsByFormat.get(format); + if (propertyGroup == null) { + int[] propertyList = getSupportedObjectProperties(format); + propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList); + mPropertyGroupsByFormat.put(new Integer(format), propertyGroup); } - - return result; - } catch (RemoteException e) { - return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR); - } finally { - if (c != null) { - c.close(); + } else { + propertyGroup = mPropertyGroupsByProperty.get(property); + if (propertyGroup == null) { + int[] propertyList = new int[] { (int)property }; + propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList); + mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup); } } - // impossible to get here, so no return statement + + return propertyGroup.getPropertyList((int)handle, format, depth, mStorageID); } private int renameFile(int handle, String newName) { @@ -1028,5 +784,4 @@ public class MtpDatabase { private native final void native_setup(); private native final void native_finalize(); - private native String format_date_time(long seconds); } diff --git a/media/java/android/media/MtpPropertyGroup.java b/media/java/android/media/MtpPropertyGroup.java new file mode 100644 index 0000000..bb733e2 --- /dev/null +++ b/media/java/android/media/MtpPropertyGroup.java @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.content.IContentProvider; +import android.database.Cursor; +import android.net.Uri; +import android.os.RemoteException; +import android.provider.MediaStore; +import android.provider.MediaStore.Audio; +import android.provider.MediaStore.Files; +import android.provider.MediaStore.Images; +import android.provider.MediaStore.MediaColumns; +import android.util.Log; + +import java.util.ArrayList; + +class MtpPropertyGroup { + + private static final String TAG = "MtpPropertyGroup"; + + private class Property { + // MTP property code + int code; + // MTP data type + int type; + // column index for our query + int column; + + Property(int code, int type, int column) { + this.code = code; + this.type = type; + this.column = column; + } + } + + private final MtpDatabase mDatabase; + private final IContentProvider mProvider; + private final String mVolumeName; + private final Uri mUri; + + // list of all properties in this group + private final Property[] mProperties; + + // list of columns for database query + private String[] mColumns; + + private static final String ID_WHERE = Files.FileColumns._ID + "=?"; + private static final String ID_FORMAT_WHERE = ID_WHERE + " AND " + + Files.FileColumns.FORMAT + "=?"; + private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?"; + private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + + Files.FileColumns.FORMAT + "=?"; + // constructs a property group for a list of properties + public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String volume, + int[] properties) { + mDatabase = database; + mProvider = provider; + mVolumeName = volume; + mUri = Files.getMtpObjectsUri(volume); + + int count = properties.length; + ArrayList<String> columns = new ArrayList<String>(count); + columns.add(Files.FileColumns._ID); + + mProperties = new Property[count]; + for (int i = 0; i < count; i++) { + mProperties[i] = createProperty(properties[i], columns); + } + count = columns.size(); + mColumns = new String[count]; + for (int i = 0; i < count; i++) { + mColumns[i] = columns.get(i); + } + } + + private Property createProperty(int code, ArrayList<String> columns) { + String column = null; + int type; + + switch (code) { + case MtpConstants.PROPERTY_STORAGE_ID: + // no query needed until we support multiple storage units + type = MtpConstants.TYPE_UINT32; + break; + case MtpConstants.PROPERTY_OBJECT_FORMAT: + column = Files.FileColumns.FORMAT; + type = MtpConstants.TYPE_UINT16; + break; + case MtpConstants.PROPERTY_PROTECTION_STATUS: + // protection status is always 0 + type = MtpConstants.TYPE_UINT16; + break; + case MtpConstants.PROPERTY_OBJECT_SIZE: + column = Files.FileColumns.SIZE; + type = MtpConstants.TYPE_UINT64; + break; + case MtpConstants.PROPERTY_OBJECT_FILE_NAME: + column = Files.FileColumns.DATA; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_NAME: + column = MediaColumns.TITLE; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_DATE_MODIFIED: + column = Files.FileColumns.DATE_MODIFIED; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_DATE_ADDED: + column = Files.FileColumns.DATE_ADDED; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: + column = Audio.AudioColumns.YEAR; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_PARENT_OBJECT: + column = Files.FileColumns.PARENT; + type = MtpConstants.TYPE_UINT32; + break; + case MtpConstants.PROPERTY_PERSISTENT_UID: + // PUID is concatenation of storageID and object handle + type = MtpConstants.TYPE_UINT128; + break; + case MtpConstants.PROPERTY_DURATION: + column = Audio.AudioColumns.DURATION; + type = MtpConstants.TYPE_UINT32; + break; + case MtpConstants.PROPERTY_TRACK: + column = Audio.AudioColumns.TRACK; + type = MtpConstants.TYPE_UINT16; + break; + case MtpConstants.PROPERTY_DISPLAY_NAME: + column = MediaColumns.DISPLAY_NAME; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_ARTIST: + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_ALBUM_NAME: + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_ALBUM_ARTIST: + column = Audio.AudioColumns.ALBUM_ARTIST; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_GENRE: + // genre requires a special query + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_COMPOSER: + column = Audio.AudioColumns.COMPOSER; + type = MtpConstants.TYPE_STR; + break; + case MtpConstants.PROPERTY_DESCRIPTION: + column = Images.ImageColumns.DESCRIPTION; + type = MtpConstants.TYPE_STR; + break; + default: + type = MtpConstants.TYPE_UNDEFINED; + Log.e(TAG, "unsupported property " + code); + break; + } + + if (column != null) { + columns.add(column); + return new Property(code, type, columns.size() - 1); + } else { + return new Property(code, type, -1); + } + } + + private String queryString(int id, String column) { + Cursor c = null; + try { + // for now we are only reading properties from the "objects" table + c = mProvider.query(mUri, + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + + private String queryAudio(int id, String column) { + Cursor c = null; + try { + c = mProvider.query(Audio.Media.getContentUri(mVolumeName), + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + + private String queryGenre(int id) { + Cursor c = null; + try { + Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id); + c = mProvider.query(uri, + new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME }, + null, null, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + Log.e(TAG, "queryGenre exception", e); + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + + private Long queryLong(int id, String column) { + Cursor c = null; + try { + // for now we are only reading properties from the "objects" table + c = mProvider.query(mUri, + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + return new Long(c.getLong(1)); + } + } catch (Exception e) { + } finally { + if (c != null) { + c.close(); + } + } + return null; + } + + private static String nameFromPath(String path) { + // extract name from full path + int start = 0; + int lastSlash = path.lastIndexOf('/'); + if (lastSlash >= 0) { + start = lastSlash + 1; + } + int end = path.length(); + if (end - start > 255) { + end = start + 255; + } + return path.substring(start, end); + } + + MtpPropertyList getPropertyList(int handle, int format, int depth, int storageID) { + Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth); + if (depth > 1) { + // we only support depth 0 and 1 + // depth 0: single object, depth 1: immediate children + return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED); + } + + String where; + String[] whereArgs; + if (format == 0) { + whereArgs = new String[] { Integer.toString(handle) }; + if (depth == 1) { + where = PARENT_WHERE; + } else { + where = ID_WHERE; + } + } else { + whereArgs = new String[] { Integer.toString(handle), Integer.toString(format) }; + if (depth == 1) { + where = PARENT_FORMAT_WHERE; + } else { + where = ID_FORMAT_WHERE; + } + } + + Cursor c = null; + try { + // don't query if not necessary + if (depth > 0 || mColumns.length > 1) { + c = mProvider.query(mUri, mColumns, where, whereArgs, null); + if (c == null) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + } + + int count = (c == null ? 1 : c.getCount()); + MtpPropertyList result = new MtpPropertyList(count * mProperties.length, + MtpConstants.RESPONSE_OK); + + // iterate over all objects in the query + for (int objectIndex = 0; objectIndex < count; objectIndex++) { + if (c != null) { + c.moveToNext(); + } + if (depth == 1) { + handle = (int)c.getLong(0); + } + + // iterate over all properties in the query for the given object + for (int propertyIndex = 0; propertyIndex < mProperties.length; propertyIndex++) { + Property property = mProperties[propertyIndex]; + int propertyCode = property.code; + int column = property.column; + + // handle some special cases + switch (propertyCode) { + case MtpConstants.PROPERTY_STORAGE_ID: + result.append(handle, propertyCode, MtpConstants.TYPE_UINT32, + storageID); + break; + case MtpConstants.PROPERTY_PROTECTION_STATUS: + // protection status is always 0 + result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0); + break; + case MtpConstants.PROPERTY_OBJECT_FILE_NAME: + // special case - need to extract file name from full path + String value = c.getString(column); + if (value != null) { + result.append(handle, propertyCode, nameFromPath(value)); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + case MtpConstants.PROPERTY_NAME: + // first try title + String name = c.getString(column); + // then try name + if (name == null) { + name = queryString(handle, Audio.PlaylistsColumns.NAME); + } + // if title and name fail, extract name from full path + if (name == null) { + name = queryString(handle, Files.FileColumns.DATA); + if (name != null) { + name = nameFromPath(name); + } + } + if (name != null) { + result.append(handle, propertyCode, name); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + case MtpConstants.PROPERTY_DATE_MODIFIED: + case MtpConstants.PROPERTY_DATE_ADDED: + // convert from seconds to DateTime + result.append(handle, propertyCode, format_date_time(c.getInt(column))); + break; + case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: + // release date is stored internally as just the year + int year = c.getInt(column); + String dateTime = Integer.toString(year) + "0101T000000"; + result.append(handle, propertyCode, dateTime); + break; + case MtpConstants.PROPERTY_PERSISTENT_UID: + // PUID is concatenation of storageID and object handle + long puid = storageID; + puid <<= 32; + puid += handle; + result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid); + break; + case MtpConstants.PROPERTY_TRACK: + result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, + c.getInt(column) % 1000); + break; + case MtpConstants.PROPERTY_ARTIST: + result.append(handle, propertyCode, + queryAudio(handle, Audio.AudioColumns.ARTIST)); + break; + case MtpConstants.PROPERTY_ALBUM_NAME: + result.append(handle, propertyCode, + queryAudio(handle, Audio.AudioColumns.ALBUM)); + break; + case MtpConstants.PROPERTY_GENRE: + String genre = queryGenre(handle); + if (genre != null) { + result.append(handle, propertyCode, genre); + } else { + result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); + } + break; + default: + if (property.type == MtpConstants.TYPE_STR) { + result.append(handle, propertyCode, c.getString(column)); + } else if (property.type == MtpConstants.TYPE_UNDEFINED) { + result.append(handle, propertyCode, property.type, 0); + } else { + result.append(handle, propertyCode, property.type, + c.getLong(column)); + } + break; + } + } + } + + return result; + } catch (RemoteException e) { + return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR); + } finally { + if (c != null) { + c.close(); + } + } + // impossible to get here, so no return statement + } + + private native String format_date_time(long seconds); +} diff --git a/media/java/android/media/MtpPropertyList.java b/media/java/android/media/MtpPropertyList.java index f598981..d3f0b34 100644 --- a/media/java/android/media/MtpPropertyList.java +++ b/media/java/android/media/MtpPropertyList.java @@ -19,14 +19,14 @@ package android.media; /** * Encapsulates the ObjectPropList dataset used by the GetObjectPropList command. * The fields of this class are read by JNI code in android_media_MtpDatabase.cpp - * - * {@hide} */ -public class MtpPropertyList { +class MtpPropertyList { // number of results returned - public final int mCount; + private int mCount; + // maximum number of results + private final int mMaxCount; // result code for GetObjectPropList public int mResult; // list of object handles (first field in quadruplet) @@ -41,18 +41,19 @@ public class MtpPropertyList { public String[] mStringValues; // constructor only called from MtpDatabase - public MtpPropertyList(int count, int result) { - mCount = count; + public MtpPropertyList(int maxCount, int result) { + mMaxCount = maxCount; mResult = result; - mObjectHandles = new int[count]; - mPropertyCodes = new int[count]; - mDataTypes = new int[count]; + mObjectHandles = new int[maxCount]; + mPropertyCodes = new int[maxCount]; + mDataTypes = new int[maxCount]; // mLongValues and mStringValues are created lazily since both might not be necessary } - public void setProperty(int index, int handle, int property, int type, long value) { + public void append(int handle, int property, int type, long value) { + int index = mCount++; if (mLongValues == null) { - mLongValues = new long[mCount]; + mLongValues = new long[mMaxCount]; } mObjectHandles[index] = handle; mPropertyCodes[index] = property; @@ -60,9 +61,10 @@ public class MtpPropertyList { mLongValues[index] = value; } - public void setProperty(int index, int handle, int property, String value) { + public void append(int handle, int property, String value) { + int index = mCount++; if (mStringValues == null) { - mStringValues = new String[mCount]; + mStringValues = new String[mMaxCount]; } mObjectHandles[index] = handle; mPropertyCodes[index] = property; diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java index 0c48556..41309dc 100755 --- a/media/java/android/media/audiofx/Visualizer.java +++ b/media/java/android/media/audiofx/Visualizer.java @@ -43,10 +43,8 @@ import android.os.Message; * <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li> * </ul> * <p>The length of the capture can be retrieved or specified by calling respectively - * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. Note that the size of the FFT - * is half of the specified capture size but both sides of the spectrum are returned yielding in a - * number of bytes equal to the capture size. The capture size must be a power of 2 in the range - * returned by {@link #getCaptureSizeRange()}. + * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. The capture size must be a + * power of 2 in the range returned by {@link #getCaptureSizeRange()}. * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and * {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by * use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. @@ -333,11 +331,43 @@ public class Visualizer { } } /** - * Returns a frequency capture of currently playing audio content. The capture is a 8-bit - * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both - * sides of the spectrum are returned yielding in a number of bytes equal to the capture size. - * {@see #getCaptureSize()}. + * Returns a frequency capture of currently playing audio content. * <p>This method must be called when the Visualizer is enabled. + * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of + * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and + * imaginary parts of a number of frequency points equal to half of the capture size plus one. + * <p>Note: only the real part is returned for the first point (DC) and the last point + * (sampling frequency / 2). + * <p>The layout in the returned byte array is as follows: + * <ul> + * <li> n is the capture size returned by getCaptureSize()</li> + * <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency + * component</li> + * <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is: + * (k*Fs)/(n/2) </li> + * </ul> + * <table border="0" cellspacing="0" cellpadding="0"> + * <tr><td>Index </p></td> + * <td>0 </p></td> + * <td>1 </p></td> + * <td>2 </p></td> + * <td>3 </p></td> + * <td>4 </p></td> + * <td>5 </p></td> + * <td>... </p></td> + * <td>n - 2 </p></td> + * <td>n - 1 </p></td></tr> + * <tr><td>Data </p></td> + * <td>Rf0 </p></td> + * <td>Rf(n/2) </p></td> + * <td>Rf1 </p></td> + * <td>If1 </p></td> + * <td>Rf2 </p></td> + * <td>If2 </p></td> + * <td>... </p></td> + * <td>Rf(n-1)/2 </p></td> + * <td>If(n-1)/2 </p></td></tr> + * </table> * @param fft array of bytes where the FFT should be returned * @return {@link #SUCCESS} in case of success, * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java index 64d3229..37bb661 100755 --- a/media/java/android/media/videoeditor/VideoEditor.java +++ b/media/java/android/media/videoeditor/VideoEditor.java @@ -98,6 +98,31 @@ public interface VideoEditor { }
/**
+ * This listener interface is used by the VideoEditor to emit export status
+ * notifications.
+ * {@link #generatePreview(MediaProcessingProgressListener listener)}
+ */
+ public interface MediaProcessingProgressListener {
+ // Values used for the action parameter
+ public static final int ACTION_ENCODE = 1;
+ public static final int ACTION_DECODE = 2;
+
+ /**
+ * This method notifies the listener of the progress status of
+ * processing a media object such as a Transition, AudioTrack or a
+ * media image item (when Ken Burns effect is applied).
+ * This method may be called maximum 100 times for one operation.
+ *
+ * @param object The object that is being processed such as a
+ * Transition or AudioTrack
+ * @param action The type of processing being performed
+ * @param progress The progress in %. At the beginning of the operation,
+ * this value is set to 0; at the end, the value is set to 100.
+ */
+ public void onProgress(Object item, int action, int progress);
+ }
+
+ /**
* @return The path where the VideoEditor stores all files related to the
* project
*/
@@ -496,8 +521,11 @@ public interface VideoEditor { * This method must be called after the aspect ratio of the project changes
* and before startPreview is called. Note that this method may block for
* an extensive period of time.
+ *
+ * @param listener The listener interface which will be used to notify
+ * the caller of the progress of each storyboard item being processed.
*/
- public void generatePreview();
+ public void generatePreview(MediaProcessingProgressListener listener);
/**
* Start the preview of all the storyboard items applied on all MediaItems
diff --git a/media/java/android/media/videoeditor/VideoEditorFactory.java b/media/java/android/media/videoeditor/VideoEditorFactory.java index 41eed16..85b2666 100755 --- a/media/java/android/media/videoeditor/VideoEditorFactory.java +++ b/media/java/android/media/videoeditor/VideoEditorFactory.java @@ -19,8 +19,8 @@ package android.media.videoeditor; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; + +import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener; /** @@ -30,34 +30,19 @@ import java.lang.reflect.InvocationTargetException; * {@hide} */ public class VideoEditorFactory { - // VideoEditor implementation classes - public static final String TEST_CLASS_IMPLEMENTATION - = "android.media.videoeditor.VideoEditorTestImpl"; - public static final String DEFAULT_CLASS_IMPLEMENTATION - = "android.media.videoeditor.VideoEditorImpl"; - /** * This is the factory method for creating a new VideoEditor instance. * * @param projectPath The path where all VideoEditor internal * files are stored. When a project is deleted the application is * responsible for deleting the path and its contents. - * @param className The implementation class name * * @return The VideoEditor instance * * @throws IOException if path does not exist or if the path can * not be accessed in read/write mode - * @throws IllegalStateException if a previous VideoEditor instance has not - * been released - * @throws ClassNotFoundException, NoSuchMethodException, - * InvocationTargetException, IllegalAccessException, - * InstantiationException if the implementation class cannot - * be instantiated. */ - public static VideoEditor create(String projectPath, String className) throws IOException, - ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException, InstantiationException { + public static VideoEditor create(String projectPath) throws IOException { // If the project path does not exist create it final File dir = new File(projectPath); if (!dir.exists()) { @@ -72,14 +57,7 @@ public class VideoEditorFactory { } } - final Class<?> cls = Class.forName(className); - final Class<?> partypes[] = new Class[1]; - partypes[0] = String.class; - final Constructor<?> ct = cls.getConstructor(partypes); - final Object arglist[] = new Object[1]; - arglist[0] = projectPath; - - return (VideoEditor)ct.newInstance(arglist); + return new VideoEditorImpl(projectPath); } /** @@ -90,32 +68,21 @@ public class VideoEditorFactory { * @param projectPath The path where all VideoEditor internal files * are stored. When a project is deleted the application is * responsible for deleting the path and its contents. - * @param className The implementation class name * @param generatePreview if set to true the - * {@link MediaEditor#generatePreview()} will be called internally to - * generate any needed transitions. + * {@link MediaEditor#generatePreview(MediaProcessingProgressListener listener)} + * will be called internally to generate any needed transitions. * * @return The VideoEditor instance * * @throws IOException if path does not exist or if the path can * not be accessed in read/write mode or if one of the resource * media files cannot be retrieved - * @throws IllegalStateException if a previous VideoEditor instance has not - * been released */ - public static VideoEditor load(String projectPath, String className, boolean generatePreview) - throws IOException, ClassNotFoundException, NoSuchMethodException, - InvocationTargetException, IllegalAccessException, InstantiationException { - final Class<?> cls = Class.forName(className); - final Class<?> partypes[] = new Class[1]; - partypes[0] = String.class; - final Constructor<?> ct = cls.getConstructor(partypes); - final Object arglist[] = new Object[1]; - arglist[0] = projectPath; - - final VideoEditor videoEditor = (VideoEditor)ct.newInstance(arglist); + public static VideoEditor load(String projectPath, boolean generatePreview) + throws IOException { + final VideoEditor videoEditor = new VideoEditorImpl(projectPath); if (generatePreview) { - videoEditor.generatePreview(); + videoEditor.generatePreview(null); } return videoEditor; } diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java index ca896c3..1a145e6 100644 --- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java +++ b/media/java/android/media/videoeditor/VideoEditorImpl.java @@ -39,7 +39,7 @@ import android.view.SurfaceHolder; /** * The VideoEditor implementation {@hide} */ -public class VideoEditorTestImpl implements VideoEditor { +public class VideoEditorImpl implements VideoEditor { // Logging private static final String TAG = "VideoEditorImpl"; @@ -165,7 +165,7 @@ public class VideoEditorTestImpl implements VideoEditor { if (mPositionMs >= mToMs) { if (!mLoop) { if (mListener != null) { - mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, true); + mListener.onProgress(VideoEditorImpl.this, mPositionMs, true); } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "PreviewThread.run playback complete"); @@ -174,13 +174,13 @@ public class VideoEditorTestImpl implements VideoEditor { } else { // Fire a notification for the end of the clip if (mListener != null) { - mListener.onProgress(VideoEditorTestImpl.this, mToMs, false); + mListener.onProgress(VideoEditorImpl.this, mToMs, false); } // Rewind mPositionMs = mFromMs; if (mListener != null) { - mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false); + mListener.onProgress(VideoEditorImpl.this, mPositionMs, false); } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "PreviewThread.run playback complete"); @@ -190,7 +190,7 @@ public class VideoEditorTestImpl implements VideoEditor { } else { if (frameCount == mCallbackAfterFrameCount) { if (mListener != null) { - mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false); + mListener.onProgress(VideoEditorImpl.this, mPositionMs, false); } frameCount = 0; } @@ -222,7 +222,7 @@ public class VideoEditorTestImpl implements VideoEditor { * * @param projectPath */ - public VideoEditorTestImpl(String projectPath) throws IOException { + public VideoEditorImpl(String projectPath) throws IOException { mProjectPath = projectPath; final File projectXml = new File(projectPath, PROJECT_FILENAME); if (projectXml.exists()) { @@ -1055,7 +1055,7 @@ public class VideoEditorTestImpl implements VideoEditor { /* * {@inheritDoc} */ - public void generatePreview() { + public void generatePreview(MediaProcessingProgressListener listener) { // Generate all the needed transitions for (Transition transition : mTransitions) { if (!transition.isGenerated()) { diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp index f04a2ae..1909e6a 100644 --- a/media/jni/android_media_MtpDatabase.cpp +++ b/media/jni/android_media_MtpDatabase.cpp @@ -134,8 +134,7 @@ public: virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property); virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, - MtpObjectFormat format, - MtpObjectProperty property, + uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet); @@ -355,7 +354,7 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, MtpDataPacket& packet) { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, - (jint)handle, 0, (jint)property, 0, 0); + (jlong)handle, 0, (jlong)property, 0, 0); MtpResponseCode result = env->GetIntField(list, field_mResult); int count = env->GetIntField(list, field_mCount); if (result == MTP_RESPONSE_OK && count != 1) @@ -646,18 +645,19 @@ MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) { } MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, - MtpObjectFormat format, - MtpObjectProperty property, + uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, - (jint)handle, (jint)format, (jint)property, (jint)groupCode, (jint)depth); + (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + if (!list) + return MTP_RESPONSE_GENERAL_ERROR; int count = env->GetIntField(list, field_mCount); MtpResponseCode result = env->GetIntField(list, field_mResult); packet.putUInt32(count); - if (count > 0) { jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); @@ -1042,7 +1042,7 @@ android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz) } static jstring -android_media_MtpDatabase_format_date_time(JNIEnv *env, jobject thiz, jlong seconds) +android_media_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds) { #ifdef HAVE_ANDROID_OS char date[20]; @@ -1055,11 +1055,14 @@ android_media_MtpDatabase_format_date_time(JNIEnv *env, jobject thiz, jlong seco // ---------------------------------------------------------------------------- -static JNINativeMethod gMethods[] = { +static JNINativeMethod gMtpDatabaseMethods[] = { {"native_setup", "()V", (void *)android_media_MtpDatabase_setup}, {"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize}, +}; + +static JNINativeMethod gMtpPropertyGroupMethods[] = { {"format_date_time", "(J)Ljava/lang/String;", - (void *)android_media_MtpDatabase_format_date_time}, + (void *)android_media_MtpPropertyGroup_format_date_time}, }; static const char* const kClassPathName = "android/media/MtpDatabase"; @@ -1131,7 +1134,7 @@ int register_android_media_MtpDatabase(JNIEnv *env) return -1; } method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList", - "(IIIII)Landroid/media/MtpPropertyList;"); + "(JIJII)Landroid/media/MtpPropertyList;"); if (method_getObjectPropertyList == NULL) { LOGE("Can't find getObjectPropertyList"); return -1; @@ -1220,6 +1223,10 @@ int register_android_media_MtpDatabase(JNIEnv *env) return -1; } + if (AndroidRuntime::registerNativeMethods(env, + "android/media/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods))) + return -1; + return AndroidRuntime::registerNativeMethods(env, - "android/media/MtpDatabase", gMethods, NELEM(gMethods)); + "android/media/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods)); } diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index 68f2e9b..a660429 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -209,8 +209,8 @@ status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform) int32_t nonzero = 0; for (uint32_t i = 0; i < mCaptureSize; i += 2) { - workspace[i >> 1] = (waveform[i] ^ 0x80) << 23; - workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7; + workspace[i >> 1] = + ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8); nonzero |= workspace[i >> 1]; } @@ -219,8 +219,8 @@ status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform) } for (uint32_t i = 0; i < mCaptureSize; i += 2) { - fft[i] = workspace[i >> 1] >> 23; - fft[i + 1] = workspace[i >> 1] >> 7; + fft[i] = workspace[i >> 1] >> 24; + fft[i + 1] = workspace[i >> 1] >> 8; } return NO_ERROR; diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index ec3b5a2..cee92d2 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -366,6 +366,9 @@ status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { return BAD_VALUE; } + if (timeUs <= 15 * 1000000LL) { + LOGW("Target duration (%lld us) too short to be respected", timeUs); + } mMaxFileDurationUs = timeUs; return OK; } @@ -376,6 +379,11 @@ status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { LOGE("Max file size is too small: %lld bytes", bytes); return BAD_VALUE; } + + if (bytes <= 100 * 1024) { + LOGW("Target file size (%lld bytes) is too small to be respected", bytes); + } + mMaxFileSizeBytes = bytes; return OK; } @@ -1156,8 +1164,11 @@ status_t StagefrightRecorder::setupVideoEncoder( CHECK_EQ(client.connect(), OK); // Use software codec for time lapse - uint32_t encoder_flags = (mCaptureTimeLapse) ? OMXCodec::kPreferSoftwareCodecs : 0; - if (mIsMetaDataStoredInVideoBuffers) { + uint32_t encoder_flags = 0; + if (mCaptureTimeLapse) { + encoder_flags |= OMXCodec::kPreferSoftwareCodecs; + } else if (mIsMetaDataStoredInVideoBuffers) { + encoder_flags |= OMXCodec::kHardwareCodecsOnly; encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers; } sp<MediaSource> encoder = OMXCodec::Create( @@ -1165,6 +1176,11 @@ status_t StagefrightRecorder::setupVideoEncoder( true /* createEncoder */, cameraSource, NULL, encoder_flags); if (encoder == NULL) { + LOGW("Failed to create the encoder"); + // When the encoder fails to be created, we need + // release the camera source due to the camera's lock + // and unlock mechanism. + cameraSource->stop(); return UNKNOWN_ERROR; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index a804866..8ebbe6c 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -426,7 +426,8 @@ status_t AwesomePlayer::setDataSource_l( // Hack to support http live. size_t len = strlen(uri); - if (!strcasecmp(&uri[len - 5], ".m3u8")) { + if (!strcasecmp(&uri[len - 5], ".m3u8") + || strstr(&uri[7], "m3u8") != NULL) { mUri = "httplive://"; mUri.append(&uri[7]); } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 6760707..602aa9f 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -878,7 +878,10 @@ bool MPEG4Writer::exceedsFileSizeLimit() { nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes(); } - return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes); + // Be conservative in the estimate: do not exceed 95% of + // the target file limit. For small target file size limit, though, + // this will not help. + return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100); } bool MPEG4Writer::exceedsFileDurationLimit() { diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp index e6a0976..e3292e6 100644 --- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp @@ -178,7 +178,7 @@ AVCEncoder::AVCEncoder( mInputFrameData(NULL), mGroup(NULL) { - LOGV("Construct software AVCEncoder"); + LOGI("Construct software AVCEncoder"); mHandle = new tagAVCHandle; memset(mHandle, 0, sizeof(tagAVCHandle)); diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp index c7a475b..15ed219 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp @@ -178,7 +178,7 @@ M4vH263Encoder::M4vH263Encoder( mInputFrameData(NULL), mGroup(NULL) { - LOGV("Construct software M4vH263Encoder"); + LOGI("Construct software M4vH263Encoder"); mHandle = new tagvideoEncControls; memset(mHandle, 0, sizeof(tagvideoEncControls)); diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp index 4b4c3d2..4451bd5 100644 --- a/media/libstagefright/httplive/LiveSource.cpp +++ b/media/libstagefright/httplive/LiveSource.cpp @@ -359,14 +359,6 @@ bool LiveSource::setupCipher() { return false; } - if (keyURI.size() >= 2 - && keyURI.c_str()[0] == '"' - && keyURI.c_str()[keyURI.size() - 1] == '"') { - // Remove surrounding quotes. - AString tmp(keyURI, 1, keyURI.size() - 2); - keyURI = tmp; - } - ssize_t index = mAESKeyForURI.indexOfKey(keyURI); sp<ABuffer> key; diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index b166cc3..d4a29c0 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -162,7 +162,7 @@ status_t M3UParser::parse(const void *_data, size_t size) { if (mIsVariantPlaylist) { return ERROR_MALFORMED; } - err = parseCipherInfo(line, &itemMeta); + err = parseCipherInfo(line, &itemMeta, mBaseURI); } else if (line.startsWith("#EXT-X-ENDLIST")) { mIsComplete = true; } else if (line.startsWith("#EXTINF")) { @@ -298,7 +298,7 @@ status_t M3UParser::parseStreamInf( // static status_t M3UParser::parseCipherInfo( - const AString &line, sp<AMessage> *meta) { + const AString &line, sp<AMessage> *meta, const AString &baseURI) { ssize_t colonPos = line.find(":"); if (colonPos < 0) { @@ -338,6 +338,24 @@ status_t M3UParser::parseCipherInfo( *meta = new AMessage; } + if (key == "uri") { + if (val.size() >= 2 + && val.c_str()[0] == '"' + && val.c_str()[val.size() - 1] == '"') { + // Remove surrounding quotes. + AString tmp(val, 1, val.size() - 2); + val = tmp; + } + + AString absURI; + if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) { + val = absURI; + } else { + LOGE("failed to make absolute url for '%s'.", + val.c_str()); + } + } + key.insert(AString("cipher-"), 0); (*meta)->setString(key.c_str(), val.c_str(), val.size()); diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h index 531d184..63895b4 100644 --- a/media/libstagefright/include/M3UParser.h +++ b/media/libstagefright/include/M3UParser.h @@ -67,7 +67,7 @@ private: const AString &line, sp<AMessage> *meta); static status_t parseCipherInfo( - const AString &line, sp<AMessage> *meta); + const AString &line, sp<AMessage> *meta, const AString &baseURI); static status_t ParseInt32(const char *s, int32_t *x); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index f06a1bb..a559b21 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -238,10 +238,15 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) { } sp<MediaSource> ATSParser::Program::getSource(SourceType type) { + size_t index = (type == MPEG2ADTS_AUDIO) ? 0 : 0; + for (size_t i = 0; i < mStreams.size(); ++i) { sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type); if (source != NULL) { - return source; + if (index == 0) { + return source; + } + --index; } } @@ -508,7 +513,10 @@ void ATSParser::Stream::onPayloadData( int64_t timeUs = mProgram->convertPTSToTimestamp(PTS); status_t err = mQueue.appendData(data, size, timeUs); - CHECK_EQ(err, (status_t)OK); + + if (err != OK) { + return; + } sp<ABuffer> accessUnit; while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) { diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index f11b3c3..37bcb23 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -55,9 +55,34 @@ status_t ElementaryStreamQueue::appendData( switch (mMode) { case H264: { +#if 0 if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { return ERROR_MALFORMED; } +#else + uint8_t *ptr = (uint8_t *)data; + + ssize_t startOffset = -1; + for (size_t i = 0; i + 3 < size; ++i) { + if (!memcmp("\x00\x00\x00\x01", &ptr[i], 4)) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + LOGI("found something resembling an H.264 syncword at " + "offset %ld", + startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; +#endif break; } @@ -65,9 +90,31 @@ status_t ElementaryStreamQueue::appendData( { uint8_t *ptr = (uint8_t *)data; +#if 0 if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) { return ERROR_MALFORMED; } +#else + ssize_t startOffset = -1; + for (size_t i = 0; i + 1 < size; ++i) { + if (ptr[i] == 0xff && (ptr[i + 1] >> 4) == 0x0f) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + LOGI("found something resembling an AAC syncword at offset %ld", + startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; +#endif break; } diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index 900b517..9929805 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -77,8 +77,7 @@ public: virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0; virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, - MtpObjectFormat format, - MtpObjectProperty property, + uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) = 0; diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp index 42945f5..4356a6f 100644 --- a/media/mtp/MtpProperty.cpp +++ b/media/mtp/MtpProperty.cpp @@ -53,7 +53,7 @@ MtpProperty::MtpProperty(MtpPropertyCode propCode, mDefaultArrayValues(NULL), mCurrentArrayLength(0), mCurrentArrayValues(NULL), - mGroupCode(-1), // disable multiple properties in GetObjectPropList for now + mGroupCode(0), mFormFlag(kFormNone), mEnumLength(0), mEnumValues(NULL) diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index c3755f3..de6cbac 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -536,8 +536,9 @@ MtpResponseCode MtpServer::doResetDevicePropValue() { MtpResponseCode MtpServer::doGetObjectPropList() { MtpObjectHandle handle = mRequest.getParameter(1); - MtpObjectFormat format = mRequest.getParameter(2); - MtpDeviceProperty property = mRequest.getParameter(3); + // use uint32_t so we can support 0xFFFFFFFF + uint32_t format = mRequest.getParameter(2); + uint32_t property = mRequest.getParameter(3); int groupCode = mRequest.getParameter(4); int depth = mRequest.getParameter(5); LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml index 078daa7..b0597c4 100755 --- a/packages/DefaultContainerService/AndroidManifest.xml +++ b/packages/DefaultContainerService/AndroidManifest.xml @@ -1,6 +1,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.defcontainer"> <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/> + <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS"/> <uses-permission android:name="android.permission.ASEC_ACCESS"/> <uses-permission android:name="android.permission.ASEC_CREATE"/> <uses-permission android:name="android.permission.ASEC_DESTROY"/> diff --git a/packages/DefaultContainerService/res/values-id/strings.xml b/packages/DefaultContainerService/res/values-in/strings.xml index 2f830b1..2f830b1 100644 --- a/packages/DefaultContainerService/res/values-id/strings.xml +++ b/packages/DefaultContainerService/res/values-in/strings.xml diff --git a/packages/DefaultContainerService/res/values-he/strings.xml b/packages/DefaultContainerService/res/values-iw/strings.xml index ede8178..ede8178 100644 --- a/packages/DefaultContainerService/res/values-he/strings.xml +++ b/packages/DefaultContainerService/res/values-iw/strings.xml diff --git a/packages/SettingsProvider/res/values-id/strings.xml b/packages/SettingsProvider/res/values-in/strings.xml index bed20eb..bed20eb 100644 --- a/packages/SettingsProvider/res/values-id/strings.xml +++ b/packages/SettingsProvider/res/values-in/strings.xml diff --git a/packages/SettingsProvider/res/values-he/strings.xml b/packages/SettingsProvider/res/values-iw/strings.xml index ad2eaf4..ad2eaf4 100644 --- a/packages/SettingsProvider/res/values-he/strings.xml +++ b/packages/SettingsProvider/res/values-iw/strings.xml diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png Binary files differindex 615c8b6..25fffd6 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png Binary files differindex 0122025..b6e4ebc 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png Binary files differindex 0786916..189a089 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png Binary files differindex 35f9240..ebade92 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png Binary files differindex 3eb22df..6ef71c7 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png Binary files differindex 1ce9bd1..f6b0a17 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png Binary files differindex fef2cf9..91bc4ee 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png Binary files differindex 05593bc..aa64de4 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png Binary files differindex 32c2c79..246c6fe 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png Binary files differindex 142c413..34515c7 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png Binary files differnew file mode 100644 index 0000000..b8adc97 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png Binary files differnew file mode 100644 index 0000000..621e980 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png diff --git a/packages/SystemUI/res/drawable-nodpi/panel_notification.png b/packages/SystemUI/res/drawable-nodpi/panel_notification.png Binary files differindex eca47d7..b0d9c18 100644 --- a/packages/SystemUI/res/drawable-nodpi/panel_notification.png +++ b/packages/SystemUI/res/drawable-nodpi/panel_notification.png diff --git a/packages/SystemUI/res/drawable/status_bar_toggle_button.xml b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml new file mode 100644 index 0000000..e17c62f --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" + android:drawable="@*android:drawable/scrubber_primary_holo" /> + <item + android:drawable="@*android:drawable/scrubber_track_holo_dark" /> +</selector> + diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml index 758377b..666bfdc 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml @@ -158,10 +158,8 @@ /> <com.android.systemui.statusbar.tablet.ShirtPocket android:id="@+id/pocket" - android:layout_width="96dip" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:paddingLeft="18dip" - android:paddingRight="18dip" android:animateLayoutChanges="true" android:clickable="true" android:descendantFocusability="blocksDescendants" @@ -170,10 +168,12 @@ <ImageView android:id="@+id/pocket_icon" android:src="@drawable/ic_sysbar_pocket" - android:visibility="invisible" - android:layout_width="match_parent" + android:paddingLeft="18dip" + android:paddingRight="18dip" + android:layout_width="96dip" android:layout_height="wrap_content" android:gravity="center" + android:visibility="gone" /> </com.android.systemui.statusbar.tablet.ShirtPocket> <com.android.systemui.statusbar.tablet.InputMethodButton diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml index 6dd97c3..5e867e5 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml @@ -17,11 +17,13 @@ <com.android.systemui.statusbar.tablet.SettingsView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/status_bar_item_background" android:paddingLeft="16dp" + android:paddingRight="46dp" > <!-- Airplane mode --> @@ -39,11 +41,12 @@ style="@style/StatusBarPanelSettingsContents" android:text="@string/status_bar_settings_airplane" /> - <CheckBox + <Switch android:id="@+id/airplane_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:layout_marginRight="5dp" /> </LinearLayout> <View style="@style/StatusBarPanelSettingsPanelSeparator" /> @@ -67,7 +70,7 @@ android:layout_height="wrap_content" android:layout_gravity="top" android:layout_marginTop="16dp" - android:layout_marginRight="8dp" + android:layout_marginRight="2dp" android:src="@drawable/ic_notification_open" /> </LinearLayout> @@ -88,11 +91,12 @@ style="@style/StatusBarPanelSettingsContents" android:text="@string/status_bar_settings_rotation_lock" /> - <CheckBox + <Switch android:id="@+id/rotate_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:layout_marginRight="5dp" /> </LinearLayout> <View style="@style/StatusBarPanelSettingsPanelSeparator" /> @@ -104,6 +108,14 @@ style="@style/StatusBarPanelSettingsIcon" android:src="@drawable/ic_sysbar_brightness" /> + <com.android.systemui.statusbar.policy.ToggleSlider + android:id="@+id/brightness" + android:layout_width="0dp" + android:layout_height="fill_parent" + android:layout_weight="1" + android:layout_marginRight="2dp" + systemui:text="@string/status_bar_settings_auto_brightness_label" + /> </LinearLayout> <View style="@style/StatusBarPanelSettingsPanelSeparator" /> @@ -114,6 +126,14 @@ style="@style/StatusBarPanelSettingsIcon" android:src="@drawable/ic_sysbar_sound_on" /> + <com.android.systemui.statusbar.policy.ToggleSlider + android:id="@+id/volume" + android:layout_width="0dp" + android:layout_height="fill_parent" + android:layout_weight="1" + android:layout_marginRight="2dp" + systemui:text="@string/status_bar_settings_mute_label" + /> </LinearLayout> <View style="@style/StatusBarPanelSettingsPanelSeparator" /> @@ -131,11 +151,12 @@ style="@style/StatusBarPanelSettingsContents" android:text="@string/status_bar_settings_notifications" /> - <CheckBox + <Switch android:id="@+id/do_not_disturb_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:layout_marginRight="5dp" /> </LinearLayout> <View style="@style/StatusBarPanelSettingsPanelSeparator" /> @@ -160,7 +181,7 @@ android:layout_height="wrap_content" android:layout_gravity="top" android:layout_marginTop="16dp" - android:layout_marginRight="8dp" + android:layout_marginRight="2dp" android:src="@drawable/ic_notification_open" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml new file mode 100644 index 0000000..cdf56c5 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> + +<!-- android:background="@drawable/status_bar_closed_default_background" --> +<merge + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + > + <CheckBox + android:id="@+id/toggle" + android:layout_width="48dp" + android:layout_height="0dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:layout_alignParentBottom="true" + android:button="@drawable/status_bar_toggle_button" + /> + <SeekBar + android:id="@+id/slider" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/toggle" + android:layout_centerVertical="true" + android:layout_alignParentRight="true" + android:paddingLeft="20dp" + android:paddingRight="20dp" + /> + <TextView + android:id="@+id/label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignLeft="@id/toggle" + android:layout_alignRight="@id/toggle" + android:layout_centerVertical="true" + android:gravity="center" + android:paddingTop="26dp" + android:textColor="#666666" + android:textSize="12sp" + /> +</merge> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index cfdf0dd..21c0645 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"UI systému"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nerušit"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Zobrazit upozornění"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Žádná oznámení"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string> <string name="battery_low_title" msgid="7923774589611311406">"Prosím připojte dobíjecí zařízení"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Baterie je vybitá:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Baterie je vybitá."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Zbývá <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Nabíjení pomocí rozhraní USB není podporováno."\n"Používejte pouze nabíječku, která byla dodána se zařízením."</string> <string name="battery_low_why" msgid="7279169609518386372">"Využití baterie"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Nejnovější"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Žádné nedávno použité aplikace."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Obrazovka se automaticky otočí."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Otáčení obrazovky je uzamčeno."</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index e4fc728..a569aa4 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"System-UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Forstyr ikke"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis meddelelser"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen meddelelser"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelelser"</string> <string name="battery_low_title" msgid="7923774589611311406">"Forbind oplader"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er ved at være fladt:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er ved at være fladt."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> tilbage"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Opladning via USB understøttes ikke."\n"Brug kun den medfølgende oplader."</string> <string name="battery_low_why" msgid="7279169609518386372">"Batteriforbrug"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Seneste"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Der er ingen nye programmer."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Skærmen roterer automatisk."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Skærmrotationen er nu låst."</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index edb1bef..fce8200 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"System-UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Bitte nicht stören"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Benachrichtigungen zeigen"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Keine Benachrichtigungen"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string> <string name="battery_low_title" msgid="7923774589611311406">"Ladegerät anschließen"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Akku ist fast leer."</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Akku ist fast leer."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Noch <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt."\n"Verwenden Sie das mitgelieferte Aufladegerät."</string> <string name="battery_low_why" msgid="7279169609518386372">"Akkuverbrauch"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Keine neuen Anwendungen"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Bildschirm wird automatisch gedreht."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Bildschirmrotation ist jetzt gesperrt."</string> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 21ea803..01bf5a1 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"UI συστήματ."</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Εκκαθάριση"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Μην ενοχλείτε"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Εμφάνιση ειδοποιήσεων"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Δεν υπάρχουν ειδοποιήσεις"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string> <string name="battery_low_title" msgid="7923774589611311406">"Συνδέστε τον φορτιστή"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Η στάθμη της μπαταρίας είναι χαμηλή:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Η στάθμη της μπαταρίας είναι χαμηλή."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Απομένει <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Δεν υποστηρίζεται η φόρτιση USB."\n"Χρησιμοποιείτε μόνο τον φορτιστή που παρέχεται."</string> <string name="battery_low_why" msgid="7279169609518386372">"Χρήση μπαταρίας"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Πρόσφατα"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Δεν υπάρχουν πρόσφατες εφαρμογές."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Θα γίνεται αυτόματη περιστροφή της οθόνης."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Η περιστροφή οθόνης είναι κλειδωμένη."</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index b6d3618..e2172c0 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Sistema UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No hay notificaciones"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string> <string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Hay poca batería:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Hay poca batería."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Quedan <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"No admite la carga USB."\n"Usa sólo el cargador provisto."</string> <string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"No hay aplicaciones recientes."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"La pantalla rotará automáticamente."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotación de la pantalla se encuentra actualmente bloqueada."</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 77773af..b402e15 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"IU sistema"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"No molestar"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificaciones"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No tienes notificaciones"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Entrante"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string> <string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Se está agotando la batería:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Se está agotando la batería."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string> + <string name="invalid_charger" msgid="4549105996740522523">"No se admite la carga por USB."\n"Utiliza solo el cargador proporcionado."</string> <string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"No hay aplicaciones recientes."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"La pantalla girará automáticamente."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotación de la pantalla esta bloqueada."</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 8877329..70d2a51 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"IU système"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ne pas déranger"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Afficher les notifications"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Aucune notification"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="7923774589611311406">"Branchez le chargeur"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Le niveau de la batterie est bas :"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Le niveau de la batterie est faible."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restant(s)"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non disponible."\n"Vous devez utiliser le chargeur fourni."</string> <string name="battery_low_why" msgid="7279169609518386372">"Utilisation de la batterie"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Récentes"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Aucune application récente"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"L\'écran pivote automatiquement."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotation de l\'écran est verrouillée."</string> </resources> diff --git a/packages/SystemUI/res/values-id/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 974498a..974498a 100644 --- a/packages/SystemUI/res/values-id/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 0507cf9..4b16033 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"UI sistema"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Non disturbare"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostra notifiche"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nessuna notifica"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string> <string name="battery_low_title" msgid="7923774589611311406">"Collegare il caricabatterie"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteria quasi scarica:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteria quasi scarica."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> rimanente"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Ricarica tramite USB non supportata."\n"Utilizza solo il caricatore in dotazione."</string> <string name="battery_low_why" msgid="7279169609518386372">"Utilizzo batteria"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Recenti"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Nessuna applicazione recente."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Lo schermo ruoterà automaticamente."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"La rotazione dello schermo è bloccata."</string> </resources> diff --git a/packages/SystemUI/res/values-he/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index ad1f755..ad1f755 100644 --- a/packages/SystemUI/res/values-he/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 782d03b..b2b6c54 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"システムUI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"通知を非表示"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"通知を表示"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"通知なし"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="7923774589611311406">"充電してください"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"電池が残り少なくなっています:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"電池が残り少なくなっています。"</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"残り<xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"USB充電には対応していません。"\n"付属の充電器をお使いください。"</string> <string name="battery_low_why" msgid="7279169609518386372">"電池使用量"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"新着"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"新着のアプリケーションはありません。"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"画面は自動的に回転します。"</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"画面の回転をロックしました。"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 5e9b9d5..d550253 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"시스템 UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"응답 거부"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"알림 표시"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 없음"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string> <string name="battery_low_title" msgid="7923774589611311406">"충전기를 연결하세요."</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"배터리 전원이 부족합니다."</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"배터리 전원이 부족합니다."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g>개 남음"</string> + <string name="invalid_charger" msgid="4549105996740522523">"USB 충전이 지원되지 않습니다."\n"제공된 충전기만 사용하세요."</string> <string name="battery_low_why" msgid="7279169609518386372">"배터리 사용량"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"최근 사용한 앱"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"최근에 사용한 애플리케이션이 없습니다."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"화면은 자동으로 회전합니다."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"현재 화면 회전이 잠겨 있습니다."</string> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 147242f..1eb7851 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Sys.gr.snitt"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Ikke forstyrr"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Vis varslinger"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen varslinger"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varslinger"</string> <string name="battery_low_title" msgid="7923774589611311406">"Koble til en lader"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet er nesten tomt:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Lavt batterinivå."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> gjenværende"</string> + <string name="invalid_charger" msgid="4549105996740522523">"USB-lading støttes ikke."\n"Bruk kun den medfølgende laderen."</string> <string name="battery_low_why" msgid="7279169609518386372">"Batteribruk"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Nylig"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Ingen nylig brukte programmer."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Skjermen vil rotere automatisk."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Skjermrotering er låst."</string> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 6067cca..c642240 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Systeem-UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Niet storen"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Meldingen weergeven"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Geen meldingen"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string> <string name="battery_low_title" msgid="7923774589611311406">"Sluit de oplader aan"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"De accu raakt op:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"De accu raakt leeg."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> resterend"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Opladen via USB niet ondersteund."\n"Gebruik alleen de bijgeleverde oplader."</string> <string name="battery_low_why" msgid="7279169609518386372">"Accugebruik"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Recent"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Geen recente toepassingen."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Scherm wordt automatisch geroteerd."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Schermrotatie is nu vergrendeld."</string> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 6b19a34..4f5f328 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Interfejs"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Nie przeszkadzać"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Pokaż powiadomienia"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Brak powiadomień"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Bieżące"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Powiadomienia"</string> <string name="battery_low_title" msgid="7923774589611311406">"Podłącz ładowarkę"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Bateria się rozładowuje:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Bateria wkrótce zostanie rozładowana."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Pozostało: <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Ładowanie przy użyciu złącza USB nie jest obsługiwane."\n"Należy używać tylko dołączonej ładowarki."</string> <string name="battery_low_why" msgid="7279169609518386372">"Użycie baterii"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Najnowsze"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Brak ostatnio używanych aplikacji."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Ekran zostanie obrócony automatycznie."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Obracanie ekranu zostało zablokowane."</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 7bf6eb4..8ec603a 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"IU do sist."</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não incomodar"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string> <string name="battery_low_title" msgid="7923774589611311406">"Ligue o carregador"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está a ficar fraca:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está a ficar fraca."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Carregamento USB não suportado. "\n"Utilize apenas o carregador fornecido."</string> <string name="battery_low_why" msgid="7279169609518386372">"Utilização da bateria"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Nenhuma aplicação recente."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"O ecrã será rodado automaticamente."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"A rotação do ecrã está agora bloqueada."</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 14b4b1f..6f98ed6 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Interf sist"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Não perturbe"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Mostrar notificações"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string> <string name="battery_low_title" msgid="7923774589611311406">"Conecte o carregador"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está ficando baixa:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"A bateria está ficando baixa."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string> + <string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado."\n"Use apenas o carregador fornecido."</string> <string name="battery_low_why" msgid="7279169609518386372">"Uso da bateria"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Nenhum aplicativo recente."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"A tela girará automaticamente."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"A rotação da tela está bloqueada."</string> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 0a15bcd..471525f 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Графический интерфейс системы"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Не беспокоить"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Показать уведомления"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нет уведомлений"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string> <string name="battery_low_title" msgid="7923774589611311406">"Подключите зарядное устройство"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея разряжена:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея разряжена."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"Осталось: <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Зарядка через порт USB не поддерживается."\n"Используйте только зарядное устройство из комплекта поставки."</string> <string name="battery_low_why" msgid="7279169609518386372">"Расход заряда батареи"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Недавние"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Новых приложений нет"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Экран будет поворачиваться автоматически."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Поворот экрана заблокирован."</string> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 06188ab..7b7f29f 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Gränssnitt"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Stör ej"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Visa aviseringar"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Inga aviseringar"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelanden"</string> <string name="battery_low_title" msgid="7923774589611311406">"Anslut laddaren"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet håller på att ta slut:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Batteriet håller på att ta slut."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> återstår"</string> + <string name="invalid_charger" msgid="4549105996740522523">"Det går inte att ladda via USB."\n"Använd endast den laddare som levererades med telefonen."</string> <string name="battery_low_why" msgid="7279169609518386372">"Batteriförbrukning"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"Senaste"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Inga nya program."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Skärmen roteras automatiskt."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Skärmrotationen är nu låst."</string> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 253fbe0..7c31960 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"Sist Arayüzü"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"Rahatsız etmeyin"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"Bildirimleri göster"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirim yok"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string> <string name="battery_low_title" msgid="7923774589611311406">"Lütfen şarj cihazını takın"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"Pil tükeniyor:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"Pil azalıyor."</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> kaldı"</string> + <string name="invalid_charger" msgid="4549105996740522523">"USB üzerinden şarj desteklenmiyor."\n"Yalnızca ürünle birlikte verilen şarj cihazını kullanın."</string> <string name="battery_low_why" msgid="7279169609518386372">"Pil kullanımı"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"En Son Görevler"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"Hiçbir yeni uygulama yok."</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"Ekran otomatik olarak dönecektir."</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"Ekran dönüşü şimdi kilitlendi."</string> </resources> diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml index e3e5148..279a135 100644 --- a/packages/SystemUI/res/values-xlarge/strings.xml +++ b/packages/SystemUI/res/values-xlarge/strings.xml @@ -26,44 +26,16 @@ <!-- Text to display underneath the graphical signal strength meter when no connection is available. [CHAR LIMIT=20] --> <string name="status_bar_settings_signal_meter_disconnected"> - no internet connection - </string> - - <!-- Text to display underneath the graphical signal strength meter when - it is displaying information about a connected, named Wi-Fi network. - [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_wifi_ssid_format"> - <xliff:g id="ssid">%s</xliff:g> + No Internet connection </string> <!-- Text to display underneath the graphical signal strength meter when it is displaying Wi-Fi status and Wi-Fi is connected to a network whose SSID is not available. [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_wifi_nossid"> - Wi-Fi: connected - </string> + <string name="status_bar_settings_signal_meter_wifi_nossid">Wi-Fi connected</string> - <!-- Text to display underneath the graphical signal strength meter when - it is displaying Wi-Fi status and Wi-Fi is in the process of - connecting to a network. [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_wifi_connecting"> - Wi-Fi: connecting… - </string> - - <!-- Text to display underneath the graphical signal strength meter when - it is displaying mobile data (3G) status and a network connection is - available. - [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_data_connected"> - Mobile data: connected - </string> + <!-- Separator for PLMN and SPN in network name. --> + <string name="status_bar_network_name_separator" translatable="false">" – "</string> - <!-- Text to display underneath the graphical signal strength meter when - it is displaying mobile data (3G) status and a network connection is - unavailable. - [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_data_connecting"> - Mobile data: connecting… - </string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 161a085..4705013 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"系统用户界面"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"请勿打扰"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"显示通知"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="7923774589611311406">"请连接充电器"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"电量所剩不多:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"电池电量低。"</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"还剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"不支持 USB 充电功能。"\n"只能使用随附的充电器充电。"</string> <string name="battery_low_why" msgid="7279169609518386372">"电量使用情况"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"近期任务"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"没有最近使用的应用程序。"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"屏幕会自动旋转。"</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"屏幕旋转现已锁定。"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index eb9108d..ec9c298 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -19,22 +19,17 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for app_label (7164937344850004466) --> - <skip /> + <string name="app_label" msgid="7164937344850004466">"系統 UI"</string> <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string> - <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) --> - <skip /> - <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) --> - <skip /> + <string name="status_bar_do_not_disturb_button" msgid="5812628897510997853">"勿干擾"</string> + <string name="status_bar_please_disturb_button" msgid="3345398298841572813">"顯示通知"</string> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"沒有通知"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"進行中"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="7923774589611311406">"請連接充電器"</string> - <!-- outdated translation 7388781709819722764 --> <string name="battery_low_subtitle" msgid="1752040062087829196">"電池電量即將不足:"</string> - <!-- no translation found for battery_low_percent_format (1077244949318261761) --> - <skip /> - <!-- no translation found for invalid_charger (4549105996740522523) --> - <skip /> + <string name="battery_low_subtitle" msgid="1752040062087829196">"電池電量即將不足。"</string> + <string name="battery_low_percent_format" msgid="1077244949318261761">"還剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string> + <string name="invalid_charger" msgid="4549105996740522523">"不支援 USB 充電。"\n"僅能使用隨附的充電器。"</string> <string name="battery_low_why" msgid="7279169609518386372">"電池使用狀況"</string> <!-- no translation found for status_bar_settings_settings_button (3023889916699270224) --> <skip /> @@ -45,12 +40,9 @@ <!-- no translation found for status_bar_settings_notifications (397146176280905137) --> <skip /> <string name="recent_tasks_title" msgid="3691764623638127888">"最新的"</string> - <!-- no translation found for recent_tasks_empty (1905484479067697884) --> - <skip /> + <string name="recent_tasks_empty" msgid="1905484479067697884">"沒有最近用過的應用程式。"</string> <!-- no translation found for recent_tasks_app_label (3796483981246752469) --> <skip /> - <!-- no translation found for toast_rotation_free (2700542202836832631) --> - <skip /> - <!-- no translation found for toast_rotation_locked (7484691306949652450) --> - <skip /> + <string name="toast_rotation_free" msgid="2700542202836832631">"螢幕會自動旋轉。"</string> + <string name="toast_rotation_locked" msgid="7484691306949652450">"螢幕旋轉功能現已鎖定。"</string> </resources> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 23bcf20..87395c1 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -18,5 +18,8 @@ <declare-styleable name="KeyButtonView"> <attr name="keyCode" format="integer" /> </declare-styleable> + <declare-styleable name="ToggleSlider"> + <attr name="text" format="string" /> + </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ed31a34..644cca0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -80,6 +80,12 @@ <!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] --> <string name="status_bar_settings_rotation_lock">Lock screen orientation</string> + <!-- Abbreviation / label for mute brightness mode button. Should be all caps. [CHAR LIMIT=6] --> + <string name="status_bar_settings_mute_label">MUTE</string> + + <!-- Abbreviation / label for automatic brightness mode button. Should be all caps. [CHAR LIMIT=6] --> + <string name="status_bar_settings_auto_brightness_label">AUTO</string> + <!-- Label in system panel saying the device will show notifications [CHAR LIMIT=30] --> <string name="status_bar_settings_notifications">Notifications</string> @@ -88,6 +94,9 @@ <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g> </string> + <!-- Separator for PLMN and SPN in network name. --> + <string name="status_bar_network_name_separator" translatable="false">"\n"</string> + <!-- Recent Tasks dialog: title [CHAR LIMIT=30] --> <string name="recent_tasks_title">Recent</string> <!-- Recent Tasks dialog: message when there are no recent applications [CHAR LIMIT=NONE]--> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java new file mode 100644 index 0000000..c11d04e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.ContentResolver; +import android.content.Context; +import android.os.IPowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Slog; +import android.view.IWindowManager; +import android.widget.CompoundButton; + +public class BrightnessController implements ToggleSlider.Listener { + private static final String TAG = "StatusBar.BrightnessController"; + + // Backlight range is from 0 - 255. Need to make sure that user + // doesn't set the backlight to 0 and get stuck + private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10; + private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON; + + private Context mContext; + private ToggleSlider mControl; + private IPowerManager mPower; + + public BrightnessController(Context context, ToggleSlider control) { + mContext = context; + mControl = control; + + boolean automaticAvailable = context.getResources().getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available); + mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power")); + + if (automaticAvailable) { + int automatic; + try { + automatic = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE); + } catch (SettingNotFoundException snfe) { + automatic = 0; + } + control.setChecked(automatic != 0); + } else { + control.setChecked(false); + //control.hideToggle(); + } + + int value; + try { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS); + } catch (SettingNotFoundException ex) { + value = MAXIMUM_BACKLIGHT; + } + + control.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT); + control.setValue(value - MINIMUM_BACKLIGHT); + + control.setOnChangedListener(this); + } + + public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { + setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC + : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + if (!automatic) { + setBrightness(value + MINIMUM_BACKLIGHT); + } + } + + private void setMode(int mode) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, mode); + } + + private void setBrightness(int brightness) { + try { + mPower.setBacklightBrightness(brightness); + } catch (RemoteException ex) { + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index ec23a3d..1090463 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -32,6 +32,7 @@ import android.net.wifi.WifiManager; import android.os.Binder; import android.os.RemoteException; import android.provider.Settings; +import android.provider.Telephony; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -65,6 +66,9 @@ public class NetworkController extends BroadcastReceiver { ServiceState mServiceState; SignalStrength mSignalStrength; int[] mDataIconList = TelephonyIcons.DATA_G[0]; + String mNetworkName; + String mNetworkNameDefault; + String mNetworkNameSeparator; int mPhoneSignalIconId; int mDataDirectionIconId; int mDataSignalIconId; @@ -116,7 +120,10 @@ public class NetworkController extends BroadcastReceiver { | PhoneStateListener.LISTEN_DATA_ACTIVITY); mHspaDataDistinguishable = mContext.getResources().getBoolean( R.bool.config_hspa_data_distinguishable); - + mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); + mNetworkNameDefault = mContext.getString( + com.android.internal.R.string.lockscreen_carrier_default); + mNetworkName = mNetworkNameDefault; // wifi mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); @@ -127,6 +134,9 @@ public class NetworkController extends BroadcastReceiver { filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); context.registerReceiver(this, filter); // yuck @@ -168,6 +178,12 @@ public class NetworkController extends BroadcastReceiver { updateSimState(intent); updateDataIcon(); refreshViews(); + } else if (action.equals(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION)) { + updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false), + intent.getStringExtra(Telephony.Intents.EXTRA_SPN), + intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false), + intent.getStringExtra(Telephony.Intents.EXTRA_PLMN)); + refreshViews(); } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { updateConnectivity(intent); @@ -516,6 +532,31 @@ public class NetworkController extends BroadcastReceiver { mDataConnected = visible; } + void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + if (false) { + Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + + " showPlmn=" + showPlmn + " plmn=" + plmn); + } + StringBuilder str = new StringBuilder(); + boolean something = false; + if (showPlmn && plmn != null) { + str.append(plmn); + something = true; + } + if (showSpn && spn != null) { + if (something) { + str.append(mNetworkNameSeparator); + } + str.append(spn); + something = true; + } + if (something) { + mNetworkName = str.toString(); + } else { + mNetworkName = mNetworkNameDefault; + } + } + // ===== Wifi =================================================================== private void updateWifiState(Intent intent) { @@ -618,14 +659,13 @@ public class NetworkController extends BroadcastReceiver { if (mWifiSsid == null) { label = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid); } else { - label = context.getString(R.string.status_bar_settings_signal_meter_wifi_ssid_format, - mWifiSsid); + label = mWifiSsid; } combinedSignalIconId = mWifiIconId; dataTypeIconId = 0; } else { if (mDataConnected) { - label = context.getString(R.string.status_bar_settings_signal_meter_data_connected); + label = mNetworkName; } else { label = context.getString(R.string.status_bar_settings_signal_meter_disconnected); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java new file mode 100644 index 0000000..46207ee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.CompoundButton; + +import com.android.systemui.R; + +public class ToggleSlider extends RelativeLayout + implements CompoundButton.OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener { + private static final String TAG = "StatusBar.ToggleSlider"; + + public interface Listener { + public void onChanged(ToggleSlider v, boolean tracking, boolean checked, int value); + } + + private Listener mListener; + private boolean mTracking; + + private CompoundButton mToggle; + private SeekBar mSlider; + private TextView mLabel; + + public ToggleSlider(Context context) { + this(context, null); + } + + public ToggleSlider(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ToggleSlider(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + View.inflate(context, R.layout.status_bar_toggle_slider, this); + + final Resources res = context.getResources(); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleSlider, + defStyle, 0); + + mToggle = (CompoundButton)findViewById(R.id.toggle); + mToggle.setOnCheckedChangeListener(this); + mToggle.setBackgroundDrawable(res.getDrawable(R.drawable.status_bar_toggle_button)); + + mSlider = (SeekBar)findViewById(R.id.slider); + mSlider.setOnSeekBarChangeListener(this); + + mLabel = (TextView)findViewById(R.id.label); + mLabel.setText(a.getString(R.styleable.ToggleSlider_text)); + + a.recycle(); + } + + public void onCheckedChanged(CompoundButton toggle, boolean checked) { + Drawable thumb; + final Resources res = getContext().getResources(); + if (checked) { + thumb = res.getDrawable(R.drawable.scrubber_control_disabled_holo); + } else { + thumb = res.getDrawable(R.drawable.scrubber_control_holo); + } + mSlider.setThumb(thumb); + + if (mListener != null) { + mListener.onChanged(this, mTracking, checked, mSlider.getProgress()); + } + } + + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (mListener != null) { + mListener.onChanged(this, mTracking, mToggle.isChecked(), progress); + } + } + + public void onStartTrackingTouch(SeekBar seekBar) { + mTracking = true; + if (mListener != null) { + mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress()); + } + mToggle.setChecked(false); + } + + public void onStopTrackingTouch(SeekBar seekBar) { + mTracking = false; + if (mListener != null) { + mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress()); + } + } + + public void setOnChangedListener(Listener l) { + mListener = l; + } + + public void setChecked(boolean checked) { + mToggle.setChecked(checked); + } + + public boolean isChecked() { + return mToggle.isChecked(); + } + + public void setMax(int max) { + mSlider.setMax(max); + } + + public void setValue(int value) { + mSlider.setProgress(value); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java new file mode 100644 index 0000000..c9da01a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.ContentResolver; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.media.AudioManager; +import android.provider.Settings; +import android.util.Slog; +import android.view.IWindowManager; +import android.widget.CompoundButton; + +public class VolumeController implements ToggleSlider.Listener { + private static final String TAG = "StatusBar.VolumeController"; + private static final int STREAM = AudioManager.STREAM_NOTIFICATION; + + private Context mContext; + private ToggleSlider mControl; + private AudioManager mAudioManager; + + private boolean mMute; + private int mVolume; + + public VolumeController(Context context, ToggleSlider control) { + mContext = context; + mControl = control; + mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + + mMute = mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; + mVolume = mAudioManager.getStreamVolume(STREAM); + control.setMax(mAudioManager.getStreamMaxVolume(STREAM)); + control.setValue(mVolume); + control.setChecked(mMute); + + control.setOnChangedListener(this); + } + + public void onChanged(ToggleSlider view, boolean tracking, boolean mute, int level) { + if (!tracking) { + if (mute) { + boolean vibeInSilent = (1 == Settings.System.getInt(mContext.getContentResolver(), + Settings.System.VIBRATE_IN_SILENT, 1)); + mAudioManager.setRingerMode( + vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE + : AudioManager.RINGER_MODE_SILENT); + } else { + mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + mAudioManager.setStreamVolume(STREAM, level, AudioManager.FLAG_PLAY_SOUND); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java index d1f8dd0..0491baa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java @@ -31,13 +31,18 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.statusbar.policy.AirplaneModeController; import com.android.systemui.statusbar.policy.AutoRotateController; +import com.android.systemui.statusbar.policy.BrightnessController; import com.android.systemui.statusbar.policy.DoNotDisturbController; +import com.android.systemui.statusbar.policy.ToggleSlider; +import com.android.systemui.statusbar.policy.VolumeController; public class SettingsView extends LinearLayout implements View.OnClickListener { static final String TAG = "SettingsView"; AirplaneModeController mAirplane; AutoRotateController mRotate; + VolumeController mVolume; + BrightnessController mBrightness; DoNotDisturbController mDoNotDisturb; public SettingsView(Context context, AttributeSet attrs) { @@ -59,6 +64,10 @@ public class SettingsView extends LinearLayout implements View.OnClickListener { findViewById(R.id.network).setOnClickListener(this); mRotate = new AutoRotateController(context, (CompoundButton)findViewById(R.id.rotate_checkbox)); + mVolume = new VolumeController(context, + (ToggleSlider)findViewById(R.id.volume)); + mBrightness = new BrightnessController(context, + (ToggleSlider)findViewById(R.id.brightness)); mDoNotDisturb = new DoNotDisturbController(context, (CompoundButton)findViewById(R.id.do_not_disturb_checkbox)); findViewById(R.id.settings).setOnClickListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java index d1e61a9..698f5af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java @@ -81,7 +81,7 @@ public class ShirtPocket extends FrameLayout { mIcon.setImageResource(mClipping == null ? R.drawable.ic_sysbar_pocket_hidden : R.drawable.ic_sysbar_pocket_holding); - mIcon.setVisibility(mClipping == null ? View.INVISIBLE : View.VISIBLE); + mIcon.setVisibility(mClipping == null ? View.GONE : View.VISIBLE); } private void showWindow() { @@ -169,7 +169,7 @@ public class ShirtPocket extends FrameLayout { thumb = new DragThumbnailBuilder(mWindow.findViewById(R.id.preview)); } - v.startDrag(clip, thumb, false); + v.startDrag(clip, thumb, false, null); // TODO: only discard the clipping if it was accepted stash(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 31311a4..563b8ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -114,6 +114,7 @@ public class TabletStatusBar extends StatusBar { NotificationPeekPanel mNotificationPeekWindow; ViewGroup mNotificationPeekRow; int mNotificationPeekIndex; + IBinder mNotificationPeekKey; LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight; int mNotificationPeekTapDuration; @@ -344,9 +345,11 @@ public class TabletStatusBar extends StatusBar { if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1); if (m.arg1 >= 0) { final int N = mNotns.size(); - if (mNotificationPeekIndex < N) { + if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) { NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex); entry.icon.setBackgroundColor(0); + mNotificationPeekIndex = -1; + mNotificationPeekKey = null; } final int peekIndex = m.arg1; @@ -373,6 +376,7 @@ public class TabletStatusBar extends StatusBar { mNotificationPanel.setVisibility(View.GONE); mNotificationPeekIndex = peekIndex; + mNotificationPeekKey = entry.key; } } break; @@ -381,10 +385,13 @@ public class TabletStatusBar extends StatusBar { mNotificationPeekWindow.setVisibility(View.GONE); mNotificationPeekRow.removeAllViews(); final int N = mNotns.size(); - if (mNotificationPeekIndex < N) { + if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) { NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex); entry.icon.setBackgroundColor(0); } + + mNotificationPeekIndex = -1; + mNotificationPeekKey = null; break; case MSG_OPEN_NOTIFICATION_PANEL: if (DEBUG) Slog.d(TAG, "opening notifications panel"); @@ -647,6 +654,12 @@ public class TabletStatusBar extends StatusBar { // called by StatusBar @Override public void setLightsOn(boolean on) { + // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app + // that can't handle lights-out mode. + if (mMenuButton.getVisibility() == View.VISIBLE + || mMenuShadow.getVisibility() == View.VISIBLE) { + on = true; + } mHandler.removeMessages(MSG_SHOW_SHADOWS); mHandler.removeMessages(MSG_HIDE_SHADOWS); mHandler.sendEmptyMessage(on ? MSG_HIDE_SHADOWS : MSG_SHOW_SHADOWS); @@ -657,6 +670,9 @@ public class TabletStatusBar extends StatusBar { Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button"); } mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE); + + // See above re: lights-out policy for legacy apps. + if (visible) setLightsOn(true); } public void setIMEButtonVisible(IBinder token, boolean visible) { @@ -814,6 +830,11 @@ public class TabletStatusBar extends StatusBar { // Remove the expanded view. ViewGroup rowParent = (ViewGroup)entry.row.getParent(); if (rowParent != null) rowParent.removeView(entry.row); + + if (key == mNotificationPeekKey) { + // must close the peek as well, since it's gone + mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); + } // Remove the icon. // ViewGroup iconParent = (ViewGroup)entry.icon.getParent(); // if (iconParent != null) iconParent.removeView(entry.icon); diff --git a/packages/VpnServices/res/values-id/strings.xml b/packages/VpnServices/res/values-in/strings.xml index 8b6b4c2..8b6b4c2 100644 --- a/packages/VpnServices/res/values-id/strings.xml +++ b/packages/VpnServices/res/values-in/strings.xml diff --git a/packages/VpnServices/res/values-he/strings.xml b/packages/VpnServices/res/values-iw/strings.xml index 74971d6..74971d6 100644 --- a/packages/VpnServices/res/values-he/strings.xml +++ b/packages/VpnServices/res/values-iw/strings.xml diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java index a8dd76c..abed18f 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java +++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java @@ -32,7 +32,8 @@ import android.view.FallbackEventHandler; import android.view.KeyEvent; public class PhoneFallbackEventHandler implements FallbackEventHandler { - static String TAG = "PhoneFallbackEventHandler"; + private static String TAG = "PhoneFallbackEventHandler"; + private static final boolean DEBUG = false; Context mContext; View mView; @@ -180,7 +181,9 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { } boolean onKeyUp(int keyCode, KeyEvent event) { - Slog.d(TAG, "up " + keyCode); + if (DEBUG) { + Slog.d(TAG, "up " + keyCode); + } final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); if (dispatcher != null) { dispatcher.handleUpEvent(event); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index cd8a065..b487d92 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -160,6 +160,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ContextMenuBuilder mContextMenu; private MenuDialogHelper mContextMenuHelper; + private ActionButtonSubmenu mActionButtonPopup; + private boolean mClosingActionMenu; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; @@ -542,6 +544,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (doCallback) { callOnPanelClosed(st.featureId, st, null); } + } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback && + mActionBar != null) { + checkCloseActionMenu(st.menu); } st.isPrepared = false; st.isHandled = false; @@ -563,6 +568,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + private void checkCloseActionMenu(Menu menu) { + if (mClosingActionMenu) { + return; + } + + boolean closed = false; + mClosingActionMenu = true; + if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) { + closed = true; + } + if (mActionButtonPopup != null) { + mActionButtonPopup.dismiss(); + closed = true; + } + Callback cb = getCallback(); + if (cb != null && closed) { + cb.onPanelClosed(FEATURE_ACTION_BAR, menu); + } + mClosingActionMenu = false; + } + @Override public final void togglePanel(int featureId, KeyEvent event) { PanelFeatureState st = getPanelState(featureId, true); @@ -841,8 +867,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (panel.isOpen) { // The window manager will give us a valid window token new MenuDialogHelper(subMenu).show(null); - } else { - new MenuPopupHelper(getContext(), subMenu).show(); + } else if (hasFeature(FEATURE_ACTION_BAR)) { + mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu); + mActionButtonPopup.show(); + Callback cb = getCallback(); + if (cb != null) { + cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); + } } return true; @@ -854,16 +885,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void reopenMenu(boolean toggleMenuMode) { if (mActionBar != null) { + final Callback cb = getCallback(); if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) { - final Callback cb = getCallback(); if (cb != null) { final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { - mActionBar.showOverflowMenu(); + cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); + mActionBar.openOverflowMenu(); } } } else { mActionBar.hideOverflowMenu(); + if (cb != null) { + final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu); + } } return; } @@ -2042,8 +2078,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (cb != null && mFeatureId < 0) { cb.onDetachedFromWindow(); } + + if (mActionButtonPopup != null) { + if (mActionButtonPopup.isShowing()) { + mActionButtonPopup.dismiss(); + } + mActionButtonPopup = null; + } } - + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + if (mActionButtonPopup != null) { + mActionButtonPopup.dismiss(); + post(mActionButtonPopup); + } + } + @Override public void onCloseSystemDialogs(String reason) { if (mFeatureId >= 0) { @@ -2921,4 +2972,29 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { void sendCloseSystemWindows(String reason) { PhoneWindowManager.sendCloseSystemWindows(getContext(), reason); } + + private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable { + private SubMenuBuilder mSubMenu; + + public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) { + super(context, subMenu); + mSubMenu = subMenu; + } + + @Override + public void onDismiss() { + super.onDismiss(); + mSubMenu.getCallback().onCloseSubMenu(mSubMenu); + mActionButtonPopup = null; + } + + @Override + public void run() { + show(); + Callback cb = getCallback(); + if (cb != null) { + cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu); + } + } + } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 1373627..c3112d8 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -1215,11 +1215,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { + public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { final boolean keyguardOn = keyguardOn(); - final boolean down = (action == KeyEvent.ACTION_DOWN); - final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0); + final int keyCode = event.getKeyCode(); + final int repeatCount = event.getRepeatCount(); + final int metaState = event.getMetaState(); + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + final boolean canceled = event.isCanceled(); if (false) { Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" @@ -1348,7 +1350,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Shortcuts are invoked through Search+key, so intercept those here if (mSearchKeyPressed) { if (down && repeatCount == 0 && !keyguardOn) { - Intent shortcutIntent = mShortcutManager.getIntent(keyCode, metaState); + Intent shortcutIntent = mShortcutManager.getIntent(event); if (shortcutIntent != null) { shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(shortcutIntent); @@ -1368,13 +1370,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public boolean dispatchUnhandledKey(WindowState win, int action, int flags, - int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { + public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) { if (false) { - Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + action - + ", flags=" + flags + ", keyCode=" + keyCode - + ", scanCode=" + scanCode + ", metaState=" + metaState - + ", repeatCount=" + repeatCount + ", policyFlags=" + policyFlags); + Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction() + + ", flags=" + event.getFlags() + + ", keyCode=" + event.getKeyCode() + + ", scanCode=" + event.getScanCode() + + ", metaState=" + event.getMetaState() + + ", repeatCount=" + event.getRepeatCount() + + ", policyFlags=" + policyFlags); } return false; } @@ -1816,12 +1820,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { final IStatusBarService sbs = mStatusBarService; if (mStatusBarService != null) { try { - if (changedFullscreen) { - sbs.setActiveWindowIsFullscreen(topIsFullscreenF); - } if (changedMenu) { sbs.setMenuKeyVisible(topNeedsMenuF); } + if (changedFullscreen) { + sbs.setActiveWindowIsFullscreen(topIsFullscreenF); + } } catch (RemoteException e) { // This should be impossible because we're in the same process. mStatusBarService = null; @@ -1970,10 +1974,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, - int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { - final boolean down = action == KeyEvent.ACTION_DOWN; - final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0; + public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + final boolean canceled = event.isCanceled(); + final int keyCode = event.getKeyCode(); final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; @@ -2164,12 +2168,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Only do this if we would otherwise not pass it to the user. In that // case, the PhoneWindow class will do the same thing, except it will // only do it if the showing app doesn't process the key on its own. - long when = whenNanos / 1000000; - KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, scanCode, flags, - InputDevice.SOURCE_KEYBOARD); mBroadcastWakeLock.acquire(); - mHandler.post(new PassHeadsetKey(keyEvent)); + mHandler.post(new PassHeadsetKey(new KeyEvent(event))); } break; } diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java index 51377d8..fc66a20 100644 --- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java +++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java @@ -25,6 +25,7 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; import android.view.KeyCharacterMap; +import android.view.KeyEvent; import java.net.URISyntaxException; @@ -100,20 +101,17 @@ class ShortcutManager extends ContentObserver { * This will first try an exact match (with modifiers), and then try a * match without modifiers (primary character on a key). * - * @param keyCode The keycode of the key pushed. - * @param modifiers The modifiers without any that are used for chording - * to invoke a shortcut. + * @param event The key event of the key that was pressed. * @return The intent that matches the shortcut, or null if not found. */ - public Intent getIntent(int keyCode, int modifiers) { - KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + public Intent getIntent(KeyEvent event) { // First try the exact keycode (with modifiers) - int shortcut = kcm.get(keyCode, modifiers); + int shortcut = event.getUnicodeChar(); Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null; if (intent != null) return intent; - + // Next try the keycode without modifiers (the primary character on that key) - shortcut = Character.toLowerCase(kcm.get(keyCode, 0)); + shortcut = Character.toLowerCase(event.getUnicodeChar(0)); return shortcut != 0 ? mShortcutIntents.get(shortcut) : null; } diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index e3b5db1..175f613 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -81,12 +81,6 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address); // keep track of SCO device address mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); -#ifdef WITH_A2DP - if (mA2dpOutput != 0 && - mPhoneState != AudioSystem::MODE_NORMAL) { - mpClientInterface->suspendOutput(mA2dpOutput); - } -#endif } } break; @@ -115,12 +109,6 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev { if (AudioSystem::isBluetoothScoDevice(device)) { mScoDeviceAddress = ""; -#ifdef WITH_A2DP - if (mA2dpOutput != 0 && - mPhoneState != AudioSystem::MODE_NORMAL) { - mpClientInterface->restoreOutput(mA2dpOutput); - } -#endif } } } break; @@ -138,6 +126,7 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) { closeA2dpOutputs(); } + checkA2dpSuspend(); #endif updateDeviceForStrategy(); setOutputDevice(mHardwareOutput, newDevice); @@ -280,14 +269,7 @@ void AudioPolicyManagerBase::setPhoneState(int state) newDevice = getNewDevice(mHardwareOutput, false); #ifdef WITH_A2DP checkOutputForAllStrategies(); - // suspend A2DP output if a SCO device is present. - if (mA2dpOutput != 0 && mScoDeviceAddress != "") { - if (oldState == AudioSystem::MODE_NORMAL) { - mpClientInterface->suspendOutput(mA2dpOutput); - } else if (state == AudioSystem::MODE_NORMAL) { - mpClientInterface->restoreOutput(mA2dpOutput); - } - } + checkA2dpSuspend(); #endif updateDeviceForStrategy(); @@ -397,6 +379,7 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst uint32_t newDevice = getNewDevice(mHardwareOutput, false); #ifdef WITH_A2DP checkOutputForAllStrategies(); + checkA2dpSuspend(); #endif updateDeviceForStrategy(); setOutputDevice(mHardwareOutput, newDevice); @@ -1025,8 +1008,10 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien #ifdef AUDIO_POLICY_TEST Thread(false), #endif //AUDIO_POLICY_TEST - mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false), - mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0) + mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), + mMusicStopTime(0), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), + mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), + mA2dpSuspended(false) { mpClientInterface = clientInterface; @@ -1322,17 +1307,6 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices } AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); - if (mScoDeviceAddress != "") { - // It is normal to suspend twice if we are both in call, - // and have the hardware audio output routed to BT SCO - if (mPhoneState != AudioSystem::MODE_NORMAL) { - mpClientInterface->suspendOutput(mA2dpOutput); - } - if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) { - mpClientInterface->suspendOutput(mA2dpOutput); - } - } - if (!a2dpUsedForSonification()) { // mute music on A2DP output if a notification or ringtone is playing uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION); @@ -1340,6 +1314,9 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); } } + + mA2dpSuspended = false; + return NO_ERROR; } @@ -1369,6 +1346,7 @@ status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devi } } mA2dpDeviceAddress = ""; + mA2dpSuspended = false; return NO_ERROR; } @@ -1467,6 +1445,48 @@ void AudioPolicyManagerBase::checkOutputForAllStrategies() checkOutputForStrategy(STRATEGY_DTMF); } +void AudioPolicyManagerBase::checkA2dpSuspend() +{ + // suspend A2DP output if: + // (NOT already suspended) && + // ((SCO device is connected && + // (forced usage for communication || for record is SCO))) || + // (phone state is ringing || in call) + // + // restore A2DP output if: + // (Already suspended) && + // ((SCO device is NOT connected || + // (forced usage NOT for communication && NOT for record is SCO))) && + // (phone state is NOT ringing && NOT in call) + // + if (mA2dpOutput == 0) { + return; + } + + if (mA2dpSuspended) { + if (((mScoDeviceAddress == "") || + ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) && + (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) && + ((mPhoneState != AudioSystem::MODE_IN_CALL) && + (mPhoneState != AudioSystem::MODE_RINGTONE))) { + + mpClientInterface->restoreOutput(mA2dpOutput); + mA2dpSuspended = false; + } + } else { + if (((mScoDeviceAddress != "") && + ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || + (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) || + ((mPhoneState == AudioSystem::MODE_IN_CALL) || + (mPhoneState == AudioSystem::MODE_RINGTONE))) { + + mpClientInterface->suspendOutput(mA2dpOutput); + mA2dpSuspended = true; + } + } +} + + #endif uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache) @@ -1714,14 +1734,7 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t // wait for the PCM output buffers to empty before proceeding with the rest of the command usleep(outputDesc->mLatency*2*1000); } -#ifdef WITH_A2DP - // suspend A2DP output if SCO device is selected - if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { - if (mA2dpOutput != 0) { - mpClientInterface->suspendOutput(mA2dpOutput); - } - } -#endif + // do the routing AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyRouting), (int)device); @@ -1729,15 +1742,6 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t // update stream volumes according to new device applyStreamVolumes(output, device, delayMs); -#ifdef WITH_A2DP - // if disconnecting SCO device, restore A2DP output - if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) { - if (mA2dpOutput != 0) { - LOGV("restore A2DP output"); - mpClientInterface->restoreOutput(mA2dpOutput); - } - } -#endif // if changing from a combined headset + speaker route, unmute media streams if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) { setStrategyMute(STRATEGY_MEDIA, false, output, delayMs); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 5c67da7..b5e3888 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -430,6 +430,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY); mSettingsObserver.observe(mContext); + + loadGlobalProxy(); } @@ -2089,7 +2091,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { ContentResolver res = mContext.getContentResolver(); Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host); Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port); - Settings.Secure.putString(res,Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, + Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclList); } @@ -2099,6 +2101,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { sendProxyBroadcast(proxyProperties); } + private void loadGlobalProxy() { + ContentResolver res = mContext.getContentResolver(); + String host = Settings.Secure.getString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST); + int port = Settings.Secure.getInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, 0); + String exclList = Settings.Secure.getString(res, + Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); + if (!TextUtils.isEmpty(host)) { + ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList); + synchronized (mGlobalProxyLock) { + mGlobalProxy = proxyProperties; + } + } + } + public ProxyProperties getGlobalProxy() { synchronized (mGlobalProxyLock) { return mGlobalProxy; diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 4364c04..8634eec 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -30,6 +30,7 @@ import android.util.Xml; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; +import android.view.KeyEvent; import android.view.Surface; import java.io.BufferedReader; @@ -41,7 +42,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Properties; /* * Wraps the C++ InputManager and provides its callbacks. @@ -358,21 +358,6 @@ public class InputManager { } } - private static final class VirtualKeyDefinition { - public int scanCode; - - // configured position data, specified in display coords - public int centerX; - public int centerY; - public int width; - public int height; - } - - private static final class InputDeviceCalibration { - public String[] keys; - public String[] values; - } - /* * Callbacks from native. */ @@ -404,26 +389,23 @@ public class InputManager { } @SuppressWarnings("unused") - public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, - int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { + public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing( - whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn); + event, policyFlags, isScreenOn); } @SuppressWarnings("unused") - public boolean interceptKeyBeforeDispatching(InputChannel focus, int action, - int flags, int keyCode, int scanCode, int metaState, int repeatCount, - int policyFlags) { - return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus, - action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags); + public boolean interceptKeyBeforeDispatching(InputChannel focus, + KeyEvent event, int policyFlags) { + return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching( + focus, event, policyFlags); } @SuppressWarnings("unused") - public boolean dispatchUnhandledKey(InputChannel focus, int action, - int flags, int keyCode, int scanCode, int metaState, int repeatCount, - int policyFlags) { - return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(focus, - action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags); + public boolean dispatchUnhandledKey(InputChannel focus, + KeyEvent event, int policyFlags) { + return mWindowManagerService.mInputMonitor.dispatchUnhandledKey( + focus, event, policyFlags); } @SuppressWarnings("unused") @@ -446,81 +428,6 @@ public class InputManager { } @SuppressWarnings("unused") - public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) { - ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>(); - - try { - FileInputStream fis = new FileInputStream( - "/sys/board_properties/virtualkeys." + deviceName); - InputStreamReader isr = new InputStreamReader(fis); - BufferedReader br = new BufferedReader(isr, 2048); - String str = br.readLine(); - if (str != null) { - String[] it = str.split(":"); - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it); - final int N = it.length-6; - for (int i=0; i<=N; i+=6) { - if (!"0x01".equals(it[i])) { - Slog.w(TAG, "Unknown virtual key type at elem #" - + i + ": " + it[i] + " for device " + deviceName); - continue; - } - try { - VirtualKeyDefinition key = new VirtualKeyDefinition(); - key.scanCode = Integer.parseInt(it[i+1]); - key.centerX = Integer.parseInt(it[i+2]); - key.centerY = Integer.parseInt(it[i+3]); - key.width = Integer.parseInt(it[i+4]); - key.height = Integer.parseInt(it[i+5]); - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key " - + key.scanCode + ": center=" + key.centerX + "," - + key.centerY + " size=" + key.width + "x" - + key.height); - keys.add(key); - } catch (NumberFormatException e) { - Slog.w(TAG, "Bad number in virtual key definition at region " - + i + " in: " + str + " for device " + deviceName, e); - } - } - } - br.close(); - } catch (FileNotFoundException e) { - Slog.i(TAG, "No virtual keys found for device " + deviceName + "."); - } catch (IOException e) { - Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e); - } - - return keys.toArray(new VirtualKeyDefinition[keys.size()]); - } - - @SuppressWarnings("unused") - public InputDeviceCalibration getInputDeviceCalibration(String deviceName) { - // Calibration is specified as a sequence of colon-delimited key value pairs. - Properties properties = new Properties(); - File calibrationFile = new File(Environment.getRootDirectory(), - CALIBRATION_DIR_PATH + deviceName + ".idc"); - if (calibrationFile.exists()) { - try { - FileInputStream fis = new FileInputStream(calibrationFile); - properties.load(fis); - fis.close(); - } catch (IOException ex) { - Slog.w(TAG, "Error reading input device calibration properties for device " - + deviceName + " from " + calibrationFile + ".", ex); - } - } else { - Slog.i(TAG, "No input device calibration properties found for device " - + deviceName + "."); - return null; - } - - InputDeviceCalibration calibration = new InputDeviceCalibration(); - calibration.keys = properties.keySet().toArray(new String[properties.size()]); - calibration.values = properties.values().toArray(new String[properties.size()]); - return calibration; - } - - @SuppressWarnings("unused") public String[] getExcludedDeviceNames() { ArrayList<String> names = new ArrayList<String>(); diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 6de7e6a..7101bb0 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -41,7 +41,6 @@ import android.database.ContentObserver; import android.hardware.Usb; import android.media.AudioManager; import android.net.Uri; -import android.os.BatteryManager; import android.os.Bundle; import android.os.Binder; import android.os.Handler; @@ -61,7 +60,6 @@ import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import android.widget.Toast; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -92,7 +90,6 @@ public class NotificationManagerService extends INotificationManager.Stub private WorkerHandler mHandler; private StatusBarManagerService mStatusBar; - private LightsService mLightsService; private LightsService.Light mNotificationLight; private LightsService.Light mAttentionLight; @@ -440,7 +437,7 @@ public class NotificationManagerService extends INotificationManager.Stub mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; } - // register for battery changed notifications + // register for various Intents IntentFilter filter = new IntentFilter(); filter.addAction(Usb.ACTION_USB_STATE); filter.addAction(Intent.ACTION_SCREEN_ON); diff --git a/services/java/com/android/server/SamplingProfilerService.java b/services/java/com/android/server/SamplingProfilerService.java index 26af7f7..61267d0 100644 --- a/services/java/com/android/server/SamplingProfilerService.java +++ b/services/java/com/android/server/SamplingProfilerService.java @@ -88,7 +88,7 @@ public class SamplingProfilerService extends Binder { private void registerSettingObserver(Context context) { ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_HZ), + Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_MS), false, new SamplingProfilerSettingsObserver(contentResolver)); } @@ -107,12 +107,11 @@ public class SamplingProfilerService extends Binder { } @Override public void onChange(boolean selfChange) { - Integer samplingProfilerHz = Settings.Secure.getInt( - mContentResolver, Settings.Secure.SAMPLING_PROFILER_HZ, 0); + Integer samplingProfilerMs = Settings.Secure.getInt( + mContentResolver, Settings.Secure.SAMPLING_PROFILER_MS, 0); // setting this secure property will start or stop sampling profiler, - // as well as adjust the frequency of taking snapshots. - SystemProperties.set("persist.sys.profiler_hz", samplingProfilerHz.toString()); + // as well as adjust the the time between taking snapshots. + SystemProperties.set("persist.sys.profiler_ms", samplingProfilerMs.toString()); } } } - diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java index 1cc6a2a..a95a6c7 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -122,7 +122,9 @@ class ScreenRotationAnimation { mSurface.unlockCanvasAndPost(c); Surface.closeTransaction(); - screenshot.recycle(); + if (screenshot != null) { + screenshot.recycle(); + } } static int deltaRotation(int oldRotation, int newRotation) { diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index d523fa8..7f81a25 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -197,7 +197,7 @@ public class WifiService extends IWifiManager.Stub { WifiServiceHandler(android.os.Looper looper, Context context) { super(looper); mWshChannel = new AsyncChannel(); - mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0); + mWshChannel.connect(context, this, mWifiStateMachine.getHandler()); } @Override diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index cbb35c6..7504bb4 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -619,7 +619,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mDragInProgress && newWin.isPotentialDragTarget()) { DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, - desc, null, false); + null, desc, null, false); try { newWin.mClient.dispatchDragEvent(event); // track each window that we've notified that the drag is starting @@ -659,7 +659,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.d(TAG, "broadcasting DRAG_ENDED"); } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - 0, 0, null, null, mDragResult); + 0, 0, null, null, null, mDragResult); for (WindowState ws: mNotifiedWindows) { try { ws.mClient.dispatchDragEvent(evt); @@ -711,7 +711,7 @@ public class WindowManagerService extends IWindowManager.Stub // force DRAG_EXITED_EVENT if appropriate DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, - null, null, false); + null, null, null, false); mTargetWindow.mClient.dispatchDragEvent(evt); if (myPid != mTargetWindow.mSession.mPid) { evt.recycle(); @@ -723,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, false); + null, null, null, false); touchedWin.mClient.dispatchDragEvent(evt); if (myPid != touchedWin.mSession.mPid) { evt.recycle(); @@ -754,7 +754,7 @@ public class WindowManagerService extends IWindowManager.Stub final IBinder token = touchedWin.mClient.asBinder(); DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, mData, false); + null, null, mData, false); try { touchedWin.mClient.dispatchDragEvent(evt); @@ -5829,30 +5829,25 @@ public class WindowManagerService extends IWindowManager.Stub /* Provides an opportunity for the window manager policy to intercept early key * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, - int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { - return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags, - keyCode, scanCode, policyFlags, isScreenOn); + public int interceptKeyBeforeQueueing( + KeyEvent event, int policyFlags, boolean isScreenOn) { + return mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); } /* Provides an opportunity for the window manager policy to process a key before * ordinary dispatch. */ - public boolean interceptKeyBeforeDispatching(InputChannel focus, - int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount, - int policyFlags) { + public boolean interceptKeyBeforeDispatching( + InputChannel focus, KeyEvent event, int policyFlags) { WindowState windowState = getWindowStateForInputChannel(focus); - return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags, - keyCode, scanCode, metaState, repeatCount, policyFlags); + return mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); } /* Provides an opportunity for the window manager policy to process a key that * the application did not handle. */ - public boolean dispatchUnhandledKey(InputChannel focus, - int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount, - int policyFlags) { + public boolean dispatchUnhandledKey( + InputChannel focus, KeyEvent event, int policyFlags) { WindowState windowState = getWindowStateForInputChannel(focus); - return mPolicy.dispatchUnhandledKey(windowState, action, flags, - keyCode, scanCode, metaState, repeatCount, policyFlags); + return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); } /* Called when the current input focus changes. @@ -6010,6 +6005,7 @@ public class WindowManagerService extends IWindowManager.Stub int deviceId = ev.getDeviceId(); int scancode = ev.getScanCode(); int source = ev.getSource(); + int flags = ev.getFlags(); if (source == InputDevice.SOURCE_UNKNOWN) { source = InputDevice.SOURCE_KEYBOARD; @@ -6019,7 +6015,7 @@ public class WindowManagerService extends IWindowManager.Stub if (downTime == 0) downTime = eventTime; KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState, - deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source); + deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -6853,6 +6849,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (!mParentFrame.equals(pf)) { + //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame + // + " to " + pf); mParentFrame.set(pf); mContentChanged = true; } @@ -7739,12 +7737,10 @@ public class WindowManagerService extends IWindowManager.Stub * sense to call from performLayoutAndPlaceSurfacesLockedInner().) */ boolean shouldAnimateMove() { - return mContentChanged && !mAnimating && !mLastHidden && !mDisplayFrozen + return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left) - && (mAttachedWindow == null - || (mAttachedWindow.mAnimation == null - && !mAttachedWindow.shouldAnimateMove())) + && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()) && mPolicy.isScreenOn(); } @@ -9228,6 +9224,7 @@ public class WindowManagerService extends IWindowManager.Stub if (!gone || !win.mHaveFrame) { if (!win.mLayoutAttached) { if (initial) { + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } mPolicy.layoutWindowLw(win, win.mAttrs, null); @@ -9262,6 +9259,7 @@ public class WindowManagerService extends IWindowManager.Stub if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) || !win.mHaveFrame) { if (initial) { + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); @@ -9460,7 +9458,6 @@ public class WindowManagerService extends IWindowManager.Stub w.setAnimation(a); animDw = w.mLastFrame.left - w.mFrame.left; animDh = w.mLastFrame.top - w.mFrame.top; - w.mContentChanged = false; } // Execute animation. @@ -10247,6 +10244,11 @@ public class WindowManagerService extends IWindowManager.Stub w.mOrientationChanging = false; } + if (w.mContentChanged) { + //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing"); + w.mContentChanged = false; + } + final boolean canBeSeen = w.isDisplayedLw(); if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 3fd6965..1996dd0 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -56,8 +56,6 @@ static struct { jmethodID checkInjectEventsPermission; jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; - jmethodID getVirtualKeyDefinitions; - jmethodID getInputDeviceCalibration; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; @@ -65,23 +63,6 @@ static struct { static struct { jclass clazz; - jfieldID scanCode; - jfieldID centerX; - jfieldID centerY; - jfieldID width; - jfieldID height; -} gVirtualKeyDefinitionClassInfo; - -static struct { - jclass clazz; - - jfieldID keys; - jfieldID values; -} gInputDeviceCalibrationClassInfo; - -static struct { - jclass clazz; - jfieldID inputChannel; jfieldID name; jfieldID layoutParamsFlags; @@ -184,10 +165,6 @@ public: int32_t* width, int32_t* height, int32_t* orientation); virtual bool filterTouchEvents(); virtual bool filterJumpyTouchEvents(); - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions); - virtual void getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration); virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -201,9 +178,7 @@ public: virtual nsecs_t getKeyRepeatTimeout(); virtual nsecs_t getKeyRepeatDelay(); virtual int32_t getMaxEventsPerSecond(); - virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId, - int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode, - uint32_t& policyFlags); + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags); virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags); virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, const KeyEvent* keyEvent, uint32_t policyFlags); @@ -454,83 +429,6 @@ bool NativeInputManager::filterJumpyTouchEvents() { return mFilterJumpyTouchEvents; } -void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) { - outVirtualKeyDefinitions.clear(); - - JNIEnv* env = jniEnv(); - - jstring deviceNameStr = env->NewStringUTF(deviceName.string()); - if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); - if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - - outVirtualKeyDefinitions.add(); - outVirtualKeyDefinitions.editTop().scanCode = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode)); - outVirtualKeyDefinitions.editTop().centerX = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX)); - outVirtualKeyDefinitions.editTop().centerY = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY)); - outVirtualKeyDefinitions.editTop().width = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width)); - outVirtualKeyDefinitions.editTop().height = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height)); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); - } - env->DeleteLocalRef(deviceNameStr); - } -} - -void NativeInputManager::getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration) { - outCalibration.clear(); - - JNIEnv* env = jniEnv(); - - jstring deviceNameStr = env->NewStringUTF(deviceName.string()); - if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration")) { - jobject result = env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getInputDeviceCalibration, deviceNameStr); - if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration") && result) { - jobjectArray keys = jobjectArray(env->GetObjectField(result, - gInputDeviceCalibrationClassInfo.keys)); - jobjectArray values = jobjectArray(env->GetObjectField(result, - gInputDeviceCalibrationClassInfo.values)); - - jsize length = env->GetArrayLength(keys); - for (jsize i = 0; i < length; i++) { - jstring keyStr = jstring(env->GetObjectArrayElement(keys, i)); - jstring valueStr = jstring(env->GetObjectArrayElement(values, i)); - - const char* keyChars = env->GetStringUTFChars(keyStr, NULL); - String8 key(keyChars); - env->ReleaseStringUTFChars(keyStr, keyChars); - - const char* valueChars = env->GetStringUTFChars(valueStr, NULL); - String8 value(valueChars); - env->ReleaseStringUTFChars(valueStr, valueChars); - - outCalibration.addProperty(key, value); - - env->DeleteLocalRef(keyStr); - env->DeleteLocalRef(valueStr); - } - env->DeleteLocalRef(keys); - env->DeleteLocalRef(values); - env->DeleteLocalRef(result); - } - env->DeleteLocalRef(deviceNameStr); - } -} - void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { outExcludedDeviceNames.clear(); @@ -770,6 +668,7 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj, outWindow.ownerUid = ownerUid; env->ReleaseStringUTFChars(name, nameStr); + env->DeleteLocalRef(name); valid = true; } else { LOGW("Dropping input target because its input channel is not initialized."); @@ -831,20 +730,8 @@ bool NativeInputManager::isScreenBright() { return android_server_PowerManagerService_isScreenBright(); } -void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, - int32_t deviceId, int32_t action, int32_t &flags, - int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, " - "keyCode=%d, scanCode=%d, policyFlags=0x%x", - when, deviceId, action, flags, keyCode, scanCode, policyFlags); -#endif - - if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { - policyFlags |= POLICY_FLAG_VIRTUAL; - flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; - } - +void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, + uint32_t& policyFlags) { // Policy: // - Ignore untrusted events and pass them along. // - Ask the window manager what to do with normal events and trusted injected events. @@ -854,21 +741,30 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; const int32_t WM_ACTION_GO_TO_SLEEP = 4; + nsecs_t when = keyEvent->getEventTime(); bool isScreenOn = this->isScreenOn(); bool isScreenBright = this->isScreenBright(); JNIEnv* env = jniEnv(); - jint wmActions = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.interceptKeyBeforeQueueing, - when, action, flags, keyCode, scanCode, policyFlags, isScreenOn); - if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + jint wmActions; + if (keyEventObj) { + wmActions = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeQueueing, + keyEventObj, policyFlags, isScreenOn); + if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { + wmActions = 0; + } + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + } else { + LOGE("Failed to obtain key event object for interceptKeyBeforeQueueing."); wmActions = 0; } - if (!(flags & POLICY_FLAG_INJECTED)) { + if (!(policyFlags & POLICY_FLAG_INJECTED)) { if (!isScreenOn) { policyFlags |= POLICY_FLAG_WOKE_HERE; - flags |= AKEY_EVENT_FLAG_WOKE_HERE; } if (!isScreenBright) { @@ -893,10 +789,6 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, } void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags); -#endif - // Policy: // - Ignore untrusted events and pass them along. // - No special filtering for injected events required at this time. @@ -921,46 +813,62 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i // - Ignore untrusted events and pass them along. // - Filter normal events and trusted injected events through the window manager policy to // handle the HOME key and the like. + bool result; if (policyFlags & POLICY_FLAG_TRUSTED) { JNIEnv* env = jniEnv(); // Note: inputChannel may be null. jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel); - jboolean consumed = env->CallBooleanMethod(mCallbacksObj, - gCallbacksClassInfo.interceptKeyBeforeDispatching, - inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), - keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), - keyEvent->getRepeatCount(), policyFlags); - bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + if (keyEventObj) { + jboolean consumed = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeDispatching, + inputChannelObj, keyEventObj, policyFlags); + bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + result = consumed && !error; + } else { + LOGE("Failed to obtain key event object for interceptKeyBeforeDispatching."); + result = false; + } env->DeleteLocalRef(inputChannelObj); - return consumed && ! error; } else { - return false; + result = false; } + return result; } bool NativeInputManager::dispatchUnhandledKey(const sp<InputChannel>& inputChannel, const KeyEvent* keyEvent, uint32_t policyFlags) { // Policy: // - Ignore untrusted events and do not perform default handling. + bool result; if (policyFlags & POLICY_FLAG_TRUSTED) { JNIEnv* env = jniEnv(); // Note: inputChannel may be null. jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel); - jboolean handled = env->CallBooleanMethod(mCallbacksObj, - gCallbacksClassInfo.dispatchUnhandledKey, - inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), - keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), - keyEvent->getRepeatCount(), policyFlags); - bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey"); + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + if (keyEventObj) { + jboolean handled = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.dispatchUnhandledKey, + inputChannelObj, keyEventObj, policyFlags); + bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey"); + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + result = handled && !error; + } else { + LOGE("Failed to obtain key event object for dispatchUnhandledKey."); + result = false; + } env->DeleteLocalRef(inputChannelObj); - return handled && ! error; } else { - return false; + result = false; } + return result; } void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { @@ -1159,13 +1067,21 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { KeyEvent keyEvent; - android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); + status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); + if (status) { + jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); + return INPUT_EVENT_INJECTION_FAILED; + } return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { MotionEvent motionEvent; - android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); + status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); + if (status) { + jniThrowRuntimeException(env, "Could not read contents of MotionEvent object."); + return INPUT_EVENT_INJECTION_FAILED; + } return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); @@ -1384,13 +1300,14 @@ int register_android_server_InputManager(JNIEnv* env) { "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, - "interceptKeyBeforeQueueing", "(JIIIIIZ)I"); + "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, - "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z"); + "interceptKeyBeforeDispatching", + "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z"); GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz, - "dispatchUnhandledKey", "(Landroid/view/InputChannel;IIIIIII)Z"); + "dispatchUnhandledKey", "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); @@ -1401,51 +1318,12 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz, "filterJumpyTouchEvents", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz, - "getVirtualKeyDefinitions", - "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;"); - - GET_METHOD_ID(gCallbacksClassInfo.getInputDeviceCalibration, gCallbacksClassInfo.clazz, - "getInputDeviceCalibration", - "(Ljava/lang/String;)Lcom/android/server/InputManager$InputDeviceCalibration;"); - GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz, "getMaxEventsPerSecond", "()I"); - // VirtualKeyDefinition - - FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz, - "com/android/server/InputManager$VirtualKeyDefinition"); - - GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz, - "scanCode", "I"); - - GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz, - "centerX", "I"); - - GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz, - "centerY", "I"); - - GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz, - "width", "I"); - - GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, - "height", "I"); - - // InputDeviceCalibration - - FIND_CLASS(gInputDeviceCalibrationClassInfo.clazz, - "com/android/server/InputManager$InputDeviceCalibration"); - - GET_FIELD_ID(gInputDeviceCalibrationClassInfo.keys, gInputDeviceCalibrationClassInfo.clazz, - "keys", "[Ljava/lang/String;"); - - GET_FIELD_ID(gInputDeviceCalibrationClassInfo.values, gInputDeviceCalibrationClassInfo.clazz, - "values", "[Ljava/lang/String;"); - // InputWindow FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow"); diff --git a/services/sensorservice/GravitySensor.cpp b/services/sensorservice/GravitySensor.cpp index 18bd359..5c6aa99 100644 --- a/services/sensorservice/GravitySensor.cpp +++ b/services/sensorservice/GravitySensor.cpp @@ -29,8 +29,8 @@ namespace android { GravitySensor::GravitySensor(sensor_t const* list, size_t count) : mSensorDevice(SensorDevice::getInstance()), - mEnabled(false), mAccTime(0), - mLowPass(M_SQRT1_2, 1), + mAccTime(0), + mLowPass(M_SQRT1_2, 1.5f), mX(mLowPass), mY(mLowPass), mZ(mLowPass) { @@ -71,15 +71,9 @@ bool GravitySensor::process(sensors_event_t* outEvent, } return false; } - -bool GravitySensor::isEnabled() const { - return mEnabled; -} - status_t GravitySensor::activate(void* ident, bool enabled) { status_t err = mSensorDevice.activate(this, mAccelerometer.getHandle(), enabled); if (err == NO_ERROR) { - mEnabled = enabled; if (enabled) { mAccTime = 0; } diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h index f9850b7..decfbb8 100644 --- a/services/sensorservice/GravitySensor.h +++ b/services/sensorservice/GravitySensor.h @@ -33,17 +33,15 @@ namespace android { class GravitySensor : public SensorInterface { SensorDevice& mSensorDevice; Sensor mAccelerometer; - bool mEnabled; double mAccTime; SecondOrderLowPassFilter mLowPass; - BiquadFilter mX, mY, mZ; + CascadedBiquadFilter mX, mY, mZ; public: GravitySensor(sensor_t const* list, size_t count); virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event); - virtual bool isEnabled() const; virtual status_t activate(void* ident, bool enabled); virtual status_t setDelay(void* ident, int handle, int64_t ns); virtual Sensor getSensor() const; diff --git a/services/sensorservice/LinearAccelerationSensor.cpp b/services/sensorservice/LinearAccelerationSensor.cpp index 2dc12dc..9425a92 100644 --- a/services/sensorservice/LinearAccelerationSensor.cpp +++ b/services/sensorservice/LinearAccelerationSensor.cpp @@ -53,10 +53,6 @@ bool LinearAccelerationSensor::process(sensors_event_t* outEvent, return result; } -bool LinearAccelerationSensor::isEnabled() const { - return mGravitySensor.isEnabled(); -} - status_t LinearAccelerationSensor::activate(void* ident, bool enabled) { return mGravitySensor.activate(ident, enabled); } diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h index ee918ce..c577086 100644 --- a/services/sensorservice/LinearAccelerationSensor.h +++ b/services/sensorservice/LinearAccelerationSensor.h @@ -40,7 +40,6 @@ class LinearAccelerationSensor : public SensorInterface { const sensors_event_t& event); public: LinearAccelerationSensor(sensor_t const* list, size_t count); - virtual bool isEnabled() const; virtual status_t activate(void* ident, bool enabled); virtual status_t setDelay(void* ident, int handle, int64_t ns); virtual Sensor getSensor() const; diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp index 6f4b8be..418e7f8 100644 --- a/services/sensorservice/RotationVectorSensor.cpp +++ b/services/sensorservice/RotationVectorSensor.cpp @@ -34,7 +34,6 @@ static inline T clamp(T v) { RotationVectorSensor::RotationVectorSensor(sensor_t const* list, size_t count) : mSensorDevice(SensorDevice::getInstance()), - mEnabled(false), mALowPass(M_SQRT1_2, 5.0f), mAX(mALowPass), mAY(mALowPass), mAZ(mALowPass), mMLowPass(M_SQRT1_2, 2.5f), @@ -114,15 +113,18 @@ bool RotationVectorSensor::process(sensors_event_t* outEvent, float qx = sqrtf( clamp( Hx - My - Az + 1) * 0.25f ); float qy = sqrtf( clamp(-Hx + My - Az + 1) * 0.25f ); float qz = sqrtf( clamp(-Hx - My + Az + 1) * 0.25f ); - const float n = 1.0f / (qw*qw + qx*qx + qy*qy + qz*qz); - qx = copysignf(qx, Ay - Mz) * n; - qy = copysignf(qy, Hz - Ax) * n; - qz = copysignf(qz, Mx - Hy) * n; + qx = copysignf(qx, Ay - Mz); + qy = copysignf(qy, Hz - Ax); + qz = copysignf(qz, Mx - Hy); + + // this quaternion is guaranteed to be normalized, by construction + // of the rotation matrix. *outEvent = event; outEvent->data[0] = qx; outEvent->data[1] = qy; outEvent->data[2] = qz; + outEvent->data[3] = qw; outEvent->sensor = '_rov'; outEvent->type = SENSOR_TYPE_ROTATION_VECTOR; return true; @@ -130,19 +132,12 @@ bool RotationVectorSensor::process(sensors_event_t* outEvent, return false; } -bool RotationVectorSensor::isEnabled() const { - return mEnabled; -} - status_t RotationVectorSensor::activate(void* ident, bool enabled) { - if (mEnabled != enabled) { - mSensorDevice.activate(this, mAcc.getHandle(), enabled); - mSensorDevice.activate(this, mMag.getHandle(), enabled); - mEnabled = enabled; - if (enabled) { - mMagTime = 0; - mAccTime = 0; - } + mSensorDevice.activate(this, mAcc.getHandle(), enabled); + mSensorDevice.activate(this, mMag.getHandle(), enabled); + if (enabled) { + mMagTime = 0; + mAccTime = 0; } return NO_ERROR; } diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h index e7f28c9..b7c9512 100644 --- a/services/sensorservice/RotationVectorSensor.h +++ b/services/sensorservice/RotationVectorSensor.h @@ -34,7 +34,6 @@ class RotationVectorSensor : public SensorInterface { SensorDevice& mSensorDevice; Sensor mAcc; Sensor mMag; - bool mEnabled; float mMagData[3]; double mAccTime; double mMagTime; @@ -47,7 +46,6 @@ public: RotationVectorSensor(sensor_t const* list, size_t count); virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event); - virtual bool isEnabled() const; virtual status_t activate(void* ident, bool enabled); virtual status_t setDelay(void* ident, int handle, int64_t ns); virtual Sensor getSensor() const; diff --git a/services/sensorservice/SecondOrderLowPassFilter.cpp b/services/sensorservice/SecondOrderLowPassFilter.cpp index e13e136..eeb6d1e 100644 --- a/services/sensorservice/SecondOrderLowPassFilter.cpp +++ b/services/sensorservice/SecondOrderLowPassFilter.cpp @@ -67,4 +67,23 @@ float BiquadFilter::operator()(float x) } // --------------------------------------------------------------------------- + +CascadedBiquadFilter::CascadedBiquadFilter(const SecondOrderLowPassFilter& s) + : mA(s), mB(s) +{ +} + +float CascadedBiquadFilter::init(float x) +{ + mA.init(x); + mB.init(x); + return x; +} + +float CascadedBiquadFilter::operator()(float x) +{ + return mB(mA(x)); +} + +// --------------------------------------------------------------------------- }; // namespace android diff --git a/services/sensorservice/SecondOrderLowPassFilter.h b/services/sensorservice/SecondOrderLowPassFilter.h index 998ca35..85698ca 100644 --- a/services/sensorservice/SecondOrderLowPassFilter.h +++ b/services/sensorservice/SecondOrderLowPassFilter.h @@ -54,6 +54,18 @@ public: float operator()(float in); }; +/* + * Two cascaded biquad IIR filters + * (4-poles IIR) + */ +class CascadedBiquadFilter { + BiquadFilter mA; + BiquadFilter mB; +public: + CascadedBiquadFilter(const SecondOrderLowPassFilter& s); + float init(float in); + float operator()(float in); +}; // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 73f85ba..f192913 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -137,9 +137,8 @@ void SensorDevice::dump(String8& result, char* buffer, size_t SIZE) Mutex::Autolock _l(mLock); for (size_t i=0 ; i<size_t(count) ; i++) { - snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d / %d\n", + snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d\n", list[i].handle, - mActivationCount.valueFor(list[i].handle).count, mActivationCount.valueFor(list[i].handle).rates.size()); result.append(buffer); } @@ -167,22 +166,25 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled) bool actuateHardware = false; Info& info( mActivationCount.editValueFor(handle) ); - int32_t& count(info.count); if (enabled) { - if (android_atomic_inc(&count) == 0) { - actuateHardware = true; - } Mutex::Autolock _l(mLock); if (info.rates.indexOfKey(ident) < 0) { info.rates.add(ident, DEFAULT_EVENTS_PERIOD); - } - } else { - if (android_atomic_dec(&count) == 1) { actuateHardware = true; + } else { + // sensor was already activated for this ident } + } else { Mutex::Autolock _l(mLock); - info.rates.removeItem(ident); + if (info.rates.removeItem(ident) >= 0) { + if (info.rates.size() == 0) { + actuateHardware = true; + } + } else { + // sensor wasn't enabled for this ident + } } + if (actuateHardware) { err = mSensorDevice->activate(mSensorDevice, handle, enabled); if (enabled) { diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 63ecbcd..c19b2ce 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -40,8 +40,7 @@ class SensorDevice : public Singleton<SensorDevice> { Mutex mLock; // protect mActivationCount[].rates // fixed-size array after construction struct Info { - Info() : count(0) { } - int32_t count; + Info() { } KeyedVector<void*, nsecs_t> rates; }; DefaultKeyedVector<int, Info> mActivationCount; diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp index 93d23d9..be8eaff 100644 --- a/services/sensorservice/SensorInterface.cpp +++ b/services/sensorservice/SensorInterface.cpp @@ -32,7 +32,7 @@ SensorInterface::~SensorInterface() HardwareSensor::HardwareSensor(const sensor_t& sensor) : mSensorDevice(SensorDevice::getInstance()), - mSensor(&sensor), mEnabled(false) + mSensor(&sensor) { LOGI("%s", sensor.name); } @@ -46,15 +46,8 @@ bool HardwareSensor::process(sensors_event_t* outEvent, return true; } -bool HardwareSensor::isEnabled() const { - return mEnabled; -} - -status_t HardwareSensor::activate(void* ident,bool enabled) { - status_t err = mSensorDevice.activate(ident, mSensor.getHandle(), enabled); - if (err == NO_ERROR) - mEnabled = enabled; - return err; +status_t HardwareSensor::activate(void* ident, bool enabled) { + return mSensorDevice.activate(ident, mSensor.getHandle(), enabled); } status_t HardwareSensor::setDelay(void* ident, int handle, int64_t ns) { diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h index eebd563..084f2f5 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -38,7 +38,6 @@ public: virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) = 0; - virtual bool isEnabled() const = 0; virtual status_t activate(void* ident, bool enabled) = 0; virtual status_t setDelay(void* ident, int handle, int64_t ns) = 0; virtual Sensor getSensor() const = 0; @@ -51,7 +50,6 @@ class HardwareSensor : public SensorInterface { SensorDevice& mSensorDevice; Sensor mSensor; - bool mEnabled; public: HardwareSensor(const sensor_t& sensor); @@ -61,7 +59,6 @@ public: virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event); - virtual bool isEnabled() const; virtual status_t activate(void* ident, bool enabled); virtual status_t setDelay(void* ident, int handle, int64_t ns); virtual Sensor getSensor() const; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 7be58c6..145618e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -231,7 +231,7 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) { hwcl->handle = NULL; return; } - hwcl->handle = const_cast<native_handle_t*>(buffer->handle); + hwcl->handle = buffer->handle; // TODO: set the crop value properly hwcl->sourceCrop.left = 0; hwcl->sourceCrop.top = 0; diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 5b49305..719e5b4 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -700,6 +700,10 @@ public final class CallManager { Log.d(LOG_TAG, this.toString()); } + if (!canDial(phone)) { + throw new CallStateException("cannot dial in current state"); + } + if ( hasActiveFgCall() ) { Phone activePhone = getActiveFgCall().getPhone(); boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle()); @@ -753,6 +757,32 @@ public final class CallManager { } /** + * Phone can make a call only if ALL of the following are true: + * - Phone is not powered off + * - There's no incoming or waiting call + * - There's available call slot in either foreground or background + * - The foreground call is ACTIVE or IDLE or DISCONNECTED. + * (We mainly need to make sure it *isn't* DIALING or ALERTING.) + * @param phone + * @return true if the phone can make a new call + */ + private boolean canDial(Phone phone) { + int serviceState = phone.getServiceState().getState(); + boolean hasRingingCall = hasActiveRingingCall(); + boolean hasActiveCall = hasActiveFgCall(); + boolean hasHoldingCall = hasActiveBgCall(); + boolean allLinesTaken = hasActiveCall && hasHoldingCall; + Call.State fgCallState = getActiveFgCallState(); + + return (serviceState != ServiceState.STATE_POWER_OFF + && !hasRingingCall + && !allLinesTaken + && ((fgCallState == Call.State.ACTIVE) + || (fgCallState == Call.State.IDLE) + || (fgCallState == Call.State.DISCONNECTED))); + } + + /** * Whether or not the phone can do explicit call transfer in the current * phone state--that is, one call holding and one call active. * @return true if the phone can do explicit call transfer; false otherwise. diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index 82fcb6a..014901d 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -20,12 +20,13 @@ import android.content.Context; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.provider.ContactsContract.PhoneLookup; import android.provider.ContactsContract.CommonDataKinds.Phone; -import static android.provider.ContactsContract.RawContacts; -import android.text.TextUtils; -import android.telephony.TelephonyManager; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.PhoneLookup; +import android.provider.ContactsContract.RawContacts; import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Log; @@ -171,33 +172,17 @@ public class CallerInfo { } } - // Look for the person ID. - - // TODO: This is pretty ugly now, see bug 2269240 for - // more details. The column to use depends upon the type of URL, - // for content://com.android.contacts/data/phones the "contact_id" - // column is used. For content/com.andriod.contacts/phone_lookup" - // the "_ID" column is used. If it is neither we leave columnIndex - // at -1 and no person ID will be available. - - columnIndex = -1; - String url = contactRef.toString(); - if (url.startsWith("content://com.android.contacts/data/phones")) { - if (VDBG) Log.v(TAG, - "URL path starts with 'data/phones' using RawContacts.CONTACT_ID"); - columnIndex = cursor.getColumnIndex(RawContacts.CONTACT_ID); - } else if (url.startsWith("content://com.android.contacts/phone_lookup")) { - if (VDBG) Log.v(TAG, - "URL path starts with 'phone_lookup' using PhoneLookup._ID"); - columnIndex = cursor.getColumnIndex(PhoneLookup._ID); - } else { - Log.e(TAG, "Bad contact URL '" + url + "'"); - } - + // Look for the person_id. + columnIndex = getColumnIndexForPersonId(contactRef, cursor); if (columnIndex != -1) { info.person_id = cursor.getLong(columnIndex); + if (VDBG) Log.v(TAG, "==> got info.person_id: " + info.person_id); } else { - Log.e(TAG, "person_id column missing for " + contactRef); + // No valid columnIndex, so we can't look up person_id. + Log.w(TAG, "Couldn't find person_id column for " + contactRef); + // Watch out: this means that anything that depends on + // person_id will be broken (like contact photo lookups in + // the in-call UI, for example.) } // look for the custom ringtone, create from the string stored @@ -411,30 +396,120 @@ public class CallerInfo { } /** + * Returns the column index to use to find the "person_id" field in + * the specified cursor, based on the contact URI that was originally + * queried. + * + * This is a helper function for the getCallerInfo() method that takes + * a Cursor. Looking up the person_id is nontrivial (compared to all + * the other CallerInfo fields) since the column we need to use + * depends on what query we originally ran. + * + * Watch out: be sure to not do any database access in this method, since + * it's run from the UI thread (see comments below for more info.) + * + * @return the columnIndex to use (with cursor.getLong()) to get the + * person_id, or -1 if we couldn't figure out what colum to use. + * + * TODO: Add a unittest for this method. (This is a little tricky to + * test, since we'll need a live contacts database to test against, + * preloaded with at least some phone numbers and SIP addresses. And + * we'll probably have to hardcode the column indexes we expect, so + * the test might break whenever the contacts schema changes. But we + * can at least make sure we handle all the URI patterns we claim to, + * and that the mime types match what we expect...) + */ + private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) { + // TODO: This is pretty ugly now, see bug 2269240 for + // more details. The column to use depends upon the type of URL: + // - content://com.android.contacts/data/phones ==> use the "contact_id" column + // - content://com.android.contacts/phone_lookup ==> use the "_ID" column + // - content://com.android.contacts/data ==> use the "contact_id" column + // If it's none of the above, we leave columnIndex=-1 which means + // that the person_id field will be left unset. + // + // The logic here *used* to be based on the mime type of contactRef + // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the + // RawContacts.CONTACT_ID column). But looking up the mime type requires + // a call to context.getContentResolver().getType(contactRef), which + // isn't safe to do from the UI thread since it can cause an ANR if + // the contacts provider is slow or blocked (like during a sync.) + // + // So instead, figure out the column to use for person_id by just + // looking at the URI itself. + + if (VDBG) Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '" + + contactRef + "'..."); + // Warning: Do not enable the following logging (due to ANR risk.) + // if (VDBG) Log.v(TAG, "- MIME type: " + // + context.getContentResolver().getType(contactRef)); + + String url = contactRef.toString(); + String columnName = null; + if (url.startsWith("content://com.android.contacts/data/phones")) { + // Direct lookup in the Phone table. + // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2") + if (VDBG) Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID"); + columnName = RawContacts.CONTACT_ID; + } else if (url.startsWith("content://com.android.contacts/data")) { + // Direct lookup in the Data table. + // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data") + if (VDBG) Log.v(TAG, "'data' URI; using Data.CONTACT_ID"); + // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.) + columnName = Data.CONTACT_ID; + } else if (url.startsWith("content://com.android.contacts/phone_lookup")) { + // Lookup in the PhoneLookup table, which provides "fuzzy matching" + // for phone numbers. + // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup") + if (VDBG) Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID"); + columnName = PhoneLookup._ID; + } else { + Log.w(TAG, "Unexpected prefix for contactRef '" + url + "'"); + } + int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1; + if (VDBG) Log.v(TAG, "==> Using column '" + columnName + + "' (columnIndex = " + columnIndex + ") for person_id lookup..."); + return columnIndex; + } + + /** * @return a string debug representation of this instance. */ public String toString() { - return new StringBuilder(384) - .append("\nname: " + /*name*/ "nnnnnn") - .append("\nphoneNumber: " + /*phoneNumber*/ "xxxxxxx") - .append("\ncnapName: " + cnapName) - .append("\nnumberPresentation: " + numberPresentation) - .append("\nnamePresentation: " + namePresentation) - .append("\ncontactExits: " + contactExists) - .append("\nphoneLabel: " + phoneLabel) - .append("\nnumberType: " + numberType) - .append("\nnumberLabel: " + numberLabel) - .append("\nphotoResource: " + photoResource) - .append("\nperson_id: " + person_id) - .append("\nneedUpdate: " + needUpdate) - .append("\ncontactRefUri: " + /*contactRefUri*/ "xxxxxxx") - .append("\ncontactRingtoneUri: " + /*contactRefUri*/ "xxxxxxx") - .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail) - .append("\ncachedPhoto: " + cachedPhoto) - .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent) - .append("\nemergency: " + mIsEmergency) - .append("\nvoicemail " + mIsVoiceMail) - .append("\ncontactExists " + contactExists) - .toString(); + // Warning: never check in this file with VERBOSE_DEBUG = true + // because that will result in PII in the system log. + final boolean VERBOSE_DEBUG = false; + + if (VERBOSE_DEBUG) { + return new StringBuilder(384) + .append("\nname: " + name) + .append("\nphoneNumber: " + phoneNumber) + .append("\ncnapName: " + cnapName) + .append("\nnumberPresentation: " + numberPresentation) + .append("\nnamePresentation: " + namePresentation) + .append("\ncontactExits: " + contactExists) + .append("\nphoneLabel: " + phoneLabel) + .append("\nnumberType: " + numberType) + .append("\nnumberLabel: " + numberLabel) + .append("\nphotoResource: " + photoResource) + .append("\nperson_id: " + person_id) + .append("\nneedUpdate: " + needUpdate) + .append("\ncontactRefUri: " + contactRefUri) + .append("\ncontactRingtoneUri: " + contactRefUri) + .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail) + .append("\ncachedPhoto: " + cachedPhoto) + .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent) + .append("\nemergency: " + mIsEmergency) + .append("\nvoicemail " + mIsVoiceMail) + .append("\ncontactExists " + contactExists) + .toString(); + } else { + return new StringBuilder(128) + .append("CallerInfo { ") + .append("name " + ((name == null) ? "null" : "non-null")) + .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null")) + .append(" }") + .toString(); + } } } diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index 27a4dca..c7da3d4 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -951,22 +951,6 @@ public interface CommandsInterface { void writeSmsToRuim(int status, String pdu, Message response); - /** - * @param apn - * @param user - * @param password - * @param response - */ - @Deprecated - void setupDefaultPDP(String apn, String user, String password, Message response); - - /** - * @param cid - * @param response - */ - @Deprecated - void deactivateDefaultPDP(int cid, Message response); - void setRadioPower(boolean on, Message response); void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response); @@ -1354,10 +1338,12 @@ public interface CommandsInterface { * * @param cid * The connection ID + * @param reason + * Data disconnect reason. * @param result * Callback message is empty on completion */ - public void deactivateDataCall(int cid, Message result); + public void deactivateDataCall(int cid, int reason, Message result); /** * Activate or deactivate cell broadcast SMS for CDMA. diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 2536745..58fb13b 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -26,6 +26,7 @@ import android.net.LinkProperties; import android.os.AsyncResult; import android.os.Message; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.EventLog; import java.net.InetAddress; @@ -312,9 +313,19 @@ public abstract class DataConnection extends HierarchicalStateMachine { * and is either a DisconnectParams or ConnectionParams. */ private void tearDownData(Object o) { + int discReason = RILConstants.DEACTIVATE_REASON_NONE; + if ((o != null) && (o instanceof DisconnectParams)) { + DisconnectParams dp = (DisconnectParams)o; + Message m = dp.onCompletedMsg; + if ((m != null) && (m.obj != null) && (m.obj instanceof String)) { + String reason = (String)m.obj; + if (TextUtils.equals(reason, Phone.REASON_RADIO_TURNED_OFF)) + discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF; + } + } if (phone.mCM.getRadioState().isOn()) { if (DBG) log("tearDownData radio is on, call deactivateDataCall"); - phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, o)); + phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o)); } else { if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately"); AsyncResult ar = new AsyncResult(o, null, null); diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index a77e73e..916602f 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -1265,33 +1265,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { } /** - * @deprecated - */ - public void - setupDefaultPDP(String apn, String user, String password, Message result) { - int radioTechnology; - int authType; - String profile = ""; //profile number, NULL for GSM/UMTS - - radioTechnology = RILConstants.SETUP_DATA_TECH_GSM; - //TODO(): Add to the APN database, AuthType is set to CHAP/PAP - authType = (user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP - : RILConstants.SETUP_DATA_AUTH_NONE; - - setupDataCall(Integer.toString(radioTechnology), profile, apn, user, - password, Integer.toString(authType), result); - - } - - /** - * @deprecated - */ - public void - deactivateDefaultPDP(int cid, Message result) { - deactivateDataCall(cid, result); - } - - /** * The preferred new alternative to setupDefaultPDP that is * CDMA-compatible. * @@ -1329,15 +1302,16 @@ public final class RIL extends BaseCommands implements CommandsInterface { } public void - deactivateDataCall(int cid, Message result) { + deactivateDataCall(int cid, int reason, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result); - rr.mp.writeInt(1); + rr.mp.writeInt(2); rr.mp.writeString(Integer.toString(cid)); + rr.mp.writeString(Integer.toString(reason)); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + - requestToString(rr.mRequest) + " " + cid); + requestToString(rr.mRequest) + " " + cid + " " + reason); send(rr); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 888f721..305e15f 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -96,6 +96,10 @@ public interface RILConstants { int SETUP_DATA_AUTH_CHAP = 2; int SETUP_DATA_AUTH_PAP_CHAP = 3; + /* Deactivate data call reasons */ + int DEACTIVATE_REASON_NONE = 0; + int DEACTIVATE_REASON_RADIO_OFF = 1; + /* cat include/telephony/ril.h | \ egrep '^#define' | \ diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index b60be6e..de15408 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -569,45 +569,24 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF; synchronized (this) { - if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) { - /* - * In 1x CDMA , during radio power off modem will disconnect the - * data call and sends the power down registration message along - * with the data call release message to the network - */ - - msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X - dcTracker.sendMessage(msg); - - Log.w(LOG_TAG, "Turn off the radio right away"); - hangupAndPowerOff(); - } else { - if (!mPendingRadioPowerOffAfterDataOff) { - DataConnectionTracker.State currentState = dcTracker.getState(); - if (currentState != DataConnectionTracker.State.CONNECTED - && currentState != DataConnectionTracker.State.DISCONNECTING - && currentState != DataConnectionTracker.State.INITING) { - - msg.arg1 = 0; // tearDown is false as it is not needed. - dcTracker.sendMessage(msg); - - if (DBG) - log("Data disconnected, turn off radio right away."); - hangupAndPowerOff(); + if (!mPendingRadioPowerOffAfterDataOff) { + DataConnectionTracker.State currentState = dcTracker.getState(); + if (currentState != DataConnectionTracker.State.CONNECTED + && currentState != DataConnectionTracker.State.DISCONNECTING + && currentState != DataConnectionTracker.State.INITING) { + msg.arg1 = 0; // tearDown is false as it is not needed. + dcTracker.sendMessage(msg); + if (DBG) log("Data disconnected, turn off radio right away."); + hangupAndPowerOff(); + } else { + msg.arg1 = 1; // tearDown is true + dcTracker.sendMessage(msg); + if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) { + if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio."); + mPendingRadioPowerOffAfterDataOff = true; } else { - // clean data connection - msg.arg1 = 1; // tearDown is true - dcTracker.sendMessage(msg); - - if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) { - if (DBG) { - log("Wait upto 30s for data to disconnect, then turn off radio."); - } - mPendingRadioPowerOffAfterDataOff = true; - } else { - Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away."); - hangupAndPowerOff(); - } + Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away."); + hangupAndPowerOff(); } } } diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java index ed578c8..2e729fe 100644 --- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java +++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java @@ -184,19 +184,12 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface { public void writeSmsToRuim(int status, String pdu, Message response) { } - public void setupDefaultPDP(String apn, String user, String password, - Message result) { - } - - public void deactivateDefaultPDP(int cid, Message result) { - } - public void setupDataCall(String radioTechnology, String profile, String apn, String user, String password, String authType, Message result) { } - public void deactivateDataCall(int cid, Message result) { + public void deactivateDataCall(int cid, int reason, Message result) { } public void setRadioPower(boolean on, Message result) { diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index ef31ddd..58a4cba 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -393,12 +393,11 @@ public class SipPhone extends SipPhoneBase { new SipProfile.Builder(calleeSipUri).build(); SipConnection c = new SipConnection(this, callee, originalNumber); - connections.add(c); c.dial(); + connections.add(c); setState(Call.State.DIALING); return c; } catch (ParseException e) { - // TODO: notify someone throw new SipException("dial", e); } } @@ -660,12 +659,6 @@ public class SipPhone extends SipPhoneBase { @Override protected void onError(DisconnectCause cause) { if (DEBUG) Log.d(LOG_TAG, "SIP error: " + cause); - if (mSipAudioCall.isInCall() - && (cause != DisconnectCause.LOST_SIGNAL)) { - // Don't end the call when in a call. - return; - } - onCallEnded(cause); } }; diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index fdcf78d..c2f88e5 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -954,20 +954,12 @@ public final class SimulatedCommands extends BaseCommands unimplemented(response); } - @Deprecated - public void setupDefaultPDP(String apn, String user, String password, Message result) { - unimplemented(result); - } - public void setupDataCall(String radioTechnology, String profile, String apn, String user, String password, String authType, Message result) { unimplemented(result); } - public void deactivateDataCall(int cid, Message result) {unimplemented(result);} - - @Deprecated - public void deactivateDefaultPDP(int cid, Message result) {unimplemented(result);} + public void deactivateDataCall(int cid, int reason, Message result) {unimplemented(result);} public void setPreferredNetworkType(int networkType , Message result) { mNetworkType = networkType; diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml index c151251..dc44b25 100644 --- a/tests/DumpRenderTree/AndroidManifest.xml +++ b/tests/DumpRenderTree/AndroidManifest.xml @@ -25,7 +25,9 @@ <category android:name="android.intent.category.TEST" /> </intent-filter> </activity> - <activity android:name="TestShellActivity" android:launchMode="singleTop" + <activity android:name="TestShellActivity" + android:launchMode="singleTop" + android:hardwareAccelerated="true" android:screenOrientation="portrait" android:theme="@android:style/Theme.Light"/> <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait" diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index b901e0d..bc6ad64 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -92,13 +92,6 @@ public class Canvas_Delegate { return mGraphicsStack.peek(); } - /** - * Disposes of the {@link Graphics2D} stack. - */ - public void dispose() { - - } - // ---- native methods ---- /*package*/ static boolean isOpaque(Canvas thisCanvas) { @@ -985,6 +978,16 @@ public class Canvas_Delegate { } /*package*/ static void finalizer(int nativeCanvas) { + // get the delegate from the native int so that it can be disposed. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return; + } + + canvasDelegate.dispose(); + + // remove it from the manager. sManager.removeDelegate(nativeCanvas); } @@ -997,6 +1000,15 @@ public class Canvas_Delegate { private Canvas_Delegate() { } + /** + * Disposes of the {@link Graphics2D} stack. + */ + private void dispose() { + while (mGraphicsStack.size() > 0) { + mGraphicsStack.pop().dispose(); + } + } + private void setBitmap(BufferedImage image) { mBufferedImage = image; mGraphicsStack.push(mBufferedImage.createGraphics()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 2de1cbb..392717e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge; +import com.android.layoutlib.api.Capabilities; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; @@ -23,6 +24,7 @@ import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutBridge; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.api.SceneResult.SceneStatus; import com.android.layoutlib.bridge.android.BridgeAssetManager; import com.android.layoutlib.bridge.impl.FontLoader; import com.android.layoutlib.bridge.impl.LayoutSceneImpl; @@ -32,12 +34,14 @@ import com.android.tools.layoutlib.create.OverrideMethod; import android.graphics.Bitmap; import android.graphics.Typeface_Delegate; +import android.os.Looper; import android.util.Finalizers; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; @@ -150,11 +154,19 @@ public final class Bridge extends LayoutBridge { } }; + private EnumSet<Capabilities> mCapabilities; + + @Override public int getApiLevel() { return LayoutBridge.API_CURRENT; } + @Override + public EnumSet<Capabilities> getCapabilities() { + return mCapabilities; + } + /* * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map) @@ -163,6 +175,15 @@ public final class Bridge extends LayoutBridge { public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { sEnumValueMap = enumValueMap; + // don't use EnumSet.allOf(), because the bridge doesn't come with it's specific version + // of layoutlib_api. It is provided by the client which could have a more recent version + // with newer, unsupported capabilities. + mCapabilities = EnumSet.of( + Capabilities.RENDER, + Capabilities.VIEW_MANIPULATION, + Capabilities.ANIMATE); + + Finalizers.init(); BridgeAssetManager.initSystem(); @@ -290,26 +311,31 @@ public final class Bridge extends LayoutBridge { @Override public BridgeLayoutScene createScene(SceneParams params) { try { - SceneResult lastResult = SceneResult.SUCCESS; + SceneResult lastResult = SceneStatus.SUCCESS.getResult(); LayoutSceneImpl scene = new LayoutSceneImpl(params); try { - scene.prepareThread(); + prepareThread(); lastResult = scene.init(params.getTimeout()); - if (lastResult == SceneResult.SUCCESS) { + if (lastResult.isSuccess()) { lastResult = scene.inflate(); - if (lastResult == SceneResult.SUCCESS) { + if (lastResult.isSuccess()) { lastResult = scene.render(); } } } finally { scene.release(); - scene.cleanupThread(); + cleanupThread(); } return new BridgeLayoutScene(scene, lastResult); } catch (Throwable t) { - t.printStackTrace(); - return new BridgeLayoutScene(null, new SceneResult("error!", t)); + // get the real cause of the exception. + Throwable t2 = t; + while (t2.getCause() != null) { + t2 = t.getCause(); + } + return new BridgeLayoutScene(null, + new SceneResult(SceneStatus.ERROR_UNKNOWN, t2.getMessage(), t2)); } } @@ -333,6 +359,31 @@ public final class Bridge extends LayoutBridge { } /** + * Prepares the current thread for rendering. + * + * Note that while this can be called several time, the first call to {@link #cleanupThread()} + * will do the clean-up, and make the thread unable to do further scene actions. + */ + public static void prepareThread() { + // we need to make sure the Looper has been initialized for this thread. + // this is required for View that creates Handler objects. + if (Looper.myLooper() == null) { + Looper.prepare(); + } + } + + /** + * Cleans up thread-specific data. After this, the thread cannot be used for scene actions. + * <p> + * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single + * call to this will prevent the thread from doing further scene actions + */ + public static void cleanupThread() { + // clean up the looper + Looper.sThreadLocal.remove(); + } + + /** * Returns details of a framework resource from its integer value. * @param value the integer value * @return an array of 2 strings containing the resource name and type, or null if the id diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java index 97bf857..a491901 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java @@ -16,12 +16,16 @@ package com.android.layoutlib.bridge; +import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutScene; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.bridge.impl.LayoutSceneImpl; +import android.view.View; +import android.view.ViewGroup; + import java.awt.image.BufferedImage; import java.util.Map; @@ -60,14 +64,14 @@ public class BridgeLayoutScene extends LayoutScene { @Override public SceneResult render(long timeout) { try { - mScene.prepareThread(); + Bridge.prepareThread(); mLastResult = mScene.acquire(timeout); - if (mLastResult == SceneResult.SUCCESS) { + if (mLastResult.isSuccess()) { mLastResult = mScene.render(); } } finally { mScene.release(); - mScene.cleanupThread(); + Bridge.cleanupThread(); } return mLastResult; @@ -77,28 +81,94 @@ public class BridgeLayoutScene extends LayoutScene { public SceneResult animate(Object targetObject, String animationName, boolean isFrameworkAnimation, IAnimationListener listener) { try { - mScene.prepareThread(); + Bridge.prepareThread(); mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); - if (mLastResult == SceneResult.SUCCESS) { + if (mLastResult.isSuccess()) { mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation, listener); } } finally { mScene.release(); - mScene.cleanupThread(); + Bridge.cleanupThread(); } return mLastResult; } @Override - public void dispose() { - // TODO Auto-generated method stub + public SceneResult insertChild(Object parentView, IXmlPullParser childXml, int index, + IAnimationListener listener) { + if (parentView instanceof ViewGroup == false) { + throw new IllegalArgumentException("parentView is not a ViewGroup"); + } + + try { + Bridge.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult.isSuccess()) { + mLastResult = mScene.insertChild((ViewGroup) parentView, childXml, index, listener); + } + } finally { + mScene.release(); + Bridge.cleanupThread(); + } + + return mLastResult; + } + + + @Override + public SceneResult moveChild(Object parentView, Object childView, int index, + Map<String, String> layoutParams, IAnimationListener listener) { + if (parentView instanceof ViewGroup == false) { + throw new IllegalArgumentException("parentView is not a ViewGroup"); + } + if (childView instanceof View == false) { + throw new IllegalArgumentException("childView is not a View"); + } + + try { + Bridge.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult.isSuccess()) { + mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView, index, + listener); + } + } finally { + mScene.release(); + Bridge.cleanupThread(); + } + + return mLastResult; + } + + @Override + public SceneResult removeChild(Object childView, IAnimationListener listener) { + if (childView instanceof View == false) { + throw new IllegalArgumentException("childView is not a View"); + } + + try { + Bridge.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult.isSuccess()) { + mLastResult = mScene.removeChild((View) childView, listener); + } + } finally { + mScene.release(); + Bridge.cleanupThread(); + } + return mLastResult; + } + + @Override + public void dispose() { } /*package*/ BridgeLayoutScene(LayoutSceneImpl scene, SceneResult lastResult) { mScene = scene; + mScene.setScene(this); mLastResult = lastResult; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java index c20bdfd..2b9d52f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java @@ -16,8 +16,11 @@ package com.android.layoutlib.bridge.impl; +import com.android.layoutlib.api.LayoutScene; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.LayoutScene.IAnimationListener; +import com.android.layoutlib.api.SceneResult.SceneStatus; +import com.android.layoutlib.bridge.Bridge; import android.animation.Animator; import android.animation.ValueAnimator; @@ -57,7 +60,7 @@ public class AnimationThread extends Thread { @Override public void run() { - mScene.prepareThread(); + Bridge.prepareThread(); try { Handler_Delegate.setCallback(new IHandlerCallback() { public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { @@ -75,6 +78,7 @@ public class AnimationThread extends Thread { mAnimator.start(); // loop the animation + LayoutScene scene = mScene.getScene(); do { // get the next message. MessageBundle bundle = mQueue.poll(); @@ -88,14 +92,14 @@ public class AnimationThread extends Thread { try { sleep(bundle.mUptimeMillis - currentTime); } catch (InterruptedException e) { - // TODO Auto-generated catch block + // FIXME log/do something/sleep again? e.printStackTrace(); } } // ready to do the work, acquire the scene. SceneResult result = mScene.acquire(250); - if (result != SceneResult.SUCCESS) { + if (result.isSuccess() == false) { mListener.done(result); return; } @@ -104,18 +108,18 @@ public class AnimationThread extends Thread { // the next message, so mQueue will have another one. try { bundle.mTarget.handleMessage(bundle.mMessage); - if (mScene.render() == SceneResult.SUCCESS) { - mListener.onNewFrame(mScene.getImage()); + if (mScene.render().isSuccess()) { + mListener.onNewFrame(scene); } } finally { mScene.release(); } } while (mQueue.size() > 0); - mListener.done(SceneResult.SUCCESS); + mListener.done(SceneStatus.SUCCESS.getResult()); } finally { Handler_Delegate.setCallback(null); - mScene.cleanupThread(); + Bridge.cleanupThread(); } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java index 0859976..74e7fb2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java @@ -24,13 +24,16 @@ import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutBridge; +import com.android.layoutlib.api.LayoutScene; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.api.IDensityBasedResourceValue.Density; import com.android.layoutlib.api.LayoutScene.IAnimationListener; import com.android.layoutlib.api.SceneParams.RenderingMode; +import com.android.layoutlib.api.SceneResult.SceneStatus; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; @@ -48,11 +51,11 @@ import android.graphics.Canvas; import android.graphics.Canvas_Delegate; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.os.Looper; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.widget.FrameLayout; @@ -92,6 +95,7 @@ public class LayoutSceneImpl { private final SceneParams mParams; // scene state + private LayoutScene mScene; private BridgeContext mContext; private BridgeXmlBlockParser mBlockParser; private BridgeInflater mInflater; @@ -99,6 +103,9 @@ public class LayoutSceneImpl { private int mScreenOffset; private IResourceValue mWindowBackground; private FrameLayout mViewRoot; + private Canvas mCanvas; + private int mMeasuredScreenWidth = -1; + private int mMeasuredScreenHeight = -1; // information being returned through the API private BufferedImage mImage; @@ -193,32 +200,7 @@ public class LayoutSceneImpl { mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(), mContext, false /* platformResourceFlag */); - return SceneResult.SUCCESS; - } - - /** - * Prepares the current thread for rendering. - * - * Note that while this can be called several time, the first call to {@link #cleanupThread()} - * will do the clean-up, and make the thread unable to do further scene actions. - */ - public void prepareThread() { - // we need to make sure the Looper has been initialized for this thread. - // this is required for View that creates Handler objects. - if (Looper.myLooper() == null) { - Looper.prepare(); - } - } - - /** - * Cleans up thread-specific data. After this, the thread cannot be used for scene actions. - * <p> - * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single - * call to this will prevent the thread from doing further scene actions - */ - public void cleanupThread() { - // clean up the looper - Looper.sThreadLocal.remove(); + return SceneStatus.SUCCESS.getResult(); } /** @@ -360,9 +342,9 @@ public class LayoutSceneImpl { mViewRoot.setBackgroundDrawable(d); } - return SceneResult.SUCCESS; + return SceneStatus.SUCCESS.getResult(); } catch (PostInflateException e) { - return new SceneResult("Error during post inflation process:\n" + e.getMessage()); + return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -373,7 +355,7 @@ public class LayoutSceneImpl { // log it mParams.getLogger().error(t); - return new SceneResult("Unknown error during inflation.", t); + return new SceneResult(SceneStatus.ERROR_INFLATION, t.getMessage(), t); } } @@ -384,94 +366,110 @@ public class LayoutSceneImpl { * * @throws IllegalStateException if the current context is different than the one owned by * the scene, or if {@link #acquire(long)} was not called. + * + * @see SceneParams#getRenderingMode() */ public SceneResult render() { checkLock(); try { - long current = System.currentTimeMillis(); if (mViewRoot == null) { - return new SceneResult("Layout has not been inflated!"); + return new SceneResult(SceneStatus.ERROR_NOT_INFLATED); } // measure the views int w_spec, h_spec; - int renderScreenWidth = mParams.getScreenWidth(); - int renderScreenHeight = mParams.getScreenHeight(); - RenderingMode renderingMode = mParams.getRenderingMode(); - if (renderingMode != RenderingMode.NORMAL) { - // measure the full size needed by the layout. - w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, - renderingMode.isHorizExpand() ? - MeasureSpec.UNSPECIFIED // this lets us know the actual needed size - : MeasureSpec.EXACTLY); - h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset, - renderingMode.isVertExpand() ? - MeasureSpec.UNSPECIFIED // this lets us know the actual needed size - : MeasureSpec.EXACTLY); - mViewRoot.measure(w_spec, h_spec); - - if (renderingMode.isHorizExpand()) { - int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth(); - if (neededWidth > renderScreenWidth) { - renderScreenWidth = neededWidth; + // only do the screen measure when needed. + boolean newRenderSize = false; + if (mMeasuredScreenWidth == -1) { + newRenderSize = true; + mMeasuredScreenWidth = mParams.getScreenWidth(); + mMeasuredScreenHeight = mParams.getScreenHeight(); + + if (renderingMode != RenderingMode.NORMAL) { + // measure the full size needed by the layout. + w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, + renderingMode.isHorizExpand() ? + MeasureSpec.UNSPECIFIED // this lets us know the actual needed size + : MeasureSpec.EXACTLY); + h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset, + renderingMode.isVertExpand() ? + MeasureSpec.UNSPECIFIED // this lets us know the actual needed size + : MeasureSpec.EXACTLY); + mViewRoot.measure(w_spec, h_spec); + + if (renderingMode.isHorizExpand()) { + int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth(); + if (neededWidth > mMeasuredScreenWidth) { + mMeasuredScreenWidth = neededWidth; + } } - } - if (renderingMode.isVertExpand()) { - int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight(); - if (neededHeight > renderScreenHeight - mScreenOffset) { - renderScreenHeight = neededHeight + mScreenOffset; + if (renderingMode.isVertExpand()) { + int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight(); + if (neededHeight > mMeasuredScreenHeight - mScreenOffset) { + mMeasuredScreenHeight = neededHeight + mScreenOffset; + } } } } // remeasure with the size we need // This must always be done before the call to layout - w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY); - h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset, + w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY); + h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset, MeasureSpec.EXACTLY); mViewRoot.measure(w_spec, h_spec); // now do the layout. - mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight); + mViewRoot.layout(0, mScreenOffset, mMeasuredScreenWidth, mMeasuredScreenHeight); // draw the views // create the BufferedImage into which the layout will be rendered. - mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset, - BufferedImage.TYPE_INT_ARGB); - - if (mParams.isCustomBackgroundEnabled()) { - Graphics2D gc = mImage.createGraphics(); - gc.setColor(new Color(mParams.getCustomBackgroundColor(), true)); - gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset); - gc.dispose(); - } + if (newRenderSize || mCanvas == null) { + if (mParams.getImageFactory() != null) { + mImage = mParams.getImageFactory().getImage(mMeasuredScreenWidth, + mMeasuredScreenHeight - mScreenOffset); + } else { + mImage = new BufferedImage(mMeasuredScreenWidth, + mMeasuredScreenHeight - mScreenOffset, BufferedImage.TYPE_INT_ARGB); + } - // create an Android bitmap around the BufferedImage - Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, - true /*isMutable*/, - Density.getEnum(mParams.getDensity())); + if (mParams.isCustomBackgroundEnabled()) { + Graphics2D gc = mImage.createGraphics(); + gc.setColor(new Color(mParams.getCustomBackgroundColor(), true)); + gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight - mScreenOffset); + gc.dispose(); + } + + // create an Android bitmap around the BufferedImage + Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, + true /*isMutable*/, + Density.getEnum(mParams.getDensity())); - // create a Canvas around the Android bitmap - Canvas canvas = new Canvas(bitmap); - canvas.setDensity(mParams.getDensity()); + // create a Canvas around the Android bitmap + mCanvas = new Canvas(bitmap); + mCanvas.setDensity(mParams.getDensity()); + } // to set the logger, get the native delegate - Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); + Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(mCanvas); canvasDelegate.setLogger(mParams.getLogger()); - mViewRoot.draw(canvas); - canvasDelegate.dispose(); + long preDrawTime = System.currentTimeMillis(); + + mViewRoot.draw(mCanvas); + + long drawTime = System.currentTimeMillis(); mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext); - System.out.println("rendering (ms): " + (System.currentTimeMillis() - current)); + System.out.println(String.format("rendering (ms): %03d", drawTime - preDrawTime)); // success! - return SceneResult.SUCCESS; + return SceneStatus.SUCCESS.getResult(); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -482,7 +480,7 @@ public class LayoutSceneImpl { // log it mParams.getLogger().error(t); - return new SceneResult("Unknown error during inflation.", t); + return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t); } } @@ -522,15 +520,93 @@ public class LayoutSceneImpl { new AnimationThread(this, anim, listener).start(); - return SceneResult.SUCCESS; + return SceneStatus.SUCCESS.getResult(); } } catch (Exception e) { - e.printStackTrace(); - return new SceneResult("", e); + // get the real cause of the exception. + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + + return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t); } } - return new SceneResult("Failed to find animation"); + return new SceneResult(SceneStatus.ERROR_ANIM_NOT_FOUND); + } + + public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml, + int index, IAnimationListener listener) { + checkLock(); + + // create a block parser for the XML + BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(childXml, mContext, + false /* platformResourceFlag */); + + // inflate the child without adding it to the root since we want to control where it'll + // get added. We do pass the parentView however to ensure that the layoutParams will + // be created correctly. + View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/); + + // add it to the parentView in the correct location + try { + parentView.addView(child, index); + } catch (UnsupportedOperationException e) { + // looks like this is a view class that doesn't support children manipulation! + return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN); + } + + invalidateRenderingSize(); + + SceneResult result = render(); + if (result.isSuccess()) { + result.setData(child); + } + + return result; + } + + public SceneResult moveChild(ViewGroup parentView, View childView, int index, + IAnimationListener listener) { + checkLock(); + + try { + ViewParent parent = childView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) parent; + parentGroup.removeView(childView); + } + + // add it to the parentView in the correct location + parentView.addView(childView, index); + } catch (UnsupportedOperationException e) { + // looks like this is a view class that doesn't support children manipulation! + return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN); + } + + invalidateRenderingSize(); + + return render(); + } + + public SceneResult removeChild(View childView, IAnimationListener listener) { + checkLock(); + + try { + ViewParent parent = childView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) parent; + parentGroup.removeView(childView); + } + } catch (UnsupportedOperationException e) { + // looks like this is a view class that doesn't support children manipulation! + return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN); + } + + invalidateRenderingSize(); + + return render(); } /** @@ -899,6 +975,10 @@ public class LayoutSceneImpl { return result; } + private void invalidateRenderingSize() { + mMeasuredScreenWidth = mMeasuredScreenHeight = -1; + } + public BufferedImage getImage() { return mImage; } @@ -910,4 +990,12 @@ public class LayoutSceneImpl { public Map<String, String> getDefaultViewPropertyValues(Object viewObject) { return mContext.getDefaultPropMap(viewObject); } + + public void setScene(LayoutScene scene) { + mScene = scene; + } + + public LayoutScene getScene() { + return mScene; + } } diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 6ec223b..097b109 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -16,6 +16,7 @@ #include <ui/KeyCharacterMap.h> #include <ui/KeyLayoutMap.h> +#include <ui/VirtualKeyMap.h> #include <utils/String8.h> #include <stdio.h> @@ -30,6 +31,7 @@ enum FileType { FILETYPE_UNKNOWN, FILETYPE_KEYLAYOUT, FILETYPE_KEYCHARACTERMAP, + FILETYPE_VIRTUALKEYDEFINITION, }; @@ -37,8 +39,10 @@ static void usage() { fprintf(stderr, "Keymap Validation Tool\n\n"); fprintf(stderr, "Usage:\n"); fprintf(stderr, - " %s [FILENAME.kl] [FILENAME.kcm] [...]\n" - " Validates the specified key layout and/or key character map files.\n\n", gProgName); + " %s [*.kl] [*.kcm] [virtualkeys.*] [...]\n" + " Validates the specified key layouts, key character maps \n" + " or virtual key definitions.\n\n", + gProgName); } static FileType getFileType(const char* filename) { @@ -51,6 +55,11 @@ static FileType getFileType(const char* filename) { return FILETYPE_KEYCHARACTERMAP; } } + + if (strstr(filename, "virtualkeys.")) { + return FILETYPE_VIRTUALKEYDEFINITION; + } + return FILETYPE_UNKNOWN; } @@ -60,7 +69,7 @@ static bool validateFile(const char* filename) { FileType fileType = getFileType(filename); switch (fileType) { case FILETYPE_UNKNOWN: - fprintf(stderr, "File extension must be .kl or .kcm.\n\n"); + fprintf(stderr, "Supported file types: *.kl, *.kcm, virtualkeys.*\n\n"); return false; case FILETYPE_KEYLAYOUT: { @@ -82,6 +91,16 @@ static bool validateFile(const char* filename) { } break; } + + case FILETYPE_VIRTUALKEYDEFINITION: { + VirtualKeyMap* map; + status_t status = VirtualKeyMap::load(String8(filename), &map); + if (status) { + fprintf(stderr, "Error %d parsing virtual key definition file.\n\n", status); + return false; + } + break; + } } fputs("No errors.\n\n", stdout); diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java index 43a3827..a6b54d8 100644 --- a/voip/java/android/net/rtp/AudioGroup.java +++ b/voip/java/android/net/rtp/AudioGroup.java @@ -21,14 +21,14 @@ import java.util.Map; /** * An AudioGroup acts as a router connected to the speaker, the microphone, and - * {@link AudioStream}s. Its pipeline has four steps. First, for each - * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming - * packets and stores in its buffer. Then, if the microphone is enabled, - * processes the recorded audio and stores in its buffer. Third, if the speaker - * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for - * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other - * buffers and sends back the encoded packets. An AudioGroup does nothing if - * there is no AudioStream in it. + * {@link AudioStream}s. Its execution loop consists of four steps. First, for + * each AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its + * incoming packets and stores in its buffer. Then, if the microphone is + * enabled, processes the recorded audio and stores in its buffer. Third, if the + * speaker is enabled, mixes and playbacks buffers of all AudioStreams. Finally, + * for each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all + * other buffers and sends back the encoded packets. An AudioGroup does nothing + * if there is no AudioStream in it. * * <p>Few things must be noticed before using these classes. The performance is * highly related to the system load and the network bandwidth. Usually a @@ -47,7 +47,12 @@ import java.util.Map; * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an * AudioStream into an AudioGroup, one should always put all other AudioGroups * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly - * initialized. + * initialized.</p> + * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#RECORD_AUDIO} permission.</p> + * + * @see AudioStream * @hide */ public class AudioGroup { @@ -78,6 +83,8 @@ public class AudioGroup { */ public static final int MODE_ECHO_SUPPRESSION = 3; + private static final int MODE_LAST = 3; + private final Map<AudioStream, Integer> mStreams; private int mMode = MODE_ON_HOLD; @@ -94,6 +101,15 @@ public class AudioGroup { } /** + * Returns the {@link AudioStream}s in this group. + */ + public AudioStream[] getStreams() { + synchronized (this) { + return mStreams.keySet().toArray(new AudioStream[mStreams.size()]); + } + } + + /** * Returns the current mode. */ public int getMode() { @@ -108,49 +124,77 @@ public class AudioGroup { * @param mode The mode to change to. * @throws IllegalArgumentException if the mode is invalid. */ - public synchronized native void setMode(int mode); - - private native void add(int mode, int socket, String remoteAddress, - int remotePort, String codecSpec, int dtmfType); + public void setMode(int mode) { + if (mode < 0 || mode > MODE_LAST) { + throw new IllegalArgumentException("Invalid mode"); + } + synchronized (this) { + nativeSetMode(mode); + mMode = mode; + } + } - synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) { - if (!mStreams.containsKey(stream)) { - try { - int socket = stream.dup(); - String codecSpec = String.format("%d %s %s", codec.type, - codec.rtpmap, codec.fmtp); - add(stream.getMode(), socket, - stream.getRemoteAddress().getHostAddress(), - stream.getRemotePort(), codecSpec, dtmfType); - mStreams.put(stream, socket); - } catch (NullPointerException e) { - throw new IllegalStateException(e); + private native void nativeSetMode(int mode); + + // Package-private method used by AudioStream.join(). + void add(AudioStream stream, AudioCodec codec, int dtmfType) { + synchronized (this) { + if (!mStreams.containsKey(stream)) { + try { + int socket = stream.dup(); + String codecSpec = String.format("%d %s %s", codec.type, + codec.rtpmap, codec.fmtp); + nativeAdd(stream.getMode(), socket, + stream.getRemoteAddress().getHostAddress(), + stream.getRemotePort(), codecSpec, dtmfType); + mStreams.put(stream, socket); + } catch (NullPointerException e) { + throw new IllegalStateException(e); + } } } } - private native void remove(int socket); + private native void nativeAdd(int mode, int socket, String remoteAddress, + int remotePort, String codecSpec, int dtmfType); - synchronized void remove(AudioStream stream) { - Integer socket = mStreams.remove(stream); - if (socket != null) { - remove(socket); + // Package-private method used by AudioStream.join(). + void remove(AudioStream stream) { + synchronized (this) { + Integer socket = mStreams.remove(stream); + if (socket != null) { + nativeRemove(socket); + } } } + private native void nativeRemove(int socket); + /** * Sends a DTMF digit to every {@link AudioStream} in this group. Currently * only event {@code 0} to {@code 15} are supported. * * @throws IllegalArgumentException if the event is invalid. */ - public native synchronized void sendDtmf(int event); + public void sendDtmf(int event) { + if (event < 0 || event > 15) { + throw new IllegalArgumentException("Invalid event"); + } + synchronized (this) { + nativeSendDtmf(event); + } + } + + private native void nativeSendDtmf(int event); /** * Removes every {@link AudioStream} in this group. */ - public synchronized void clear() { - remove(-1); + public void clear() { + synchronized (this) { + mStreams.clear(); + nativeRemove(-1); + } } @Override diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java index e5197ce..0edae6b 100644 --- a/voip/java/android/net/rtp/AudioStream.java +++ b/voip/java/android/net/rtp/AudioStream.java @@ -34,8 +34,12 @@ import java.net.SocketException; * of the setter methods are disabled. This is designed to ease the task of * managing native resources. One can always make an AudioStream leave its * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it - * back after the modification is done. + * back after the modification is done.</p> * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#INTERNET} permission.</p> + * + * @see RtpStream * @see AudioGroup * @hide */ @@ -82,16 +86,18 @@ public class AudioStream extends RtpStream { * @see AudioGroup */ public void join(AudioGroup group) { - if (mGroup == group) { - return; - } - if (mGroup != null) { - mGroup.remove(this); - mGroup = null; - } - if (group != null) { - group.add(this, mCodec, mDtmfType); - mGroup = group; + synchronized (this) { + if (mGroup == group) { + return; + } + if (mGroup != null) { + mGroup.remove(this); + mGroup = null; + } + if (group != null) { + group.add(this, mCodec, mDtmfType); + mGroup = group; + } } } diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java index 23fb258..87d8bc6 100644 --- a/voip/java/android/net/rtp/RtpStream.java +++ b/voip/java/android/net/rtp/RtpStream.java @@ -24,6 +24,9 @@ import java.net.SocketException; /** * RtpStream represents the base class of streams which send and receive network * packets with media payloads over Real-time Transport Protocol (RTP). + * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#INTERNET} permission.</p> * @hide */ public class RtpStream { @@ -43,6 +46,8 @@ public class RtpStream { */ public static final int MODE_RECEIVE_ONLY = 2; + private static final int MODE_LAST = 2; + private final InetAddress mLocalAddress; private final int mLocalPort; @@ -129,7 +134,7 @@ public class RtpStream { if (isBusy()) { throw new IllegalStateException("Busy"); } - if (mode != MODE_NORMAL && mode != MODE_SEND_ONLY && mode != MODE_RECEIVE_ONLY) { + if (mode < 0 || mode > MODE_LAST) { throw new IllegalArgumentException("Invalid mode"); } mMode = mode; diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java index f275e39..51236fe 100644 --- a/voip/java/android/net/sip/SipAudioCall.java +++ b/voip/java/android/net/sip/SipAudioCall.java @@ -594,12 +594,10 @@ public class SipAudioCall { */ public void holdCall(int timeout) throws SipException { synchronized (this) { - if (mHold) return; + if (mHold) return; mSipSession.changeCall(createHoldOffer().encode(), timeout); mHold = true; - - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + setAudioGroupMode(); } } @@ -643,8 +641,7 @@ public class SipAudioCall { if (!mHold) return; mSipSession.changeCall(createContinueOffer().encode(), timeout); mHold = false; - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL); + setAudioGroupMode(); } } @@ -767,13 +764,8 @@ public class SipAudioCall { /** Toggles mute. */ public void toggleMute() { synchronized (this) { - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) { - audioGroup.setMode(mMuted - ? AudioGroup.MODE_NORMAL - : AudioGroup.MODE_MUTED); - mMuted = !mMuted; - } + mMuted = !mMuted; + setAudioGroupMode(); } } @@ -792,14 +784,22 @@ public class SipAudioCall { * Puts the device to speaker mode. * <p class="note"><strong>Note:</strong> Requires the * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p> + * + * @param speakerMode set true to enable speaker mode; false to disable */ public void setSpeakerMode(boolean speakerMode) { synchronized (this) { ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) .setSpeakerphoneOn(speakerMode); + setAudioGroupMode(); } } + private boolean isSpeakerOn() { + return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) + .isSpeakerphoneOn(); + } + /** * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>, * event 0--9 maps to decimal @@ -876,7 +876,11 @@ public class SipAudioCall { /** * Sets the {@link AudioGroup} object which the {@link AudioStream} object * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object - * will be dynamically created when needed. + * will be dynamically created when needed. Note that the mode of the + * {@code AudioGroup} is not changed according to the audio settings (i.e., + * hold, mute, speaker phone) of this object. This is mainly used to merge + * multiple {@code SipAudioCall} objects to form a conference call. The + * settings of the first object (that merges others) override others'. * * @see #getAudioStream * @hide @@ -992,16 +996,25 @@ public class SipAudioCall { // AudioGroup logic: AudioGroup audioGroup = getAudioGroup(); if (mHold) { - if (audioGroup != null) { - audioGroup.setMode(AudioGroup.MODE_ON_HOLD); - } // don't create an AudioGroup here; doing so will fail if // there's another AudioGroup out there that's active } else { if (audioGroup == null) audioGroup = new AudioGroup(); stream.join(audioGroup); - if (mMuted) { + } + setAudioGroupMode(); + } + + // set audio group mode based on current audio configuration + private void setAudioGroupMode() { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup != null) { + if (mHold) { + audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + } else if (mMuted) { audioGroup.setMode(AudioGroup.MODE_MUTED); + } else if (isSpeakerOn()) { + audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); } else { audioGroup.setMode(AudioGroup.MODE_NORMAL); } diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java index 8aaa805..2e38662 100644 --- a/voip/java/android/net/sip/SipManager.java +++ b/voip/java/android/net/sip/SipManager.java @@ -314,10 +314,6 @@ public class SipManager { SipAudioCall call = new SipAudioCall(mContext, localProfile); call.setListener(listener); SipSession s = createSipSession(localProfile, null); - if (s == null) { - throw new SipException( - "Failed to create SipSession; network available?"); - } call.makeCall(peerProfile, s, timeout); return call; } @@ -366,7 +362,9 @@ public class SipManager { */ public SipAudioCall takeAudioCall(Intent incomingCallIntent, SipAudioCall.Listener listener) throws SipException { - if (incomingCallIntent == null) return null; + if (incomingCallIntent == null) { + throw new SipException("Cannot retrieve session with null intent"); + } String callId = getCallId(incomingCallIntent); if (callId == null) { @@ -381,7 +379,9 @@ public class SipManager { try { ISipSession session = mSipService.getPendingSession(callId); - if (session == null) return null; + if (session == null) { + throw new SipException("No pending session for the call"); + } SipAudioCall call = new SipAudioCall( mContext, session.getLocalProfile()); call.attachCall(new SipSession(session), offerSd); @@ -526,6 +526,10 @@ public class SipManager { SipSession.Listener listener) throws SipException { try { ISipSession s = mSipService.createSession(localProfile, null); + if (s == null) { + throw new SipException( + "Failed to create SipSession; network unavailable?"); + } return new SipSession(s, listener); } catch (RemoteException e) { throw new SipException("createSipSession()", e); @@ -541,7 +545,7 @@ public class SipManager { try { return mSipService.getListOfProfiles(); } catch (RemoteException e) { - return null; + return new SipProfile[0]; } } diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java index f480fec..3af6e78 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -135,7 +135,7 @@ public final class SipService extends ISipService.Stub { switch (state) { case WifiManager.WIFI_STATE_ENABLED: mWifiEnabled = true; - if (anyOpened()) grabWifiLock(); + if (anyOpenedToReceiveCalls()) grabWifiLock(); break; case WifiManager.WIFI_STATE_DISABLED: mWifiEnabled = false; @@ -231,7 +231,7 @@ public final class SipService extends ISipService.Stub { notifyProfileRemoved(group.getLocalProfile()); group.close(); - if (!anyOpened()) { + if (!anyOpenedToReceiveCalls()) { releaseWifiLock(); mMyWakeLock.reset(); // in case there's leak } @@ -243,7 +243,7 @@ public final class SipService extends ISipService.Stub { SipSessionGroupExt group = mSipGroups.get(localProfileUri); if (group == null) return false; if (isCallerCreatorOrRadio(group)) { - return group.isOpened(); + return true; } else { Log.w(TAG, "only creator or radio can query on the profile"); return false; @@ -358,9 +358,9 @@ public final class SipService extends ISipService.Stub { mContext.sendBroadcast(intent); } - private boolean anyOpened() { + private boolean anyOpenedToReceiveCalls() { for (SipSessionGroupExt group : mSipGroups.values()) { - if (group.isOpened()) return true; + if (group.isOpenedToReceiveCalls()) return true; } return false; } @@ -479,7 +479,7 @@ public final class SipService extends ISipService.Stub { private class SipSessionGroupExt extends SipSessionAdapter { private SipSessionGroup mSipGroup; private PendingIntent mIncomingCallPendingIntent; - private boolean mOpened; + private boolean mOpenedToReceiveCalls; private AutoRegistrationProcess mAutoRegistration = new AutoRegistrationProcess(); @@ -541,7 +541,7 @@ public final class SipService extends ISipService.Stub { } public void openToReceiveCalls() throws SipException { - mOpened = true; + mOpenedToReceiveCalls = true; if (mConnected) { mSipGroup.openToReceiveCalls(this); mAutoRegistration.start(mSipGroup); @@ -555,9 +555,9 @@ public final class SipService extends ISipService.Stub { mSipGroup.onConnectivityChanged(); if (connected) { resetGroup(mLocalIp); - if (mOpened) openToReceiveCalls(); + if (mOpenedToReceiveCalls) openToReceiveCalls(); } else { - // close mSipGroup but remember mOpened + // close mSipGroup but remember mOpenedToReceiveCalls if (DEBUG) Log.d(TAG, " close auto reg temporarily: " + getUri() + ": " + mIncomingCallPendingIntent); mSipGroup.close(); @@ -582,7 +582,7 @@ public final class SipService extends ISipService.Stub { } public void close() { - mOpened = false; + mOpenedToReceiveCalls = false; mSipGroup.close(); mAutoRegistration.stop(); if (DEBUG) Log.d(TAG, " close: " + getUri() + ": " @@ -629,8 +629,8 @@ public final class SipService extends ISipService.Stub { + SipErrorCode.toString(errorCode) + ": " + message); } - public boolean isOpened() { - return mOpened; + public boolean isOpenedToReceiveCalls() { + return mOpenedToReceiveCalls; } public boolean isRegistered() { diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index 50ce7dc..30ddfb5 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -190,6 +190,7 @@ class SipSessionGroup implements SipListener { public synchronized void close() { Log.d(TAG, " close stack for " + mLocalProfile.getUriString()); + onConnectivityChanged(); mSessionMap.clear(); closeToNotReceiveCalls(); if (mSipStack != null) { @@ -526,11 +527,14 @@ class SipSessionGroup implements SipListener { } public void answerCall(String sessionDescription, int timeout) { - try { - processCommand(new MakeCallCommand(mPeerProfile, - sessionDescription, timeout)); - } catch (SipException e) { - onError(e); + synchronized (SipSessionGroup.this) { + if (mPeerProfile == null) return; + try { + processCommand(new MakeCallCommand(mPeerProfile, + sessionDescription, timeout)); + } catch (SipException e) { + onError(e); + } } } @@ -539,14 +543,11 @@ class SipSessionGroup implements SipListener { } public void changeCall(String sessionDescription, int timeout) { - doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription, - timeout)); - } - - public void changeCallWithTimeout( - String sessionDescription, int timeout) { - doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription, - timeout)); + synchronized (SipSessionGroup.this) { + if (mPeerProfile == null) return; + doCommandAsync(new MakeCallCommand(mPeerProfile, + sessionDescription, timeout)); + } } public void register(int duration) { @@ -1162,11 +1163,6 @@ class SipSessionGroup implements SipListener { mProxy.onCallEstablished(this, mPeerSessionDescription); } - private void fallbackToPreviousInCall(int errorCode, String message) { - mState = SipSession.State.IN_CALL; - mProxy.onCallChangeFailed(this, errorCode, message); - } - private void endCallNormally() { reset(); mProxy.onCallEnded(this); @@ -1190,12 +1186,7 @@ class SipSessionGroup implements SipListener { onRegistrationFailed(errorCode, message); break; default: - if ((errorCode != SipErrorCode.DATA_CONNECTION_LOST) - && mInCall) { - fallbackToPreviousInCall(errorCode, message); - } else { - endCallOnError(errorCode, message); - } + endCallOnError(errorCode, message); } } diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp index 72ee44e..84c7166 100644 --- a/voip/jni/rtp/AmrCodec.cpp +++ b/voip/jni/rtp/AmrCodec.cpp @@ -73,7 +73,7 @@ int AmrCodec::set(int sampleRate, const char *fmtp) } // Handle mode-set and octet-align. - char *modes = (char*)strcasestr(fmtp, "mode-set="); + const char *modes = strcasestr(fmtp, "mode-set="); if (modes) { mMode = 0; mModeSet = 0; diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp index 0c8a725..cba1123 100644 --- a/voip/jni/rtp/AudioGroup.cpp +++ b/voip/jni/rtp/AudioGroup.cpp @@ -90,6 +90,7 @@ public: void encode(int tick, AudioStream *chain); void decode(int tick); +private: enum { NORMAL = 0, SEND_ONLY = 1, @@ -97,7 +98,6 @@ public: LAST_MODE = 2, }; -private: int mMode; int mSocket; sockaddr_storage mRemote; @@ -463,6 +463,7 @@ public: bool add(AudioStream *stream); bool remove(int socket); +private: enum { ON_HOLD = 0, MUTED = 1, @@ -471,7 +472,6 @@ public: LAST_MODE = 3, }; -private: AudioStream *mChain; int mEventQueue; volatile int mDtmfEvent; @@ -948,16 +948,10 @@ void remove(JNIEnv *env, jobject thiz, jint socket) void setMode(JNIEnv *env, jobject thiz, jint mode) { - if (mode < 0 || mode > AudioGroup::LAST_MODE) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative); if (group && !group->setMode(mode)) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; } - env->SetIntField(thiz, gMode, mode); } void sendDtmf(JNIEnv *env, jobject thiz, jint event) @@ -969,10 +963,10 @@ void sendDtmf(JNIEnv *env, jobject thiz, jint event) } JNINativeMethod gMethods[] = { - {"add", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add}, - {"remove", "(I)V", (void *)remove}, - {"setMode", "(I)V", (void *)setMode}, - {"sendDtmf", "(I)V", (void *)sendDtmf}, + {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add}, + {"nativeRemove", "(I)V", (void *)remove}, + {"nativeSetMode", "(I)V", (void *)setMode}, + {"nativeSendDtmf", "(I)V", (void *)sendDtmf}, }; } // namespace diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp index 4cff588..6127d3c 100644 --- a/voip/jni/rtp/EchoSuppressor.cpp +++ b/voip/jni/rtp/EchoSuppressor.cpp @@ -161,22 +161,27 @@ void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded) } // Compute correlations. - float corr2 = 0.0f; int latency = 0; + float corr2 = 0.0f; + float varX = 0.0f; float varY = mY2Sum - mWeight * mYSum * mYSum; for (int i = mTailLength - 1; i >= 0; --i) { - float varX = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i]; float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum; - float c2 = cov * cov / (varX * varY + 1); - if (c2 > corr2) { - corr2 = c2; - latency = i; + if (cov > 0.0f) { + float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i]; + float corr2i = cov * cov / (varXi * varY + 1); + if (corr2i > corr2) { + varX = varXi; + corr2 = corr2i; + latency = i; + } } } - //LOGI("correlation^2 = %.10f, latency = %d", corr2, latency * mScale); + //LOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY, + // latency * mScale); // Do echo suppression. - if (corr2 > 0.1f) { + if (corr2 > 0.1f && varX > 10000.0f) { int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096; for (int i = 0; i < mSampleCount; ++i) { recorded[i] = recorded[i] * factor >> 16; |