diff options
94 files changed, 3200 insertions, 1116 deletions
diff --git a/apps/Development/res/layout/connectivity.xml b/apps/Development/res/layout/connectivity.xml index 2df645ccf..53f1ed7ef 100644 --- a/apps/Development/res/layout/connectivity.xml +++ b/apps/Development/res/layout/connectivity.xml @@ -213,6 +213,33 @@ <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <Button android:id="@+id/startTdls" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/start_tdls" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/mac_tdls" /> + <EditText android:id="@+id/sc_ip_mac" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minEms="10" /> + <Button android:id="@+id/stopTdls" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/stop_tdls" /> + </LinearLayout> + + <!-- divider line --> + <View android:background="#FFFFFFFF" + android:layout_width="match_parent" + android:layout_height="3dip" /> + + <LinearLayout + android:orientation="horizontal" android:paddingTop="4dip" android:layout_width="match_parent" android:layout_height="wrap_content"> diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml index b7ed5e12e..ced7b727d 100644 --- a/apps/Development/res/values/strings.xml +++ b/apps/Development/res/values/strings.xml @@ -34,6 +34,10 @@ <string name="scan_cycles">Scan Cycles: </string> <string name="disconnect">Disconnect</string> + <string name="start_tdls">Start TDLS</string> + <string name="stop_tdls">Stop TDLS</string> + <string name="mac_tdls"> IP/MAC: </string> + <string name="start_mms">Start MMS</string> <string name="stop_mms">Stop MMS</string> <string name="start_hipri">Start HiPri</string> diff --git a/apps/Development/src/com/android/development/Connectivity.java b/apps/Development/src/com/android/development/Connectivity.java index 95487dc1d..0da1410f0 100644 --- a/apps/Development/src/com/android/development/Connectivity.java +++ b/apps/Development/src/com/android/development/Connectivity.java @@ -117,6 +117,8 @@ public class Connectivity extends Activity { private long mTotalScanTime = 0; private long mTotalScanCount = 0; + private String mTdlsAddr = null; + private WifiManager mWm; private PowerManager mPm; private ConnectivityManager mCm; @@ -235,6 +237,7 @@ public class Connectivity extends Activity { unregisterReceiver(mScanRecv); mScanButton.setText(GET_SCAN_RES); } else { + Log.d(TAG, "Scan: START " + mScanCur); mStartTime = SystemClock.elapsedRealtime(); mWm.startScan(); } @@ -289,6 +292,9 @@ public class Connectivity extends Activity { mIntentFilter = new IntentFilter(); mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + findViewById(R.id.startTdls).setOnClickListener(mClickListener); + findViewById(R.id.stopTdls).setOnClickListener(mClickListener); + findViewById(R.id.start_mms).setOnClickListener(mClickListener); findViewById(R.id.stop_mms).setOnClickListener(mClickListener); findViewById(R.id.start_hipri).setOnClickListener(mClickListener); @@ -338,6 +344,12 @@ public class Connectivity extends Activity { case R.id.startScan: onStartScanCycle(); break; + case R.id.startTdls: + onStartTdls(); + break; + case R.id.stopTdls: + onStopTdls(); + break; case R.id.start_mms: mCm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_MMS); @@ -469,6 +481,7 @@ public class Connectivity extends Activity { mWm.disconnect(); mTotalScanTime = 0; mTotalScanCount = 0; + Log.d(TAG, "Scan: START " + mScanCur); mStartTime = SystemClock.elapsedRealtime(); mWm.startScan(); } else { @@ -485,6 +498,30 @@ public class Connectivity extends Activity { } } + private void onStartTdls() { + mTdlsAddr = ((EditText)findViewById(R.id.sc_ip_mac)).getText().toString(); + Log.d(TAG, "TDLS: START " + mTdlsAddr); + InetAddress inetAddress = null; + try { + inetAddress = InetAddress.getByName(mTdlsAddr); + mWm.enableTdls(inetAddress, true); + } catch (Exception e) { + mWm.enableTdlsWithMacAddress(mTdlsAddr, true); + } + } + + private void onStopTdls() { + if (mTdlsAddr == null) return; + Log.d(TAG, "TDLS: STOP " + mTdlsAddr); + InetAddress inetAddress = null; + try { + inetAddress = InetAddress.getByName(mTdlsAddr); + mWm.enableTdls(inetAddress, false); + } catch (Exception e) { + mWm.enableTdlsWithMacAddress(mTdlsAddr, false); + } + } + private void onAddDefaultRoute() { try { mNetd.addRoute("eth0", new RouteInfo(null, diff --git a/build/windows_sdk_whitelist.mk b/build/windows_sdk_whitelist.mk index 8d6ac8fb7..f2f4d8e53 100644 --- a/build/windows_sdk_whitelist.mk +++ b/build/windows_sdk_whitelist.mk @@ -54,6 +54,7 @@ subdirs += \ system/core/liblog \ system/core/libsparse \ system/core/libzipfile \ + system/core/libutils \ system/extras/ext4_utils # ----- diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java index 89679a14a..e41a0699e 100644 --- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java +++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java @@ -139,7 +139,8 @@ public class MonkeySourceNetworkViews { int viewId = Integer.parseInt(viewString); int connectionId = sUiTestAutomationBridge.getConnectionId(); AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId, viewId, 0); + return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId, viewId, + false, 0); } private static AccessibilityNodeInfo getNodeByViewId(String viewId) throws MonkeyViewException { diff --git a/host/windows/usb/android_winusb.inf b/host/windows/usb/android_winusb.inf index f26a75cbc..8dbfe094d 100755 --- a/host/windows/usb/android_winusb.inf +++ b/host/windows/usb/android_winusb.inf @@ -6,7 +6,7 @@ Signature = "$Windows NT$" Class = AndroidUsbDeviceClass
ClassGuid = {3F966BD9-FA04-4ec5-991C-D326973B5128}
Provider = %ProviderName%
-DriverVer = 08/27/2012,7.0.0000.00001
+DriverVer = 07/09/2013,8.0.0000.00000
CatalogFile.NTx86 = androidwinusb86.cat
CatalogFile.NTamd64 = androidwinusba64.cat
@@ -38,8 +38,6 @@ HKR,,Icon,,-1 ;Google Nexus 7
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4E40
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E41
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E44&MI_01
@@ -49,12 +47,8 @@ HKR,,Icon,,-1 ;Google Nexus (generic)
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4EE0
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE1
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2&MI_01
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE3
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_01
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE5
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_02
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE6&MI_01
@@ -74,8 +68,6 @@ HKR,,Icon,,-1 ;Google Nexus 7
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4E40
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E41
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E44&MI_01
@@ -85,12 +77,8 @@ HKR,,Icon,,-1 ;Google Nexus (generic)
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4EE0
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE1
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2&MI_01
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE3
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_01
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE5
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_02
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE6&MI_01
[USB_Install]
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath index bfbd04ec1..3e30be1a8 100644 --- a/ide/eclipse/.classpath +++ b/ide/eclipse/.classpath @@ -16,6 +16,7 @@ <classpathentry kind="src" path="packages/apps/Gallery2/src_pd"/> <classpathentry kind="src" path="packages/apps/Gallery2/gallerycommon/src"/> <classpathentry kind="src" path="packages/apps/HTMLViewer/src"/> + <classpathentry kind="src" path="packages/apps/InCallUI/src"/> <classpathentry kind="src" path="packages/apps/Launcher2/src"/> <classpathentry kind="src" path="packages/apps/Mms/src"/> <classpathentry kind="src" path="packages/apps/Nfc/src"/> @@ -38,6 +39,8 @@ <classpathentry kind="src" path="packages/screensavers/Basic/src"/> <classpathentry kind="src" path="packages/screensavers/PhotoTable/src"/> <classpathentry kind="src" path="packages/screensavers/WebView/src"/> + <classpathentry kind="src" path="packages/services/Telephony/src"/> + <classpathentry kind="src" path="packages/services/Telephony/common/src"/> <classpathentry kind="src" path="frameworks/base/cmds/am/src"/> <classpathentry kind="src" path="frameworks/base/cmds/input/src"/> <classpathentry kind="src" path="frameworks/base/cmds/pm/src"/> @@ -50,6 +53,7 @@ <classpathentry kind="src" path="frameworks/base/location/java"/> <classpathentry kind="src" path="frameworks/base/location/lib/java"/> <classpathentry kind="src" path="frameworks/base/media/java"/> + <classpathentry kind="src" path="frameworks/base/media/tests/MediaFrameworkTest/src"/> <classpathentry kind="src" path="frameworks/base/media/mca/effect/java"/> <classpathentry kind="src" path="frameworks/base/media/mca/filterfw/java"/> <classpathentry kind="src" path="frameworks/base/media/mca/filterpacks/java"/> @@ -111,21 +115,24 @@ <classpathentry kind="src" path="out/target/common/obj/APPS/SystemUI_intermediates/src/src"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/android-common-carousel_intermediates/src/renderscript/src"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates/src/src"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/keystore/java"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location/java"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java"/> - <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/wifi/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/keystore/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/location/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/telephony/java"/> + <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/wifi/java"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/NfcLogTags_intermediates/src/src"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/services_intermediates/src"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/src/src/java"/> <classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/voip-common_intermediates/src/src/java"/> <classpathentry kind="src" path="out/target/common/R"/> + <classpathentry kind="src" path="pdk/apps/TestingCamera2/src"/> <classpathentry kind="src" path="external/apache-http/src"/> <classpathentry kind="src" path="external/bouncycastle/bcprov/src/main/java"/> <classpathentry kind="src" path="external/guava/guava/src"/> + <classpathentry kind="src" path="external/junit/src"/> <classpathentry kind="src" path="external/libphonenumber/java/src"/> + <classpathentry kind="src" path="external/mockito/src"/> <classpathentry kind="src" path="external/mp4parser/isoparser/src/main/java"/> <classpathentry kind="src" path="external/nist-sip/java"/> <classpathentry kind="src" path="external/tagsoup/src"/> diff --git a/ide/eclipse/README.importing-to-eclipse.txt b/ide/eclipse/README.importing-to-eclipse.txt index dffa0ab22..c98bf52e5 100644 --- a/ide/eclipse/README.importing-to-eclipse.txt +++ b/ide/eclipse/README.importing-to-eclipse.txt @@ -1,6 +1,33 @@ +(Java) + To import the formatter, go to the preferences, section Java > Code Style > formatter, then click on import and choose development/ide/eclipse/android-formatting.xml To import the import order, to go into Java > Code Style > Organize Import, then click on import and choose development/ide/eclipse/android.importorder + +(C++) + +To import the include paths, go to Project > Properties > C/C++ General > +Paths and Symbols, then click on "Includes" and then click on "Import Settings". +Choose development/ide/eclipse/android-include-paths.xml and hit Finish. +You will need to re-index for the changes to get picked up (right click project +in Package Explorer, then Index > Rebuild). + +To import the symbols, go to Project > Properties > C/C++ General > +Paths and Symbols, then click on "Symbols" and then click on "Import Settings". +Choose development/ide/eclipse/android-symbols.xml and hit Finish. +You will need to re-index for the changes to get picked up (right click project +in Package Explorer, then Index > Rebuild). + +In addition, you will need to add some include files (no way to import this +from an XML file) by hand. Go to Project > Properties > C/C++ General > +Paths and Symbols, then click on "Include Files" and click on "Add". Check +"Add to all configurations" and "Add to all languages". Repeat for these files: + + ${ProjDirPath}/build/core/combo/include/arch/linux-arm/AndroidConfig.h + +If you are having trouble seeing the "Include Files" tab, you will need to +enable it in the global preference panel under "C/C++" / +"Property Pages Settings". diff --git a/ide/eclipse/android-include-paths.xml b/ide/eclipse/android-include-paths.xml index be188bd47..4487fa43a 100644 --- a/ide/eclipse/android-include-paths.xml +++ b/ide/eclipse/android-include-paths.xml @@ -31,6 +31,7 @@ <includepath>${ProjDirPath}/system/core/include/arch/linux-arm</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include/nativehelper</includepath> +<includepath>${ProjDirPath}/system/media/camera/include</includepath> </language> <language name="GNU C++"> @@ -60,6 +61,7 @@ <includepath>${ProjDirPath}/system/core/include/arch/linux-arm</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include/nativehelper</includepath> +<includepath>${ProjDirPath}/system/media/camera/include</includepath> </language> <language name="GNU C"> @@ -89,6 +91,7 @@ <includepath>${ProjDirPath}/system/core/include/arch/linux-arm</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include</includepath> <includepath>${ProjDirPath}/dalvik/libnativehelper/include/nativehelper</includepath> +<includepath>${ProjDirPath}/system/media/camera/include</includepath> </language> </section> diff --git a/ide/intellij/codestyles/AndroidStyle.xml b/ide/intellij/codestyles/AndroidStyle.xml index cd6beb4a5..672c4e584 100644 --- a/ide/intellij/codestyles/AndroidStyle.xml +++ b/ide/intellij/codestyles/AndroidStyle.xml @@ -9,40 +9,49 @@ <option name="SMART_TABS" value="false" /> <option name="LABEL_INDENT_SIZE" value="0" /> <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> </value> </option> - <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" /> - <option name="ALIGN_MULTILINE_PARAMETERS" value="false" /> - <option name="ALIGN_MULTILINE_FOR" value="false" /> - <option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> - <option name="BLANK_LINES_AROUND_FIELD" value="1" /> - <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" /> <option name="FIELD_NAME_PREFIX" value="m" /> <option name="STATIC_FIELD_NAME_PREFIX" value="m" /> - <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> - <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> + <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" /> + <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" /> <option name="IMPORT_LAYOUT_TABLE"> <value> - <package name="com.google" withSubpackages="true" /> + <package name="com.google" withSubpackages="true" static="false" /> <emptyLine /> - <package name="com" withSubpackages="true" /> + <package name="com" withSubpackages="true" static="false" /> <emptyLine /> - <package name="junit" withSubpackages="true" /> + <package name="junit" withSubpackages="true" static="false" /> <emptyLine /> - <package name="net" withSubpackages="true" /> + <package name="net" withSubpackages="true" static="false" /> <emptyLine /> - <package name="org" withSubpackages="true" /> + <package name="org" withSubpackages="true" static="false" /> <emptyLine /> - <package name="android" withSubpackages="true" /> + <package name="android" withSubpackages="true" static="false" /> <emptyLine /> - <package name="java" withSubpackages="true" /> + <package name="java" withSubpackages="true" static="false" /> <emptyLine /> - <package name="javax" withSubpackages="true" /> + <package name="javax" withSubpackages="true" static="false" /> <emptyLine /> - <package name="" withSubpackages="true" /> + <package name="" withSubpackages="true" static="false" /> + <emptyLine /> + <package name="" withSubpackages="true" static="true" /> </value> </option> <option name="RIGHT_MARGIN" value="100" /> + <option name="JD_P_AT_EMPTY_LINES" value="false" /> + <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" /> + <option name="JD_KEEP_EMPTY_PARAMETER" value="false" /> + <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" /> + <option name="JD_KEEP_EMPTY_RETURN" value="false" /> + <option name="JD_PRESERVE_LINE_FEEDS" value="true" /> + <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" /> + <option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> + <option name="BLANK_LINES_AROUND_FIELD" value="1" /> + <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" /> + <option name="ALIGN_MULTILINE_PARAMETERS" value="false" /> + <option name="ALIGN_MULTILINE_FOR" value="false" /> <option name="CALL_PARAMETERS_WRAP" value="1" /> <option name="METHOD_PARAMETERS_WRAP" value="1" /> <option name="EXTENDS_LIST_WRAP" value="1" /> @@ -63,9 +72,104 @@ <option name="DOWHILE_BRACE_FORCE" value="3" /> <option name="WHILE_BRACE_FORCE" value="3" /> <option name="FOR_BRACE_FORCE" value="3" /> - <option name="JD_P_AT_EMPTY_LINES" value="false" /> - <option name="JD_KEEP_EMPTY_PARAMETER" value="false" /> - <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" /> - <option name="JD_KEEP_EMPTY_RETURN" value="false" /> + <ADDITIONAL_INDENT_OPTIONS fileType="css"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="java"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="8" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="js"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="4" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="jsp"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="sql"> + <option name="INDENT_SIZE" value="2" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="xml"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="yml"> + <option name="INDENT_SIZE" value="2" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + <option name="USE_RELATIVE_INDENTS" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <codeStyleSettings language="JavaScript"> + <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" /> + <option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> + <option name="BLANK_LINES_AROUND_FIELD" value="1" /> + <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" /> + <option name="ALIGN_MULTILINE_PARAMETERS" value="false" /> + <option name="ALIGN_MULTILINE_FOR" value="false" /> + <option name="CALL_PARAMETERS_WRAP" value="1" /> + <option name="METHOD_PARAMETERS_WRAP" value="1" /> + <option name="EXTENDS_LIST_WRAP" value="1" /> + <option name="THROWS_LIST_WRAP" value="1" /> + <option name="EXTENDS_KEYWORD_WRAP" value="1" /> + <option name="THROWS_KEYWORD_WRAP" value="1" /> + <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> + <option name="BINARY_OPERATION_WRAP" value="1" /> + <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> + <option name="TERNARY_OPERATION_WRAP" value="1" /> + <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" /> + <option name="FOR_STATEMENT_WRAP" value="1" /> + <option name="ARRAY_INITIALIZER_WRAP" value="1" /> + <option name="ASSIGNMENT_WRAP" value="1" /> + <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" /> + <option name="WRAP_COMMENTS" value="true" /> + <option name="IF_BRACE_FORCE" value="3" /> + <option name="DOWHILE_BRACE_FORCE" value="3" /> + <option name="WHILE_BRACE_FORCE" value="3" /> + <option name="FOR_BRACE_FORCE" value="3" /> + <option name="PARENT_SETTINGS_INSTALLED" value="true" /> + </codeStyleSettings> </code_scheme> diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 34464a0f6..608220a53 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -643,6 +643,11 @@ </intent-filter> </activity> + <!-- Stub for memory testing. --> + + <receiver android:name=".app.DoNothing" + android:process=":empty" android:exported="true" /> + <!-- ============================ --> <!-- Accessibility examples --> <!-- ============================ --> @@ -990,16 +995,6 @@ </intent-filter> </activity> - <!-- Accessibility Samples --> - <activity android:name=".accessibility.AccessibilityNodeProviderActivity" - android:label="@string/accessibility_node_provider" - android:enabled="@bool/atLeastIceCreamSandwich"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.SAMPLE_CODE" /> - </intent-filter> - </activity> - <!-- Application Updating Samples --> <!-- BEGIN_INCLUDE(app_update_declaration) --> @@ -1010,6 +1005,13 @@ </receiver> <!-- END_INCLUDE(app_update_declaration) --> + <receiver android:name=".app.AppUpdateSspReceiver"> + <intent-filter> + <action android:name="android.intent.action.PACKAGE_REPLACED" /> + <data android:scheme="package" android:ssp="com.example.android.apis" /> + </intent-filter> + </receiver> + <!-- ************************************* --> <!-- PREFERENCE PACKAGE SAMPLES --> <!-- ************************************* --> @@ -1117,6 +1119,14 @@ </intent-filter> </activity> + <activity android:name=".content.TextUndoActivity" android:label="@string/activity_text_undo"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + <category android:name="android.intent.category.EMBED" /> + </intent-filter> + </activity> + <activity android:name=".content.ResourcesLayoutReference" android:label="@string/activity_resources_layout_reference"> <intent-filter> diff --git a/samples/ApiDemos/res/layout/intents.xml b/samples/ApiDemos/res/layout/intents.xml index aed709d76..e301b0bdf 100644 --- a/samples/ApiDemos/res/layout/intents.xml +++ b/samples/ApiDemos/res/layout/intents.xml @@ -17,7 +17,8 @@ <!-- Demonstrates launching various intents. See corresponding Java code com.example.android.apis.app.Intents.java. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:padding="4dip" android:gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -29,9 +30,22 @@ <Button android:id="@+id/get_music" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/get_music"> + android:text="@string/get_music" + android:onClick="onGetMusic"> <requestFocus /> </Button> + <Button android:id="@+id/get_image" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@string/get_image" + android:onClick="onGetImage"> + </Button> + + <Button android:id="@+id/get_stream" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@string/get_stream" + android:onClick="onGetStream"> + </Button> + </LinearLayout> diff --git a/samples/ApiDemos/res/layout/text_undo.xml b/samples/ApiDemos/res/layout/text_undo.xml new file mode 100644 index 000000000..f7d74e820 --- /dev/null +++ b/samples/ApiDemos/res/layout/text_undo.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<!-- Demonstrates saving and restoring activity state. + See corresponding Java code com.android.sdk.app.SaveRestoreState.java. --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent"> + <LinearLayout android:orientation="vertical" android:padding="4dip" + android:layout_width="match_parent" android:layout_height="wrap_content"> + + <TextView android:id="@+id/msg" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="0" android:textAppearance="?android:attr/textAppearanceMedium" + android:paddingBottom="8dip" + android:text="@string/text_undo_msg" /> + + <EditText android:id="@+id/text" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="?android:attr/textAppearanceMedium" + android:freezesText="true"> + </EditText> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingTop="8dip"> + <Button + android:id="@+id/undo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/undo" + android:layout_gravity="bottom" /> + <Button + android:id="@+id/redo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/redo" + android:layout_gravity="bottom" /> + </LinearLayout> + + </LinearLayout> +</ScrollView> diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 9329cdd8e..d81f76920 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -421,6 +421,12 @@ <string name="styled_text">Plain, <b>bold</b>, <i>italic</i>, <b><i>bold-italic</i></b></string> <string name="styled_text_prog">Assigned programmatically:</string> + <string name="activity_text_undo">Content/Undo Manager/Text</string> + <string name="text_undo_msg">Demonstrates simple use of UndoManager with text editing + in a TextView.</string> + <string name="undo">Undo</string> + <string name="redo">Redo</string> + <string name="activity_resources_layout_reference">Content/Resources/Layout Reference</string> <string name="resources_layout_reference_description">Shows how to write layout resource references, so that you can define multiple different configurations of @@ -461,6 +467,8 @@ <string name="activity_intents">App/Activity/Intents</string> <string name="intents">Example of launching various Intents.</string> <string name="get_music">Get Music</string> + <string name="get_image">Get Image</string> + <string name="get_stream">Get Stream</string> <!-- ============================== --> <!-- app/intents activity flags examples strings --> @@ -1419,10 +1427,6 @@ <!-- Accessibility examples strings --> <!-- ============================ --> - <string name="accessibility_node_provider">Accessibility/Accessibility Node Provider</string> - <string name="accessibility_node_provider_instructions">Enable TalkBack and Explore-by-touch from accessibility - settings. Then touch the colored squares.</string> - <string name="accessibility_service">Accessibility/Accessibility Service</string> <string name="accessibility_service_label">ClockBack</string> <string name="accessibility_service_instructions"> diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java deleted file mode 100644 index 1ca036a55..000000000 --- a/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2011 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.example.android.apis.accessibility; - -import com.example.android.apis.R; - -import android.app.Activity; -import android.app.Service; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeProvider; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * This sample demonstrates how a View can expose a virtual view sub-tree - * rooted at it. A virtual sub-tree is composed of imaginary Views - * that are reported as a part of the view hierarchy for accessibility - * purposes. This enables custom views that draw complex content to report - * them selves as a tree of virtual views, thus conveying their logical - * structure. - * <p> - * For example, a View may draw a monthly calendar as a grid of days while - * each such day may contains some events. From a perspective of the View - * hierarchy the calendar is composed of a single View but an accessibility - * service would benefit of traversing the logical structure of the calendar - * by examining each day and each event on that day. - * </p> - */ -public class AccessibilityNodeProviderActivity extends Activity { - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.accessibility_node_provider); - } - - /** - * This class presents a View that is composed of three virtual children - * each of which is drawn with a different color and represents a region - * of the View that has different semantics compared to other such regions. - * While the virtual view tree exposed by this class is one level deep - * for simplicity, there is no bound on the complexity of that virtual - * sub-tree. - */ - public static class VirtualSubtreeRootView extends View { - - /** Paint object for drawing the virtual sub-tree */ - private final Paint mPaint = new Paint(); - - /** Temporary rectangle to minimize object creation. */ - private final Rect mTempRect = new Rect(); - - /** Handle to the system accessibility service. */ - private final AccessibilityManager mAccessibilityManager; - - /** The virtual children of this View. */ - private final List<VirtualView> mChildren = new ArrayList<VirtualView>(); - - /** The instance of the node provider for the virtual tree - lazily instantiated. */ - private AccessibilityNodeProvider mAccessibilityNodeProvider; - - /** The last hovered child used for event dispatching. */ - private VirtualView mLastHoveredChild; - - public VirtualSubtreeRootView(Context context, AttributeSet attrs) { - super(context, attrs); - mAccessibilityManager = (AccessibilityManager) context.getSystemService( - Service.ACCESSIBILITY_SERVICE); - createVirtualChildren(); - } - - /** - * {@inheritDoc} - */ - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider() { - // Instantiate the provide only when requested. Since the system - // will call this method multiple times it is a good practice to - // cache the provider instance. - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = new VirtualDescendantsProvider(); - } - return mAccessibilityNodeProvider; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean dispatchHoverEvent(MotionEvent event) { - // This implementation assumes that the virtual children - // cannot overlap and are always visible. Do NOT use this - // code as a reference of how to implement hover event - // dispatch. Instead, refer to ViewGroup#dispatchHoverEvent. - boolean handled = false; - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - Rect childBounds = child.mBounds; - final int childCoordsX = (int) event.getX() + getScrollX(); - final int childCoordsY = (int) event.getY() + getScrollY(); - if (!childBounds.contains(childCoordsX, childCoordsY)) { - continue; - } - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_HOVER_ENTER: { - mLastHoveredChild = child; - handled |= onHoverVirtualView(child, event); - event.setAction(action); - } break; - case MotionEvent.ACTION_HOVER_MOVE: { - if (child == mLastHoveredChild) { - handled |= onHoverVirtualView(child, event); - event.setAction(action); - } else { - MotionEvent eventNoHistory = event.getHistorySize() > 0 - ? MotionEvent.obtainNoHistory(event) : event; - eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT); - onHoverVirtualView(mLastHoveredChild, eventNoHistory); - eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER); - onHoverVirtualView(child, eventNoHistory); - mLastHoveredChild = child; - eventNoHistory.setAction(MotionEvent.ACTION_HOVER_MOVE); - handled |= onHoverVirtualView(child, eventNoHistory); - if (eventNoHistory != event) { - eventNoHistory.recycle(); - } else { - event.setAction(action); - } - } - } break; - case MotionEvent.ACTION_HOVER_EXIT: { - mLastHoveredChild = null; - handled |= onHoverVirtualView(child, event); - event.setAction(action); - } break; - } - } - if (!handled) { - handled |= onHoverEvent(event); - } - return handled; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - // The virtual children are ordered horizontally next to - // each other and take the entire space of this View. - int offsetX = 0; - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - Rect childBounds = child.mBounds; - childBounds.set(offsetX, 0, offsetX + childBounds.width(), childBounds.height()); - offsetX += childBounds.width(); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // The virtual children are ordered horizontally next to - // each other and take the entire space of this View. - int width = 0; - int height = 0; - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - width += child.mBounds.width(); - height = Math.max(height, child.mBounds.height()); - } - setMeasuredDimension(width, height); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onDraw(Canvas canvas) { - // Draw the virtual children with the reusable Paint object - // and with the bounds and color which are child specific. - Rect drawingRect = mTempRect; - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - drawingRect.set(child.mBounds); - mPaint.setColor(child.mColor); - mPaint.setAlpha(child.mAlpha); - canvas.drawRect(drawingRect, mPaint); - } - } - - /** - * Creates the virtual children of this View. - */ - private void createVirtualChildren() { - // The virtual portion of the tree is one level deep. Note - // that implementations can use any way of representing and - // drawing virtual view. - VirtualView firstChild = new VirtualView(0, new Rect(0, 0, 150, 150), Color.RED, - "Virtual view 1"); - mChildren.add(firstChild); - VirtualView secondChild = new VirtualView(1, new Rect(0, 0, 150, 150), Color.GREEN, - "Virtual view 2"); - mChildren.add(secondChild); - VirtualView thirdChild = new VirtualView(2, new Rect(0, 0, 150, 150), Color.BLUE, - "Virtual view 3"); - mChildren.add(thirdChild); - } - - /** - * Set the selected state of a virtual view. - * - * @param virtualView The virtual view whose selected state to set. - * @param selected Whether the virtual view is selected. - */ - private void setVirtualViewSelected(VirtualView virtualView, boolean selected) { - virtualView.mAlpha = selected ? VirtualView.ALPHA_SELECTED : VirtualView.ALPHA_NOT_SELECTED; - } - - /** - * Handle a hover over a virtual view. - * - * @param virtualView The virtual view over which is hovered. - * @param event The event to dispatch. - * @return Whether the event was handled. - */ - private boolean onHoverVirtualView(VirtualView virtualView, MotionEvent event) { - // The implementation of hover event dispatch can be implemented - // in any way that is found suitable. However, each virtual View - // should fire a corresponding accessibility event whose source - // is that virtual view. Accessibility services get the event source - // as the entry point of the APIs for querying the window content. - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_HOVER_ENTER: { - sendAccessibilityEventForVirtualView(virtualView, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); - } break; - case MotionEvent.ACTION_HOVER_EXIT: { - sendAccessibilityEventForVirtualView(virtualView, - AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); - } break; - } - return true; - } - - /** - * Sends a properly initialized accessibility event for a virtual view.. - * - * @param virtualView The virtual view. - * @param eventType The type of the event to send. - */ - private void sendAccessibilityEventForVirtualView(VirtualView virtualView, int eventType) { - // If touch exploration, i.e. the user gets feedback while touching - // the screen, is enabled we fire accessibility events. - if (mAccessibilityManager.isTouchExplorationEnabled()) { - AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - event.setPackageName(getContext().getPackageName()); - event.setClassName(virtualView.getClass().getName()); - event.setSource(VirtualSubtreeRootView.this, virtualView.mId); - event.getText().add(virtualView.mText); - getParent().requestSendAccessibilityEvent(VirtualSubtreeRootView.this, event); - } - } - - /** - * Finds a virtual view given its id. - * - * @param id The virtual view id. - * @return The found virtual view. - */ - private VirtualView findVirtualViewById(int id) { - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - if (child.mId == id) { - return child; - } - } - return null; - } - - /** - * Represents a virtual View. - */ - private class VirtualView { - public static final int ALPHA_SELECTED = 255; - public static final int ALPHA_NOT_SELECTED = 127; - - public final int mId; - public final int mColor; - public final Rect mBounds; - public final String mText; - public int mAlpha; - - public VirtualView(int id, Rect bounds, int color, String text) { - mId = id; - mColor = color; - mBounds = bounds; - mText = text; - mAlpha = ALPHA_NOT_SELECTED; - } - } - - /** - * This is the provider that exposes the virtual View tree to accessibility - * services. From the perspective of an accessibility service the - * {@link AccessibilityNodeInfo}s it receives while exploring the sub-tree - * rooted at this View will be the same as the ones it received while - * exploring a View containing a sub-tree composed of real Views. - */ - private class VirtualDescendantsProvider extends AccessibilityNodeProvider { - - /** - * {@inheritDoc} - */ - @Override - public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { - AccessibilityNodeInfo info = null; - if (virtualViewId == View.NO_ID) { - // We are requested to create an AccessibilityNodeInfo describing - // this View, i.e. the root of the virtual sub-tree. Note that the - // host View has an AccessibilityNodeProvider which means that this - // provider is responsible for creating the node info for that root. - info = AccessibilityNodeInfo.obtain(VirtualSubtreeRootView.this); - onInitializeAccessibilityNodeInfo(info); - // Add the virtual children of the root View. - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - info.addChild(VirtualSubtreeRootView.this, child.mId); - } - } else { - // Find the view that corresponds to the given id. - VirtualView virtualView = findVirtualViewById(virtualViewId); - if (virtualView == null) { - return null; - } - // Obtain and initialize an AccessibilityNodeInfo with - // information about the virtual view. - info = AccessibilityNodeInfo.obtain(); - info.addAction(AccessibilityNodeInfo.ACTION_SELECT); - info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); - info.setPackageName(getContext().getPackageName()); - info.setClassName(virtualView.getClass().getName()); - info.setSource(VirtualSubtreeRootView.this, virtualViewId); - info.setBoundsInParent(virtualView.mBounds); - info.setParent(VirtualSubtreeRootView.this); - info.setText(virtualView.mText); - } - return info; - } - - /** - * {@inheritDoc} - */ - @Override - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched, - int virtualViewId) { - if (TextUtils.isEmpty(searched)) { - return Collections.emptyList(); - } - String searchedLowerCase = searched.toLowerCase(); - List<AccessibilityNodeInfo> result = null; - if (virtualViewId == View.NO_ID) { - // If the search is from the root, i.e. this View, go over the virtual - // children and look for ones that contain the searched string since - // this View does not contain text itself. - List<VirtualView> children = mChildren; - final int childCount = children.size(); - for (int i = 0; i < childCount; i++) { - VirtualView child = children.get(i); - String textToLowerCase = child.mText.toLowerCase(); - if (textToLowerCase.contains(searchedLowerCase)) { - if (result == null) { - result = new ArrayList<AccessibilityNodeInfo>(); - } - result.add(createAccessibilityNodeInfo(child.mId)); - } - } - } else { - // If the search is from a virtual view, find the view. Since the tree - // is one level deep we add a node info for the child to the result if - // the child contains the searched text. - VirtualView virtualView = findVirtualViewById(virtualViewId); - if (virtualView != null) { - String textToLowerCase = virtualView.mText.toLowerCase(); - if (textToLowerCase.contains(searchedLowerCase)) { - result = new ArrayList<AccessibilityNodeInfo>(); - result.add(createAccessibilityNodeInfo(virtualViewId)); - } - } - } - if (result == null) { - return Collections.emptyList(); - } - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean performAction(int virtualViewId, int action, Bundle arguments) { - if (virtualViewId == View.NO_ID) { - // Perform the action on the host View. - switch (action) { - case AccessibilityNodeInfo.ACTION_SELECT: - if (!isSelected()) { - setSelected(true); - return isSelected(); - } - break; - case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: - if (isSelected()) { - setSelected(false); - return !isSelected(); - } - break; - } - } else { - // Find the view that corresponds to the given id. - VirtualView child = findVirtualViewById(virtualViewId); - if (child == null) { - return false; - } - // Perform the action on a virtual view. - switch (action) { - case AccessibilityNodeInfo.ACTION_SELECT: - setVirtualViewSelected(child, true); - invalidate(); - return true; - case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: - setVirtualViewSelected(child, false); - invalidate(); - return true; - } - } - return false; - } - } - } -} diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html index df54e966b..1324f8682 100644 --- a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html +++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html @@ -21,12 +21,6 @@ xml files, and adding additional information to AccessibilityEvents using AccessibilityRecords. </dd> - <dt><a href="AccessibilityNodeProviderActivity.html">Accessibility Node Provider</a></dt> - <dd>Demonstrates how to develop an accessibility node provider which manages a virtual - View tree reported to accessibility services. The virtual subtree is rooted at a View - that draws complex content and reports itself as a tree of virtual views, thus conveying - its logical structure. - </dd> </dl> <dl> diff --git a/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java b/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java new file mode 100644 index 000000000..dfa265c64 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 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.example.android.apis.app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.widget.Toast; + +/** + * Executed when a new version of the application is is installed. + */ +public class AppUpdateSspReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String msg = "Ssp update received: " + intent.getData(); + Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java b/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java new file mode 100644 index 000000000..133a80260 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 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.example.android.apis.app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class DoNothing extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/Intents.java b/samples/ApiDemos/src/com/example/android/apis/app/Intents.java index 8f02b8322..6207dd805 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/Intents.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/Intents.java @@ -31,17 +31,23 @@ public class Intents extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.intents); + } + + public void onGetMusic(View view) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("audio/*"); + startActivity(Intent.createChooser(intent, "Select music")); + } - // Watch for button clicks. - Button button = (Button)findViewById(R.id.get_music); - button.setOnClickListener(mGetMusicListener); + public void onGetImage(View view) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + startActivity(Intent.createChooser(intent, "Select image")); } - private OnClickListener mGetMusicListener = new OnClickListener() { - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("audio/*"); - startActivity(Intent.createChooser(intent, "Select music")); - } - }; + public void onGetStream(View view) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + startActivity(Intent.createChooser(intent, "Select stream")); + } } diff --git a/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java b/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java new file mode 100644 index 000000000..dd309c955 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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.example.android.apis.content; + +import android.app.Activity; +import android.content.UndoManager; +import android.os.Parcelable; +import android.view.View; +import android.widget.Button; +import com.example.android.apis.R; + +import android.os.Bundle; +import android.widget.TextView; + +/** + * Simple example of using an UndoManager for editing text in a TextView. + */ +public class TextUndoActivity extends Activity { + UndoManager mUndoManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mUndoManager = new UndoManager(); + if (savedInstanceState != null) { + Parcelable p = savedInstanceState.getParcelable("undo"); + if (p != null) { + mUndoManager.restoreInstanceState(p); + } + } + + setContentView(R.layout.text_undo); + + ((TextView)findViewById(R.id.text)).setUndoManager(mUndoManager, "text"); + ((Button)findViewById(R.id.undo)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mUndoManager.undo(null, 1); + } + }); + ((Button)findViewById(R.id.redo)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mUndoManager.redo(null, 1); + } + }); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable("undo", mUndoManager.saveInstanceState()); + } +} diff --git a/samples/HelloActivity/src/com/example/android/helloactivity/HelloActivity.java b/samples/HelloActivity/src/com/example/android/helloactivity/HelloActivity.java index f983d7a99..f4babe7b2 100644 --- a/samples/HelloActivity/src/com/example/android/helloactivity/HelloActivity.java +++ b/samples/HelloActivity/src/com/example/android/helloactivity/HelloActivity.java @@ -37,17 +37,6 @@ public class HelloActivity extends Activity { // in res/layout/hello_activity.xml View view = getLayoutInflater().inflate(R.layout.hello_activity, null); setContentView(view); - - WindowManager.LayoutParams params = getWindow().getAttributes(); - params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - getWindow().setAttributes(params); - view.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { - @Override public void onSystemUiVisibilityChange(int visibility) { - WindowManager.LayoutParams params = getWindow().getAttributes(); - params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - getWindow().setAttributes(params); - } - }); } } diff --git a/samples/Support4Demos/AndroidManifest.xml b/samples/Support4Demos/AndroidManifest.xml index cc98540a5..ce6c29c22 100644 --- a/samples/Support4Demos/AndroidManifest.xml +++ b/samples/Support4Demos/AndroidManifest.xml @@ -321,6 +321,14 @@ </intent-filter> </activity> + <activity android:name=".widget.ExploreByTouchHelperActivity" + android:label="@string/explore_by_touch_helper_support"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" /> + </intent-filter> + </activity> + <provider android:authorities="com.example.supportv4.content.sharingsupportprovider" android:name=".content.SharingSupportProvider" /> diff --git a/samples/ApiDemos/res/layout/accessibility_node_provider.xml b/samples/Support4Demos/res/layout/explore_by_touch_helper.xml index cc10c9cd2..0b367a458 100644 --- a/samples/ApiDemos/res/layout/accessibility_node_provider.xml +++ b/samples/Support4Demos/res/layout/explore_by_touch_helper.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- Copyright (C) 2013 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. @@ -13,22 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="50dip" - android:text="@string/accessibility_node_provider_instructions"> - </TextView> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > <view - class="com.example.android.apis.accessibility.AccessibilityNodeProviderActivity$VirtualSubtreeRootView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" > - </view> + class="com.example.android.supportv4.widget.ExploreByTouchHelperActivity$CustomView" + android:id="@+id/custom_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> -</LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml index 9949ee067..ce41aafc7 100644 --- a/samples/Support4Demos/res/values/strings.xml +++ b/samples/Support4Demos/res/values/strings.xml @@ -167,6 +167,13 @@ <string name="sliding_pane_layout_summary">This activity illustrates the use of sliding panes. The content pane may be slid to one side on narrow devices to reveal the left pane used to select content. Sliding panes can be used to fit a UI intended for wider screens in a smaller space. Tapping the Action Bar\'s Up button at the left side of the bar will navigate up in the hierarchy, represented by the left pane. If you rotate the device to landscape mode, on most devices you will see that both panes fit together side by side with no sliding necessary.</string> + <!-- ExploreByTouchHelper --> + + <string name="explore_by_touch_helper_support">Widget/Explore by Touch helper</string> + <string name="sample_item_a">Sample item A</string> + <string name="sample_item_b">Sample item B</string> + <!-- ContentLoadingProgressBar --> <string name="content_loading_progress_bar">Widget/Content Loading Progress Bar</string> + </resources> diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java new file mode 100644 index 000000000..946de010a --- /dev/null +++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2013 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.example.android.supportv4.widget; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; +import android.support.v4.widget.ExploreByTouchHelper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import com.example.android.supportv4.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * This example shows how to use the {@link ExploreByTouchHelper} class in the + * Android support library to add accessibility support to a custom view that + * represents multiple logical items. + * <p> + * The {@link ExploreByTouchHelper} class wraps + * {@link AccessibilityNodeProviderCompat} and simplifies exposing information + * about a custom view's logical structure to accessibility services. + * <p> + * The custom view in this example is responsible for: + * <ul> + * <li>Creating a helper class that extends {@link ExploreByTouchHelper} + * <li>Setting the helper as the accessibility delegate using + * {@link ViewCompat#setAccessibilityDelegate} + * <li>Dispatching hover events to the helper in {@link View#dispatchHoverEvent} + * </ul> + * <p> + * The helper class implementation in this example is responsible for: + * <ul> + * <li>Mapping hover event coordinates to logical items + * <li>Exposing information about logical items to accessibility services + * <li>Handling accessibility actions + * <ul> + */ +public class ExploreByTouchHelperActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.explore_by_touch_helper); + + final CustomView customView = (CustomView) findViewById(R.id.custom_view); + + // Adds an item at the top-left quarter of the custom view. + customView.addItem(getString(R.string.sample_item_a), 0, 0, 0.5f, 0.5f); + + // Adds an item at the bottom-right quarter of the custom view. + customView.addItem(getString(R.string.sample_item_b), 0.5f, 0.5f, 1, 1); + } + + /** + * Simple custom view that draws rectangular items to the screen. Each item + * has a checked state that may be toggled by tapping on the item. + */ + public static class CustomView extends View { + private static final int NO_ITEM = -1; + + private final Paint mPaint = new Paint(); + private final Rect mTempBounds = new Rect(); + private final List<CustomItem> mItems = new ArrayList<CustomItem>(); + private CustomViewTouchHelper mTouchHelper; + + public CustomView(Context context, AttributeSet attrs) { + super(context, attrs); + + // Set up accessibility helper class. + mTouchHelper = new CustomViewTouchHelper(this); + ViewCompat.setAccessibilityDelegate(this, mTouchHelper); + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Always attempt to dispatch hover events to accessibility first. + if (mTouchHelper.dispatchHoverEvent(event)) { + return true; + } + + return super.dispatchHoverEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + return true; + case MotionEvent.ACTION_UP: + final int itemIndex = getItemIndexUnder(event.getX(), event.getY()); + if (itemIndex >= 0) { + onItemClicked(itemIndex); + } + return true; + } + + return super.onTouchEvent(event); + } + + /** + * Adds an item to the custom view. The item is positioned relative to + * the custom view bounds and its descriptions is drawn at its center. + * + * @param description The item's description. + * @param top Top coordinate as a fraction of the parent height, range + * is [0,1]. + * @param left Left coordinate as a fraction of the parent width, range + * is [0,1]. + * @param bottom Bottom coordinate as a fraction of the parent height, + * range is [0,1]. + * @param right Right coordinate as a fraction of the parent width, + * range is [0,1]. + */ + public void addItem(String description, float top, float left, float bottom, float right) { + final CustomItem item = new CustomItem(); + item.bounds = new RectF(top, left, bottom, right); + item.description = description; + item.checked = false; + mItems.add(item); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + final Paint paint = mPaint; + final Rect bounds = mTempBounds; + final int height = getHeight(); + final int width = getWidth(); + + for (CustomItem item : mItems) { + paint.setColor(item.checked ? Color.RED : Color.BLUE); + paint.setStyle(Style.FILL); + scaleRectF(item.bounds, bounds, width, height); + canvas.drawRect(bounds, paint); + paint.setColor(Color.WHITE); + paint.setTextAlign(Align.CENTER); + canvas.drawText(item.description, bounds.centerX(), bounds.centerY(), paint); + } + } + + protected boolean onItemClicked(int index) { + final CustomItem item = getItem(index); + if (item == null) { + return false; + } + + item.checked = !item.checked; + invalidate(); + + // Since the item's checked state is exposed to accessibility + // services through its AccessibilityNodeInfo, we need to invalidate + // the item's virtual view. At some point in the future, the + // framework will obtain an updated version of the virtual view. + mTouchHelper.invalidateVirtualView(index); + + // We also need to let the framework know what type of event + // happened. Accessibility services may use this event to provide + // appropriate feedback to the user. + mTouchHelper.sendEventForVirtualView(index, AccessibilityEvent.TYPE_VIEW_CLICKED); + + return true; + } + + protected int getItemIndexUnder(float x, float y) { + final float scaledX = (x / getWidth()); + final float scaledY = (y / getHeight()); + final int n = mItems.size(); + + for (int i = 0; i < n; i++) { + final CustomItem item = mItems.get(i); + if (item.bounds.contains(scaledX, scaledY)) { + return i; + } + } + + return NO_ITEM; + } + + protected CustomItem getItem(int index) { + if ((index < 0) || (index >= mItems.size())) { + return null; + } + + return mItems.get(index); + } + + protected static void scaleRectF(RectF in, Rect out, int width, int height) { + out.top = (int) (in.top * height); + out.bottom = (int) (in.bottom * height); + out.left = (int) (in.left * width); + out.right = (int) (in.right * width); + } + + private class CustomViewTouchHelper extends ExploreByTouchHelper { + private final Rect mTempRect = new Rect(); + + public CustomViewTouchHelper(View forView) { + super(forView); + } + + @Override + protected int getVirtualViewAt(float x, float y) { + // We also perform hit detection in onTouchEvent(), and we can + // reuse that logic here. This will ensure consistency whether + // accessibility is on or off. + final int index = getItemIndexUnder(x, y); + if (index == NO_ITEM) { + return ExploreByTouchHelper.INVALID_ID; + } + + return index; + } + + @Override + protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { + // Since every item should be visible, and since we're mapping + // directly from item index to virtual view id, we can just add + // every available index in the item list. + final int n = mItems.size(); + for (int i = 0; i < n; i++) { + virtualViewIds.add(i); + } + } + + @Override + protected void onPopulateEventForVirtualView( + int virtualViewId, AccessibilityEvent event) { + final CustomItem item = getItem(virtualViewId); + if (item == null) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + + // The event must be populated with text, either using + // getText().add() or setContentDescription(). Since the item's + // description is displayed visually, we'll add it to the event + // text. If it was only used for accessibility, we would use + // setContentDescription(). + event.getText().add(item.description); + } + + @Override + protected void onPopulateNodeForVirtualView( + int virtualViewId, AccessibilityNodeInfoCompat node) { + final CustomItem item = getItem(virtualViewId); + if (item == null) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + + // Node and event text and content descriptions are usually + // identical, so we'll use the exact same string as before. + node.setText(item.description); + + // Reported bounds should be consistent with those used to draw + // the item in onDraw(). They should also be consistent with the + // hit detection performed in getVirtualViewAt() and + // onTouchEvent(). + final Rect bounds = mTempRect; + final int height = getHeight(); + final int width = getWidth(); + scaleRectF(item.bounds, bounds, width, height); + node.setBoundsInParent(bounds); + + // Since the user can tap an item, add the CLICK action. We'll + // need to handle this later in onPerformActionForVirtualView. + node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); + + // This item has a checked state. + node.setCheckable(true); + node.setChecked(item.checked); + } + + @Override + protected boolean onPerformActionForVirtualView( + int virtualViewId, int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfoCompat.ACTION_CLICK: + // Click handling should be consistent with + // onTouchEvent(). This ensures that the view works the + // same whether accessibility is turned on or off. + return onItemClicked(virtualViewId); + } + + return false; + } + + } + + public static class CustomItem { + private String description; + private RectF bounds; + private boolean checked; + } + } +} diff --git a/samples/training/testingfun/app/AndroidManifest.xml b/samples/training/testingfun/app/AndroidManifest.xml new file mode 100644 index 000000000..a71247a3b --- /dev/null +++ b/samples/training/testingfun/app/AndroidManifest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.testingfun" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="17" /> + <application + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme" + android:allowBackup="false"> + + <activity + android:name=".lesson2.MyFirstTestActivity" + android:label="@string/my_first_test"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".lesson3.ClickFunActivity" + android:label="@string/click_fun"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".lesson4.LaunchActivity" + android:label="@string/launch_next"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".lesson4.NextActivity" + android:label="@string/next_activity" /> + <activity + android:name=".lesson5.SenderActivity" + android:label="@string/sender_activity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".lesson5.ReceiverActivity" + android:label="@string/receiver_activity" /> + </application> +</manifest> diff --git a/samples/training/testingfun/app/build.gradle b/samples/training/testingfun/app/build.gradle new file mode 100644 index 000000000..c68484f2f --- /dev/null +++ b/samples/training/testingfun/app/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'android' + +android { + compileSdkVersion 17 + buildToolsVersion "17.0.0" + + defaultConfig { + minSdkVersion 8 + } + + android { + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src'] + aild.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + + instrumentTest.setRoot('tests') + instrumentTest.java.srcDirs = ['tests/src'] + } + } +}
\ No newline at end of file diff --git a/samples/training/testingfun/app/project.properties b/samples/training/testingfun/app/project.properties new file mode 100644 index 000000000..a3ee5ab64 --- /dev/null +++ b/samples/training/testingfun/app/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/samples/training/testingfun/app/res/drawable-hdpi/ic_launcher.png b/samples/training/testingfun/app/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..136343ea3 --- /dev/null +++ b/samples/training/testingfun/app/res/drawable-hdpi/ic_launcher.png diff --git a/samples/training/testingfun/app/res/drawable-mdpi/ic_launcher.png b/samples/training/testingfun/app/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..68372666f --- /dev/null +++ b/samples/training/testingfun/app/res/drawable-mdpi/ic_launcher.png diff --git a/samples/training/testingfun/app/res/drawable-xhdpi/ic_launcher.png b/samples/training/testingfun/app/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..e88e97585 --- /dev/null +++ b/samples/training/testingfun/app/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/training/testingfun/app/res/drawable-xxhdpi/ic_launcher.png b/samples/training/testingfun/app/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..ac31a9075 --- /dev/null +++ b/samples/training/testingfun/app/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/training/testingfun/app/res/layout/activity_click_fun.xml b/samples/training/testingfun/app/res/layout/activity_click_fun.xml new file mode 100644 index 000000000..c960c3bd8 --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_click_fun.xml @@ -0,0 +1,32 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + android:id="@+id/launch_next_activity_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/label_click_me" /> + + <TextView + android:id="@+id/info_text_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" /> +</LinearLayout>
\ No newline at end of file diff --git a/samples/training/testingfun/app/res/layout/activity_launch_next.xml b/samples/training/testingfun/app/res/layout/activity_launch_next.xml new file mode 100644 index 000000000..cfd0114f3 --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_launch_next.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2013 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. + --> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + android:id="@+id/launch_next_activity_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/label_launch_next" /> +</merge>
\ No newline at end of file diff --git a/samples/training/testingfun/app/res/layout/activity_my_first_test.xml b/samples/training/testingfun/app/res/layout/activity_my_first_test.xml new file mode 100644 index 000000000..3499bc7fd --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_my_first_test.xml @@ -0,0 +1,27 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/my_first_test_text_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/my_first_test" /> +</LinearLayout> + diff --git a/samples/training/testingfun/app/res/layout/activity_next.xml b/samples/training/testingfun/app/res/layout/activity_next.xml new file mode 100644 index 000000000..92f30f7b9 --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_next.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2013 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. + --> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/next_activity_info_text_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + +</merge>
\ No newline at end of file diff --git a/samples/training/testingfun/app/res/layout/activity_receiver.xml b/samples/training/testingfun/app/res/layout/activity_receiver.xml new file mode 100644 index 000000000..e93d16b7b --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_receiver.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2013 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. + --> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/received_message_text_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + +</merge>
\ No newline at end of file diff --git a/samples/training/testingfun/app/res/layout/activity_sender.xml b/samples/training/testingfun/app/res/layout/activity_sender.xml new file mode 100644 index 000000000..e18f4c448 --- /dev/null +++ b/samples/training/testingfun/app/res/layout/activity_sender.xml @@ -0,0 +1,33 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <EditText + android:id="@+id/message_input_edit_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/message_input_hint_edit_text" /> + + <Button + android:id="@+id/send_message_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/label_send_message" /> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/training/testingfun/app/res/values-sw720dp-land/dimens.xml b/samples/training/testingfun/app/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..3201e93b6 --- /dev/null +++ b/samples/training/testingfun/app/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/training/testingfun/app/res/values-v11/styles.xml b/samples/training/testingfun/app/res/values-v11/styles.xml new file mode 100644 index 000000000..90ff160ba --- /dev/null +++ b/samples/training/testingfun/app/res/values-v11/styles.xml @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar" /> +</resources> diff --git a/samples/training/testingfun/app/res/values-v14/styles.xml b/samples/training/testingfun/app/res/values-v14/styles.xml new file mode 100644 index 000000000..90ff160ba --- /dev/null +++ b/samples/training/testingfun/app/res/values-v14/styles.xml @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar" /> +</resources> diff --git a/samples/training/testingfun/app/res/values/dimens.xml b/samples/training/testingfun/app/res/values/dimens.xml new file mode 100644 index 000000000..e96acda70 --- /dev/null +++ b/samples/training/testingfun/app/res/values/dimens.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/samples/training/testingfun/app/res/values/strings.xml b/samples/training/testingfun/app/res/values/strings.xml new file mode 100644 index 000000000..1fc3bc02a --- /dev/null +++ b/samples/training/testingfun/app/res/values/strings.xml @@ -0,0 +1,34 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <string name="app_name">Android Testing Fun</string> + + <string name="my_first_test">My First Test</string> + + <string name="click_fun">Click Fun Test</string> + <string name="label_click_me">Click me</string> + + <string name="launch_next">Launch Activity</string> + <string name="next_activity">Next Activity</string> + + <string name="sender_activity">Sender Activity</string> + <string name="receiver_activity">Receiver Activity</string> + + <string name="info_text">Button clicked!</string> + <string name="label_launch_next">Launch Next</string> + <string name="label_send_message">Send</string> + <string name="message_input_hint_edit_text">Enter a message</string> +</resources> diff --git a/samples/training/testingfun/app/res/values/styles.xml b/samples/training/testingfun/app/res/values/styles.xml new file mode 100644 index 000000000..fa3d1bd9d --- /dev/null +++ b/samples/training/testingfun/app/res/values/styles.xml @@ -0,0 +1,20 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <style name="AppBaseTheme" parent="android:Theme.Light" /> + + <style name="AppTheme" parent="AppBaseTheme" /> +</resources> diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson2/MyFirstTestActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson2/MyFirstTestActivity.java new file mode 100644 index 000000000..db977cfeb --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson2/MyFirstTestActivity.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson2; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.os.Bundle; + +/** + * Activity with a TextView that contains a String label. + */ +public class MyFirstTestActivity extends Activity { + + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_my_first_test); + } +} diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson3/ClickFunActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson3/ClickFunActivity.java new file mode 100644 index 000000000..09db69462 --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson3/ClickFunActivity.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson3; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +/** + * Activity which shows a "click me" button. When the button is clicked, a TextView is shown below + * the button. + */ +public class ClickFunActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_click_fun); + + final TextView infoTextView = (TextView) findViewById(R.id.info_text_view); + final Button clickMeButton = (Button) findViewById(R.id.launch_next_activity_button); + clickMeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + infoTextView.setVisibility(View.VISIBLE); + infoTextView.setText(getString(R.string.info_text)); + } + }); + } +}
\ No newline at end of file diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/LaunchActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/LaunchActivity.java new file mode 100644 index 000000000..8f1fb9b8c --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/LaunchActivity.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson4; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +/** + * Launches NextActivity and passes a payload in the Bundle. + */ +public class LaunchActivity extends Activity { + + /** + * The payload that is passed as Intent data to NextActivity. + */ + public final static String STRING_PAYLOAD = "Started from LaunchActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_launch_next); + Button launchNextButton = (Button) findViewById(R.id.launch_next_activity_button); + launchNextButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(NextActivity.makeIntent(LaunchActivity.this, STRING_PAYLOAD)); + finish(); + } + }); + } +} diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/NextActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/NextActivity.java new file mode 100644 index 000000000..68965c2bb --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson4/NextActivity.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson4; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.widget.TextView; + +/** + * This activity is started from LaunchActivity. It reads the payload from the given bundle and + * displays it using a TextView. + */ +public class NextActivity extends Activity { + + /** + * Extras key for the payload. + */ + public final static String EXTRAS_PAYLOAD_KEY + = "com.example.android.testingfun.lesson4.EXTRAS_PAYLOAD_KEY"; + + /** + * Factory method to create a launch Intent for this activity. + * + * @param context the context that intent should be bound to + * @param payload the payload data that should be added for this intent + * @return a configured intent to launch this activity with a String payload. + */ + public static Intent makeIntent(Context context, String payload) { + return new Intent(context, NextActivity.class).putExtra(EXTRAS_PAYLOAD_KEY, payload); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_next); + + final String stringPayload = getIntent().getStringExtra(EXTRAS_PAYLOAD_KEY); + + if (stringPayload != null) { + ((TextView) findViewById(R.id.next_activity_info_text_view)).setText(stringPayload); + } + + } +} diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/ReceiverActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/ReceiverActivity.java new file mode 100644 index 000000000..2bc401dbe --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/ReceiverActivity.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson5; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; +import android.widget.TextView; + +/** + * Receives a message from SenderActivity and displays the message. + */ +public class ReceiverActivity extends Activity { + + /** + * The extra key that is used to identify the message in the Intents data bundle + */ + private static final String EXTRA_SENDER_MESSAGE_TEXT + = "com.example.android.testingfun.lesson5.extra.sender.message.text"; + + /** + * Factory method to create an launch intent for this activity. + * + * @param context the context to this intent should be bound to + * @param message the message data that should be added to this intent + * @return a configured intent to launch this activity with a given message + */ + public static Intent makeIntent(Context context, CharSequence message) { + return new Intent(context, ReceiverActivity.class) + .putExtra(EXTRA_SENDER_MESSAGE_TEXT, message); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_receiver); + final CharSequence senderMessage = getIntent() + .getCharSequenceExtra(EXTRA_SENDER_MESSAGE_TEXT); + final TextView receiverTextView = (TextView) findViewById(R.id.received_message_text_view); + if (!TextUtils.isEmpty(senderMessage)) { + receiverTextView.setText(senderMessage); + } + } +} diff --git a/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/SenderActivity.java b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/SenderActivity.java new file mode 100644 index 000000000..495f1e6da --- /dev/null +++ b/samples/training/testingfun/app/src/com/example/android/testingfun/lesson5/SenderActivity.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.lesson5; + +import com.example.android.testingfun.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +/** + * Sends a user generated message to the ReceiverActivity + */ +public class SenderActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sender); + final Button sendMessageButton = (Button) findViewById(R.id.send_message_button); + final EditText messageInputEditText = (EditText) findViewById(R.id.message_input_edit_text); + sendMessageButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (messageInputEditText != null) { + final CharSequence message = messageInputEditText.getText(); + startActivity(ReceiverActivity.makeIntent(SenderActivity.this, message)); + } + } + }); + } +} diff --git a/samples/training/testingfun/app/tests/AndroidManifest.xml b/samples/training/testingfun/app/tests/AndroidManifest.xml new file mode 100644 index 000000000..13ce6abd8 --- /dev/null +++ b/samples/training/testingfun/app/tests/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.testingfun.tests" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="17" /> + + <!-- We add an application tag here just so that we can indicate that + this package needs to link against the android.test library, + which is needed when building test cases. --> + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <!-- + Specifies the instrumentation test runner used to run the tests. + --> + <instrumentation + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.example.android.testingfun" + android:label="Tests for com.example.android.testingfun" /> +</manifest>
\ No newline at end of file diff --git a/samples/training/testingfun/app/tests/project.properties b/samples/training/testingfun/app/tests/project.properties new file mode 100644 index 000000000..a3ee5ab64 --- /dev/null +++ b/samples/training/testingfun/app/tests/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson2/MyFirstTestActivityTest.java b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson2/MyFirstTestActivityTest.java new file mode 100644 index 000000000..ad18b5536 --- /dev/null +++ b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson2/MyFirstTestActivityTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.tests.lesson2; + +import com.example.android.testingfun.R; +import com.example.android.testingfun.lesson2.MyFirstTestActivity; + +import android.test.ActivityInstrumentationTestCase2; +import android.widget.TextView; + +/** + * Tests for MyFirstTestActivity. + */ +public class MyFirstTestActivityTest extends ActivityInstrumentationTestCase2<MyFirstTestActivity> { + + private MyFirstTestActivity mFirstTestActivity; + private TextView mFirstTestText; + + public MyFirstTestActivityTest() { + super(MyFirstTestActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Starts the activity under test using the default Intent with: + // action = {@link Intent#ACTION_MAIN} + // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK} + // All other fields are null or empty. + mFirstTestActivity = getActivity(); + mFirstTestText = (TextView) mFirstTestActivity.findViewById(R.id.my_first_test_text_view); + } + + /** + * Test if your test fixture has been set up correctly. You should always implement a test that + * checks the correct setup of your test fixture. If this tests fails all other tests are + * likely to fail as well. + */ + public void testPreconditions() { + //Try to add a message to add context to your assertions. These messages will be shown if + //a tests fails and make it easy to understand why a test failed + assertNotNull("mFirstTestActivity is null", mFirstTestActivity); + assertNotNull("mFirstTestText is null", mFirstTestText); + } + + /** + * Tests the correctness of the initial text. + */ + public void testMyFirstTestTextView_labelText() { + //It is good practice to read the string from your resources in order to not break + //multiple tests when a string changes. + final String expected = mFirstTestActivity.getString(R.string.my_first_test); + final String actual = mFirstTestText.getText().toString(); + assertEquals("mFirstTestText contains wrong text", expected, actual); + } +}
\ No newline at end of file diff --git a/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson3/ClickFunActivityTest.java b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson3/ClickFunActivityTest.java new file mode 100644 index 000000000..5d3d387c7 --- /dev/null +++ b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson3/ClickFunActivityTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.tests.lesson3; + + +import com.example.android.testingfun.R; +import com.example.android.testingfun.lesson3.ClickFunActivity; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; +import android.test.ViewAsserts; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.TextView; + +/** + * Tests for ClickFunActivity. Introduces touch mode, test size annotations and TouchUtils. + */ +public class ClickFunActivityTest extends ActivityInstrumentationTestCase2<ClickFunActivity> { + + private ClickFunActivity mClickFunActivity; + private Button mClickMeButton; + private TextView mInfoTextView; + + public ClickFunActivityTest() { + super(ClickFunActivity.class); + } + + /** + * Sets up the test fixture for this test case. This method is always called before every test + * run. + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + + //Sets the initial touch mode for the Activity under test. This must be called before + //getActivity() + setActivityInitialTouchMode(true); + + //Get a reference to the Activity under test, starting it if necessary. + mClickFunActivity = getActivity(); + + //Get references to all views + mClickMeButton = (Button) mClickFunActivity.findViewById(R.id.launch_next_activity_button); + mInfoTextView = (TextView) mClickFunActivity.findViewById(R.id.info_text_view); + } + + /** + * Tests the preconditions of this test fixture. + */ + @MediumTest + public void testPreconditions() { + assertNotNull("mClickFunActivity is null", mClickFunActivity); + assertNotNull("mClickMeButton is null", mClickMeButton); + assertNotNull("mInfoTextView is null", mInfoTextView); + } + + @MediumTest + public void testClickMeButton_layout() { + //Retrieve the top-level window decor view + final View decorView = mClickFunActivity.getWindow().getDecorView(); + + //Verify that the mClickMeButton is on screen + ViewAsserts.assertOnScreen(decorView, mClickMeButton); + + //Verify width and heights + final ViewGroup.LayoutParams layoutParams = mClickMeButton.getLayoutParams(); + assertNotNull(layoutParams); + assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); + assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); + } + + @MediumTest + public void testClickMeButton_labelText() { + //Verify that mClickMeButton uses the correct string resource + final String expectedNextButtonText = mClickFunActivity.getString(R.string.label_click_me); + final String actualNextButtonText = mClickMeButton.getText().toString(); + assertEquals(expectedNextButtonText, actualNextButtonText); + } + + @MediumTest + public void testInfoTextView_layout() { + //Retrieve the top-level window decor view + final View decorView = mClickFunActivity.getWindow().getDecorView(); + + //Verify that the mInfoTextView is on screen and is not visible + ViewAsserts.assertOnScreen(decorView, mInfoTextView); + assertTrue(View.GONE == mInfoTextView.getVisibility()); + } + + @MediumTest + public void testInfoTextViewText_isEmpty() { + //Verify that the mInfoTextView is initialized with the correct default value + assertEquals("", mInfoTextView.getText()); + } + + @MediumTest + public void testClickMeButton_clickButtonAndExpectInfoText() { + String expectedInfoText = mClickFunActivity.getString(R.string.info_text); + //Perform a click on mClickMeButton + TouchUtils.clickView(this, mClickMeButton); + //Verify the that mClickMeButton was clicked. mInfoTextView is visible and contains + //the correct text. + assertTrue(View.VISIBLE == mInfoTextView.getVisibility()); + assertEquals(expectedInfoText, mInfoTextView.getText()); + } +} diff --git a/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson4/LaunchActivityTest.java b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson4/LaunchActivityTest.java new file mode 100644 index 000000000..7d472c39b --- /dev/null +++ b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson4/LaunchActivityTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.tests.lesson4; + +import com.example.android.testingfun.R; +import com.example.android.testingfun.lesson4.LaunchActivity; +import com.example.android.testingfun.lesson4.NextActivity; + +import android.content.Intent; +import android.test.ActivityUnitTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.widget.Button; + +/** + * Tests LaunchActivity in isolation from the system. + */ +public class LaunchActivityTest extends ActivityUnitTestCase<LaunchActivity> { + + private Intent mLaunchIntent; + + public LaunchActivityTest() { + super(LaunchActivity.class); + } + + + @Override + protected void setUp() throws Exception { + super.setUp(); + //Create an intent to launch target Activity + mLaunchIntent = new Intent(getInstrumentation().getTargetContext(), + LaunchActivity.class); + } + + /** + * Tests the preconditions of this test fixture. + */ + @MediumTest + public void testPreconditions() { + //Start the activity under test in isolation, without values for savedInstanceState and + //lastNonConfigurationInstance + startActivity(mLaunchIntent, null, null); + final Button launchNextButton = (Button) getActivity().findViewById(R.id.launch_next_activity_button); + + assertNotNull("mLaunchActivity is null", getActivity()); + assertNotNull("mLaunchNextButton is null", launchNextButton); + } + + + @MediumTest + public void testLaunchNextActivityButton_labelText() { + startActivity(mLaunchIntent, null, null); + final Button launchNextButton = (Button) getActivity().findViewById(R.id.launch_next_activity_button); + + final String expectedButtonText = getActivity().getString(R.string.label_launch_next); + assertEquals("Unexpected button label text", expectedButtonText, + launchNextButton.getText()); + } + + @MediumTest + public void testNextActivityWasLaunchedWithIntent() { + startActivity(mLaunchIntent, null, null); + final Button launchNextButton = (Button) getActivity().findViewById(R.id.launch_next_activity_button); + //Because this is an isolated ActivityUnitTestCase we have to directly click the + //button from code + launchNextButton.performClick(); + + // Get the intent for the next started activity + final Intent launchIntent = getStartedActivityIntent(); + //Verify the intent was not null. + assertNotNull("Intent was null", launchIntent); + //Verify that LaunchActivity was finished after button click + assertTrue(isFinishCalled()); + + + final String payload = launchIntent.getStringExtra(NextActivity.EXTRAS_PAYLOAD_KEY); + //Verify that payload data was added to the intent + assertEquals("Payload is empty", LaunchActivity.STRING_PAYLOAD + , payload); + } +}
\ No newline at end of file diff --git a/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson5/SenderActivityTest.java b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson5/SenderActivityTest.java new file mode 100644 index 000000000..75bc302c3 --- /dev/null +++ b/samples/training/testingfun/app/tests/src/com/example/android/testingfun/tests/lesson5/SenderActivityTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 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.example.android.testingfun.tests.lesson5; + +import com.example.android.testingfun.R; +import com.example.android.testingfun.lesson5.ReceiverActivity; +import com.example.android.testingfun.lesson5.SenderActivity; + +import android.app.Instrumentation; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; +import android.test.suitebuilder.annotation.MediumTest; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +/** + * Functional test across multiple Activities. Tests SenderActivity and ReceiverActivity. Introduces + * advanced Instrumentation testing practices as sending key events and interaction monitoring + * between Activities and the system. + */ +public class SenderActivityTest extends ActivityInstrumentationTestCase2<SenderActivity> { + + private static final int TIMEOUT_IN_MS = 5000; + private static final String TEST_MESSAGE = "Hello Receiver"; + private SenderActivity mSenderActivity; + + public SenderActivityTest() { + super(SenderActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + mSenderActivity = getActivity(); + } + + /** + * Tests the preconditions of this test fixture. + */ + @MediumTest + public void testPreconditions() { + assertNotNull("mSenderActivity is null", mSenderActivity); + } + + @MediumTest + public void testSendMessageToReceiverActivity() { + + //Because this functional test tests interaction across multiple components these views + //are part of the actual test method and not of the test fixture + final Button sendToReceiverButton = (Button) mSenderActivity + .findViewById(R.id.send_message_button); + final EditText senderMessageEditText = (EditText) mSenderActivity + .findViewById(R.id.message_input_edit_text); + + //Create and add an ActivityMonitor to monitor interaction between the system and the + //ReceiverActivity + Instrumentation.ActivityMonitor receiverActivityMonitor = getInstrumentation() + .addMonitor(ReceiverActivity.class.getName(), null, false); + + //Request focus on the EditText field. This must be done on the UiThread because + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + senderMessageEditText.requestFocus(); + } + }); + //Wait until all events from the MainHandler's queue are processed + getInstrumentation().waitForIdleSync(); + + //Send the text message + getInstrumentation().sendStringSync(TEST_MESSAGE); + getInstrumentation().waitForIdleSync(); + + //Click on the sendToReceiverButton to send the message to ReceiverActivity + TouchUtils.clickView(this, sendToReceiverButton); + + //Wait until ReceiverActivity was launched and get a reference to it. + ReceiverActivity receiverActivity = (ReceiverActivity) receiverActivityMonitor + .waitForActivityWithTimeout(TIMEOUT_IN_MS); + //Verify that ReceiverActivity was started + assertNotNull("ReceiverActivity is null", receiverActivity); + assertEquals("Monitor for ReceiverActivity has not been called", 1, + receiverActivityMonitor.getHits()); + assertEquals("Activity is of wrong type", ReceiverActivity.class, + receiverActivity.getClass()); + + //Read the message received by ReceiverActivity + final TextView receivedMessage = (TextView) receiverActivity + .findViewById(R.id.received_message_text_view); + //Verify that received message is correct + assertNotNull(receivedMessage); + assertEquals("Wrong received message", TEST_MESSAGE, receivedMessage.getText().toString()); + + //Unregister monitor for ReceiverActivity + getInstrumentation().removeMonitor(receiverActivityMonitor); + } +}
\ No newline at end of file diff --git a/samples/training/testingfun/build.gradle b/samples/training/testingfun/build.gradle new file mode 100644 index 000000000..06b60301f --- /dev/null +++ b/samples/training/testingfun/build.gradle @@ -0,0 +1,8 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.4.2' + } +} diff --git a/samples/training/testingfun/gradle/wrapper/gradle-wrapper.jar b/samples/training/testingfun/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 000000000..8c0fb64a8 --- /dev/null +++ b/samples/training/testingfun/gradle/wrapper/gradle-wrapper.jar diff --git a/samples/training/testingfun/gradle/wrapper/gradle-wrapper.properties b/samples/training/testingfun/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..5c22dec06 --- /dev/null +++ b/samples/training/testingfun/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip diff --git a/samples/training/testingfun/gradlew b/samples/training/testingfun/gradlew new file mode 100755 index 000000000..91a7e269e --- /dev/null +++ b/samples/training/testingfun/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/samples/training/testingfun/gradlew.bat b/samples/training/testingfun/gradlew.bat new file mode 100644 index 000000000..aec99730b --- /dev/null +++ b/samples/training/testingfun/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/samples/training/testingfun/settings.gradle b/samples/training/testingfun/settings.gradle new file mode 100644 index 000000000..e7b4def49 --- /dev/null +++ b/samples/training/testingfun/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/scripts/compare-installed-size.py b/scripts/compare-installed-size.py new file mode 100755 index 000000000..488723d9d --- /dev/null +++ b/scripts/compare-installed-size.py @@ -0,0 +1,109 @@ +#!/usr/bin/python + +# Copyright (C) 2013 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. + +"""summarize and compare the component sizes in installed-files.txt.""" + +import sys + +bin_size1 = {} +bin_size2 = {} +bin_sizes = [bin_size1, bin_size2] + +file_sizes = {} + +def PrintUsage(): + print "usage: " + sys.argv[0] + " filename [filename2]" + print "" + print " Input file is installed-files.txt from the build output directory." + print " When only one input file is given, it will generate module_0.csv." + print " When two input files are given, in addition it will generate" + print " module_1.csv and comparison.csv." + print "" + print " The module_x.csv file shows the aggregated file size in each module" + print " (eg bin, lib, app, ...)" + print " The comparison.cvs file shows the individual file sizes side by side" + print " from two different builds" + print "" + print " These files can be uploaded to Google Doc for further processing." + sys.exit(1) + +def ParseFile(install_file, idx): + input_stream = open(install_file, 'r') + for line in input_stream: + # line = "25027208 /system/lib/libchromeview.so" + line = line.strip() + + # size = "25027208", name = "/system/lib/libchromeview.so" + size, name = line.split() + + # components = ["", "system", "lib", "libchromeview.so"] + components = name.split('/') + + # module = "lib" + module = components[2] + + # filename = libchromeview.so" + filename = components[-1] + + # sum up the file sizes by module name + if module not in bin_sizes[idx]: + bin_sizes[idx][module] = int(size) + else: + bin_sizes[idx][module] += int(size) + + # sometimes a file only exists on one build but not the other - use 0 as the + # default size. + if idx == 0: + file_sizes[name] = [module, size, 0] + else: + if name in file_sizes: + file_sizes[name][-1] = size + else: + file_sizes[name] = [module, 0, size] + + input_stream.close() + + # output the module csv file + output = open("module_%d.csv" % idx, 'w') + total = 0 + for key in bin_sizes[idx]: + output.write("%s, %d\n" % (key, bin_sizes[idx][key])) + output.close() + +def main(): + if len(sys.argv) < 2 or len(sys.argv) > 3: + PrintUsage() + # Parse the first installed-files.txt + ParseFile(sys.argv[1], 0) + + # Parse the second installed-files.txt + if len(sys.argv) == 3: + ParseFile(sys.argv[2], 1) + # comparison.csv has the following columns: + # filename, module, size1, size2, size2-size1 + # eg: /system/lib/libchromeview.so, lib, 25027208, 33278460, 8251252 + output = open("comparison.csv", 'w') + for key in file_sizes: + output.write("%s, %s, %s, %s, %d\n" % + (key, file_sizes[key][0], file_sizes[key][1], + file_sizes[key][2], + int(file_sizes[key][2]) - int(file_sizes[key][1]))) + output.close() + +if __name__ == '__main__': + main() + +# vi: ts=2 sw=2 diff --git a/scripts/gdb/dalvik.gdb b/scripts/gdb/dalvik.gdb new file mode 100644 index 000000000..cab0951d7 --- /dev/null +++ b/scripts/gdb/dalvik.gdb @@ -0,0 +1,51 @@ +# dump dalvik backtrace +define dbt + if $argc == 1 + set $FP = $arg0 + else + set $FP = $r5 + end + + set $frame = 0 + set $savedPC = 0 + while $FP + set $stackSave = $FP - sizeof(StackSaveArea) + set $savedPC = ((StackSaveArea *)$stackSave)->savedPc + set $method = ((StackSaveArea *)$stackSave)->method + printf "#%d\n", $frame + printf " FP = %#x\n", $FP + printf " stack save = %#x\n", $stackSave + printf " Curr pc = %#x\n", ((StackSaveArea *) $stackSave)->xtra.currentPc + printf " FP prev = %#x\n", ((StackSaveArea *) $stackSave)->prevFrame + if $method != 0 + printf " returnAddr: 0x%x\n", \ + ((StackSaveArea *)$stackSave)->returnAddr + printf " class = %s\n", ((Method *) $method)->clazz->descriptor + printf " method = %s (%#08x)\n", ((Method *) $method)->name, $method + printf " signature = %s\n", ((Method *) $method)->shorty + printf " bytecode offset = 0x%x\n", (short *) (((StackSaveArea *) $stackSave)->xtra.currentPc) - (short *) (((Method *) $method)->insns) + set $regSize = ((Method *) $method)->registersSize + set $insSize = ((Method *) $method)->insSize + set $index = 0 + while $index < $regSize + printf " v%d = %d", $index, ((int *)$FP)[$index] + if $regSize - $index <= $insSize + printf " (in%d)\n", $insSize - $regSize + $index + else + printf " (local%d)\n", $index + end + set $index = $index + 1 + end + else + printf " break frame\n" + end + set $FP = (int) ((StackSaveArea *)$stackSave)->prevFrame + set $frame = $frame + 1 + end +end + +document dbt + Unwind Dalvik stack frames. Argument 0 is the frame address of the top + frame. If omitted r5 will be used as the default (as the case in the + interpreter and JIT'ed code). +end diff --git a/scripts/stack b/scripts/stack index 6750752af..6bb8d0acb 100755 --- a/scripts/stack +++ b/scripts/stack @@ -1,18 +1,25 @@ #!/usr/bin/env python # -# Copyright 2006 Google Inc. All Rights Reserved. +# Copyright (C) 2013 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. """stack symbolizes native crash dumps.""" import getopt -import getpass -import glob -import os -import re -import subprocess import sys -import urllib +import stack_core import symbol @@ -22,17 +29,8 @@ def PrintUsage(): print print " usage: " + sys.argv[0] + " [options] [FILE]" print - print " --symbols-dir=path" - print " the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols" - print - print " --symbols-zip=path" - print " the path to a symbols zip file, such as =dream-symbols-12345.zip" - print - print " --auto" - print " attempt to:" - print " 1) automatically find the build number in the crash" - print " 2) if it's an official build, download the symbols " - print " from the build server, and use them" + print " --arch=arm|x86" + print " the target architecture" print print " FILE should contain a stack trace in it somewhere" print " the tool will find that and re-print it with" @@ -44,347 +42,23 @@ def PrintUsage(): sys.exit(1) -class SSOCookie(object): - """Creates a cookie file so we can download files from the build server.""" - - def __init__(self, cookiename=".sso.cookie", keep=False): - self.sso_server = "login.corp.google.com" - self.name = cookiename - self.keeper = keep - if not os.path.exists(self.name): - user = os.environ["USER"] - print "\n%s, to access the symbols, please enter your LDAP " % user, - sys.stdout.flush() - password = getpass.getpass() - params = urllib.urlencode({"u": user, "pw": password}) - url = "https://%s/login?ssoformat=CORP_SSO" % self.sso_server - # login to SSO - curlcmd = ["/usr/bin/curl", - "--cookie", self.name, - "--cookie-jar", self.name, - "--silent", - "--location", - "--data", params, - "--output", "/dev/null", - url] - subprocess.check_call(curlcmd) - if os.path.exists(self.name): - os.chmod(self.name, 0600) - else: - print "Could not log in to SSO" - sys.exit(1) - - def __del__(self): - """Clean up.""" - if not self.keeper: - os.remove(self.name) - - -class NoBuildIDException(Exception): - pass - - -def FindBuildFingerprint(lines): - """Searches the given file (array of lines) for the build fingerprint.""" - fingerprint_regex = re.compile("^.*Build fingerprint:\s'(?P<fingerprint>.*)'") - for line in lines: - fingerprint_search = fingerprint_regex.match(line.strip()) - if fingerprint_search: - return fingerprint_search.group("fingerprint") - - return None # didn't find the fingerprint string, so return none - - -class SymbolDownloadException(Exception): - pass - - -DEFAULT_SYMROOT = "/tmp/symbols" - - -def DownloadSymbols(fingerprint, cookie): - """Attempts to download the symbols from the build server. - - If successful, extracts them, and returns the path. - - Args: - fingerprint: build fingerprint from the input stack trace - cookie: SSOCookie - - Returns: - tuple (None, None) if no fingerprint is provided. Otherwise - tuple (root directory, symbols directory). - - Raises: - SymbolDownloadException: Problem downloading symbols for fingerprint - """ - if fingerprint is None: - return (None, None) - symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(fingerprint)) - if not os.path.exists(symdir): - os.makedirs(symdir) - # build server figures out the branch based on the CL - params = { - "op": "GET-SYMBOLS-LINK", - "fingerprint": fingerprint, - } - print "url: http://android-build/buildbot-update?" + urllib.urlencode(params) - url = urllib.urlopen("http://android-build/buildbot-update?", - urllib.urlencode(params)).readlines()[0] - if not url: - raise SymbolDownloadException("Build server down? Failed to find syms...") - - regex_str = (r"(?P<base_url>http\:\/\/android-build\/builds\/.*\/[0-9]+)" - r"(?P<img>.*)") - url_regex = re.compile(regex_str) - url_match = url_regex.match(url) - if url_match is None: - raise SymbolDownloadException("Unexpected results from build server URL...") - - base_url = url_match.group("base_url") - img = url_match.group("img") - symbolfile = img.replace("-img-", "-symbols-") - symurl = base_url + symbolfile - localsyms = symdir + symbolfile - - if not os.path.exists(localsyms): - print "downloading %s ..." % symurl - curlcmd = ["/usr/bin/curl", - "--cookie", cookie.name, - "--silent", - "--location", - "--write-out", "%{http_code}", - "--output", localsyms, - symurl] - p = subprocess.Popen(curlcmd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - close_fds=True) - code = p.stdout.read() - err = p.stderr.read() - if err: - raise SymbolDownloadException("stderr from curl download: %s" % err) - if code != "200": - raise SymbolDownloadException("Faied to download %s" % symurl) - else: - print "using existing cache for symbols" - - return UnzipSymbols(localsyms, symdir) - - -def UnzipSymbols(symbolfile, symdir=None): - """Unzips a file to DEFAULT_SYMROOT and returns the unzipped location. - - Args: - symbolfile: The .zip file to unzip - symdir: Optional temporary directory to use for extraction - - Returns: - A tuple containing (the directory into which the zip file was unzipped, - the path to the "symbols" directory in the unzipped file). To clean - up, the caller can delete the first element of the tuple. - - Raises: - SymbolDownloadException: When the unzip fails. - """ - if not symdir: - symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(symbolfile)) - if not os.path.exists(symdir): - os.makedirs(symdir) - - print "extracting %s..." % symbolfile - saveddir = os.getcwd() - os.chdir(symdir) - try: - unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile]) - if unzipcode > 0: - os.remove(symbolfile) - raise SymbolDownloadException("failed to extract symbol files (%s)." - % symbolfile) - finally: - os.chdir(saveddir) - - return (symdir, glob.glob("%s/out/target/product/*/symbols" % symdir)[0]) - - -def PrintTraceLines(trace_lines): - """Print back trace.""" - maxlen = max(map(lambda tl: len(tl[1]), trace_lines)) - print - print "Stack Trace:" - print " RELADDR " + "FUNCTION".ljust(maxlen) + " FILE:LINE" - for tl in trace_lines: - (addr, symbol_with_offset, location) = tl - print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location) - return - - -def PrintValueLines(value_lines): - """Print stack data values.""" - print - print "Stack Data:" - print " ADDR VALUE FILE:LINE/FUNCTION" - for vl in value_lines: - (addr, value, symbol_with_offset, location) = vl - print " " + addr + " " + value + " " + location - if location: - print " " + symbol_with_offset - return - -UNKNOWN = "<unknown>" -HEAP = "[heap]" -STACK = "[stack]" - - -def ConvertTrace(lines): - """Convert strings containing native crash to a stack.""" - process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)") - signal_line = re.compile("(signal [0-9]+ \(.*\).*)") - register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})") - thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-") - # Note taht both trace and value line matching allow for variable amounts of - # whitespace (e.g. \t). This is because the we want to allow for the stack - # tool to operate on AndroidFeedback provided system logs. AndroidFeedback - # strips out double spaces that are found in tombsone files and logcat output. - # - # Examples of matched trace lines include lines from tombstone files like: - # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so - # Or lines from AndroidFeedback crash report system logs like: - # 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so - # Please note the spacing differences. - trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?") # pylint: disable-msg=C6310 - # Examples of matched value lines include: - # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so - # 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so - # Again, note the spacing differences. - value_line = re.compile("(.*)([0-9a-f]{8})[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)") - # Lines from 'code around' sections of the output will be matched before - # value lines because otheriwse the 'code around' sections will be confused as - # value lines. - # - # Examples include: - # 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 - # 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 - code_line = re.compile("(.*)[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[ \r\n]") # pylint: disable-msg=C6310 - - trace_lines = [] - value_lines = [] - - for ln in lines: - # AndroidFeedback adds zero width spaces into its crash reports. These - # should be removed or the regular expresssions will fail to match. - line = unicode(ln, errors='ignore') - header = process_info_line.search(line) - if header: - print header.group(1) - continue - header = signal_line.search(line) - if header: - print header.group(1) - continue - header = register_line.search(line) - if header: - print header.group(1) - continue - if trace_line.match(line): - match = trace_line.match(line) - (unused_0, unused_1, unused_2, - code_addr, area, symbol_present, symbol_name) = match.groups() - - if area == UNKNOWN or area == HEAP or area == STACK: - trace_lines.append((code_addr, area, area)) - else: - # If a calls b which further calls c and c is inlined to b, we want to - # display "a -> b -> c" in the stack trace instead of just "a -> c" - (source_symbol, - source_location, - object_symbol_with_offset) = symbol.SymbolInformation(area, code_addr) - if not source_symbol: - if symbol_present: - source_symbol = symbol.CallCppFilt(symbol_name) - else: - source_symbol = UNKNOWN - if not source_location: - source_location = area - if not object_symbol_with_offset: - object_symbol_with_offset = source_symbol - if not object_symbol_with_offset.startswith(source_symbol): - trace_lines.append(("v------>", source_symbol, source_location)) - trace_lines.append((code_addr, - object_symbol_with_offset, - source_location)) - else: - trace_lines.append((code_addr, - object_symbol_with_offset, - source_location)) - if code_line.match(line): - # Code lines should be ignored. If this were exluded the 'code around' - # sections would trigger value_line matches. - continue; - if value_line.match(line): - match = value_line.match(line) - (unused_, addr, value, area) = match.groups() - if area == UNKNOWN or area == HEAP or area == STACK or not area: - value_lines.append((addr, value, area, "")) - else: - (source_symbol, - source_location, - object_symbol_with_offset) = symbol.SymbolInformation(area, value) - if not source_location: - source_location = "" - if not object_symbol_with_offset: - object_symbol_with_offset = UNKNOWN - value_lines.append((addr, - value, - object_symbol_with_offset, - source_location)) - header = thread_line.search(line) - if header: - if trace_lines: - PrintTraceLines(trace_lines) - - if value_lines: - PrintValueLines(value_lines) - trace_lines = [] - value_lines = [] - print - print "-----------------------------------------------------\n" - - if trace_lines: - PrintTraceLines(trace_lines) - - if value_lines: - PrintValueLines(value_lines) - - def main(): try: options, arguments = getopt.getopt(sys.argv[1:], "", - ["auto", - "symbols-dir=", - "symbols-zip=", + ["arch=", "help"]) except getopt.GetoptError, unused_error: PrintUsage() - zip_arg = None - auto = False - fingerprint = None for option, value in options: if option == "--help": PrintUsage() - elif option == "--symbols-dir": - symbol.SYMBOLS_DIR = os.path.expanduser(value) - elif option == "--symbols-zip": - zip_arg = os.path.expanduser(value) - elif option == "--auto": - auto = True + elif option == "--arch": + symbol.ARCH = value if len(arguments) > 1: PrintUsage() - if auto: - cookie = SSOCookie(".symbols.cookie") - if not arguments or arguments[0] == "-": print "Reading native crash info from stdin" f = sys.stdin @@ -395,22 +69,8 @@ def main(): lines = f.readlines() f.close() - rootdir = None - if auto: - fingerprint = FindBuildFingerprint(lines) - print "fingerprint:", fingerprint - rootdir, symbol.SYMBOLS_DIR = DownloadSymbols(fingerprint, cookie) - elif zip_arg: - rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg) - print "Reading symbols from", symbol.SYMBOLS_DIR - ConvertTrace(lines) - - if rootdir: - # be a good citizen and clean up...os.rmdir and os.removedirs() don't work - cmd = "rm -rf \"%s\"" % rootdir - print "\ncleaning up (%s)" % cmd - os.system(cmd) + stack_core.ConvertTrace(lines) if __name__ == "__main__": main() diff --git a/scripts/stack_core.py b/scripts/stack_core.py new file mode 100755 index 000000000..42285d462 --- /dev/null +++ b/scripts/stack_core.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# +# Copyright (C) 2013 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. + +"""stack symbolizes native crash dumps.""" + +import re + +import symbol + +def PrintTraceLines(trace_lines): + """Print back trace.""" + maxlen = max(map(lambda tl: len(tl[1]), trace_lines)) + print + print "Stack Trace:" + print " RELADDR " + "FUNCTION".ljust(maxlen) + " FILE:LINE" + for tl in trace_lines: + (addr, symbol_with_offset, location) = tl + print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location) + return + + +def PrintValueLines(value_lines): + """Print stack data values.""" + maxlen = max(map(lambda tl: len(tl[2]), value_lines)) + print + print "Stack Data:" + print " ADDR VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE" + for vl in value_lines: + (addr, value, symbol_with_offset, location) = vl + print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location) + return + +UNKNOWN = "<unknown>" +HEAP = "[heap]" +STACK = "[stack]" + + +def PrintOutput(trace_lines, value_lines): + if trace_lines: + PrintTraceLines(trace_lines) + if value_lines: + PrintValueLines(value_lines) + +def PrintDivider(): + print + print "-----------------------------------------------------\n" + +def ConvertTrace(lines): + """Convert strings containing native crash to a stack.""" + process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)") + signal_line = re.compile("(signal [0-9]+ \(.*\).*)") + register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})") + thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-") + dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)") + dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)") + # Note that both trace and value line matching allow for variable amounts of + # whitespace (e.g. \t). This is because the we want to allow for the stack + # tool to operate on AndroidFeedback provided system logs. AndroidFeedback + # strips out double spaces that are found in tombsone files and logcat output. + # + # Examples of matched trace lines include lines from tombstone files like: + # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so + # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so (symbol) + # Or lines from AndroidFeedback crash report system logs like: + # 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so + # Please note the spacing differences. + trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?") # pylint: disable-msg=C6310 + # Examples of matched value lines include: + # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so + # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol) + # 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so + # Again, note the spacing differences. + value_line = re.compile("(.*)([0-9a-f]{8})[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?") + # Lines from 'code around' sections of the output will be matched before + # value lines because otheriwse the 'code around' sections will be confused as + # value lines. + # + # Examples include: + # 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 + # 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 + code_line = re.compile("(.*)[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[ \r\n]") # pylint: disable-msg=C6310 + + trace_lines = [] + value_lines = [] + last_frame = -1 + + for ln in lines: + # AndroidFeedback adds zero width spaces into its crash reports. These + # should be removed or the regular expresssions will fail to match. + line = unicode(ln, errors='ignore') + process_header = process_info_line.search(line) + signal_header = signal_line.search(line) + register_header = register_line.search(line) + thread_header = thread_line.search(line) + dalvik_jni_thread_header = dalvik_jni_thread_line.search(line) + dalvik_native_thread_header = dalvik_native_thread_line.search(line) + if process_header or signal_header or register_header or thread_header \ + or dalvik_jni_thread_header or dalvik_native_thread_header: + if trace_lines or value_lines: + PrintOutput(trace_lines, value_lines) + PrintDivider() + trace_lines = [] + value_lines = [] + last_frame = -1 + if process_header: + print process_header.group(1) + if signal_header: + print signal_header.group(1) + if register_header: + print register_header.group(1) + if thread_header: + print thread_header.group(1) + if dalvik_jni_thread_header: + print dalvik_jni_thread_header.group(1) + if dalvik_native_thread_header: + print dalvik_native_thread_header.group(1) + continue + if trace_line.match(line): + match = trace_line.match(line) + (unused_0, frame, unused_1, + code_addr, area, symbol_present, symbol_name) = match.groups() + + if frame <= last_frame and (trace_lines or value_lines): + PrintOutput(trace_lines, value_lines) + PrintDivider() + trace_lines = [] + value_lines = [] + last_frame = frame + + if area == UNKNOWN or area == HEAP or area == STACK: + trace_lines.append((code_addr, "", area)) + else: + # If a calls b which further calls c and c is inlined to b, we want to + # display "a -> b -> c" in the stack trace instead of just "a -> c" + info = symbol.SymbolInformation(area, code_addr) + nest_count = len(info) - 1 + for (source_symbol, source_location, object_symbol_with_offset) in info: + if not source_symbol: + if symbol_present: + source_symbol = symbol.CallCppFilt(symbol_name) + else: + source_symbol = UNKNOWN + if not source_location: + source_location = area + if nest_count > 0: + nest_count = nest_count - 1 + trace_lines.append(("v------>", source_symbol, source_location)) + else: + if not object_symbol_with_offset: + object_symbol_with_offset = source_symbol + trace_lines.append((code_addr, + object_symbol_with_offset, + source_location)) + if code_line.match(line): + # Code lines should be ignored. If this were exluded the 'code around' + # sections would trigger value_line matches. + continue; + if value_line.match(line): + match = value_line.match(line) + (unused_, addr, value, area, symbol_present, symbol_name) = match.groups() + if area == UNKNOWN or area == HEAP or area == STACK or not area: + value_lines.append((addr, value, "", area)) + else: + info = symbol.SymbolInformation(area, value) + (source_symbol, source_location, object_symbol_with_offset) = info.pop() + if not source_symbol: + if symbol_present: + source_symbol = symbol.CallCppFilt(symbol_name) + else: + source_symbol = UNKNOWN + if not source_location: + source_location = area + if not object_symbol_with_offset: + object_symbol_with_offset = source_symbol + value_lines.append((addr, + value, + object_symbol_with_offset, + source_location)) + + PrintOutput(trace_lines, value_lines) + + +# vi: ts=2 sw=2 diff --git a/scripts/symbol.py b/scripts/symbol.py index b0e94d825..0f58df64c 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -1,6 +1,18 @@ #!/usr/bin/python # -# Copyright 2006 Google Inc. All Rights Reserved. +# Copyright (C) 2013 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. """Module for looking up symbolic debugging information. @@ -29,6 +41,10 @@ def FindSymbolsDir(): SYMBOLS_DIR = FindSymbolsDir() +ARCH = "arm" + +TOOLCHAIN_INFO = None + def Uname(): """'uname' for constructing prebuilt/<...> and out/host/<...> paths.""" uname = os.uname()[0] @@ -44,9 +60,9 @@ def Uname(): def ToolPath(tool, toolchain_info=None): """Return a full qualified path to the specified tool""" if not toolchain_info: - toolchain_info = TOOLCHAIN_INFO - (label, target) = toolchain_info - return os.path.join(ANDROID_BUILD_TOP, "prebuilts", "gcc", Uname(), "arm", label, "bin", + toolchain_info = FindToolchain() + (label, platform, target) = toolchain_info + return os.path.join(ANDROID_BUILD_TOP, "prebuilts/gcc", Uname(), platform, label, "bin", target + "-" + tool) def FindToolchain(): @@ -58,27 +74,32 @@ def FindToolchain(): Returns: A pair of strings containing toolchain label and target prefix. """ + global TOOLCHAIN_INFO + if TOOLCHAIN_INFO is not None: + return TOOLCHAIN_INFO ## Known toolchains, newer ones in the front. - known_toolchains = [ - ("arm-eabi-4.6", "arm-eabi"), - ("arm-linux-androideabi-4.4.x", "arm-linux-androideabi"), - ("arm-eabi-4.4.3", "arm-eabi"), - ("arm-eabi-4.4.0", "arm-eabi"), - ("arm-eabi-4.3.1", "arm-eabi"), - ("arm-eabi-4.2.1", "arm-eabi") - ] + if ARCH == "arm": + gcc_version = os.environ["TARGET_GCC_VERSION"] + known_toolchains = [ + ("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi"), + ] + elif ARCH =="x86": + known_toolchains = [ + ("i686-android-linux-4.4.3", "x86", "i686-android-linux") + ] + else: + known_toolchains = [] # Look for addr2line to check for valid toolchain path. - for (label, target) in known_toolchains: - toolchain_info = (label, target); + for (label, platform, target) in known_toolchains: + toolchain_info = (label, platform, target); if os.path.exists(ToolPath("addr2line", toolchain_info)): + TOOLCHAIN_INFO = toolchain_info return toolchain_info raise Exception("Could not find tool chain") -TOOLCHAIN_INFO = FindToolchain() - def SymbolInformation(lib, addr): """Look up symbol information about an address. @@ -87,20 +108,19 @@ def SymbolInformation(lib, addr): addr: string hexidecimal address Returns: - For a given library and address, return tuple of: (source_symbol, - source_location, object_symbol_with_offset) the values may be None - if the information was unavailable. + A list of the form [(source_symbol, source_location, + object_symbol_with_offset)]. - source_symbol may not be a prefix of object_symbol_with_offset if - the source function was inlined in the object code of another - function. + If the function has been inlined then the list may contain + more than one element with the symbols for the most deeply + nested inlined location appearing first. The list is + always non-empty, even if no information is available. - usually you want to display the object_symbol_with_offset and - source_location, the source_symbol is only useful to show if the - address was from an inlined function. + Usually you want to display the source_location and + object_symbol_with_offset from the last element in the list. """ info = SymbolInformationForSet(lib, set([addr])) - return (info and info.get(addr)) or (None, None, None) + return (info and info.get(addr)) or [(None, None, None)] def SymbolInformationForSet(lib, unique_addrs): @@ -111,17 +131,17 @@ def SymbolInformationForSet(lib, unique_addrs): unique_addrs: set of hexidecimal addresses Returns: - For a given library and set of addresses, returns a dictionary of the form - {addr: (source_symbol, source_location, object_symbol_with_offset)}. The - values may be None if the information was unavailable. + A dictionary of the form {addr: [(source_symbol, source_location, + object_symbol_with_offset)]} where each address has a list of + associated symbols and locations. The list is always non-empty. - For a given address, source_symbol may not be a prefix of - object_symbol_with_offset if the source function was inlined in the - object code of another function. + If the function has been inlined then the list may contain + more than one element with the symbols for the most deeply + nested inlined location appearing first. The list is + always non-empty, even if no information is available. - Usually you want to display the object_symbol_with_offset and - source_location; the source_symbol is only useful to show if the - address was from an inlined function. + Usually you want to display the source_location and + object_symbol_with_offset from the last element in the list. """ if not lib: return None @@ -136,14 +156,17 @@ def SymbolInformationForSet(lib, unique_addrs): result = {} for addr in unique_addrs: - (source_symbol, source_location) = addr_to_line.get(addr, (None, None)) + source_info = addr_to_line.get(addr) + if not source_info: + source_info = [(None, None)] if addr in addr_to_objdump: (object_symbol, object_offset) = addr_to_objdump.get(addr) object_symbol_with_offset = FormatSymbolWithOffset(object_symbol, object_offset) else: object_symbol_with_offset = None - result[addr] = (source_symbol, source_location, object_symbol_with_offset) + result[addr] = [(source_symbol, source_location, object_symbol_with_offset) + for (source_symbol, source_location) in source_info] return result @@ -156,8 +179,13 @@ def CallAddr2LineForSet(lib, unique_addrs): unique_addrs: set of string hexidecimal addresses look up. Returns: - A dictionary of the form {addr: (symbol, file:line)}. The values may - be (None, None) if the address could not be looked up. + A dictionary of the form {addr: [(symbol, file:line)]} where + each address has a list of associated symbols and locations + or an empty list if no symbol information was found. + + If the function has been inlined then the list may contain + more than one element with the symbols for the most deeply + nested inlined location appearing first. """ if not lib: return None @@ -167,8 +195,9 @@ def CallAddr2LineForSet(lib, unique_addrs): if not os.path.exists(symbols): return None - (label, target) = TOOLCHAIN_INFO - cmd = [ToolPath("addr2line"), "--functions", "--demangle", "--exe=" + symbols] + (label, platform, target) = FindToolchain() + cmd = [ToolPath("addr2line"), "--functions", "--inlines", + "--demangle", "--exe=" + symbols] child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) result = {} @@ -176,18 +205,45 @@ def CallAddr2LineForSet(lib, unique_addrs): for addr in addrs: child.stdin.write("0x%s\n" % addr) child.stdin.flush() - symbol = child.stdout.readline().strip() - if symbol == "??": - symbol = None - location = child.stdout.readline().strip() - if location == "??:0": - location = None - result[addr] = (symbol, location) + records = [] + first = True + while True: + symbol = child.stdout.readline().strip() + if symbol == "??": + symbol = None + location = child.stdout.readline().strip() + if location == "??:0": + location = None + if symbol is None and location is None: + break + records.append((symbol, location)) + if first: + # Write a blank line as a sentinel so we know when to stop + # reading inlines from the output. + # The blank line will cause addr2line to emit "??\n??:0\n". + child.stdin.write("\n") + first = False + result[addr] = records child.stdin.close() child.stdout.close() return result +def StripPC(addr): + """Strips the Thumb bit a program counter address when appropriate. + + Args: + addr: the program counter address + + Returns: + The stripped program counter address. + """ + global ARCH + + if ARCH == "arm": + return addr & ~1 + return addr + def CallObjdumpForSet(lib, unique_addrs): """Use objdump to find out the names of the containing functions. @@ -210,13 +266,13 @@ def CallObjdumpForSet(lib, unique_addrs): return None addrs = sorted(unique_addrs) - start_addr_hex = addrs[0] - stop_addr_dec = str(int(addrs[-1], 16) + 8) + start_addr_dec = str(StripPC(int(addrs[0], 16))) + stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8) cmd = [ToolPath("objdump"), "--section=.text", "--demangle", "--disassemble", - "--start-address=0x" + start_addr_hex, + "--start-address=" + start_addr_dec, "--stop-address=" + stop_addr_dec, symbols] @@ -261,7 +317,7 @@ def CallObjdumpForSet(lib, unique_addrs): addr = components.group(1) target_addr = addrs[addr_index] i_addr = int(addr, 16) - i_target = int(target_addr, 16) + i_target = StripPC(int(target_addr, 16)) if i_addr == i_target: result[target_addr] = (current_symbol, i_target - current_symbol_addr) addr_index += 1 diff --git a/sdk/build_tools_source.prop_template b/sdk/build_tools_source.prop_template new file mode 100644 index 000000000..5d623074d --- /dev/null +++ b/sdk/build_tools_source.prop_template @@ -0,0 +1,3 @@ +Pkg.UserSrc=false +Pkg.Revision=${PLATFORM_SDK_VERSION}.0.0 + diff --git a/sdk/build_tools_source.properties b/sdk/build_tools_source.properties deleted file mode 100644 index 1ac7da004..000000000 --- a/sdk/build_tools_source.properties +++ /dev/null @@ -1,3 +0,0 @@ -Pkg.UserSrc=false -Pkg.Revision=18.0.1 - diff --git a/sdk/images_armeabi-v7a_source.prop_template b/sdk/images_armeabi-v7a_source.prop_template index 4f2daacd9..86fc2a0bb 100644 --- a/sdk/images_armeabi-v7a_source.prop_template +++ b/sdk/images_armeabi-v7a_source.prop_template @@ -1,6 +1,6 @@ Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION} Pkg.UserSrc=false -Pkg.Revision=2 +Pkg.Revision=1 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION} AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME} SystemImage.Abi=armeabi-v7a diff --git a/sdk/plat_tools_source.prop_template b/sdk/plat_tools_source.prop_template new file mode 100644 index 000000000..5d623074d --- /dev/null +++ b/sdk/plat_tools_source.prop_template @@ -0,0 +1,3 @@ +Pkg.UserSrc=false +Pkg.Revision=${PLATFORM_SDK_VERSION}.0.0 + diff --git a/sdk/plat_tools_source.properties b/sdk/plat_tools_source.properties deleted file mode 100644 index 0025a2add..000000000 --- a/sdk/plat_tools_source.properties +++ /dev/null @@ -1,3 +0,0 @@ -Pkg.UserSrc=false -Pkg.Revision=18.1.0 - diff --git a/sdk/support_source.properties b/sdk/support_source.prop_template index 16170962f..ba7e6fb1a 100644 --- a/sdk/support_source.properties +++ b/sdk/support_source.prop_template @@ -1,5 +1,5 @@ Pkg.UserSrc=false -Pkg.Revision=18 +Pkg.Revision=${PLATFORM_SDK_VERSION} Extra.Vendor=android Extra.VendorId=android Extra.VendorDisplay=Android diff --git a/sdk/usbdriver_source.properties b/sdk/usbdriver_source.properties index bff71f970..c33d0324b 100755 --- a/sdk/usbdriver_source.properties +++ b/sdk/usbdriver_source.properties @@ -1,4 +1,4 @@ -Pkg.Revision=7
+Pkg.Revision=8
Archive.Os=WINDOWS
Archive.Arch=ANY
Extra.Path=usb_driver
diff --git a/testrunner/android_build.py b/testrunner/android_build.py index a10d43b6f..cacd67e00 100644 --- a/testrunner/android_build.py +++ b/testrunner/android_build.py @@ -48,6 +48,25 @@ def GetTop(): return root_path +def GetHostOutDir(): + """Returns the full pathname of out/host/arch of the Android development tree. + + Assumes build environment has been properly configured by envsetup & + lunch/choosecombo. + + Returns: + the absolute file path of the Android host output directory. + Raises: + AbortError: if Android host output directory could not be found. + """ + host_out_path = os.getenv("ANDROID_HOST_OUT") + if host_out_path is None: + logger.Log("Error: ANDROID_HOST_OUT not defined. Please run " + "envsetup.sh and lunch/choosecombo") + raise errors.AbortError + return host_out_path + + def GetHostOsArch(): """Identify the host os and arch. @@ -72,10 +91,25 @@ def GetHostOsArch(): return (host_os, host_arch, "%s-%s" % (host_os, host_arch)) +def GetOutDir(): + """Returns the full pathname of the "out" of the Android development tree. + + Assumes build environment has been properly configured by envsetup & + lunch/choosecombo. + + Returns: + the absolute file path of the Android build output directory. + """ + root_path = os.getenv("OUT_DIR") + if root_path is None: + root_path = os.path.join(GetTop(), "out") + return root_path + + def GetHostBin(): """Compute the full pathname to the host binary directory. - Typically $ANDROID_BUILD_TOP/out/host/linux-x86/bin. + Typically $ANDROID_HOST_OUT/bin. Assumes build environment has been properly configured by envsetup & lunch/choosecombo. @@ -86,8 +120,7 @@ def GetHostBin(): Raises: AbortError: if Android host binary directory could not be found. """ - (_, _, os_arch) = GetHostOsArch() - path = os.path.join(GetTop(), "out", "host", os_arch, "bin") + path = os.path.join(GetHostOutDir(), "bin") if not os.path.exists(path): logger.Log("Error: Host bin path could not be found %s" % path) raise errors.AbortError @@ -139,7 +172,7 @@ def GetTargetSystemBin(): def GetHostLibraryPath(): """Returns the full pathname to the host java library output directory. - Typically $ANDROID_BUILD_TOP/out/host/<host_os>/framework. + Typically $ANDROID_HOST_OUT/framework. Assumes build environment has been properly configured by envsetup & lunch/choosecombo. @@ -150,8 +183,7 @@ def GetHostLibraryPath(): Raises: AbortError: if Android host java library directory could not be found. """ - (_, _, os_arch) = GetHostOsArch() - path = os.path.join(GetTop(), "out", "host", os_arch, "framework") + path = os.path.join(GetHostOutDir(), "framework") if not os.path.exists(path): logger.Log("Error: Host library path could not be found %s" % path) raise errors.AbortError diff --git a/testrunner/coverage/coverage.py b/testrunner/coverage/coverage.py index 570527d56..824f5c5f0 100755 --- a/testrunner/coverage/coverage.py +++ b/testrunner/coverage/coverage.py @@ -43,7 +43,7 @@ class CoverageGenerator(object): _EMMA_JAR = os.path.join("external", "emma", "lib", "emma.jar") _TEST_COVERAGE_EXT = "ec" # root path of generated coverage report files, relative to Android build root - _COVERAGE_REPORT_PATH = os.path.join("out", "emma") + _COVERAGE_REPORT_PATH = "emma" _TARGET_DEF_FILE = "coverage_targets.xml" _CORE_TARGET_PATH = os.path.join("development", "testrunner", _TARGET_DEF_FILE) @@ -53,12 +53,13 @@ class CoverageGenerator(object): _TARGET_DEF_FILE) # path to root of target build intermediates - _TARGET_INTERMEDIATES_BASE_PATH = os.path.join("out", "target", "common", + _TARGET_INTERMEDIATES_BASE_PATH = os.path.join("target", "common", "obj") def __init__(self, adb_interface): self._root_path = android_build.GetTop() - self._output_root_path = os.path.join(self._root_path, + self._out_path = android_build.GetOut() + self._output_root_path = os.path.join(self._out_path, self._COVERAGE_REPORT_PATH) self._emma_jar_path = os.path.join(self._root_path, self._EMMA_JAR) self._adb = adb_interface @@ -78,7 +79,7 @@ class CoverageGenerator(object): target: the CoverageTarget to use as basis for coverage calculation device_coverage_path: location of coverage file on device output_path: path to place output files in. If None will use - <android_root_path>/<_COVERAGE_REPORT_PATH>/<target>/<test[-qualifier]> + <android_out_path>/<_COVERAGE_REPORT_PATH>/<target>/<test[-qualifier]> test_qualifier: designates mode test was run with. e.g size=small. If not None, this will be used to customize output_path as shown above. @@ -89,7 +90,7 @@ class CoverageGenerator(object): report_name = test_suite_name if test_qualifier: report_name = report_name + "-" + test_qualifier - output_path = os.path.join(self._root_path, + output_path = os.path.join(self._out_path, self._COVERAGE_REPORT_PATH, target.GetName(), report_name) @@ -153,7 +154,7 @@ class CoverageGenerator(object): def _GetBuildIntermediatePath(self, target): return os.path.join( - self._root_path, self._TARGET_INTERMEDIATES_BASE_PATH, target.GetType(), + self._out_path, self._TARGET_INTERMEDIATES_BASE_PATH, target.GetType(), "%s_intermediates" % target.GetName()) def _GatherSrcs(self, targets): diff --git a/testrunner/runtest.py b/testrunner/runtest.py index f7a4759a1..470dc423b 100755 --- a/testrunner/runtest.py +++ b/testrunner/runtest.py @@ -78,13 +78,15 @@ class TestRunner(object): # regular expression to match install: statements in make output _RE_MAKE_INSTALL = re.compile(r'Install:\s(.+)') - # regular expression to find remote device path from a file path relative - # to build root - _RE_MAKE_INSTALL_PATH = re.compile(r'out\/target\/product\/\w+\/(.+)$') def __init__(self): # disable logging of timestamp self._root_path = android_build.GetTop() + out_base_name = os.path.basename(android_build.GetOutDir()) + # regular expression to find remote device path from a file path relative + # to build root + pattern = r'' + out_base_name + r'\/target\/product\/\w+\/(.+)$' + self._re_make_install_path = re.compile(pattern) logger.SetTimestampLogging(False) self._adb = None self._known_tests = None @@ -298,9 +300,12 @@ class TestRunner(object): self._PushInstallFileToDevice(install_path) def _PushInstallFileToDevice(self, install_path): - m = self._RE_MAKE_INSTALL_PATH.match(install_path) + m = self._re_make_install_path.match(install_path) if m: remote_path = m.group(1) + remote_dir = os.path.dirname(remote_path) + logger.Log("adb shell mkdir -p %s" % remote_dir) + self._adb.SendShellCommand("mkdir -p %s" % remote_dir) abs_install_path = os.path.join(self._root_path, install_path) logger.Log("adb push %s %s" % (abs_install_path, remote_path)) self._adb.Push(abs_install_path, remote_path) diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml index 516dc7c1e..6dfb14b15 100644 --- a/testrunner/test_defs.xml +++ b/testrunner/test_defs.xml @@ -137,9 +137,13 @@ See test_defs.xsd for more information. description="Framework libutils unit tests." /> <test-native name="libinput" - build_path="frameworks/base/services/input/tests" + build_path="frameworks/native/libs/input/tests" description="Framework libinput unit tests." /> +<test-native name="libinputservice" + build_path="frameworks/base/services/input/tests" + description="Framework libinputservice unit tests." /> + <test name="volley" build_path="frameworks/support/volley/tests" package="com.android.volley.tests" @@ -187,6 +191,12 @@ See test_defs.xsd for more information. runner=".MediaFrameworkUnitTestRunner" coverage_target="framework" /> +<test name="mediaintegrationtest" + build_path="frameworks/base/media/tests/MediaFrameworkTest" + package="com.android.mediaframeworktest" + runner=".MediaFrameworkIntegrationTestRunner" + coverage_target="framework" /> + <test-native name="camera-client-native" build_path="frameworks/av/camera/tests/" description="Camera client native tests." /> diff --git a/testrunner/test_defs/gtest.py b/testrunner/test_defs/gtest.py index 094ceea1d..dc72f94ca 100644 --- a/testrunner/test_defs/gtest.py +++ b/testrunner/test_defs/gtest.py @@ -89,7 +89,8 @@ class GTestFactory(test_suite.AbstractTestFactory): logger.SilentLog('Creating gtest suite for file %s' % test_file) suite = GTestSuite() suite.SetBuildPath(self.GetBuildPath()) - suite.SetTargetExecPath(os.path.join(target_root_path, test_file)) + # expect tests in /data/nativetest/test_file/test_file + suite.SetTargetExecPath(os.path.join(target_root_path, test_file, test_file)) test_suites.append(suite) return test_suites diff --git a/testrunner/test_defs/native_test.py b/testrunner/test_defs/native_test.py index dc26c738c..caef8771f 100644 --- a/testrunner/test_defs/native_test.py +++ b/testrunner/test_defs/native_test.py @@ -154,7 +154,7 @@ class NativeTestSuite(test_suite.AbstractTestSuite): Args: binary: basename of the file to be run. It is expected to be under - out/host/<os>-<arch>/bin. + $ANDROID_HOST_OUT/bin. valgrind: If True the command will be run under valgrind. Returns: diff --git a/tools/emulator/test-apps/ConnectivityTest/Android.mk b/tools/emulator/test-apps/ConnectivityTest/Android.mk index 097d1189a..ca20d574a 100644 --- a/tools/emulator/test-apps/ConnectivityTest/Android.mk +++ b/tools/emulator/test-apps/ConnectivityTest/Android.mk @@ -24,6 +24,8 @@ LOCAL_PACKAGE_NAME := ConnectivityTest LOCAL_SDK_VERSION := 4 +LOCAL_PROGUARD_ENABLED := disabled + include $(BUILD_PACKAGE) # Use the following include to make our test apk. diff --git a/tools/emulator/test-apps/GpsLocationTest/Android.mk b/tools/emulator/test-apps/GpsLocationTest/Android.mk index 35c944393..5f90f3a5b 100644 --- a/tools/emulator/test-apps/GpsLocationTest/Android.mk +++ b/tools/emulator/test-apps/GpsLocationTest/Android.mk @@ -24,6 +24,8 @@ LOCAL_PACKAGE_NAME := GpsLocationTest LOCAL_SDK_VERSION := 4 +LOCAL_PROGUARD_ENABLED := disabled + include $(BUILD_PACKAGE) # Use the following include to make our test apk. diff --git a/tools/idegen/Android.mk b/tools/idegen/Android.mk index d424ab6be..8771160dd 100644 --- a/tools/idegen/Android.mk +++ b/tools/idegen/Android.mk @@ -2,8 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_STATIC_JAVA_LIBRARIES := \ - guavalib \ +LOCAL_STATIC_JAVA_LIBRARIES := guavalib LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/tools/idegen/index-gen.sh b/tools/idegen/index-gen.sh index eadaa98e5..5b9e24bb1 100755 --- a/tools/idegen/index-gen.sh +++ b/tools/idegen/index-gen.sh @@ -14,12 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Generates a module index file by searching through android source tree for make files. The -# intellij-gen.sh script automatically calls this script the first time or if you delete the -# generated indexed file. The only time you need to run this manually is if modules are added or -# deleted. -# -# To use: +# Generates a module index file by searching through android source +# tree for make files. The intellij-gen.sh script automatically calls +# this script the first time or if you delete the generated indexed +# file. The only time you need to run this manually is if modules are +# added or deleted. +# +# To use, run the following command from either your repo root or +# development/tools/idegen: # index-gen.sh # # Only tested on linux. Should work for macs but have not tried. @@ -27,7 +29,15 @@ set -e script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -root_dir=`readlink -f -n $script_dir/../../..` +#root_dir=`readlink -f -n $script_dir/../../..` +root_dir=$PWD +if [ ! -e $root_dir/.repo ]; then + root_dir=$PWD/../../.. + if [ ! -e $root_dir/.repo ]; then + echo "Repo root not found. Run this script from your repo root or the idegen directory." + exit 1 + fi +fi tmp_file=tmp.txt dest_file=module-index.txt diff --git a/tools/idegen/intellij-gen.sh b/tools/idegen/intellij-gen.sh index 7f00eba06..d67c1f867 100755 --- a/tools/idegen/intellij-gen.sh +++ b/tools/idegen/intellij-gen.sh @@ -14,7 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# To use: +# To use, run the following command from either your repo root or +# development/tools/idegen: # intellij-gen.sh <module name> # # where module name is the LOCAL_PACKAGE_NAME in Android.mk for the project. @@ -22,27 +23,38 @@ # For example, to generate a project for Contacts, use: # intellij-gen.sh Contacts # -# The project directory (.idea) will be put in the root directory of the module. Sharable iml -# files will be put into each respective module directory. +# The project directory (.idea) will be put in the root directory of +# the module. Sharable iml files will be put into each respective +# module directory. # # Only tested on linux. Should work for macs but have not tried. # set -e -script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -root_dir=`readlink -f -n $script_dir/../../..` -index_file=$script_dir/module-index.txt -idegenjar=$root_dir/out/host/common/obj/JAVA_LIBRARIES/idegen_intermediates/javalib.jar - progname=`basename $0` if [ $# -ne 1 ] then echo "Usage: $progname <module_name>" exit 1 fi - module_name=$1 +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +root_dir=$PWD +if [ ! -e $root_dir/.repo ]; then + root_dir=$PWD/../../.. + if [ ! -e $root_dir/.repo ]; then + echo "Repo root not found. Run this script from your repo root or the idegen directory." + exit 1 + fi +fi +index_file=$root_dir/module-index.txt +idegenjar=$script_dir/idegen.jar +if [ ! -e $idegenjar ]; then + # See if the jar is in the build directory. + idegenjar=$root_dir/out/host/linux-x86/framework/idegen.jar +fi + if [ ! -e "$index_file" ]; then echo "Module index file missing; generating this is only done the first time." echo "If any dependencies change, you should generate a new index file by running index-gen.sh." diff --git a/tools/idegen/src/com/android/idegen/Constants.java b/tools/idegen/src/com/android/idegen/Constants.java deleted file mode 100644 index f541dd48d..000000000 --- a/tools/idegen/src/com/android/idegen/Constants.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 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.idegen; - -import java.nio.charset.Charset; - -/** - * Constants - */ -public class Constants { - - public static final Charset CHARSET = Charset.forName("UTF-8"); - - public static final String REL_TEMPLATE_DIR = "development/tools/idegen/templates"; - public static final String REL_MODULES_TEMPLATE = REL_TEMPLATE_DIR + "/idea/modules.xml"; - public static final String REL_VCS_TEMPLATE = REL_TEMPLATE_DIR + "/idea/vcs.xml"; - public static final String REL_IML_TEMPLATE = REL_TEMPLATE_DIR + "/module-template.iml"; - - public static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS"; - - public static final String FRAMEWORK_MODULE = "framework"; - public static final String[] AUTO_DEPENDENCIES = new String[]{ - FRAMEWORK_MODULE, "libcore" - }; - public static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[] { - "packages", "vendor", "frameworks/ex", "frameworks/opt", "frameworks/support" - }; - - // Framework needs a special constant for it's intermediates because it does not follow - // normal conventions. - public static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates"; -} diff --git a/tools/idegen/src/com/android/idegen/DirectorySearch.java b/tools/idegen/src/com/android/idegen/DirectorySearch.java index 1bbf99ff8..2ff23e3de 100644 --- a/tools/idegen/src/com/android/idegen/DirectorySearch.java +++ b/tools/idegen/src/com/android/idegen/DirectorySearch.java @@ -21,8 +21,12 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.io.File; +import java.io.FileNotFoundException; import java.io.FilenameFilter; +import java.net.URISyntaxException; import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -31,6 +35,8 @@ import java.util.regex.Pattern; */ public class DirectorySearch { + private static final Logger logger = Logger.getLogger(DirectorySearch.class.getName()); + private static final HashSet<String> SOURCE_DIRS = Sets.newHashSet(); static { SOURCE_DIRS.add("src"); @@ -40,6 +46,9 @@ public class DirectorySearch { private static final Pattern EXCLUDE_PATTERN = Pattern.compile("values-..(-.*)*"); private static File repoRoot = null; + public static final String REL_TEMPLATE_DIR = "templates"; + public static final String REL_TEMPLATE_PATH_FROM_ROOT = "development/tools/idegen/" + + REL_TEMPLATE_DIR; /** * Find the repo root. This is the root branch directory of a full repo checkout. @@ -138,4 +147,38 @@ public class DirectorySearch { return builder.build(); } + + private static File templateDirCurrent = null; + private static File templateDirRoot = null; + + public static File findTemplateDir() throws FileNotFoundException { + // Cache optimization. + if (templateDirCurrent != null && templateDirCurrent.exists()) return templateDirCurrent; + if (templateDirRoot != null && templateDirRoot.exists()) return templateDirRoot; + + File currentDir = null; + try { + currentDir = new File(IntellijProject.class.getProtectionDomain().getCodeSource() + .getLocation().toURI().getPath()).getParentFile(); + } catch (URISyntaxException e) { + logger.log(Level.SEVERE, "Could not get jar location.", e); + return null; + } + + // First check relative to current run directory. + templateDirCurrent = new File(currentDir, REL_TEMPLATE_DIR); + if (templateDirCurrent.exists()) { + return templateDirCurrent; + } else { + // Then check relative to root directory. + templateDirRoot = new File(repoRoot, REL_TEMPLATE_PATH_FROM_ROOT); + if (templateDirRoot.exists()) { + return templateDirRoot; + } + } + throw new FileNotFoundException( + "Unable to find template dir. Tried the following locations:\n" + + templateDirCurrent.getAbsolutePath() + "\n" + + templateDirRoot.getAbsolutePath()); + } } diff --git a/tools/idegen/src/com/android/idegen/FrameworkModule.java b/tools/idegen/src/com/android/idegen/FrameworkModule.java index bfcd1fab3..8743925e4 100644 --- a/tools/idegen/src/com/android/idegen/FrameworkModule.java +++ b/tools/idegen/src/com/android/idegen/FrameworkModule.java @@ -25,15 +25,19 @@ import java.io.File; */ public class FrameworkModule extends StandardModule { + // Framework needs a special constant for it's intermediates because it does not follow + // normal conventions. + private static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates"; + public FrameworkModule(String moduleName, String makeFile) { - super(Constants.FRAMEWORK_MODULE, makeFile, true); + super(IntellijProject.FRAMEWORK_MODULE, makeFile, true); } @Override protected String buildIntermediates() { StringBuilder sb = new StringBuilder(); File intermediates = new File(repoRoot, - Constants.REL_OUT_APP_DIR + File.separator + Constants.FRAMEWORK_INTERMEDIATES); + REL_OUT_APP_DIR + File.separator + FRAMEWORK_INTERMEDIATES); ImmutableList<File> intermediateSrcDirs = DirectorySearch.findSourceDirs(intermediates); sb.append(" <content url=\"file://").append(intermediates).append("\">\n"); for (File src : intermediateSrcDirs) { diff --git a/tools/idegen/src/com/android/idegen/IntellijProject.java b/tools/idegen/src/com/android/idegen/IntellijProject.java index d5564d599..e49a12b72 100644 --- a/tools/idegen/src/com/android/idegen/IntellijProject.java +++ b/tools/idegen/src/com/android/idegen/IntellijProject.java @@ -22,6 +22,7 @@ import com.google.common.io.Files; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Set; import java.util.logging.Logger; @@ -31,9 +32,16 @@ import java.util.logging.Logger; */ public class IntellijProject { + public static final String FRAMEWORK_MODULE = "framework"; + public static final Charset CHARSET = Charset.forName("UTF-8"); + private static final Logger logger = Logger.getLogger(IntellijProject.class.getName()); + private static final String MODULES_TEMPLATE_FILE_NAME = "modules.xml"; + private static final String VCS_TEMPLATE_FILE_NAME = "vcs.xml"; + ModuleCache cache = ModuleCache.getInstance(); + File indexFile; File repoRoot; File projectIdeaDir; @@ -55,6 +63,11 @@ public class IntellijProject { // First pass, find all dependencies and cache them. Module module = cache.getAndCache(moduleName); + if (module == null) { + logger.info("Module '" + moduleName + "' not found." + + " Module names are case senstive."); + return; + } projectIdeaDir = new File(module.getDir(), ".idea"); projectIdeaDir.mkdir(); copyTemplates(); @@ -104,19 +117,27 @@ public class IntellijProject { /** * Framework module needs special handling due to one off resource path: - * frameworks/base/Android.mk + * frameworks/base/Android.mk */ private void buildFrameWorkModule() throws IOException { - String makeFile = cache.getMakeFile(Constants.FRAMEWORK_MODULE); - logger.info("makefile: " + makeFile); - StandardModule frameworkModule = new FrameworkModule(Constants.FRAMEWORK_MODULE, makeFile); - frameworkModule.build(); - cache.put(frameworkModule); + String makeFile = cache.getMakeFile(FRAMEWORK_MODULE); + if (makeFile == null) { + logger.warning("Unable to find framework module: " + FRAMEWORK_MODULE + + ". Skipping."); + } else { + logger.info("makefile: " + makeFile); + StandardModule frameworkModule = new FrameworkModule(FRAMEWORK_MODULE, + makeFile); + frameworkModule.build(); + cache.put(frameworkModule); + } } private void createModulesFile(Module module) throws IOException { - String modulesContent = Files.toString(new File(repoRoot, Constants.REL_MODULES_TEMPLATE), - Constants.CHARSET); + String modulesContent = Files.toString( + new File(DirectorySearch.findTemplateDir(), + "idea" + File.separator + MODULES_TEMPLATE_FILE_NAME), + CHARSET); StringBuilder sb = new StringBuilder(); File moduleIml = module.getImlFile(); sb.append(" <module fileurl=\"file://").append(moduleIml.getAbsolutePath()) @@ -131,12 +152,14 @@ public class IntellijProject { File out = new File(projectIdeaDir, "modules.xml"); logger.info("Creating " + out.getAbsolutePath()); - Files.write(modulesContent, out, Constants.CHARSET); + Files.write(modulesContent, out, CHARSET); } private void createVcsFile(Module module) throws IOException { - String vcsTemplate = Files.toString(new File(module.getRepoRoot(), - Constants.REL_VCS_TEMPLATE), Constants.CHARSET); + String vcsTemplate = Files.toString( + new File(DirectorySearch.findTemplateDir(), + "idea" + File.separator + VCS_TEMPLATE_FILE_NAME), + CHARSET); StringBuilder sb = new StringBuilder(); for (String name : module.getAllDependencies()) { @@ -149,18 +172,17 @@ public class IntellijProject { } } vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString()); - Files.write(vcsTemplate, new File(projectIdeaDir, "vcs.xml"), Constants.CHARSET); + Files.write(vcsTemplate, new File(projectIdeaDir, "vcs.xml"), CHARSET); } private void createNameFile(String name) throws IOException { - File out = new File(projectIdeaDir, ".name"); - Files.write(name, out, Constants.CHARSET); + File out = new File(projectIdeaDir, ".name"); + Files.write(name, out, CHARSET); } private void copyTemplates() throws IOException { - File templateDir = new File(repoRoot, - Constants.REL_TEMPLATE_DIR + File.separatorChar + "idea"); - copyTemplates(templateDir, projectIdeaDir); + File templateDir = DirectorySearch.findTemplateDir(); + copyTemplates(new File(templateDir, "idea"), projectIdeaDir); } private void copyTemplates(File fromDir, File toDir) throws IOException { diff --git a/tools/idegen/src/com/android/idegen/Module.java b/tools/idegen/src/com/android/idegen/Module.java index 7ba42a3f4..deb22811f 100644 --- a/tools/idegen/src/com/android/idegen/Module.java +++ b/tools/idegen/src/com/android/idegen/Module.java @@ -34,6 +34,8 @@ public abstract class Module { private static final Logger logger = Logger.getLogger(Module.class.getName()); + private static final String IML_TEMPLATE_FILE_NAME = "module-template.iml"; + /** * All possible attributes for the make file. */ @@ -44,23 +46,35 @@ public abstract class Module { } ModuleCache moduleCache = ModuleCache.getInstance(); + private File imlFile; + private Set<String> allDependencies = Sets.newHashSet(); // direct + indirect + private Set<File> allDependentImlFiles = Sets.newHashSet(); protected abstract void build() throws IOException; + protected abstract String getName(); + protected abstract File getDir(); + protected abstract boolean isAndroidModule(); + protected abstract List<File> getIntermediatesDirs(); + public abstract Set<String> getDirectDependencies(); + protected abstract ImmutableList<File> getSourceDirs(); + protected abstract ImmutableList<File> getExcludeDirs(); + public abstract File getRepoRoot(); public void buildImlFile() throws IOException { - String imlTemplate = Files.toString(new File(getRepoRoot(), Constants.REL_IML_TEMPLATE), - Constants.CHARSET); + String imlTemplate = Files.toString( + new File(DirectorySearch.findTemplateDir(), IML_TEMPLATE_FILE_NAME), + IntellijProject.CHARSET); String facetXml = ""; if (isAndroidModule()) { @@ -100,7 +114,7 @@ public abstract class Module { imlFile = new File(moduleDir, getName() + ".iml"); logger.info("Creating " + imlFile.getAbsolutePath()); - Files.write(imlTemplate, imlFile, Constants.CHARSET); + Files.write(imlTemplate, imlFile, IntellijProject.CHARSET); } protected String buildIntermediates() { diff --git a/tools/idegen/src/com/android/idegen/StandardModule.java b/tools/idegen/src/com/android/idegen/StandardModule.java index dffb95e01..f7b24b085 100644 --- a/tools/idegen/src/com/android/idegen/StandardModule.java +++ b/tools/idegen/src/com/android/idegen/StandardModule.java @@ -17,6 +17,7 @@ package com.android.idegen; import com.google.common.base.Objects; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -45,10 +46,18 @@ import java.util.regex.Pattern; */ public class StandardModule extends Module { + static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS"; + private static final Logger logger = Logger.getLogger(StandardModule.class.getName()); private static final Pattern SRC_PATTERN = Pattern.compile( ".*\\(call all-java-files-under, (.*)\\)"); + private static final String[] AUTO_DEPENDENCIES = new String[]{ + IntellijProject.FRAMEWORK_MODULE, "libcore" + }; + private static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[]{ + "packages", "vendor", "frameworks/ex", "frameworks/opt", "frameworks/support" + }; String moduleName; File makeFile; @@ -66,7 +75,8 @@ public class StandardModule extends Module { } public StandardModule(String moduleName, String makeFile, boolean searchForSrc) { - this(moduleName, new File(makeFile), searchForSrc); + this(Preconditions.checkNotNull(moduleName), new File(Preconditions.checkNotNull(makeFile)), + searchForSrc); } public StandardModule(String moduleName, File makeFile, boolean searchForSrc) { @@ -75,16 +85,16 @@ public class StandardModule extends Module { this.moduleRoot = makeFile.getParentFile(); this.repoRoot = DirectorySearch.findRepoRoot(makeFile); this.intermediatesDir = new File(repoRoot.getAbsolutePath() + File.separator + - Constants.REL_OUT_APP_DIR + File.separator + getName() + "_intermediates" + + REL_OUT_APP_DIR + File.separator + getName() + "_intermediates" + File.separator + "src"); this.searchForSrc = searchForSrc; // TODO: auto-detect when framework dependency is needed instead of using coded list. - for (String dir : Constants.DIRS_WITH_AUTO_DEPENDENCIES) { + for (String dir : DIRS_WITH_AUTO_DEPENDENCIES) { // length + 2 to account for slash boolean isDir = makeFile.getAbsolutePath().startsWith(repoRoot + "/" + dir); if (isDir) { - for (String dependency : Constants.AUTO_DEPENDENCIES) { + for (String dependency : AUTO_DEPENDENCIES) { this.directDependencies.add(dependency); } } diff --git a/tutorials/ReverseDebug/Android.mk b/tutorials/ReverseDebug/Android.mk new file mode 100644 index 000000000..586daf85a --- /dev/null +++ b/tutorials/ReverseDebug/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2013 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. + +ifneq ($(filter arm ,$(TARGET_ARCH)),) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= main.c.arm + +LOCAL_CFLAGS := -std=gnu99 -O0 + +LOCAL_MODULE := reverse_debug + +include $(BUILD_EXECUTABLE) + +endif # arm in TARGET_ARCH diff --git a/tutorials/ReverseDebug/README.txt b/tutorials/ReverseDebug/README.txt new file mode 100644 index 000000000..2578a4d1a --- /dev/null +++ b/tutorials/ReverseDebug/README.txt @@ -0,0 +1,137 @@ +This is a tutorial/unittest for gdb's reverse debugging feature. It is a new +feature that allows users to take a snapshot of the machine state, continue +until a later stage of the program, then return to the previously recorded +state and execute again. An ideal usage case is to help track down the reason +why a memory location is clobbered. + +In the sample below, the "clobber" function trashes a neighboring variable "p" +on the stack next to the "values" variable, and the program will crash at +line 42 when "p" is being dereferenced. + + 18 #include <stdio.h> + 19 #include <stdlib.h> + 20 + 21 #define ARRAY_LENGTH 10 + 22 + 23 int flag; + 24 + 25 void clobber(int *array, int size) { + 26 /* Make sure it clobbers something. */ + 27 array[-1] = 0x123; + 28 array[size] = 0x123; + 29 } + 30 + 31 int main(void) { + 32 int values[ARRAY_LENGTH]; + 33 int *p = (int *) malloc(sizeof(int)); + 34 *p = 10; + 35 + 36 while (!flag) { + 37 sleep(1); + 38 } + 39 + 40 /* Set a breakpint here: "b main.c:41" */ + 41 clobber(values, ARRAY_LENGTH); + 42 printf("*p = %d\n", *p); + 43 free(p); + 44 + 45 return 0; + 46 } + +The test program can be built/installed on the device by doing: + +> mmm development/tutorials/ReverseDebug +> adb sync +> adb shell reverse_debug + +In another window the following command can be used to attach to the running +program: + +> gdbclient reverse_debug :5039 reverse_debug +[1] 12802 +Attached; pid = 1842 +Listening on port 5039 +GNU gdb (GDB) 7.6 +Copyright (C) 2013 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. Type "show copying" +and "show warranty" for details. +This GDB was configured as "--host=x86_64-linux-gnu --target=arm-linux-android". +For bug reporting instructions, please see: +<http://source.android.com/source/report-bugs.html>... +Reading symbols from /usr/local/google/work/master/out/target/product/manta/symbols/system/bin/reverse_debug...done. +Remote debugging from host 127.0.0.1 +nanosleep () at bionic/libc/arch-arm/syscalls/nanosleep.S:10 +10 mov r7, ip + +==== + +Now set a breakpoint on line 41 and set flag to 1 so that the program can + continue. + +(gdb) b main.c:41 +Breakpoint 1 at 0xb6f174a8: file development/tutorials/ReverseDebug/main.c, line 41. +(gdb) p flag=1 +$1 = 1 +(gdb) c +Continuing. + +==== + +Now try the new "record" command to take a snapshot of the machine state. + +Breakpoint 1, main () at development/tutorials/ReverseDebug/main.c:41 +41 clobber(values, ARRAY_LENGTH); +(gdb) record +(gdb) c +Continuing. + +==== + +Now the program crashes as expected as "p" has been clobbered. The +"reverse-continue" command will bring the program back to line 41 and let you +replay each instruction from there. + +Program received signal SIGSEGV, Segmentation fault. +0xb6f174bc in main () at development/tutorials/ReverseDebug/main.c:42 +42 printf("*p = %d\n", *p); +(gdb) reverse-continue +Continuing. + +No more reverse-execution history. +main () at development/tutorials/ReverseDebug/main.c:41 +41 clobber(values, ARRAY_LENGTH); + + +==== + +Now let's add a watch point at "&p" to hopefully catch the clobber on the spot: + +(gdb) watch *(&p) +Hardware watchpoint 2: *(&p) +(gdb) c +Continuing. +Hardware watchpoint 2: *(&p) + +==== + +And here it is: + +Old value = (int *) 0xb728c020 +New value = (int *) 0x123 +0xb6f17440 in clobber (array=0xbebcaab0, size=10) + at development/tutorials/ReverseDebug/main.c:28 +28 array[size] = 0x123; + + +=============================== + +That said, reverse debugging on ARM is still in the infant stage. Currently +(as of gdb 7.6) it only recognizes ARM instructions and will punt on all +Thumb(2) instructions. To give it a try you will need to recompile your +program in ARM mode. To do that you have to add the ".arm" suffix to the +desired file in Android.mk: + +LOCAL_SRC_FILES:= main.c.arm + diff --git a/tutorials/ReverseDebug/main.c b/tutorials/ReverseDebug/main.c new file mode 100644 index 000000000..5ae18901b --- /dev/null +++ b/tutorials/ReverseDebug/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#define ARRAY_LENGTH 10 + +int flag; + +void clobber(int *array, int size) { + /* Make sure it clobbers something. */ + array[-1] = 0x123; + array[size] = 0x123; +} + +int main(void) { + int values[ARRAY_LENGTH]; + int *p = (int *) malloc(sizeof(int)); + *p = 10; + + while (!flag) { + sleep(1); + } + + /* Set a breakpint here: "b main.c:41" */ + clobber(values, ARRAY_LENGTH); + printf("*p = %d\n", *p); + free(p); + + return 0; +} |