Merge "Set HAS_CONTENT field from the app after writing audio content."
diff --git a/apps/BuildWidget/res/layout/widget.xml b/apps/BuildWidget/res/layout/widget.xml
index 81b3ae4..41be777 100644
--- a/apps/BuildWidget/res/layout/widget.xml
+++ b/apps/BuildWidget/res/layout/widget.xml
@@ -14,31 +14,64 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="6dip"
>
- <TextView
- android:id="@+id/build_info"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- style="@style/Text.BuildInfo.Fancy"
- android:textSize="18dip"
- />
+ <LinearLayout
+ android:id="@+id/block1x1"
+ android:layout_width="80dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ >
- <TextView
- android:id="@+id/build_changelist"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- android:layout_marginTop="4dip"
- style="@style/Text.BuildInfo.Fancy"
- android:textSize="9dip"
- />
+ <TextView
+ android:id="@+id/build_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/Text.BuildInfo.Fancy"
+ android:textSize="18dip"
+ />
-</LinearLayout>
+ <TextView
+ android:id="@+id/build_date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/Text.BuildInfo.Fancy"
+ android:textSize="14dip"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_toRightOf="@id/block1x1"
+ >
+ <TextView
+ android:id="@+id/build_extra"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="32dp"
+ android:gravity="left|center_vertical"
+ android:minWidth="60dp"
+ style="@style/Text.BuildInfo.Fancy"
+ android:textSize="10dip"
+ android:layout_weight="1"
+ />
+
+ <ImageView
+ android:id="@+id/platlogo"
+ android:layout_width="64dp"
+ android:layout_height="match_parent"
+ android:padding="3dp"
+ android:src="@*android:drawable/platlogo"
+ android:scaleType="centerInside"
+ android:layout_weight="0"
+ />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/apps/BuildWidget/res/xml/widget_build.xml b/apps/BuildWidget/res/xml/widget_build.xml
index c7c443a..a5a444d 100644
--- a/apps/BuildWidget/res/xml/widget_build.xml
+++ b/apps/BuildWidget/res/xml/widget_build.xml
@@ -15,7 +15,12 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="150dip"
+ android:minResizeWidth="72dip"
+ android:minResizeHeight="72dip"
+ android:minWidth="72dip"
android:minHeight="72dip"
android:updatePeriodMillis="0"
- android:initialLayout="@layout/widget" />
+ android:initialLayout="@layout/widget"
+ android:resizeMode="horizontal"
+/>
+
diff --git a/apps/BuildWidget/src/com/android/buildwidget/BuildWidget.java b/apps/BuildWidget/src/com/android/buildwidget/BuildWidget.java
index 327a34b..bd2db0b 100644
--- a/apps/BuildWidget/src/com/android/buildwidget/BuildWidget.java
+++ b/apps/BuildWidget/src/com/android/buildwidget/BuildWidget.java
@@ -26,6 +26,7 @@
import android.content.res.Resources;
import android.net.Uri;
import android.os.IBinder;
+import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Log;
import android.widget.RemoteViews;
@@ -69,12 +70,11 @@
0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent);
- updateViews.setTextViewText(R.id.build_info,
- android.os.Build.VERSION.CODENAME + " " +
- android.os.Build.ID);
- updateViews.setTextViewText(R.id.build_changelist,
- android.os.Build.FINGERPRINT
- );
+ updateViews.setTextViewText(R.id.build_info, android.os.Build.ID);
+ updateViews.setTextViewText(R.id.build_date,
+ DateUtils.formatDateTime(context, android.os.Build.TIME,
+ DateUtils.FORMAT_NUMERIC_DATE));
+ updateViews.setTextViewText(R.id.build_extra, android.os.Build.FINGERPRINT);
return updateViews;
}
diff --git a/apps/Development/src/com/android/development/SyncAdapterDriver.java b/apps/Development/src/com/android/development/SyncAdapterDriver.java
index 0fde732..3a21903 100644
--- a/apps/Development/src/com/android/development/SyncAdapterDriver.java
+++ b/apps/Development/src/com/android/development/SyncAdapterDriver.java
@@ -371,8 +371,11 @@
com.android.internal.R.styleable.SyncAdapter_isAlwaysSyncable, false);
final boolean allowParallelSyncs = sa.getBoolean(
com.android.internal.R.styleable.SyncAdapter_allowParallelSyncs, false);
+ final String settingsActivity =
+ sa.getString(com.android.internal.R.styleable
+ .SyncAdapter_settingsActivity);
return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
- isAlwaysSyncable, allowParallelSyncs);
+ isAlwaysSyncable, allowParallelSyncs, settingsActivity);
} finally {
sa.recycle();
}
diff --git a/apps/Fallback/res/values-af/strings.xml b/apps/Fallback/res/values-af/strings.xml
new file mode 100644
index 0000000..8cf16a7
--- /dev/null
+++ b/apps/Fallback/res/values-af/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="appTitle" msgid="161410001913116606">"Terugval"</string>
+ <string name="title" msgid="8156274565006125136">"Nie-ondersteunde handeling"</string>
+ <string name="error" msgid="6539615832923362301">"Hierdie handeling word tans nie ondersteun nie."</string>
+</resources>
diff --git a/apps/Fallback/res/values-am/strings.xml b/apps/Fallback/res/values-am/strings.xml
new file mode 100644
index 0000000..0c89122
--- /dev/null
+++ b/apps/Fallback/res/values-am/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"የማይደገፍ ድርጊት"</string>
+ <string name="error" msgid="6539615832923362301">"ያድርጊት በአሁኑ ጊዜ የማይደገፍ ነው።"</string>
+</resources>
diff --git a/apps/Fallback/res/values-iw/strings.xml b/apps/Fallback/res/values-iw/strings.xml
index 671919b..cbf35ef 100644
--- a/apps/Fallback/res/values-iw/strings.xml
+++ b/apps/Fallback/res/values-iw/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle" msgid="161410001913116606">"החזרה"</string>
+ <string name="appTitle" msgid="161410001913116606">"חלופי"</string>
<string name="title" msgid="8156274565006125136">"פעולה לא נתמכת"</string>
<string name="error" msgid="6539615832923362301">"הפעולה אינה נתמכת בשלב זה."</string>
</resources>
diff --git a/apps/Fallback/res/values-ms/strings.xml b/apps/Fallback/res/values-ms/strings.xml
new file mode 100644
index 0000000..930fe79
--- /dev/null
+++ b/apps/Fallback/res/values-ms/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="appTitle" msgid="161410001913116606">"Jatuh balik"</string>
+ <string name="title" msgid="8156274565006125136">"Tindakan tidak disokong"</string>
+ <string name="error" msgid="6539615832923362301">"Tindakan tidak disokong pada masa ini."</string>
+</resources>
diff --git a/apps/Fallback/res/values-sw/strings.xml b/apps/Fallback/res/values-sw/strings.xml
new file mode 100644
index 0000000..4b07acb
--- /dev/null
+++ b/apps/Fallback/res/values-sw/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="appTitle" msgid="161410001913116606">"Kimbilio"</string>
+ <string name="title" msgid="8156274565006125136">"Kitendo kisichohimiliwa"</string>
+ <string name="error" msgid="6539615832923362301">"Kitendo hakihimili kwa sasa."</string>
+</resources>
diff --git a/apps/Fallback/res/values-zu/strings.xml b/apps/Fallback/res/values-zu/strings.xml
new file mode 100644
index 0000000..2c85d28
--- /dev/null
+++ b/apps/Fallback/res/values-zu/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="appTitle" msgid="161410001913116606">"Buyela emuva"</string>
+ <string name="title" msgid="8156274565006125136">"Isenzo esingasekelwe"</string>
+ <string name="error" msgid="6539615832923362301">"Leso senzo okwamanje asisekelwe."</string>
+</resources>
diff --git a/build/sdk-android-armeabi-v7a.atree b/build/sdk-android-armeabi-v7a.atree
index 8a79994..8867bf7 100644
--- a/build/sdk-android-armeabi-v7a.atree
+++ b/build/sdk-android-armeabi-v7a.atree
@@ -15,4 +15,7 @@
#
# Copy the ARMv7 specific kernel image to .../kernel-qemu
-prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu-armv7 platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/kernel-qemu
+prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu-armv7 system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/kernel-qemu
+
+# version files for the SDK updater, from development.git
+development/sdk/images_armeabi-v7a_source.properties system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/source.properties
diff --git a/build/sdk-android-armeabi.atree b/build/sdk-android-armeabi.atree
index d90b069..a31eca6 100644
--- a/build/sdk-android-armeabi.atree
+++ b/build/sdk-android-armeabi.atree
@@ -14,4 +14,7 @@
# limitations under the License.
#
-prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/kernel-qemu
+prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/kernel-qemu
+
+# version files for the SDK updater, from development.git
+development/sdk/images_armeabi_source.properties system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/source.properties
diff --git a/build/sdk-android-x86.atree b/build/sdk-android-x86.atree
index d90b069..748478a 100644
--- a/build/sdk-android-x86.atree
+++ b/build/sdk-android-x86.atree
@@ -14,4 +14,7 @@
# limitations under the License.
#
-prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/kernel-qemu
+prebuilt/android-${TARGET_ARCH}/kernel/kernel-qemu system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/kernel-qemu
+
+# version files for the SDK updater, from development.git
+development/sdk/images_x86_source.properties system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/source.properties
diff --git a/build/sdk-darwin-x86.atree b/build/sdk-darwin-x86.atree
index bc88d16..2460d5d 100644
--- a/build/sdk-darwin-x86.atree
+++ b/build/sdk-darwin-x86.atree
@@ -18,7 +18,6 @@
# Platform Tools Component
##############################################################################
-prebuilt/darwin-x86/llvm-rs-cc/llvm-rs-cc platform-tools/llvm-rs-cc
##############################################################################
diff --git a/build/sdk-linux-x86.atree b/build/sdk-linux-x86.atree
index c74216e..1a9a890 100644
--- a/build/sdk-linux-x86.atree
+++ b/build/sdk-linux-x86.atree
@@ -18,7 +18,6 @@
# Platform Tools Component
##############################################################################
-prebuilt/linux-x86/llvm-rs-cc/llvm-rs-cc platform-tools/llvm-rs-cc
##############################################################################
diff --git a/build/sdk-windows-x86.atree b/build/sdk-windows-x86.atree
new file mode 100644
index 0000000..68105a2
--- /dev/null
+++ b/build/sdk-windows-x86.atree
@@ -0,0 +1,60 @@
+#
+# 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.
+#
+
+#
+# These are the files that comprise the *Windows* SDK.
+#
+# The Windows SDK is based on the Linux one so in this file we
+# need to remove Linux binaries and replace them by their
+# Windows counterparts.
+#
+# This file only includes platform-dependent files.
+# Tools-dependent files (and not tied to a specific platform)
+# are controled by sdk/build/tools.windows.atree.
+#
+
+rm platform-tools/adb
+bin/adb.exe strip platform-tools/adb.exe
+bin/AdbWinUsbApi.dll platform-tools/AdbWinUsbApi.dll
+bin/AdbWinApi.dll platform-tools/AdbWinApi.dll
+
+rm platform-tools/aapt
+bin/aapt.exe strip platform-tools/aapt.exe
+
+rm platform-tools/aidl
+bin/aidl.exe strip platform-tools/aidl.exe
+
+rm platform-tools/dx
+dalvik/dx/etc/dx.bat platform-tools/dx.bat
+
+rm platform-tools/dexdump
+bin/dexdump.exe strip platform-tools/dexdump.exe
+
+rm platform-tools/llvm-rs-cc
+bin/llvm-rs-cc.exe strip platform-tools/llvm-rs-cc.exe
+
+prebuilt/windows/swt/swt.jar tools/lib/x86/swt.jar
+prebuilt/windows-x86_64/swt/swt.jar tools/lib/x86_64/swt.jar
+
+external/sonivox/jet_tools/JetCreator tools/Jet/JetCreator
+external/sonivox/jet_tools/JetCreator_content tools/Jet/demo_content
+external/sonivox/jet_tools/logic_templates tools/Jet/logic_templates
+prebuilt/windows/jetcreator/EASDLL.dll tools/Jet/JetCreator/EASDLL.dll
+external/sonivox/docs/JET_Authoring_Guidelines.html docs/JetCreator/JET_Authoring_Guidelines.html
+external/sonivox/docs/JET_Authoring_Guidelines_files docs/JetCreator/JET_Authoring_Guidelines_files
+external/sonivox/docs/JET_Creator_User_Manual.html docs/JetCreator/JET_Creator_User_Manual.html
+external/sonivox/docs/JET_Creator_User_Manual_files docs/JetCreator/JET_Creator_User_Manual_files
+
diff --git a/build/sdk.atree b/build/sdk.atree
index 355fa1a..35ca61a 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -38,11 +38,10 @@
##############################################################################
# host tools from out/host/$(HOST_OS)-$(HOST_ARCH)/
-bin/adb platform-tools/adb
-bin/aapt platform-tools/aapt
-bin/aidl platform-tools/aidl
-bin/llvm-rs-cc platform-tools/llvm-rs-cc-2
-development/sdk/llvm-rs-cc.txt platform-tools/llvm-rs-cc.txt
+bin/adb strip platform-tools/adb
+bin/aapt strip platform-tools/aapt
+bin/aidl strip platform-tools/aidl
+bin/llvm-rs-cc strip platform-tools/llvm-rs-cc
# dx
bin/dx platform-tools/dx
@@ -55,7 +54,7 @@
# Platform Component
##############################################################################
-# version files for the SDK updater, from sdk.git
+# version files for the SDK updater, from development.git
development/sdk/platform_source.properties platforms/${PLATFORM_NAME}/source.properties
# copy build prop from out/.../sdk/
@@ -72,14 +71,6 @@
external/clang/lib/Headers platforms/${PLATFORM_NAME}/renderscript/clang-include
external/clang/LICENSE.TXT platforms/${PLATFORM_NAME}/renderscript/clang-include/LICENSE.TXT
-# System images + Kernel
-system.img platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/system.img
-ramdisk.img platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/ramdisk.img
-userdata.img platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/userdata.img
-system/build.prop platforms/${PLATFORM_NAME}/images/${TARGET_CPU_ABI}/build.prop
-
-# Note: the kernel image is handled by sdk-android-<abi>.atree now.
-
# emulator skins from sdk.git
development/tools/emulator/skins/QVGA platforms/${PLATFORM_NAME}/skins/QVGA
development/tools/emulator/skins/WQVGA432 platforms/${PLATFORM_NAME}/skins/WQVGA432
@@ -88,21 +79,22 @@
development/tools/emulator/skins/WVGA800 platforms/${PLATFORM_NAME}/skins/WVGA800
development/tools/emulator/skins/WVGA854 platforms/${PLATFORM_NAME}/skins/WVGA854
development/tools/emulator/skins/WSVGA platforms/${PLATFORM_NAME}/skins/WSVGA
-development/tools/emulator/skins/WXGA platforms/${PLATFORM_NAME}/skins/WXGA
+development/tools/emulator/skins/WXGA720 platforms/${PLATFORM_NAME}/skins/WXGA720
+development/tools/emulator/skins/WXGA800 platforms/${PLATFORM_NAME}/skins/WXGA800
# Platform SDK properties
development/sdk/sdk.properties platforms/${PLATFORM_NAME}/sdk.properties
# sdk.git Ant templates for project files
-sdk/templates/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
-sdk/templates/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
-sdk/templates/java_file.template platforms/${PLATFORM_NAME}/templates/java_file.template
-sdk/templates/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
-sdk/templates/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
-sdk/templates/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
-sdk/templates/icon_ldpi.png platforms/${PLATFORM_NAME}/templates/icon_ldpi.png
-sdk/templates/icon_mdpi.png platforms/${PLATFORM_NAME}/templates/icon_mdpi.png
-sdk/templates/icon_hdpi.png platforms/${PLATFORM_NAME}/templates/icon_hdpi.png
+development/tools/templates/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
+development/tools/templates/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
+development/tools/templates/java_file.template platforms/${PLATFORM_NAME}/templates/java_file.template
+development/tools/templates/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
+development/tools/templates/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
+development/tools/templates/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
+development/tools/templates/ic_launcher_ldpi.png platforms/${PLATFORM_NAME}/templates/ic_launcher_ldpi.png
+development/tools/templates/ic_launcher_mdpi.png platforms/${PLATFORM_NAME}/templates/ic_launcher_mdpi.png
+development/tools/templates/ic_launcher_hdpi.png platforms/${PLATFORM_NAME}/templates/ic_launcher_hdpi.png
# Eclipse Editors support
framework/layoutlib.jar platforms/${PLATFORM_NAME}/data/layoutlib.jar
@@ -123,6 +115,18 @@
sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/skins/NOTICE.txt
##############################################################################
+# System image Component
+##############################################################################
+
+# System images + Kernel
+system.img system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/system.img
+ramdisk.img system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/ramdisk.img
+userdata.img system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/userdata.img
+system/build.prop system-images/${PLATFORM_NAME}/${TARGET_CPU_ABI}/build.prop
+
+# Note: the kernel image is handled by sdk-android-<abi>.atree now.
+
+##############################################################################
# Docs Component
##############################################################################
@@ -149,6 +153,8 @@
#
development/samples/AccessibilityService samples/${PLATFORM_NAME}/AccessibilityService
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/AccelerometerPlay
+development/samples/ActionBarCompat samples/${PLATFORM_NAME}/ActionBarCompat
+development/samples/AndroidBeam samples/${PLATFORM_NAME}/AndroidBeam
development/samples/ApiDemos samples/${PLATFORM_NAME}/ApiDemos
development/samples/BackupRestore samples/${PLATFORM_NAME}/BackupRestore
development/samples/BasicGLSurfaceView samples/${PLATFORM_NAME}/BasicGLSurfaceView
@@ -162,7 +168,9 @@
development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution
development/samples/NotePad samples/${PLATFORM_NAME}/NotePad
+development/samples/NFCDemo samples/${PLATFORM_NAME}/NFCDemo
development/samples/RandomMusicPlayer samples/${PLATFORM_NAME}/RandomMusicPlayer
+development/samples/SampleSpellCheckerService samples/${PLATFORM_NAME}/SampleSpellCheckerService
development/samples/SampleSyncAdapter samples/${PLATFORM_NAME}/SampleSyncAdapter
development/samples/SearchableDictionary samples/${PLATFORM_NAME}/SearchableDictionary
development/samples/SipDemo samples/${PLATFORM_NAME}/SipDemo
@@ -180,6 +188,7 @@
development/samples/VoicemailProviderDemo samples/${PLATFORM_NAME}/VoicemailProviderDemo
development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
+development/samples/WiFiDirectDemo samples/${PLATFORM_NAME}/WiFiDirectDemo
development/samples/Wiktionary samples/${PLATFORM_NAME}/Wiktionary
development/samples/WiktionarySimple samples/${PLATFORM_NAME}/WiktionarySimple
development/samples/XmlAdapters samples/${PLATFORM_NAME}/XmlAdapters
@@ -200,21 +209,22 @@
sdk/files/README_add-ons.txt add-ons/README.txt
##############################################################################
-# Extra Component: Compatibility
+# Extra Component: Support
##############################################################################
-development/sdk/compatibility_source.properties extras/android/compatibility/source.properties
-development/sdk/compatibility_README.txt extras/android/compatibility/README.txt
-sdk/files/sdk_files_NOTICE.txt extras/android/compatibility/NOTICE.txt
-${OUT_DIR}/target/common/obj/PACKAGING/android-support-v4_intermediates/android-support-v4.jar extras/android/compatibility/v4/android-support-v4.jar
-frameworks/support/v4 extras/android/compatibility/v4/src
-development/samples/Support4Demos extras/android/compatibility/v4/samples/Support4Demos
-${OUT_DIR}/target/common/obj/PACKAGING/android-support-v13_intermediates/android-support-v13.jar extras/android/compatibility/v13/android-support-v13.jar
-frameworks/support/v13 extras/android/compatibility/v13/src
-development/samples/Support13Demos extras/android/compatibility/v13/samples/Support13Demos
+development/sdk/support_source.properties extras/android/support/source.properties
+development/sdk/support_README.txt extras/android/support/README.txt
+sdk/files/sdk_files_NOTICE.txt extras/android/support/NOTICE.txt
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-v4_intermediates/android-support-v4.jar extras/android/support/v4/android-support-v4.jar
+frameworks/support/v4 extras/android/support/v4/src
+development/samples/Support4Demos extras/android/support/v4/samples/Support4Demos
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-v13_intermediates/android-support-v13.jar extras/android/support/v13/android-support-v13.jar
+frameworks/support/v13 extras/android/support/v13/src
+development/samples/Support13Demos extras/android/support/v13/samples/Support13Demos
##############################################################################
# Tests Component
##############################################################################
framework/layoutlib-tests.jar tests/libtests/layoutlib-tests.jar
system/app/ConnectivityTest.apk tests/emulator-test-apps/ConnectivityTest.apk
+system/app/GpsLocationTest.apk tests/emulator-test-apps/GpsLocationTest.apk
diff --git a/build/tools/mk_sdk_repo_xml.sh b/build/tools/mk_sdk_repo_xml.sh
index 56f426b..a97ecaf 100755
--- a/build/tools/mk_sdk_repo_xml.sh
+++ b/build/tools/mk_sdk_repo_xml.sh
@@ -6,7 +6,7 @@
PROG_DIR=$(dirname $0)
-TYPES="tool platform-tool platform sample doc add-on"
+TYPES="tool platform-tool platform sample doc add-on system-image source support"
OSES="linux macosx windows any linux-x86 darwin"
TMP_DIR=$(mktemp -d -t sdkrepo.tmp.XXXXXXXX)
@@ -86,12 +86,17 @@
Platform.Version version
AndroidVersion.ApiLevel api-level
AndroidVersion.CodeName codename
+ Platform.IncludedAbi included-abi
Platform.MinToolsRev min-tools-rev
Platform.MinPlatformToolsRev min-platform-tools-rev
- Extra.Path path
Extra.Vendor vendor
+ Extra.Path path
+ Extra.OldPaths old-paths
Extra.MinApiLevel min-api-level
Sample.MinApiLevel min-api-level
+ SystemImage.Abi abi
+ Layoutlib.Api layoutlib/api
+ Layoutlib.Revision layoutlib/revision
# for addon packages
vendor vendor
name name
@@ -124,23 +129,37 @@
local OUT="$1"
shift
local KEY VALUE
+ local NODE LAST_NODE
while [[ "$1" ]]; do
KEY="$1"
VALUE="${2//@/ }"
+ NODE="${KEY%%/*}"
+ KEY="${KEY##*/}"
+ [[ "$NODE" == "$KEY" ]] && NODE=""
+ if [[ "$NODE" != "$LAST_NODE" ]]; then
+ [[ "$LAST_NODE" ]] && echo " </sdk:$LAST_NODE>" >> "$OUT"
+ LAST_NODE="$NODE"
+ [[ "$NODE" ]] && echo " <sdk:$NODE>" >> "$OUT"
+ fi
echo " <sdk:$KEY>$VALUE</sdk:$KEY>" >> "$OUT"
shift
shift
done
+ if [[ "$LAST_NODE" ]]; then echo " </sdk:$LAST_NODE>" >> "$OUT"; fi
}
while [[ -n "$1" ]]; do
# Process archives.
- # First we expect a type. For conveniency the type can be plural.
+ # First we expect a type. For convenience the type can be plural.
TYPE=$(check_enum "${1%%s}" $TYPES)
[[ -z $TYPE ]] && error "Unknown archive type '$1'."
shift
+ ELEMENT="$TYPE"
+ # The element name is different for extras:
+ [[ "$TYPE" == "support" ]] && ELEMENT="extra"
+
MAP=""
FIRST="1"
LIBS_XML=""
@@ -168,13 +187,16 @@
# - description all
# - revision all
# - version platform
- # - api-level platform sample doc add-on
- # - codename platform sample doc add-on
+ # - included-abi platform
+ # - api-level platform sample doc add-on system-image
+ # - codename platform sample doc add-on system-image
# - min-tools-rev platform sample
# - min-platform-tools-rev tool
# - min-api-level extra
# - vendor extra add-on
# - path extra
+ # - old-paths extra
+ # - abi system-image
#
# We don't actually validate here.
# Just take whatever is defined and put it in the XML.
@@ -212,7 +234,7 @@
MAP=$(parse_attributes "$PROPS" ${ATTRS[@]})
# Time to generate the XML for the package
- echo " <sdk:${TYPE}>" >> "$OUT"
+ echo " <sdk:${ELEMENT}>" >> "$OUT"
output_attributes "$OUT" $MAP
[[ -n "$LIBS_XML" ]] && echo "$LIBS_XML" >> "$OUT"
echo " <sdk:archives>" >> "$OUT"
@@ -241,7 +263,7 @@
if [[ ! "$OS" ]]; then
echo " </sdk:archives>" >> "$OUT"
- echo " </sdk:${TYPE}>" >> "$OUT"
+ echo " </sdk:${ELEMENT}>" >> "$OUT"
fi
done
diff --git a/build/tools/mk_sources_zip.py b/build/tools/mk_sources_zip.py
new file mode 100755
index 0000000..3d4c582
--- /dev/null
+++ b/build/tools/mk_sources_zip.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+#
+# 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.
+#
+
+import re
+import os
+import sys
+import getopt
+import zipfile
+
+VERBOSE = False
+TOP_FOLDER = "src"
+_RE_PKG = re.compile("^\s*package\s+([^\s;]+)\s*;.*")
+
+# Holds cmd-line arguments and context information
+class Params(object):
+ def __init__(self):
+ self.DRY = False
+ self.PROPS = None
+ self.SRC = None
+ self.DST = None
+ self.CNT_USED = 0
+ self.CNT_NOPKG = 0
+ # DIR is the list of directories to scan in TOPDIR.
+ self.DIR = "frameworks libcore"
+ # IGNORE is a list of namespaces to ignore. Must be java
+ # package definitions (e.g. "com.blah.foo.")
+ self.IGNORE = [ "sun.", "libcore.", "dalvik.",
+ "com.test.", "com.google.",
+ "coretestutils.", "test.", "test2.", "tests." ]
+ self.zipfile = None
+
+
+def verbose(msg, *args):
+ """Prints a verbose message to stderr if --verbose is set."""
+ global VERBOSE
+ if VERBOSE:
+ if args:
+ msg = msg % args
+ print >>sys.stderr, msg
+
+
+# Prints a usage summary
+def usage(error=None):
+ print """
+ Description:
+ This script collects all framework Java sources from the current android
+ source code and places them in a source.zip file that can be distributed
+ by the SDK Manager.
+
+ Usage:
+ %s [-n|-v] <source.properties> <sources.zip> <topdir>
+
+ The source.properties file must exist and will be injected in the Zip file.
+ The source directory must already exist.
+ Use -v for verbose output (lists each file being picked up or ignored).
+ Use -n for a dry-run (doesn't write the zip file).
+
+""" % sys.argv[0]
+
+ if error:
+ print >>sys.stderr, "Error:", error
+
+
+# Parse command line args, returns a Params instance or sys.exit(2) on error
+# after printing the error and the usage.
+def parseArgs(argv):
+ global VERBOSE
+ p = Params()
+ error = None
+
+ try:
+ opts, args = getopt.getopt(argv[1:],
+ "vns:",
+ [ "--verbose", "--dry", "--sourcedir=" ])
+ except getopt.GetoptError, e:
+ error = str(e)
+
+ if error is None:
+ for o, a in opts:
+ if o in [ "-n", "--dry" ]:
+ p.DRY = True
+ if o in [ "-v", "--verbose" ]:
+ VERBOSE = True
+ elif o in [ "-s", "--sourcedir" ]:
+ p.DIR = a
+
+ if len(args) != 3:
+ error = "Missing arguments: <source> <dest>"
+ else:
+ p.PROPS = args[0]
+ p.DST = args[1]
+ p.SRC = args[2]
+
+ if not os.path.isfile(p.PROPS):
+ error = "%s is not a file" % p.PROPS
+ if not os.path.isdir(p.SRC):
+ error = "%s is not a directory" % p.SRC
+
+ if error:
+ usage(error)
+ sys.exit(2)
+
+ return p
+
+
+# Recursively parses the given directory and processes java files found
+def parseSrcDir(p, srcdir):
+ if not os.path.exists(srcdir):
+ verbose("Error: Skipping unknown directory %s", srcdir)
+ return
+
+ for filename in os.listdir(srcdir):
+ filepath = os.path.join(srcdir, filename)
+ if filename.endswith(".java") and os.path.isfile(filepath):
+ pkg = checkJavaFile(filepath)
+ if not pkg:
+ verbose("No package found in %s", filepath)
+ if pkg:
+ # Should we ignore this package?
+ for ignore in p.IGNORE:
+ if pkg.startswith(ignore):
+ verbose("Ignore package %s [%s]", pkg, filepath)
+ pkg = None
+ break
+
+ if pkg:
+ pkg = pkg.replace(".", os.path.sep) # e.g. android.view => android/view
+ copy(p, filepath, pkg)
+ p.CNT_USED += 1
+ else:
+ p.CNT_NOPKG += 1
+ elif os.path.isdir(filepath):
+ parseSrcDir(p, filepath)
+
+
+# Check a java file to find its package declaration, if any
+def checkJavaFile(path):
+ try:
+ f = None
+ try:
+ f = file(path)
+ for l in f.readlines():
+ m = _RE_PKG.match(l)
+ if m:
+ return m.group(1)
+ finally:
+ if f: f.close()
+ except Exception:
+ pass
+
+ return None
+
+
+# Copy the given file (given its absolute filepath) to
+# the relative desk_pkg directory in the zip file.
+def copy(p, filepath, dest_pkg):
+ arc_path = os.path.join(TOP_FOLDER, dest_pkg, os.path.basename(filepath))
+ if p.DRY:
+ print >>sys.stderr, "zip %s [%s]" % (arc_path, filepath)
+ elif p.zipfile is not None:
+ p.zipfile.write(filepath, arc_path)
+
+
+def main():
+ p = parseArgs(sys.argv)
+ z = None
+ try:
+ if not p.DRY:
+ p.zipfile = z = zipfile.ZipFile(p.DST, "w", zipfile.ZIP_DEFLATED)
+ z.write(p.PROPS, TOP_FOLDER + "/source.properties")
+ for d in p.DIR.split():
+ if d:
+ parseSrcDir(p, os.path.join(p.SRC, d))
+ finally:
+ if z is not None:
+ z.close()
+ print "%s: %d java files copied" % (p.DST, p.CNT_USED)
+ if p.CNT_NOPKG:
+ print "%s: %d java files ignored" % (p.DST, p.CNT_NOPKG)
+ if p.DRY:
+ print >>sys.stderr, "This was in *DRY* mode. No copies done."
+
+
+if __name__ == "__main__":
+ main()
+
+# For emacs:
+# -*- tab-width: 4; -*-
diff --git a/build/tools/patch_windows_sdk.sh b/build/tools/patch_windows_sdk.sh
index 49cb78c..6d30577 100755
--- a/build/tools/patch_windows_sdk.sh
+++ b/build/tools/patch_windows_sdk.sh
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
# This script takes a Linux SDK, cleans it and injects the necessary Windows
# binaries needed by the SDK. The script has 2 parts:
@@ -53,58 +68,13 @@
$USB_DRIVER_HOOK $V $TEMP_SDK_DIR $TOPDIR
fi
-# Remove obsolete stuff from tools & platform
-TOOLS=$TEMP_SDK_DIR/tools
-PLATFORM_TOOLS=$TEMP_SDK_DIR/platform-tools
-LIB=$TEMP_SDK_DIR/tools/lib
-rm $V $TOOLS/{dmtracedump,etc1tool,hprof-conv,sqlite3,zipalign}
-rm $V $LIB/*/swt.jar
-rm $V $PLATFORM_TOOLS/{adb,aapt,aidl,dx,dexdump,llvm-rs-cc,llvm-rs-cc-2}
-# Copy all the new stuff in tools
-# Note: some tools are first copied here and then moved in platform-tools
-cp $V $WIN_OUT_DIR/host/windows-x86/bin/*.{exe,dll} $TOOLS/
-# Remove some tools we don't want to take in the SDK
-rm $V -f $TOOLS/{fastboot.exe,rs-spec-gen.exe,tblgen.exe}
-mkdir -pv $LIB/x86
-cp $V ${TOPDIR}prebuilt/windows/swt/swt.jar $LIB/x86/
-mkdir -pv $LIB/x86_64
-cp $V ${TOPDIR}prebuilt/windows-x86_64/swt/swt.jar $LIB/x86_64/
-
-# Put the JetCreator tools, content and docs (not available in the linux SDK)
-JET=$TOOLS/Jet
-JETCREATOR=$JET/JetCreator
-JETDEMOCONTENT=$JET/demo_content
-JETLOGICTEMPLATES=$JET/logic_templates
-JETDOC=$TEMP_SDK_DIR/docs/JetCreator
-
-# need to rm these folders since a Mac SDK will have them and it might create a conflict
-rm -rf $V $JET
-rm -rf $V $JETDOC
-
-# now create fresh folders for JetCreator
-mkdir $V $JET
-mkdir $V $JETDOC
-
-cp -r $V ${TOPDIR}external/sonivox/jet_tools/JetCreator $JETCREATOR/
-cp -r $V ${TOPDIR}external/sonivox/jet_tools/JetCreator_content $JETDEMOCONTENT/
-cp -r $V ${TOPDIR}external/sonivox/jet_tools/logic_templates $JETLOGICTEMPLATES/
-chmod $V -R u+w $JETCREATOR # fixes an issue where Cygwin might copy the above as u+rx only
-cp $V ${TOPDIR}prebuilt/windows/jetcreator/EASDLL.dll $JETCREATOR/
-
-cp $V ${TOPDIR}external/sonivox/docs/JET_Authoring_Guidelines.html $JETDOC/
-cp -r $V ${TOPDIR}external/sonivox/docs/JET_Authoring_Guidelines_files $JETDOC/
-cp $V ${TOPDIR}external/sonivox/docs/JET_Creator_User_Manual.html $JETDOC/
-cp -r $V ${TOPDIR}external/sonivox/docs/JET_Creator_User_Manual_files $JETDOC/
-
-# Copy or move platform specific tools to the default platform.
-cp $V ${TOPDIR}dalvik/dx/etc/dx.bat $PLATFORM_TOOLS/
-mv $V $TOOLS/{adb.exe,aapt.exe,aidl.exe,dexdump.exe} $PLATFORM_TOOLS/
-mv $V $TOOLS/Adb*.dll $PLATFORM_TOOLS/
-# The platform actually produces llvm-rs-cc-2 (via sdk.atree), whereas
-# the original version 1 (just named llvm-rs-cc) is stored in the prebuilts.
-cp $V ${TOPDIR}prebuilt/windows/llvm-rs-cc/llvm-rs-cc.exe $PLATFORM_TOOLS/llvm-rs-cc.exe
-mv $V $TOOLS/llvm-rs-cc.exe $PLATFORM_TOOLS/llvm-rs-cc-2.exe
+# Invoke atree to copy the files
+# TODO: pass down OUT_HOST_EXECUTABLE to get the right bin/atree directory
+${TOPDIR}out/host/linux-x86/bin/atree -f ${TOPDIR}development/build/sdk-windows-x86.atree \
+ -I $WIN_OUT_DIR/host/windows-x86 \
+ -I ${TOPDIR:-.} \
+ -o $TEMP_SDK_DIR
# Fix EOL chars to make window users happy - fix all files at the top level
# as well as all batch files including those in platform-tools/
diff --git a/build/tools/sdk_repo.mk b/build/tools/sdk_repo.mk
index c709502..de48e57 100644
--- a/build/tools/sdk_repo.mk
+++ b/build/tools/sdk_repo.mk
@@ -56,6 +56,48 @@
$(call sdk-repo-pkg-zip,$(1),$(2),$(3)):$(notdir $(call sdk-repo-pkg-zip,$(1),$(2),$(3)))
endef
+# Defines the rule to build an SDK repository package when the
+# package directory contains 3 levels from the sdk dir, for example
+# to package SDK/extra/android/support or SDK/system-images/android-N/armeabi.
+# Because we do not know the intermediary directory name, this only works
+# if each directory contains a single sub-directory (e.g. sdk/$4/*/* must be
+# unique.)
+#
+# $1=OS (e.g. linux-x86, windows, etc)
+# $2=sdk zip (e.g. out/host/linux.../android-eng-sdk.zip)
+# $3=package to create (e.g. system-images, support, etc.)
+# $4=the root of directory to package in the sdk (e.g. extra/android).
+# this must be a 2-segment path, the last one can be *.
+#
+# The rule depends on the SDK zip file, which is defined by $2.
+#
+define mk-sdk-repo-pkg-3
+$(call sdk-repo-pkg-zip,$(1),$(2),$(3)): $(2)
+ @echo "Building SDK repository package $(3) from $(notdir $(2))"
+ $(hide) cd $(basename $(2))/$(4) && \
+ zip -9rq ../../../$(notdir $(call sdk-repo-pkg-zip,$(1),$(2),$(3))) *
+$(call dist-for-goals, sdk_repo, $(call sdk-repo-pkg-zip,$(1),$(2),$(3)))
+SDK_REPO_XML_ARGS += $(3) $(1) \
+ $(call sdk-repo-pkg-zip,$(1),$(2),$(3)):$(notdir $(call sdk-repo-pkg-zip,$(1),$(2),$(3)))
+endef
+
+# Defines the rule to build an SDK sources package.
+#
+# $1=OS (e.g. linux-x86, windows, etc)
+# $2=sdk zip (e.g. out/host/linux.../android-eng-sdk.zip)
+# $3=package to create, must be "sources"
+#
+define mk-sdk-repo-sources
+$(call sdk-repo-pkg-zip,$(1),$(2),$(3)): $(2) $(TOPDIR)development/sdk/source_source.properties
+ @echo "Building SDK sources package"
+ $(hide) $(TOPDIR)development/build/tools/mk_sources_zip.py \
+ $(TOPDIR)development/sdk/source_source.properties \
+ $(call sdk-repo-pkg-zip,$(1),$(2),$(3)) \
+ $(TOPDIR).
+$(call dist-for-goals, sdk_repo, $(call sdk-repo-pkg-zip,$(1),$(2),$(3)))
+SDK_REPO_XML_ARGS += $(3) $(1) \
+ $(call sdk-repo-pkg-zip,$(1),$(2),$(3)):$(notdir $(call sdk-repo-pkg-zip,$(1),$(2),$(3)))
+endef
# -----------------------------------------------------------------
# Rules for win_sdk
@@ -67,7 +109,7 @@
$(eval $(call mk-sdk-repo-pkg-1,windows,$(WIN_SDK_ZIP),platform-tools))
SDK_REPO_DEPS += \
- $(call sdk-repo-pkg-zip,windows,$(WIN_SDK_ZIP),tools) \
+ $(call sdk-repo-pkg-zip,windows,$(WIN_SDK_ZIP),tools) \
$(call sdk-repo-pkg-zip,windows,$(WIN_SDK_ZIP),platform-tools)
endif
@@ -82,6 +124,9 @@
$(eval $(call mk-sdk-repo-pkg-1,$(HOST_OS),$(MAIN_SDK_ZIP),docs))
$(eval $(call mk-sdk-repo-pkg-2,$(HOST_OS),$(MAIN_SDK_ZIP),platforms))
$(eval $(call mk-sdk-repo-pkg-2,$(HOST_OS),$(MAIN_SDK_ZIP),samples))
+$(eval $(call mk-sdk-repo-pkg-3,$(HOST_OS),$(MAIN_SDK_ZIP),system-images,system-images/*))
+$(eval $(call mk-sdk-repo-pkg-3,$(HOST_OS),$(MAIN_SDK_ZIP),support,extras/android))
+$(eval $(call mk-sdk-repo-sources,$(HOST_OS),$(MAIN_SDK_ZIP),sources))
SDK_REPO_DEPS += \
$(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),tools) \
@@ -89,6 +134,9 @@
$(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),docs) \
$(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),platforms) \
$(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),samples) \
+ $(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),system-images) \
+ $(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),support) \
+ $(call sdk-repo-pkg-zip,$(HOST_OS),$(MAIN_SDK_ZIP),sources)
endif
diff --git a/build/tools/windows_sdk.mk b/build/tools/windows_sdk.mk
index e259d29..2a6fc74 100644
--- a/build/tools/windows_sdk.mk
+++ b/build/tools/windows_sdk.mk
@@ -22,10 +22,13 @@
$(error Need a unix2dos command. Please 'apt-get install tofrodos')
endif
+# Define WIN_SDK_TARGETS, the list of targets located in topdir/sdk
+# and are tools-dependent, not platform-dependent.
include $(TOPDIR)sdk/build/windows_sdk_tools.mk
-# This is the list of target that we want to generate as
-# Windows executables.
+# This is the list of targets that we want to generate as
+# Windows executables. All the targets specified here are located in
+# the topdir/development directory and are somehow platform-dependent.
WIN_TARGETS := \
aapt adb aidl \
etc1tool \
@@ -84,8 +87,6 @@
$(hide) USB_DRIVER_HOOK=$(USB_DRIVER_HOOK) \
$(TOPDIR)development/build/tools/patch_windows_sdk.sh $(subst @,-q,$(hide)) \
$(WIN_SDK_DIR)/$(WIN_SDK_NAME) $(OUT_DIR) $(TOPDIR)
- $(hide) strip --strip-all $(WIN_SDK_DIR)/$(WIN_SDK_NAME)/platform-tools/llvm-rs-cc.exe
- $(hide) strip --strip-all $(WIN_SDK_DIR)/$(WIN_SDK_NAME)/platform-tools/llvm-rs-cc-2.exe
$(hide) $(TOPDIR)sdk/build/patch_windows_sdk.sh $(subst @,-q,$(hide)) \
$(WIN_SDK_DIR)/$(WIN_SDK_NAME) $(OUT_DIR) $(TOPDIR)
$(hide) ( \
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyActivityEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyActivityEvent.java
index 4661d8c..75c3a58 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyActivityEvent.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyActivityEvent.java
@@ -67,7 +67,7 @@
try {
iam.startActivity(null, intent, null, null, 0, null, null, 0,
- false, false);
+ false, false, null, null, false);
} catch (RemoteException e) {
System.err.println("** Failed talking with activity manager!");
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java
index a32b8a5..b6b08a9 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkViews.java
@@ -28,6 +28,7 @@
import android.os.ServiceManager;
import android.os.RemoteException;
import android.util.Log;
+import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.AccessibilityEvent;
@@ -203,16 +204,18 @@
}
private static AccessibilityNodeInfo getNodeByAccessibilityIds(
- String windowString, String viewString) throws RemoteException {
+ String windowString, String viewString) {
int windowId = Integer.parseInt(windowString);
int viewId = Integer.parseInt(viewString);
- return sConnection.findAccessibilityNodeInfoByAccessibilityId(windowId, viewId);
+ return AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByAccessibilityId(sConnection, windowId, viewId);
}
private static AccessibilityNodeInfo getNodeByViewId(String viewId, AccessibilityEvent event)
- throws RemoteException, MonkeyViewException {
+ throws MonkeyViewException {
int id = getId(viewId, event);
- return sConnection.findAccessibilityNodeInfoByViewIdInActiveWindow(id);
+ return AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnection, id);
}
/**
@@ -281,8 +284,6 @@
node = getNodeByViewId(command.get(2), lastEvent);
viewQuery = command.get(3);
args = command.subList(4, command.size());
- } catch (RemoteException e) {
- return new MonkeyCommandReturn(false, REMOTE_ERROR);
} catch (MonkeyViewException e) {
return new MonkeyCommandReturn(false, e.getMessage());
}
@@ -291,8 +292,6 @@
node = getNodeByAccessibilityIds(command.get(2), command.get(3));
viewQuery = command.get(4);
args = command.subList(5, command.size());
- } catch (RemoteException e) {
- return new MonkeyCommandReturn(false, REMOTE_ERROR);
} catch (NumberFormatException e) {
return EARG;
}
@@ -344,17 +343,9 @@
return new MonkeyCommandReturn(false, NO_CONNECTION);
}
if (command.size() == 2) {
- StringBuilder logs = new StringBuilder();
String text = command.get(1);
- List<AccessibilityNodeInfo> nodes;
- try {
- nodes = sConnection.findAccessibilityNodeInfosByViewTextInActiveWindow(text);
- } catch (RemoteException e) {
- return new MonkeyCommandReturn(false, REMOTE_ERROR);
- }
- if (nodes == null) {
- return new MonkeyCommandReturn(true, "");
- }
+ List<AccessibilityNodeInfo> nodes = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfosByViewTextInActiveWindow(sConnection, text);
ViewIntrospectionCommand idGetter = new GetAccessibilityIds();
List<String> emptyArgs = new ArrayList<String>();
StringBuilder ids = new StringBuilder();
diff --git a/data/etc/apns-conf.xml b/data/etc/apns-conf.xml
index 156fec2..2fe90d9 100644
--- a/data/etc/apns-conf.xml
+++ b/data/etc/apns-conf.xml
@@ -19,7 +19,7 @@
<!-- use empty string to specify no proxy or port -->
<!-- This version must agree with that in apps/common/res/apns.xml -->
-<apns version="6">
+<apns version="7">
<apn carrier="T-Mobile US"
mcc="310"
mnc="260"
diff --git a/data/etc/apns-conf_sdk.xml b/data/etc/apns-conf_sdk.xml
index 5129dc7..0e9cf7d 100644
--- a/data/etc/apns-conf_sdk.xml
+++ b/data/etc/apns-conf_sdk.xml
@@ -23,7 +23,7 @@
<!-- use empty string to specify no proxy or port -->
<!-- This version must agree with that in apps/common/res/apns.xml -->
-<apns version="6">
+<apns version="7">
<apn carrier="Android"
mcc="310"
mnc="995"
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index 23023e8..f6bcc4c 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -11,8 +11,9 @@
<classpathentry kind="src" path="packages/apps/Email/src"/>
<classpathentry kind="src" path="packages/apps/Email/emailcommon/src"/>
<classpathentry kind="src" path="packages/apps/Exchange/src"/>
- <classpathentry kind="src" path="packages/apps/Gallery3D/new3d/src"/>
- <classpathentry kind="src" path="packages/apps/Gallery3D/PicasaProvider/src"/>
+ <classpathentry kind="src" path="packages/apps/Gallery2/src"/>
+ <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/Launcher2/src"/>
<classpathentry kind="src" path="packages/apps/Mms/src"/>
diff --git a/ndk/platforms/android-3/include/jni.h b/ndk/platforms/android-3/include/jni.h
index ad954c8..495902c 100644
--- a/ndk/platforms/android-3/include/jni.h
+++ b/ndk/platforms/android-3/include/jni.h
@@ -1,13 +1,28 @@
/*
- * Copyright 2006 The Android Open Source Project
+ * Copyright (C) 2006 The Android Open Source Project
*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
* JNI specification, as defined by Sun:
* http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
*
* Everything here is expected to be VM-neutral.
*/
-#ifndef _JNI_H
-#define _JNI_H
+
+#ifndef JNI_H_
+#define JNI_H_
#include <stdarg.h>
@@ -124,10 +139,10 @@
JNIWeakGlobalRefType = 3
} jobjectRefType;
-typedef struct {
- const char* name;
- const char* signature;
- void* fnPtr;
+typedef struct {
+ const char* name;
+ const char* signature;
+ void* fnPtr;
} JNINativeMethod;
struct _JNIEnv;
@@ -1037,7 +1052,7 @@
void* reserved0;
void* reserved1;
void* reserved2;
-
+
jint (*DestroyJavaVM)(JavaVM*);
jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
jint (*DetachCurrentThread)(JavaVM*);
@@ -1097,16 +1112,22 @@
*
* Note these are the only symbols exported for JNI by the VM.
*/
+#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
+#endif
+
+#define JNIIMPORT
+#define JNIEXPORT __attribute__ ((visibility ("default")))
+#define JNICALL
/*
* Prototypes for functions exported by loadable shared libs. These are
* called by JNI, not provided by JNI.
*/
-jint JNI_OnLoad(JavaVM* vm, void* reserved);
-void JNI_OnUnload(JavaVM* vm, void* reserved);
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
#ifdef __cplusplus
}
@@ -1132,9 +1153,4 @@
#define JNI_COMMIT 1 /* copy content, do not free buffer */
#define JNI_ABORT 2 /* free buffer w/o copying back */
-/* need these for Windows-aware headers */
-#define JNIIMPORT
-#define JNIEXPORT
-#define JNICALL
-
-#endif /*_JNI_H*/
+#endif /* JNI_H_ */
diff --git a/samples/ActionBarCompat/Android.mk b/samples/ActionBarCompat/Android.mk
new file mode 100644
index 0000000..d9da54a
--- /dev/null
+++ b/samples/ActionBarCompat/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ActionBarCompat
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/ActionBarCompat/AndroidManifest.xml b/samples/ActionBarCompat/AndroidManifest.xml
new file mode 100644
index 0000000..1a48274
--- /dev/null
+++ b/samples/ActionBarCompat/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.actionbarcompat"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="14" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity" android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/ActionBarCompat/_index.html b/samples/ActionBarCompat/_index.html
new file mode 100644
index 0000000..8808de1
--- /dev/null
+++ b/samples/ActionBarCompat/_index.html
@@ -0,0 +1,25 @@
+<!--
+ Copyright 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.
+ -->
+
+<p>This sample shows how to use the action bar design pattern on pre-API 11 devices and the built-in
+<a href="../../../reference/android/app/ActionBar.html">ActionBar</a> on devices supporting API
+11 or greater. The example 'compatible' action bar, instantiated on pre-Android 3.0 devices,
+uses the same <a href="../../../guide/topics/resources/menu-resource.html">menu resource</a>-based
+action item definition mechanism as the new framework API, even supporting the
+<code>android:showAsAction</code> attribute to a limited extent.</p>
+
+<img alt="" src="../images/ActionBarCompat1.png" height="320" />
+<img alt="" src="../images/ActionBarCompat2.png" height="320" />
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/actionbar_shadow.9.png b/samples/ActionBarCompat/res/drawable-hdpi/actionbar_shadow.9.png
new file mode 100644
index 0000000..3c80a3f
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/actionbar_shadow.9.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_action_refresh.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_refresh.png
new file mode 100644
index 0000000..efe99e0
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_action_search.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_search.png
new file mode 100644
index 0000000..f6719d2
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_search.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_action_share.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_share.png
new file mode 100644
index 0000000..7d0b872
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_action_share.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_home.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_home.png
new file mode 100644
index 0000000..7e52ff5
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_home.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_launcher.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..e4b0492
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-hdpi/ic_menu_share.png b/samples/ActionBarCompat/res/drawable-hdpi/ic_menu_share.png
new file mode 100755
index 0000000..a78bf7a
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-hdpi/ic_menu_share.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/actionbar_shadow.9.png b/samples/ActionBarCompat/res/drawable-mdpi/actionbar_shadow.9.png
new file mode 100644
index 0000000..cae1778
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/actionbar_shadow.9.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_action_refresh.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_refresh.png
new file mode 100644
index 0000000..a85eee3
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_action_search.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_search.png
new file mode 100755
index 0000000..45d2398
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_search.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_action_share.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_share.png
new file mode 100644
index 0000000..26a39bd
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_action_share.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_home.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_home.png
new file mode 100644
index 0000000..949b4aa
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_home.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_launcher.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..78d0e29
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-mdpi/ic_menu_share.png b/samples/ActionBarCompat/res/drawable-mdpi/ic_menu_share.png
new file mode 100755
index 0000000..3329b6b
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-mdpi/ic_menu_share.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/actionbar_shadow.9.png b/samples/ActionBarCompat/res/drawable-xhdpi/actionbar_shadow.9.png
new file mode 100644
index 0000000..30778e3
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/actionbar_shadow.9.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_refresh.png b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_refresh.png
new file mode 100644
index 0000000..6ce9376
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_search.png b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_search.png
new file mode 100644
index 0000000..f89c4e9
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_search.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_share.png b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_share.png
new file mode 100644
index 0000000..47b27d1
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/ic_action_share.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/ic_home.png b/samples/ActionBarCompat/res/drawable-xhdpi/ic_home.png
new file mode 100644
index 0000000..03eb53d
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/ic_home.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable-xhdpi/ic_launcher.png b/samples/ActionBarCompat/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..485f7be
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ActionBarCompat/res/drawable/actionbar_compat_item.xml b/samples/ActionBarCompat/res/drawable/actionbar_compat_item.xml
new file mode 100644
index 0000000..4b3960c
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable/actionbar_compat_item.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/actionbar_compat_item_pressed"
+ android:state_pressed="true" />
+ <item android:drawable="@drawable/actionbar_compat_item_focused"
+ android:state_focused="true" />
+ <item android:drawable="@android:color/transparent" />
+</selector>
diff --git a/samples/ActionBarCompat/res/drawable/actionbar_compat_item_focused.xml b/samples/ActionBarCompat/res/drawable/actionbar_compat_item_focused.xml
new file mode 100644
index 0000000..04811d3
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable/actionbar_compat_item_focused.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ef7000" />
+</shape>
diff --git a/samples/ActionBarCompat/res/drawable/actionbar_compat_item_pressed.xml b/samples/ActionBarCompat/res/drawable/actionbar_compat_item_pressed.xml
new file mode 100644
index 0000000..72ff4b4
--- /dev/null
+++ b/samples/ActionBarCompat/res/drawable/actionbar_compat_item_pressed.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#eda700" />
+</shape>
diff --git a/samples/ActionBarCompat/res/layout-v11/actionbar_indeterminate_progress.xml b/samples/ActionBarCompat/res/layout-v11/actionbar_indeterminate_progress.xml
new file mode 100644
index 0000000..c05750e
--- /dev/null
+++ b/samples/ActionBarCompat/res/layout-v11/actionbar_indeterminate_progress.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center">
+ <ProgressBar android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:layout_gravity="center"
+ style="?android:attr/indeterminateProgressStyle" />
+</FrameLayout>
diff --git a/samples/HoneycombGallery/res/layout/action_bar_custom.xml b/samples/ActionBarCompat/res/layout/actionbar_compat.xml
similarity index 60%
copy from samples/HoneycombGallery/res/layout/action_bar_custom.xml
copy to samples/ActionBarCompat/res/layout/actionbar_compat.xml
index 5f111d9..ae6c44b 100644
--- a/samples/HoneycombGallery/res/layout/action_bar_custom.xml
+++ b/samples/ActionBarCompat/res/layout/actionbar_compat.xml
@@ -1,24 +1,21 @@
<!--
- Copyright (C) 2011 The Android Open Source Project
+ Copyright 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
+ 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="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent">Hello Action Bar</TextView>
-</LinearLayout>
+ android:id="@id/actionbar_compat"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" />
diff --git a/samples/ActionBarCompat/res/layout/main.xml b/samples/ActionBarCompat/res/layout/main.xml
new file mode 100644
index 0000000..a58a346
--- /dev/null
+++ b/samples/ActionBarCompat/res/layout/main.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <Button android:id="@+id/toggle_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/toggle_title" />
+</FrameLayout>
diff --git a/samples/ActionBarCompat/res/menu/main.xml b/samples/ActionBarCompat/res/menu/main.xml
new file mode 100644
index 0000000..a306a3c
--- /dev/null
+++ b/samples/ActionBarCompat/res/menu/main.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright 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.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_refresh"
+ android:title="@string/menu_refresh"
+ android:icon="@drawable/ic_action_refresh"
+ android:orderInCategory="1"
+ android:showAsAction="always" />
+ <item android:id="@+id/menu_search"
+ android:title="@string/menu_search"
+ android:icon="@drawable/ic_action_search"
+ android:orderInCategory="0"
+ android:showAsAction="always" />
+
+ <item android:id="@+id/menu_share"
+ android:title="@string/menu_share"
+ android:icon="@drawable/ic_menu_share"
+ android:orderInCategory="1"
+ android:showAsAction="never" />
+</menu>
diff --git a/samples/ActionBarCompat/res/values-v11/styles.xml b/samples/ActionBarCompat/res/values-v11/styles.xml
new file mode 100644
index 0000000..dc1aa7c
--- /dev/null
+++ b/samples/ActionBarCompat/res/values-v11/styles.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+
+ <style name="AppTheme" parent="android:style/Theme.Holo.Light">
+ <item name="android:actionBarStyle">@style/ActionBar</item>
+ <item name="android:windowContentOverlay">@drawable/actionbar_shadow</item>
+ </style>
+
+ <style name="ActionBar" parent="android:style/Widget.Holo.Light.ActionBar">
+ <item name="android:background">#eee</item>
+ <item name="android:titleTextStyle">@style/ActionBarTitle</item>
+ <item name="android:icon">@drawable/ic_home</item>
+ </style>
+
+ <style name="ActionBarTitle">
+ <item name="android:textColor">@color/actionbar_title_color</item>
+ </style>
+
+</resources>
diff --git a/samples/ActionBarCompat/res/values-v13/styles.xml b/samples/ActionBarCompat/res/values-v13/styles.xml
new file mode 100644
index 0000000..8a042b4
--- /dev/null
+++ b/samples/ActionBarCompat/res/values-v13/styles.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+
+ <style name="ActionBarTitle" parent="android:style/TextAppearance.Holo.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/actionbar_title_color</item>
+ </style>
+
+</resources>
diff --git a/samples/ActionBarCompat/res/values/attrs.xml b/samples/ActionBarCompat/res/values/attrs.xml
new file mode 100644
index 0000000..c59822c
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/attrs.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+
+ <declare-styleable name="AppTheme">
+ <attr name="actionbarCompatTitleStyle" format="reference" />
+ <attr name="actionbarCompatItemStyle" format="reference" />
+ <attr name="actionbarCompatItemHomeStyle" format="reference" />
+ <attr name="actionbarCompatProgressIndicatorStyle" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="BezelImageView">
+ <attr name="maskDrawable" format="reference" />
+ <attr name="borderDrawable" format="reference" />
+ </declare-styleable>
+
+</resources>
diff --git a/samples/ActionBarCompat/res/values/colors.xml b/samples/ActionBarCompat/res/values/colors.xml
new file mode 100644
index 0000000..4edc6d6
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/colors.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+ <color name="actionbar_title_color">#224894</color>
+</resources>
diff --git a/samples/ActionBarCompat/res/values/dimens.xml b/samples/ActionBarCompat/res/values/dimens.xml
new file mode 100644
index 0000000..67c8436
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+ <dimen name="actionbar_compat_height">48dp</dimen>
+ <dimen name="actionbar_compat_button_width">48dp</dimen>
+ <dimen name="actionbar_compat_button_home_width">56dp</dimen>
+</resources>
diff --git a/samples/ActionBarCompat/res/values/ids.xml b/samples/ActionBarCompat/res/values/ids.xml
new file mode 100644
index 0000000..e0a4745
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/ids.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+ <item type="id" name="actionbar_compat" />
+ <item type="id" name="actionbar_compat_title" />
+ <item type="id" name="actionbar_compat_item_refresh_progress" />
+ <item type="id" name="actionbar_compat_item_refresh" />
+ <item type="id" name="menu_refresh" />
+</resources>
diff --git a/samples/ActionBarCompat/res/values/strings.xml b/samples/ActionBarCompat/res/values/strings.xml
new file mode 100644
index 0000000..234aa78
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/strings.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+ <string name="app_name">Action Bar Demo</string>
+ <string name="alternate_title">An alternate title that is long</string>
+ <string name="toggle_title">Toggle title</string>
+
+ <string name="menu_refresh">Refresh</string>
+ <string name="menu_search">Search</string>
+ <string name="menu_share">Share</string>
+</resources>
diff --git a/samples/ActionBarCompat/res/values/styles.xml b/samples/ActionBarCompat/res/values/styles.xml
new file mode 100644
index 0000000..5d0c029
--- /dev/null
+++ b/samples/ActionBarCompat/res/values/styles.xml
@@ -0,0 +1,66 @@
+<!--
+ Copyright 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.
+ -->
+
+<resources>
+
+ <style name="AppTheme" parent="android:style/Theme.Light">
+ <item name="android:windowTitleSize">@dimen/actionbar_compat_height</item>
+ <item name="android:windowTitleBackgroundStyle">@style/ActionBarCompat</item>
+ <item name="android:windowContentOverlay">@drawable/actionbar_shadow</item>
+
+ <!-- for programmatic instantiation -->
+ <item name="actionbarCompatTitleStyle">@style/ActionBarCompatTitle</item>
+ <item name="actionbarCompatItemStyle">@style/ActionBarCompatItem</item>
+ <item name="actionbarCompatItemHomeStyle">@style/ActionBarCompatHomeItem</item>
+ <item name="actionbarCompatProgressIndicatorStyle">@style/ActionBarCompatProgressIndicator</item>
+ </style>
+
+ <style name="ActionBarCompat">
+ <item name="android:background">#eee</item>
+ </style>
+
+ <style name="ActionBarCompatItemBase">
+ <!-- layout_width/height must be set in code -->
+ <item name="android:scaleType">center</item>
+ <item name="android:background">@drawable/actionbar_compat_item</item>
+ </style>
+
+ <style name="ActionBarCompatProgressIndicator" parent="android:style/Widget.ProgressBar.Large">
+ <item name="android:indeterminate">true</item>
+ </style>
+
+ <style name="ActionBarCompatTitleBase">
+ <item name="android:id">@id/actionbar_compat_title</item>
+ <!-- layout_width/height/weight must be set in code -->
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:paddingLeft">6dp</item>
+ <item name="android:paddingRight">6dp</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ </style>
+
+ <style name="ActionBarCompatTitle" parent="style/ActionBarCompatTitleBase">
+ <item name="android:textColor">@color/actionbar_title_color</item>
+ </style>
+
+ <style name="ActionBarCompatItem" parent="style/ActionBarCompatItemBase">
+ </style>
+
+ <style name="ActionBarCompatHomeItem" parent="style/ActionBarCompatItemBase">
+ </style>
+
+</resources>
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarActivity.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarActivity.java
new file mode 100644
index 0000000..251e411
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+/**
+ * A base activity that defers common functionality across app activities to an {@link
+ * ActionBarHelper}.
+ *
+ * NOTE: dynamically marking menu items as invisible/visible is not currently supported.
+ *
+ * NOTE: this may used with the Android Compatibility Package by extending
+ * android.support.v4.app.FragmentActivity instead of {@link Activity}.
+ */
+public abstract class ActionBarActivity extends Activity {
+ final ActionBarHelper mActionBarHelper = ActionBarHelper.createInstance(this);
+
+ /**
+ * Returns the {@link ActionBarHelper} for this activity.
+ */
+ protected ActionBarHelper getActionBarHelper() {
+ return mActionBarHelper;
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public MenuInflater getMenuInflater() {
+ return mActionBarHelper.getMenuInflater(super.getMenuInflater());
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActionBarHelper.onCreate(savedInstanceState);
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ mActionBarHelper.onPostCreate(savedInstanceState);
+ }
+
+ /**
+ * Base action bar-aware implementation for
+ * {@link Activity#onCreateOptionsMenu(android.view.Menu)}.
+ *
+ * Note: marking menu items as invisible/visible is not currently supported.
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ boolean retValue = false;
+ retValue |= mActionBarHelper.onCreateOptionsMenu(menu);
+ retValue |= super.onCreateOptionsMenu(menu);
+ return retValue;
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ mActionBarHelper.onTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelper.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelper.java
new file mode 100644
index 0000000..075f993
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+/**
+ * An abstract class that handles some common action bar-related functionality in the app. This
+ * class provides functionality useful for both phones and tablets, and does not require any Android
+ * 3.0-specific features, although it uses them if available.
+ *
+ * Two implementations of this class are {@link ActionBarHelperBase} for a pre-Honeycomb version of
+ * the action bar, and {@link ActionBarHelperHoneycomb}, which uses the built-in ActionBar features
+ * in Android 3.0 and later.
+ */
+public abstract class ActionBarHelper {
+ protected Activity mActivity;
+
+ /**
+ * Factory method for creating {@link ActionBarHelper} objects for a
+ * given activity. Depending on which device the app is running, either a basic helper or
+ * Honeycomb-specific helper will be returned.
+ */
+ public static ActionBarHelper createInstance(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return new ActionBarHelperICS(activity);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return new ActionBarHelperHoneycomb(activity);
+ } else {
+ return new ActionBarHelperBase(activity);
+ }
+ }
+
+ protected ActionBarHelper(Activity activity) {
+ mActivity = activity;
+ }
+
+ /**
+ * Action bar helper code to be run in {@link Activity#onCreate(android.os.Bundle)}.
+ */
+ public void onCreate(Bundle savedInstanceState) {
+ }
+
+ /**
+ * Action bar helper code to be run in {@link Activity#onPostCreate(android.os.Bundle)}.
+ */
+ public void onPostCreate(Bundle savedInstanceState) {
+ }
+
+ /**
+ * Action bar helper code to be run in {@link Activity#onCreateOptionsMenu(android.view.Menu)}.
+ *
+ * NOTE: Setting the visibility of menu items in <em>menu</em> is not currently supported.
+ */
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ /**
+ * Action bar helper code to be run in {@link Activity#onTitleChanged(CharSequence, int)}.
+ */
+ protected void onTitleChanged(CharSequence title, int color) {
+ }
+
+ /**
+ * Sets the indeterminate loading state of the item with ID {@link R.id.menu_refresh}.
+ * (where the item ID was menu_refresh).
+ */
+ public abstract void setRefreshActionItemState(boolean refreshing);
+
+ /**
+ * Returns a {@link MenuInflater} for use when inflating menus. The implementation of this
+ * method in {@link ActionBarHelperBase} returns a wrapped menu inflater that can read
+ * action bar metadata from a menu resource pre-Honeycomb.
+ */
+ public MenuInflater getMenuInflater(MenuInflater superMenuInflater) {
+ return superMenuInflater;
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperBase.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperBase.java
new file mode 100644
index 0000000..f70b54a
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperBase.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.view.InflateException;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class that implements the action bar pattern for pre-Honeycomb devices.
+ */
+public class ActionBarHelperBase extends ActionBarHelper {
+ private static final String MENU_RES_NAMESPACE = "http://schemas.android.com/apk/res/android";
+ private static final String MENU_ATTR_ID = "id";
+ private static final String MENU_ATTR_SHOW_AS_ACTION = "showAsAction";
+
+ protected Set<Integer> mActionItemIds = new HashSet<Integer>();
+
+ protected ActionBarHelperBase(Activity activity) {
+ super(activity);
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mActivity.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onPostCreate(Bundle savedInstanceState) {
+ mActivity.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
+ R.layout.actionbar_compat);
+ setupActionBar();
+
+ SimpleMenu menu = new SimpleMenu(mActivity);
+ mActivity.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
+ mActivity.onPrepareOptionsMenu(menu);
+ for (int i = 0; i < menu.size(); i++) {
+ MenuItem item = menu.getItem(i);
+ if (mActionItemIds.contains(item.getItemId())) {
+ addActionItemCompatFromMenuItem(item);
+ }
+ }
+ }
+
+ /**
+ * Sets up the compatibility action bar with the given title.
+ */
+ private void setupActionBar() {
+ final ViewGroup actionBarCompat = getActionBarCompat();
+ if (actionBarCompat == null) {
+ return;
+ }
+
+ LinearLayout.LayoutParams springLayoutParams = new LinearLayout.LayoutParams(
+ 0, ViewGroup.LayoutParams.FILL_PARENT);
+ springLayoutParams.weight = 1;
+
+ // Add Home button
+ SimpleMenu tempMenu = new SimpleMenu(mActivity);
+ SimpleMenuItem homeItem = new SimpleMenuItem(
+ tempMenu, android.R.id.home, 0, mActivity.getString(R.string.app_name));
+ homeItem.setIcon(R.drawable.ic_home);
+ addActionItemCompatFromMenuItem(homeItem);
+
+ // Add title text
+ TextView titleText = new TextView(mActivity, null, R.attr.actionbarCompatTitleStyle);
+ titleText.setLayoutParams(springLayoutParams);
+ titleText.setText(mActivity.getTitle());
+ actionBarCompat.addView(titleText);
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void setRefreshActionItemState(boolean refreshing) {
+ View refreshButton = mActivity.findViewById(R.id.actionbar_compat_item_refresh);
+ View refreshIndicator = mActivity.findViewById(
+ R.id.actionbar_compat_item_refresh_progress);
+
+ if (refreshButton != null) {
+ refreshButton.setVisibility(refreshing ? View.GONE : View.VISIBLE);
+ }
+ if (refreshIndicator != null) {
+ refreshIndicator.setVisibility(refreshing ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /**
+ * Action bar helper code to be run in {@link Activity#onCreateOptionsMenu(android.view.Menu)}.
+ *
+ * NOTE: This code will mark on-screen menu items as invisible.
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Hides on-screen action items from the options menu.
+ for (Integer id : mActionItemIds) {
+ menu.findItem(id).setVisible(false);
+ }
+ return true;
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ TextView titleView = (TextView) mActivity.findViewById(R.id.actionbar_compat_title);
+ if (titleView != null) {
+ titleView.setText(title);
+ }
+ }
+
+ /**
+ * Returns a {@link android.view.MenuInflater} that can read action bar metadata on
+ * pre-Honeycomb devices.
+ */
+ public MenuInflater getMenuInflater(MenuInflater superMenuInflater) {
+ return new WrappedMenuInflater(mActivity, superMenuInflater);
+ }
+
+ /**
+ * Returns the {@link android.view.ViewGroup} for the action bar on phones (compatibility action
+ * bar). Can return null, and will return null on Honeycomb.
+ */
+ private ViewGroup getActionBarCompat() {
+ return (ViewGroup) mActivity.findViewById(R.id.actionbar_compat);
+ }
+
+ /**
+ * Adds an action button to the compatibility action bar, using menu information from a {@link
+ * android.view.MenuItem}. If the menu item ID is <code>menu_refresh</code>, the menu item's
+ * state can be changed to show a loading spinner using
+ * {@link com.example.android.actionbarcompat.ActionBarHelperBase#setRefreshActionItemState(boolean)}.
+ */
+ private View addActionItemCompatFromMenuItem(final MenuItem item) {
+ final int itemId = item.getItemId();
+
+ final ViewGroup actionBar = getActionBarCompat();
+ if (actionBar == null) {
+ return null;
+ }
+
+ // Create the button
+ ImageButton actionButton = new ImageButton(mActivity, null,
+ itemId == android.R.id.home
+ ? R.attr.actionbarCompatItemHomeStyle
+ : R.attr.actionbarCompatItemStyle);
+ actionButton.setLayoutParams(new ViewGroup.LayoutParams(
+ (int) mActivity.getResources().getDimension(
+ itemId == android.R.id.home
+ ? R.dimen.actionbar_compat_button_home_width
+ : R.dimen.actionbar_compat_button_width),
+ ViewGroup.LayoutParams.FILL_PARENT));
+ if (itemId == R.id.menu_refresh) {
+ actionButton.setId(R.id.actionbar_compat_item_refresh);
+ }
+ actionButton.setImageDrawable(item.getIcon());
+ actionButton.setScaleType(ImageView.ScaleType.CENTER);
+ actionButton.setContentDescription(item.getTitle());
+ actionButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
+ }
+ });
+
+ actionBar.addView(actionButton);
+
+ if (item.getItemId() == R.id.menu_refresh) {
+ // Refresh buttons should be stateful, and allow for indeterminate progress indicators,
+ // so add those.
+ ProgressBar indicator = new ProgressBar(mActivity, null,
+ R.attr.actionbarCompatProgressIndicatorStyle);
+
+ final int buttonWidth = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.actionbar_compat_button_width);
+ final int buttonHeight = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.actionbar_compat_height);
+ final int progressIndicatorWidth = buttonWidth / 2;
+
+ LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(
+ progressIndicatorWidth, progressIndicatorWidth);
+ indicatorLayoutParams.setMargins(
+ (buttonWidth - progressIndicatorWidth) / 2,
+ (buttonHeight - progressIndicatorWidth) / 2,
+ (buttonWidth - progressIndicatorWidth) / 2,
+ 0);
+ indicator.setLayoutParams(indicatorLayoutParams);
+ indicator.setVisibility(View.GONE);
+ indicator.setId(R.id.actionbar_compat_item_refresh_progress);
+ actionBar.addView(indicator);
+ }
+
+ return actionButton;
+ }
+
+ /**
+ * A {@link android.view.MenuInflater} that reads action bar metadata.
+ */
+ private class WrappedMenuInflater extends MenuInflater {
+ MenuInflater mInflater;
+
+ public WrappedMenuInflater(Context context, MenuInflater inflater) {
+ super(context);
+ mInflater = inflater;
+ }
+
+ @Override
+ public void inflate(int menuRes, Menu menu) {
+ loadActionBarMetadata(menuRes);
+ mInflater.inflate(menuRes, menu);
+ }
+
+ /**
+ * Loads action bar metadata from a menu resource, storing a list of menu item IDs that
+ * should be shown on-screen (i.e. those with showAsAction set to always or ifRoom).
+ * @param menuResId
+ */
+ private void loadActionBarMetadata(int menuResId) {
+ XmlResourceParser parser = null;
+ try {
+ parser = mActivity.getResources().getXml(menuResId);
+
+ int eventType = parser.getEventType();
+ int itemId;
+ int showAsAction;
+
+ boolean eof = false;
+ while (!eof) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ if (!parser.getName().equals("item")) {
+ break;
+ }
+
+ itemId = parser.getAttributeResourceValue(MENU_RES_NAMESPACE,
+ MENU_ATTR_ID, 0);
+ if (itemId == 0) {
+ break;
+ }
+
+ showAsAction = parser.getAttributeIntValue(MENU_RES_NAMESPACE,
+ MENU_ATTR_SHOW_AS_ACTION, -1);
+ if (showAsAction == MenuItem.SHOW_AS_ACTION_ALWAYS ||
+ showAsAction == MenuItem.SHOW_AS_ACTION_IF_ROOM) {
+ mActionItemIds.add(itemId);
+ }
+ break;
+
+ case XmlPullParser.END_DOCUMENT:
+ eof = true;
+ break;
+ }
+
+ eventType = parser.next();
+ }
+ } catch (XmlPullParserException e) {
+ throw new InflateException("Error inflating menu XML", e);
+ } catch (IOException e) {
+ throw new InflateException("Error inflating menu XML", e);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperHoneycomb.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperHoneycomb.java
new file mode 100644
index 0000000..16fba69
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperHoneycomb.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+/**
+ * An extension of {@link ActionBarHelper} that provides Android 3.0-specific functionality for
+ * Honeycomb tablets. It thus requires API level 11.
+ */
+public class ActionBarHelperHoneycomb extends ActionBarHelper {
+ private Menu mOptionsMenu;
+ private View mRefreshIndeterminateProgressView = null;
+
+ protected ActionBarHelperHoneycomb(Activity activity) {
+ super(activity);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ mOptionsMenu = menu;
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public void setRefreshActionItemState(boolean refreshing) {
+ // On Honeycomb, we can set the state of the refresh button by giving it a custom
+ // action view.
+ if (mOptionsMenu == null) {
+ return;
+ }
+
+ final MenuItem refreshItem = mOptionsMenu.findItem(R.id.menu_refresh);
+ if (refreshItem != null) {
+ if (refreshing) {
+ if (mRefreshIndeterminateProgressView == null) {
+ LayoutInflater inflater = (LayoutInflater)
+ getActionBarThemedContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ mRefreshIndeterminateProgressView = inflater.inflate(
+ R.layout.actionbar_indeterminate_progress, null);
+ }
+
+ refreshItem.setActionView(mRefreshIndeterminateProgressView);
+ } else {
+ refreshItem.setActionView(null);
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link Context} suitable for inflating layouts for the action bar. The
+ * implementation for this method in {@link ActionBarHelperICS} asks the action bar for a
+ * themed context.
+ */
+ protected Context getActionBarThemedContext() {
+ return mActivity;
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperICS.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperICS.java
new file mode 100644
index 0000000..d2239db
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/ActionBarHelperICS.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.Menu;
+import android.view.MenuItem;
+
+/**
+ * An extension of {@link com.example.android.actionbarcompat.ActionBarHelper} that provides Android
+ * 4.0-specific functionality for IceCreamSandwich devices. It thus requires API level 14.
+ */
+public class ActionBarHelperICS extends ActionBarHelperHoneycomb {
+ protected ActionBarHelperICS(Activity activity) {
+ super(activity);
+ }
+
+ @Override
+ protected Context getActionBarThemedContext() {
+ return mActivity.getActionBar().getThemedContext();
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/MainActivity.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/MainActivity.java
new file mode 100644
index 0000000..facf1ae
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/MainActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+public class MainActivity extends ActionBarActivity {
+ private boolean mAlternateTitle = false;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ findViewById(R.id.toggle_title).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mAlternateTitle) {
+ setTitle(R.string.app_name);
+ } else {
+ setTitle(R.string.alternate_title);
+ }
+ mAlternateTitle = !mAlternateTitle;
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.main, menu);
+
+ // Calling super after populating the menu is necessary here to ensure that the
+ // action bar helpers have a chance to handle this event.
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ Toast.makeText(this, "Tapped home", Toast.LENGTH_SHORT).show();
+ break;
+
+ case R.id.menu_refresh:
+ Toast.makeText(this, "Fake refreshing...", Toast.LENGTH_SHORT).show();
+ getActionBarHelper().setRefreshActionItemState(true);
+ getWindow().getDecorView().postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ getActionBarHelper().setRefreshActionItemState(false);
+ }
+ }, 1000);
+ break;
+
+ case R.id.menu_search:
+ Toast.makeText(this, "Tapped search", Toast.LENGTH_SHORT).show();
+ break;
+
+ case R.id.menu_share:
+ Toast.makeText(this, "Tapped share", Toast.LENGTH_SHORT).show();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenu.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenu.java
new file mode 100644
index 0000000..8b694d0
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenu.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+import java.util.ArrayList;
+
+/**
+ * A <em>really</em> dumb implementation of the {@link android.view.Menu} interface, that's only
+ * useful for our actionbar-compat purposes. See
+ * <code>com.android.internal.view.menu.MenuBuilder</code> in AOSP for a more complete
+ * implementation.
+ */
+public class SimpleMenu implements Menu {
+
+ private Context mContext;
+ private Resources mResources;
+
+ private ArrayList<SimpleMenuItem> mItems;
+
+ public SimpleMenu(Context context) {
+ mContext = context;
+ mResources = context.getResources();
+ mItems = new ArrayList<SimpleMenuItem>();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Resources getResources() {
+ return mResources;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return addInternal(0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return addInternal(0, 0, mResources.getString(titleRes));
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ return addInternal(itemId, order, title);
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return addInternal(itemId, order, mResources.getString(titleRes));
+ }
+
+ /**
+ * Adds an item to the menu. The other add methods funnel to this.
+ */
+ private MenuItem addInternal(int itemId, int order, CharSequence title) {
+ final SimpleMenuItem item = new SimpleMenuItem(this, itemId, order, title);
+ mItems.add(findInsertIndex(mItems, order), item);
+ return item;
+ }
+
+ private static int findInsertIndex(ArrayList<? extends MenuItem> items, int order) {
+ for (int i = items.size() - 1; i >= 0; i--) {
+ MenuItem item = items.get(i);
+ if (item.getOrder() <= order) {
+ return i + 1;
+ }
+ }
+
+ return 0;
+ }
+
+ public int findItemIndex(int id) {
+ final int size = size();
+
+ for (int i = 0; i < size; i++) {
+ SimpleMenuItem item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public void removeItem(int itemId) {
+ removeItemAtInt(findItemIndex(itemId));
+ }
+
+ private void removeItemAtInt(int index) {
+ if ((index < 0) || (index >= mItems.size())) {
+ return;
+ }
+ mItems.remove(index);
+ }
+
+ public void clear() {
+ mItems.clear();
+ }
+
+ public MenuItem findItem(int id) {
+ final int size = size();
+ for (int i = 0; i < size; i++) {
+ SimpleMenuItem item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ // Unsupported operations.
+
+ public SubMenu addSubMenu(CharSequence charSequence) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public int addIntentOptions(int i, int i1, int i2, ComponentName componentName,
+ Intent[] intents, Intent intent, int i3, MenuItem[] menuItems) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void removeGroup(int i) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void setGroupCheckable(int i, boolean b, boolean b1) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void setGroupVisible(int i, boolean b) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void setGroupEnabled(int i, boolean b) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public boolean hasVisibleItems() {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void close() {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public boolean performShortcut(int i, KeyEvent keyEvent, int i1) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public boolean isShortcutKey(int i, KeyEvent keyEvent) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public boolean performIdentifierAction(int i, int i1) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+
+ public void setQwertyMode(boolean b) {
+ throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");
+ }
+}
diff --git a/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenuItem.java b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenuItem.java
new file mode 100644
index 0000000..425ba72
--- /dev/null
+++ b/samples/ActionBarCompat/src/com/example/android/actionbarcompat/SimpleMenuItem.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 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.actionbarcompat;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ActionProvider;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+
+/**
+ * A <em>really</em> dumb implementation of the {@link android.view.MenuItem} interface, that's only
+ * useful for our actionbar-compat purposes. See
+ * <code>com.android.internal.view.menu.MenuItemImpl</code> in AOSP for a more complete
+ * implementation.
+ */
+public class SimpleMenuItem implements MenuItem {
+
+ private SimpleMenu mMenu;
+
+ private final int mId;
+ private final int mOrder;
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Drawable mIconDrawable;
+ private int mIconResId = 0;
+ private boolean mEnabled = true;
+
+ public SimpleMenuItem(SimpleMenu menu, int id, int order, CharSequence title) {
+ mMenu = menu;
+ mId = id;
+ mOrder = order;
+ mTitle = title;
+ }
+
+ public int getItemId() {
+ return mId;
+ }
+
+ public int getOrder() {
+ return mOrder;
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ public MenuItem setTitle(int titleRes) {
+ return setTitle(mMenu.getContext().getString(titleRes));
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+ return this;
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed != null ? mTitleCondensed : mTitle;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconResId = 0;
+ mIconDrawable = icon;
+ return this;
+ }
+
+ public MenuItem setIcon(int iconResId) {
+ mIconDrawable = null;
+ mIconResId = iconResId;
+ return this;
+ }
+
+ public Drawable getIcon() {
+ if (mIconDrawable != null) {
+ return mIconDrawable;
+ }
+
+ if (mIconResId != 0) {
+ return mMenu.getResources().getDrawable(mIconResId);
+ }
+
+ return null;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ // No-op operations. We use no-ops to allow inflation from menu XML.
+
+ public int getGroupId() {
+ // Noop
+ return 0;
+ }
+
+ public View getActionView() {
+ // Noop
+ return null;
+ }
+
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ // Noop
+ return this;
+ }
+
+ public ActionProvider getActionProvider() {
+ // Noop
+ return null;
+ }
+
+ public boolean expandActionView() {
+ // Noop
+ return false;
+ }
+
+ public boolean collapseActionView() {
+ // Noop
+ return false;
+ }
+
+ public boolean isActionViewExpanded() {
+ // Noop
+ return false;
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener onActionExpandListener) {
+ // Noop
+ return this;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ // Noop
+ return this;
+ }
+
+ public Intent getIntent() {
+ // Noop
+ return null;
+ }
+
+ public MenuItem setShortcut(char c, char c1) {
+ // Noop
+ return this;
+ }
+
+ public MenuItem setNumericShortcut(char c) {
+ // Noop
+ return this;
+ }
+
+ public char getNumericShortcut() {
+ // Noop
+ return 0;
+ }
+
+ public MenuItem setAlphabeticShortcut(char c) {
+ // Noop
+ return this;
+ }
+
+ public char getAlphabeticShortcut() {
+ // Noop
+ return 0;
+ }
+
+ public MenuItem setCheckable(boolean b) {
+ // Noop
+ return this;
+ }
+
+ public boolean isCheckable() {
+ // Noop
+ return false;
+ }
+
+ public MenuItem setChecked(boolean b) {
+ // Noop
+ return this;
+ }
+
+ public boolean isChecked() {
+ // Noop
+ return false;
+ }
+
+ public MenuItem setVisible(boolean b) {
+ // Noop
+ return this;
+ }
+
+ public boolean isVisible() {
+ // Noop
+ return true;
+ }
+
+ public boolean hasSubMenu() {
+ // Noop
+ return false;
+ }
+
+ public SubMenu getSubMenu() {
+ // Noop
+ return null;
+ }
+
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
+ // Noop
+ return this;
+ }
+
+ public ContextMenu.ContextMenuInfo getMenuInfo() {
+ // Noop
+ return null;
+ }
+
+ public void setShowAsAction(int i) {
+ // Noop
+ }
+
+ public MenuItem setShowAsActionFlags(int i) {
+ // Noop
+ return null;
+ }
+
+ public MenuItem setActionView(View view) {
+ // Noop
+ return this;
+ }
+
+ public MenuItem setActionView(int i) {
+ // Noop
+ return this;
+ }
+}
diff --git a/samples/AndroidBeam/AndroidManifest.xml b/samples/AndroidBeam/AndroidManifest.xml
new file mode 100644
index 0000000..e01eb2d
--- /dev/null
+++ b/samples/AndroidBeam/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.beam"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-sdk android:minSdkVersion="14" />
+ <uses-feature android:name="android.hardware.nfc" />
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:name="com.example.android.beam.Beam"
+ android:label="@string/app_name"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="application/com.example.android.beam" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/AndroidBeam/_index.html b/samples/AndroidBeam/_index.html
new file mode 100755
index 0000000..ad4e71a
--- /dev/null
+++ b/samples/AndroidBeam/_index.html
@@ -0,0 +1,3 @@
+<p>The Beam application demonstrates how to use the Android Beam feature introduced in API level 14. This
+application beams a simple message from one device to another when they are in close enough proximity. This
+application must be installed on two devices that have NFC capabilities, running Android 4.0 or later.</p>
diff --git a/samples/AndroidBeam/res/drawable-hdpi/icon.png b/samples/AndroidBeam/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/samples/AndroidBeam/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/samples/AndroidBeam/res/drawable-ldpi/icon.png b/samples/AndroidBeam/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/samples/AndroidBeam/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/samples/AndroidBeam/res/drawable-mdpi/icon.png b/samples/AndroidBeam/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/samples/AndroidBeam/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/samples/AndroidBeam/res/layout/main.xml b/samples/AndroidBeam/res/layout/main.xml
new file mode 100644
index 0000000..dce6169
--- /dev/null
+++ b/samples/AndroidBeam/res/layout/main.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textView"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
diff --git a/samples/AndroidBeam/res/values/strings.xml b/samples/AndroidBeam/res/values/strings.xml
new file mode 100644
index 0000000..ff4492f
--- /dev/null
+++ b/samples/AndroidBeam/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Beam</string>
+</resources>
diff --git a/samples/AndroidBeam/src/com/example/android/beam/Beam.java b/samples/AndroidBeam/src/com/example/android/beam/Beam.java
new file mode 100644
index 0000000..17ec325
--- /dev/null
+++ b/samples/AndroidBeam/src/com/example/android/beam/Beam.java
@@ -0,0 +1,112 @@
+/*
+ * 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.beam;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.CreateNdefMessageCallback;
+import android.nfc.NfcEvent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.nio.charset.Charset;
+
+
+public class Beam extends Activity implements CreateNdefMessageCallback {
+ NfcAdapter mNfcAdapter;
+ TextView textView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ TextView textView = (TextView) findViewById(R.id.textView);
+ // Check for available NFC Adapter
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter == null) {
+ Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ // Register callback
+ mNfcAdapter.setNdefPushMessageCallback(this, this);
+ }
+
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ String text = ("Beam me up, Android!\n\n" +
+ "Beam Time: " + System.currentTimeMillis());
+ NdefMessage msg = new NdefMessage(
+ new NdefRecord[] { createMimeRecord(
+ "application/com.example.android.beam", text.getBytes())
+ /**
+ * The Android Application Record (AAR) is commented out. When a device
+ * receives a push with an AAR in it, the application specified in the AAR
+ * is guaranteed to run. The AAR overrides the tag dispatch system.
+ * You can add it back in to guarantee that this
+ * activity starts when receiving a beamed message. For now, this code
+ * uses the tag dispatch system.
+ */
+ //,NdefRecord.createApplicationRecord("com.example.android.beam")
+ });
+ return msg;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Check to see that the Activity started due to an Android Beam
+ if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
+ processIntent(getIntent());
+ }
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ // onResume gets called after this to handle the intent
+ setIntent(intent);
+ }
+
+ /**
+ * Parses the NDEF Message from the intent and prints to the TextView
+ */
+ void processIntent(Intent intent) {
+ textView = (TextView) findViewById(R.id.textView);
+ Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
+ NfcAdapter.EXTRA_NDEF_MESSAGES);
+ // only one message sent during the beam
+ NdefMessage msg = (NdefMessage) rawMsgs[0];
+ // record 0 contains the MIME type, record 1 is the AAR, if present
+ textView.setText(new String(msg.getRecords()[0].getPayload()));
+ }
+
+ /**
+ * Creates a custom MIME type encapsulated in an NDEF record
+ *
+ * @param mimeType
+ */
+ public NdefRecord createMimeRecord(String mimeType, byte[] payload) {
+ byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
+ NdefRecord mimeRecord = new NdefRecord(
+ NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);
+ return mimeRecord;
+ }
+}
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 7b3fabd..39293e1 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -984,6 +984,18 @@
</intent-filter>
</activity>
+ <activity android:name=".content.InstallApk" android:label="@string/activity_install_apk"
+ android:enabled="@bool/atLeastHoneycombMR2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
+ <provider android:name=".content.FileProvider"
+ android:authorities="com.example.android.apis.content.FileProvider"
+ android:enabled="@bool/atLeastHoneycombMR2" />
+
<!-- ************************************* -->
<!-- OS PACKAGE SAMPLES -->
<!-- ************************************* -->
@@ -2090,6 +2102,13 @@
</intent-filter>
</activity>
+ <activity android:name=".view.Switches" android:label="Views/Switches">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<!-- ************************************* -->
<!-- GRAPHICS SAMPLES -->
<!-- ************************************* -->
diff --git a/samples/ApiDemos/assets/HelloActivity.apk b/samples/ApiDemos/assets/HelloActivity.apk
new file mode 100644
index 0000000..4e4b487
--- /dev/null
+++ b/samples/ApiDemos/assets/HelloActivity.apk
Binary files differ
diff --git a/samples/ApiDemos/res/layout/install_apk.xml b/samples/ApiDemos/res/layout/install_apk.xml
new file mode 100644
index 0000000..1482d2c
--- /dev/null
+++ b/samples/ApiDemos/res/layout/install_apk.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Demonstrates starting and stopping a local service.
+ See corresponding Java code com.android.sdk.app.LocalSerice.java. -->
+
+<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">
+
+ <TextView
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:paddingBottom="4dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Demonstrates using the INSTALL_PACKAGE Intent to install an application."/>
+
+ <Button android:id="@+id/unknown_source"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="Unknown Source">
+ </Button>
+
+ <Button android:id="@+id/my_source"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="My Source">
+ </Button>
+
+ <Button android:id="@+id/replace"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="Replace">
+ </Button>
+
+ <Button android:id="@+id/uninstall"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="Uninstall">
+ </Button>
+
+ <Button android:id="@+id/uninstall_result"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="Uninstall w/Result">
+ </Button>
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/layout_animations_by_default.xml b/samples/ApiDemos/res/layout/layout_animations_by_default.xml
index a5062bb..6236088 100644
--- a/samples/ApiDemos/res/layout/layout_animations_by_default.xml
+++ b/samples/ApiDemos/res/layout/layout_animations_by_default.xml
@@ -24,18 +24,11 @@
android:text="Add Button"
android:id="@+id/addNewButton"
/>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/horizontalContainer"
- android:animateLayoutChanges="true"
- />
- <LinearLayout
- android:orientation="vertical"
+ <GridLayout
+ android:columnCount="4"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:id="@+id/verticalContainer"
+ android:layout_height="wrap_content"
+ android:id="@+id/gridContainer"
android:animateLayoutChanges="true"
/>
</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/searchview_actionbar.xml b/samples/ApiDemos/res/layout/searchview_actionbar.xml
index 37736cd..2ed62d4 100644
--- a/samples/ApiDemos/res/layout/searchview_actionbar.xml
+++ b/samples/ApiDemos/res/layout/searchview_actionbar.xml
@@ -19,22 +19,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <Button
- android:id="@+id/open_button"
- android:text="@string/open_search"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- />
-
- <Button
- android:id="@+id/close_button"
- android:text="@string/close_search"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- />
-
<TextView
android:id="@+id/status_text"
android:layout_width="match_parent"
diff --git a/samples/ApiDemos/res/layout/switches.xml b/samples/ApiDemos/res/layout/switches.xml
new file mode 100644
index 0000000..9add5b6
--- /dev/null
+++ b/samples/ApiDemos/res/layout/switches.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<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:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Switch android:text="Standard switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="Default is on"
+ android:checked="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:id="@+id/monitored_switch"
+ android:text="Monitored switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="Customized text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="YES"
+ android:textOff="NO"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="This is an example of a switch with a lot of text in it. It may end up wrapping to another line. The switch will be pinned at the top."
+ android:singleLine="false"
+ android:layout_width="300dip"
+ android:layout_height="wrap_content"
+ android:gravity="top|left"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="This is an example of a switch with a lot of text in it. It may end up wrapping to another line. The switch will be vertically centered."
+ android:singleLine="false"
+ android:layout_width="300dip"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|left"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="This is an example of a switch with a lot of text in it. It may end up wrapping to another line. The switch will be pinned at the bottom."
+ android:singleLine="false"
+ android:layout_width="300dip"
+ android:layout_height="wrap_content"
+ android:gravity="bottom|left"
+ android:layout_marginBottom="32dip" />
+
+ <Switch android:text="Switch with match_parent width"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dip" />
+
+ <TextView android:text="Standalone switch below:"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Switch android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/samples/SampleSyncAdapter/res/values/styles.xml b/samples/ApiDemos/res/values-v14/bools.xml
similarity index 64%
rename from samples/SampleSyncAdapter/res/values/styles.xml
rename to samples/ApiDemos/res/values-v14/bools.xml
index 074613e..92aa94a 100644
--- a/samples/SampleSyncAdapter/res/values/styles.xml
+++ b/samples/ApiDemos/res/values-v14/bools.xml
@@ -15,13 +15,6 @@
-->
<resources>
- <!--
- These styles will only be used in Honeycomb and later because
- Android doesn't support third-party contact editing in pre-
- Honeycomb versions.
- -->
- <color name="EditPanelBackgroundColor">#ffffff</color>
- <color name="EditPanelBorderColor">#cccccc</color>
- <style name="ContactEditTheme" parent="android:Theme.Holo.Light">
- </style>
+ <!-- True if running under IceCreamSandwich or later. -->
+ <bool name="atLeastIceCreamSandwich">true</bool>
</resources>
diff --git a/samples/ApiDemos/res/values/bools.xml b/samples/ApiDemos/res/values/bools.xml
index 5d2eaa0..bc48079 100644
--- a/samples/ApiDemos/res/values/bools.xml
+++ b/samples/ApiDemos/res/values/bools.xml
@@ -27,6 +27,6 @@
<!-- This resource is true if running under at least IceCreamSandwich
API level. The default value is false; an alternative value
for IceCreamSandwich is true. -->
- <bool name="atLeastIceCreamSandwich">true</bool>
+ <bool name="atLeastIceCreamSandwich">false</bool>
</resources>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index ddf7e39..45d4755 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -363,6 +363,8 @@
<string name="pick_phone">Pick a Phone</string>
<string name="pick_address">Pick an Address</string>
+ <string name="activity_install_apk">Content/Packages/Install Apk</string>
+
<!-- ============================== -->
<!-- app/intents examples strings -->
<!-- ============================== -->
diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
index 67b9b51..8898b55 100644
--- a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
+++ b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
@@ -21,11 +21,11 @@
import com.example.android.apis.R;
import android.view.View;
-import android.view.ViewGroup;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
+import android.widget.GridLayout;
/**
* This application demonstrates how to use the animateLayoutChanges tag in XML to automate
@@ -35,36 +35,25 @@
private int numButtons = 1;
- /** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_animations_by_default);
- final ViewGroup horizontalContainer = (ViewGroup) findViewById(R.id.horizontalContainer);
- final ViewGroup verticalContainer = (ViewGroup) findViewById(R.id.verticalContainer);
+ final GridLayout gridContainer = (GridLayout) findViewById(R.id.gridContainer);
Button addButton = (Button) findViewById(R.id.addNewButton);
addButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Button newButton = new Button(LayoutAnimationsByDefault.this);
- newButton.setText("Click To Remove " + (numButtons++));
+ newButton.setText(String.valueOf(numButtons++));
newButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- horizontalContainer.removeView(v);
+ gridContainer.removeView(v);
}
});
- horizontalContainer.addView(newButton, Math.min(1, horizontalContainer.getChildCount()));
-
- newButton = new Button(LayoutAnimationsByDefault.this);
- newButton.setText("Click To Remove " + (numButtons++));
- newButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- verticalContainer.removeView(v);
- }
- });
- verticalContainer.addView(newButton, Math.min(1, verticalContainer.getChildCount()));
+ gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount()));
}
});
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsHideShow.java b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsHideShow.java
index 3ee7b0c..1a5d9c0 100644
--- a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsHideShow.java
+++ b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsHideShow.java
@@ -44,6 +44,7 @@
private int numButtons = 1;
ViewGroup container = null;
+ private LayoutTransition mTransitioner;
/** Called when the activity is first created. */
@Override
@@ -69,8 +70,8 @@
}
});
}
- final LayoutTransition transitioner = new LayoutTransition();
- container.setLayoutTransition(transitioner);
+
+ resetTransition();
ViewGroup parent = (ViewGroup) findViewById(R.id.parent);
parent.addView(container);
@@ -90,25 +91,25 @@
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
long duration;
if (isChecked) {
- transitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
- transitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
- setupAnimations(transitioner);
+ mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
+ mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
+ setupCustomAnimations();
duration = 500;
} else {
- transitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 0);
- transitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 0);
- transitioner.setAnimator(LayoutTransition.APPEARING, null);
- transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
- transitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, null);
- transitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, null);
+ resetTransition();
duration = 300;
}
- transitioner.setDuration(duration);
+ mTransitioner.setDuration(duration);
}
});
}
- private void setupAnimations(LayoutTransition transition) {
+ private void resetTransition() {
+ mTransitioner = new LayoutTransition();
+ container.setLayoutTransition(mTransitioner);
+ }
+
+ private void setupCustomAnimations() {
// Changing while Adding
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
@@ -124,8 +125,8 @@
PropertyValuesHolder.ofFloat("scaleY", 1f, 0f, 1f);
final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX, pvhScaleY).
- setDuration(transition.getDuration(LayoutTransition.CHANGE_APPEARING));
- transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
+ setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
+ mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
changeIn.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
@@ -142,8 +143,8 @@
PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
- setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
- transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
+ setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
+ mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
changeOut.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
@@ -153,8 +154,8 @@
// Adding
ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f, 0f).
- setDuration(transition.getDuration(LayoutTransition.APPEARING));
- transition.setAnimator(LayoutTransition.APPEARING, animIn);
+ setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
+ mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);
animIn.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
@@ -164,9 +165,9 @@
// Removing
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotationX", 0f, 90f).
- setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
- transition.setAnimator(LayoutTransition.DISAPPEARING, animOut);
- animIn.addListener(new AnimatorListenerAdapter() {
+ setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
+ mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);
+ animOut.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotationX(0f);
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
new file mode 100644
index 0000000..156625a
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
@@ -0,0 +1,115 @@
+/*
+ * 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.content;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.ContentProvider.PipeDataWriter;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * A very simple content provider that can serve arbitrary asset files from
+ * our .apk.
+ */
+public class FileProvider extends ContentProvider
+ implements PipeDataWriter<InputStream> {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ // Don't support queries.
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ // Don't support inserts.
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // Don't support deletes.
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ // Don't support updates.
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ // For this sample, assume all files are .apks.
+ return "application/vnd.android.package-archive";
+ }
+
+ @Override
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+ // Try to open an asset with the given name.
+ try {
+ InputStream is = getContext().getAssets().open(uri.getPath());
+ // Start a new thread that pipes the stream data back to the caller.
+ return new AssetFileDescriptor(
+ openPipeHelper(uri, null, null, is, this), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ } catch (IOException e) {
+ FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
+ throw fnf;
+ }
+ }
+
+ @Override
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+ Bundle opts, InputStream args) {
+ // Transfer data from the asset to the pipe the client is reading.
+ byte[] buffer = new byte[8192];
+ int n;
+ FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
+ try {
+ while ((n=args.read(buffer)) >= 0) {
+ fout.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ Log.i("InstallApk", "Failed transferring", e);
+ } finally {
+ try {
+ args.close();
+ } catch (IOException e) {
+ }
+ try {
+ fout.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java
new file mode 100644
index 0000000..9036ee0
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java
@@ -0,0 +1,179 @@
+/*
+ * 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.content;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ContentProvider.PipeDataWriter;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Demonstration of styled text resources.
+ */
+public class InstallApk extends Activity {
+ static final int REQUEST_INSTALL = 1;
+ static final int REQUEST_UNINSTALL = 2;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.install_apk);
+
+ // Watch for button clicks.
+ Button button = (Button)findViewById(R.id.unknown_source);
+ button.setOnClickListener(mUnknownSourceListener);
+ button = (Button)findViewById(R.id.my_source);
+ button.setOnClickListener(mMySourceListener);
+ button = (Button)findViewById(R.id.replace);
+ button.setOnClickListener(mReplaceListener);
+ button = (Button)findViewById(R.id.uninstall);
+ button.setOnClickListener(mUninstallListener);
+ button = (Button)findViewById(R.id.uninstall_result);
+ button.setOnClickListener(mUninstallResultListener);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_INSTALL) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ Toast.makeText(this, "Install canceled!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Install Failed!", Toast.LENGTH_SHORT).show();
+ }
+ } else if (requestCode == REQUEST_UNINSTALL) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(this, "Uninstall succeeded!", Toast.LENGTH_SHORT).show();
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ Toast.makeText(this, "Uninstall canceled!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Uninstall Failed!", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private OnClickListener mUnknownSourceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ startActivity(intent);
+ }
+ };
+
+ private OnClickListener mMySourceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ getApplicationInfo().packageName);
+ startActivityForResult(intent, REQUEST_INSTALL);
+ }
+ };
+
+ private OnClickListener mReplaceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ intent.putExtra(Intent.EXTRA_ALLOW_REPLACE, true);
+ intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ getApplicationInfo().packageName);
+ startActivityForResult(intent, REQUEST_INSTALL);
+ }
+ };
+
+ private OnClickListener mUninstallListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.parse(
+ "package:com.example.android.helloactivity"));
+ startActivity(intent);
+ }
+ };
+
+ private OnClickListener mUninstallResultListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.parse(
+ "package:com.example.android.helloactivity"));
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ startActivityForResult(intent, REQUEST_UNINSTALL);
+ }
+ };
+
+ private File prepareApk(String assetName) {
+ // Copy the given asset out into a file so that it can be installed.
+ // Returns the path to the file.
+ byte[] buffer = new byte[8192];
+ InputStream is = null;
+ FileOutputStream fout = null;
+ try {
+ is = getAssets().open(assetName);
+ fout = openFileOutput("tmp.apk", Context.MODE_WORLD_READABLE);
+ int n;
+ while ((n=is.read(buffer)) >= 0) {
+ fout.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ Log.i("InstallApk", "Failed transferring", e);
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ }
+ try {
+ if (fout != null) {
+ fout.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ return getFileStreamPath("tmp.apk");
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
index 9a3eb65..c3c8a6c 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -29,57 +31,101 @@
import android.view.MotionEvent;
import android.view.View;
-//Need the following import to get access to the app resources, since this
-//class is in a sub-package.
-
+import java.util.Random;
/**
- * Demonstrates the handling of touch screen and trackball events to
+ * Demonstrates the handling of touch screen, stylus, mouse and trackball events to
* implement a simple painting app.
+ * <p>
+ * Drawing with a touch screen is accomplished by drawing a point at the
+ * location of the touch. When pressure information is available, it is used
+ * to change the intensity of the color. When size and orientation information
+ * is available, it is used to directly adjust the size and orientation of the
+ * brush.
+ * </p><p>
+ * Drawing with a stylus is similar to drawing with a touch screen, with a
+ * few added refinements. First, there may be multiple tools available including
+ * an eraser tool. Second, the tilt angle and orientation of the stylus can be
+ * used to control the direction of paint. Third, the stylus buttons can be used
+ * to perform various actions. Here we use one button to cycle colors and the
+ * other to airbrush from a distance.
+ * </p><p>
+ * Drawing with a mouse is similar to drawing with a touch screen, but as with
+ * a stylus we have extra buttons. Here we use the primary button to draw,
+ * the secondary button to cycle colors and the tertiary button to airbrush.
+ * </p><p>
+ * Drawing with a trackball is a simple matter of using the relative motions
+ * of the trackball to move the paint brush around. The trackball may also
+ * have a button, which we use to cycle through colors.
+ * </p>
*/
public class TouchPaint extends GraphicsActivity {
/** Used as a pulse to gradually fade the contents of the window. */
- private static final int FADE_MSG = 1;
+ private static final int MSG_FADE = 1;
/** Menu ID for the command to clear the window. */
private static final int CLEAR_ID = Menu.FIRST;
+
/** Menu ID for the command to toggle fading. */
private static final int FADE_ID = Menu.FIRST+1;
/** How often to fade the contents of the window (in ms). */
private static final int FADE_DELAY = 100;
+ /** Colors to cycle through. */
+ static final int[] COLORS = new int[] {
+ Color.WHITE, Color.RED, Color.YELLOW, Color.GREEN,
+ Color.CYAN, Color.BLUE, Color.MAGENTA,
+ };
+
+ /** Background color. */
+ static final int BACKGROUND_COLOR = Color.BLACK;
+
/** The view responsible for drawing the window. */
- MyView mView;
+ PaintView mView;
+
/** Is fading mode enabled? */
boolean mFading;
- @Override protected void onCreate(Bundle savedInstanceState) {
+ /** The index of the current color to use. */
+ int mColorIndex;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create and attach the view that is responsible for painting.
- mView = new MyView(this);
+ mView = new PaintView(this);
setContentView(mView);
mView.requestFocus();
// Restore the fading option if we are being thawed from a
// previously saved state. Note that we are not currently remembering
// the contents of the bitmap.
- mFading = savedInstanceState != null ? savedInstanceState.getBoolean("fading", true) : true;
+ if (savedInstanceState != null) {
+ mFading = savedInstanceState.getBoolean("fading", true);
+ mColorIndex = savedInstanceState.getInt("color", 0);
+ } else {
+ mFading = true;
+ mColorIndex = 0;
+ }
}
- @Override public boolean onCreateOptionsMenu(Menu menu) {
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, CLEAR_ID, 0, "Clear");
menu.add(0, FADE_ID, 0, "Fade").setCheckable(true);
return super.onCreateOptionsMenu(menu);
}
- @Override public boolean onPrepareOptionsMenu(Menu menu) {
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(FADE_ID).setChecked(mFading);
return super.onPrepareOptionsMenu(menu);
}
- @Override public boolean onOptionsItemSelected(MenuItem item) {
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CLEAR_ID:
mView.clear();
@@ -97,8 +143,10 @@
}
}
- @Override protected void onResume() {
+ @Override
+ protected void onResume() {
super.onResume();
+
// If fading mode is enabled, then as long as we are resumed we want
// to run pulse to fade the contents.
if (mFading) {
@@ -106,15 +154,20 @@
}
}
- @Override protected void onSaveInstanceState(Bundle outState) {
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+
// Save away the fading state to restore if needed later. Note that
// we do not currently save the contents of the display.
outState.putBoolean("fading", mFading);
+ outState.putInt("color", mColorIndex);
}
- @Override protected void onPause() {
+ @Override
+ protected void onPause() {
super.onPause();
+
// Make sure to never run the fading pulse while we are paused or
// stopped.
stopFading();
@@ -125,28 +178,34 @@
* ensure that we don't have multiple pulses running at a time.
*/
void startFading() {
- mHandler.removeMessages(FADE_MSG);
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(FADE_MSG), FADE_DELAY);
+ mHandler.removeMessages(MSG_FADE);
+ scheduleFade();
}
/**
* Stop the pulse to fade the screen.
*/
void stopFading() {
- mHandler.removeMessages(FADE_MSG);
+ mHandler.removeMessages(MSG_FADE);
+ }
+
+ /**
+ * Schedule a fade message for later.
+ */
+ void scheduleFade() {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FADE), FADE_DELAY);
}
private Handler mHandler = new Handler() {
- @Override public void handleMessage(Message msg) {
+ @Override
+ public void handleMessage(Message msg) {
switch (msg.what) {
// Upon receiving the fade pulse, we have the view perform a
// fade and then enqueue a new message to pulse at the desired
// next time.
- case FADE_MSG: {
+ case MSG_FADE: {
mView.fade();
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(FADE_MSG), FADE_DELAY);
+ scheduleFade();
break;
}
default:
@@ -155,36 +214,52 @@
}
};
- public class MyView extends View {
+ enum PaintMode {
+ Draw,
+ Splat,
+ Erase,
+ }
+
+ /**
+ * This view implements the drawing canvas.
+ *
+ * It handles all of the input events and drawing functions.
+ */
+ class PaintView extends View {
private static final int FADE_ALPHA = 0x06;
- private static final int MAX_FADE_STEPS = 256/FADE_ALPHA + 4;
+ private static final int MAX_FADE_STEPS = 256 / FADE_ALPHA + 4;
private static final int TRACKBALL_SCALE = 10;
+ private static final int SPLAT_VECTORS = 40;
+
+ private final Random mRandom = new Random();
private Bitmap mBitmap;
private Canvas mCanvas;
- private final Rect mRect = new Rect();
private final Paint mPaint;
private final Paint mFadePaint;
private float mCurX;
private float mCurY;
+ private int mOldButtonState;
private int mFadeSteps = MAX_FADE_STEPS;
- public MyView(Context c) {
+ public PaintView(Context c) {
super(c);
setFocusable(true);
+
mPaint = new Paint();
mPaint.setAntiAlias(true);
- mPaint.setARGB(255, 255, 255, 255);
+
mFadePaint = new Paint();
- mFadePaint.setDither(true);
- mFadePaint.setARGB(FADE_ALPHA, 0, 0, 0);
+ mFadePaint.setColor(BACKGROUND_COLOR);
+ mFadePaint.setAlpha(FADE_ALPHA);
}
public void clear() {
if (mCanvas != null) {
- mPaint.setARGB(0xff, 0, 0, 0);
+ mPaint.setColor(BACKGROUND_COLOR);
mCanvas.drawPaint(mPaint);
invalidate();
+
mFadeSteps = MAX_FADE_STEPS;
}
}
@@ -193,12 +268,13 @@
if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) {
mCanvas.drawPaint(mFadePaint);
invalidate();
+
mFadeSteps++;
}
}
- @Override protected void onSizeChanged(int w, int h, int oldw,
- int oldh) {
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int curW = mBitmap != null ? mBitmap.getWidth() : 0;
int curH = mBitmap != null ? mBitmap.getHeight() : 0;
if (curW >= w && curH >= h) {
@@ -208,8 +284,7 @@
if (curW < w) curW = w;
if (curH < h) curH = h;
- Bitmap newBitmap = Bitmap.createBitmap(curW, curH,
- Bitmap.Config.RGB_565);
+ Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null) {
@@ -220,69 +295,234 @@
mFadeSteps = MAX_FADE_STEPS;
}
- @Override protected void onDraw(Canvas canvas) {
+ @Override
+ protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
- @Override public boolean onTrackballEvent(MotionEvent event) {
- int N = event.getHistorySize();
- final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
- final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
- for (int i=0; i<N; i++) {
- //Log.i("TouchPaint", "Intermediate trackball #" + i
- // + ": x=" + event.getHistoricalX(i)
- // + ", y=" + event.getHistoricalY(i));
- mCurX += event.getHistoricalX(i) * scaleX;
- mCurY += event.getHistoricalY(i) * scaleY;
- drawPoint(mCurX, mCurY, 1.0f, 16.0f);
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ // Advance color when the trackball button is pressed.
+ advanceColor();
}
- //Log.i("TouchPaint", "Trackball: x=" + event.getX()
- // + ", y=" + event.getY());
- mCurX += event.getX() * scaleX;
- mCurY += event.getY() * scaleY;
- drawPoint(mCurX, mCurY, 1.0f, 16.0f);
+
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ final int N = event.getHistorySize();
+ final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
+ final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
+ for (int i = 0; i < N; i++) {
+ moveTrackball(event.getHistoricalX(i) * scaleX,
+ event.getHistoricalY(i) * scaleY);
+ }
+ moveTrackball(event.getX() * scaleX, event.getY() * scaleY);
+ }
return true;
}
- @Override public boolean onTouchEvent(MotionEvent event) {
- int action = event.getActionMasked();
- if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL) {
- int N = event.getHistorySize();
- int P = event.getPointerCount();
+ private void moveTrackball(float deltaX, float deltaY) {
+ final int curW = mBitmap != null ? mBitmap.getWidth() : 0;
+ final int curH = mBitmap != null ? mBitmap.getHeight() : 0;
+
+ mCurX = Math.max(Math.min(mCurX + deltaX, curW - 1), 0);
+ mCurY = Math.max(Math.min(mCurY + deltaY, curH - 1), 0);
+ paint(PaintMode.Draw, mCurX, mCurY);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return onTouchOrHoverEvent(event, true /*isTouch*/);
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ return onTouchOrHoverEvent(event, false /*isTouch*/);
+ }
+
+ private boolean onTouchOrHoverEvent(MotionEvent event, boolean isTouch) {
+ final int buttonState = event.getButtonState();
+ int pressedButtons = buttonState & ~mOldButtonState;
+ mOldButtonState = buttonState;
+
+ if ((pressedButtons & MotionEvent.BUTTON_SECONDARY) != 0) {
+ // Advance color when the right mouse button or first stylus button
+ // is pressed.
+ advanceColor();
+ }
+
+ PaintMode mode;
+ if ((buttonState & MotionEvent.BUTTON_TERTIARY) != 0) {
+ // Splat paint when the middle mouse button or second stylus button is pressed.
+ mode = PaintMode.Splat;
+ } else if (isTouch || (buttonState & MotionEvent.BUTTON_PRIMARY) != 0) {
+ // Draw paint when touching or if the primary button is pressed.
+ mode = PaintMode.Draw;
+ } else {
+ // Otherwise, do not paint anything.
+ return false;
+ }
+
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE
+ || action == MotionEvent.ACTION_HOVER_MOVE) {
+ final int N = event.getHistorySize();
+ final int P = event.getPointerCount();
for (int i = 0; i < N; i++) {
for (int j = 0; j < P; j++) {
- mCurX = event.getHistoricalX(j, i);
- mCurY = event.getHistoricalY(j, i);
- drawPoint(mCurX, mCurY,
+ paint(getPaintModeForTool(event.getToolType(j), mode),
+ event.getHistoricalX(j, i),
+ event.getHistoricalY(j, i),
event.getHistoricalPressure(j, i),
- event.getHistoricalTouchMajor(j, i));
+ event.getHistoricalTouchMajor(j, i),
+ event.getHistoricalTouchMinor(j, i),
+ event.getHistoricalOrientation(j, i),
+ event.getHistoricalAxisValue(MotionEvent.AXIS_DISTANCE, j, i),
+ event.getHistoricalAxisValue(MotionEvent.AXIS_TILT, j, i));
}
}
for (int j = 0; j < P; j++) {
- mCurX = event.getX(j);
- mCurY = event.getY(j);
- drawPoint(mCurX, mCurY, event.getPressure(j), event.getTouchMajor(j));
+ paint(getPaintModeForTool(event.getToolType(j), mode),
+ event.getX(j),
+ event.getY(j),
+ event.getPressure(j),
+ event.getTouchMajor(j),
+ event.getTouchMinor(j),
+ event.getOrientation(j),
+ event.getAxisValue(MotionEvent.AXIS_DISTANCE, j),
+ event.getAxisValue(MotionEvent.AXIS_TILT, j));
}
+ mCurX = event.getX();
+ mCurY = event.getY();
}
return true;
}
- private void drawPoint(float x, float y, float pressure, float width) {
- //Log.i("TouchPaint", "Drawing: " + x + "x" + y + " p="
- // + pressure + " width=" + width);
- if (width < 1) width = 1;
+ private PaintMode getPaintModeForTool(int toolType, PaintMode defaultMode) {
+ if (toolType == MotionEvent.TOOL_TYPE_ERASER) {
+ return PaintMode.Erase;
+ }
+ return defaultMode;
+ }
+
+ private void advanceColor() {
+ mColorIndex = (mColorIndex + 1) % COLORS.length;
+ }
+
+ private void paint(PaintMode mode, float x, float y) {
+ paint(mode, x, y, 1.0f, 0, 0, 0, 0, 0);
+ }
+
+ private void paint(PaintMode mode, float x, float y, float pressure,
+ float major, float minor, float orientation,
+ float distance, float tilt) {
if (mBitmap != null) {
- float radius = width / 2;
- int pressureLevel = (int)(pressure * 255);
- mPaint.setARGB(pressureLevel, 255, 255, 255);
- mCanvas.drawCircle(x, y, radius, mPaint);
- mRect.set((int) (x - radius - 2), (int) (y - radius - 2),
- (int) (x + radius + 2), (int) (y + radius + 2));
- invalidate(mRect);
+ if (major <= 0 || minor <= 0) {
+ // If size is not available, use a default value.
+ major = minor = 16;
+ }
+
+ switch (mode) {
+ case Draw:
+ mPaint.setColor(COLORS[mColorIndex]);
+ mPaint.setAlpha(Math.min((int)(pressure * 128), 255));
+ drawOval(mCanvas, x, y, major, minor, orientation, mPaint);
+ break;
+
+ case Erase:
+ mPaint.setColor(BACKGROUND_COLOR);
+ mPaint.setAlpha(Math.min((int)(pressure * 128), 255));
+ drawOval(mCanvas, x, y, major, minor, orientation, mPaint);
+ break;
+
+ case Splat:
+ mPaint.setColor(COLORS[mColorIndex]);
+ mPaint.setAlpha(64);
+ drawSplat(mCanvas, x, y, orientation, distance, tilt, mPaint);
+ break;
+ }
}
mFadeSteps = 0;
+ invalidate();
+ }
+
+ /**
+ * Draw an oval.
+ *
+ * When the orienation is 0 radians, orients the major axis vertically,
+ * angles less than or greater than 0 radians rotate the major axis left or right.
+ */
+ private final RectF mReusableOvalRect = new RectF();
+ private void drawOval(Canvas canvas, float x, float y, float major, float minor,
+ float orientation, Paint paint) {
+ canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.rotate((float) (orientation * 180 / Math.PI), x, y);
+ mReusableOvalRect.left = x - minor / 2;
+ mReusableOvalRect.right = x + minor / 2;
+ mReusableOvalRect.top = y - major / 2;
+ mReusableOvalRect.bottom = y + major / 2;
+ canvas.drawOval(mReusableOvalRect, paint);
+ canvas.restore();
+ }
+
+ /**
+ * Splatter paint in an area.
+ *
+ * Chooses random vectors describing the flow of paint from a round nozzle
+ * across a range of a few degrees. Then adds this vector to the direction
+ * indicated by the orientation and tilt of the tool and throws paint at
+ * the canvas along that vector.
+ *
+ * Repeats the process until a masterpiece is born.
+ */
+ private void drawSplat(Canvas canvas, float x, float y, float orientation,
+ float distance, float tilt, Paint paint) {
+ float z = distance * 2 + 10;
+
+ // Calculate the center of the spray.
+ float nx = (float) (Math.sin(orientation) * Math.sin(tilt));
+ float ny = (float) (- Math.cos(orientation) * Math.sin(tilt));
+ float nz = (float) Math.cos(tilt);
+ if (nz < 0.05) {
+ return;
+ }
+ float cd = z / nz;
+ float cx = nx * cd;
+ float cy = ny * cd;
+
+ for (int i = 0; i < SPLAT_VECTORS; i++) {
+ // Make a random 2D vector that describes the direction of a speck of paint
+ // ejected by the nozzle in the nozzle's plane, assuming the tool is
+ // perpendicular to the surface.
+ double direction = mRandom.nextDouble() * Math.PI * 2;
+ double dispersion = mRandom.nextGaussian() * 0.2;
+ double vx = Math.cos(direction) * dispersion;
+ double vy = Math.sin(direction) * dispersion;
+ double vz = 1;
+
+ // Apply the nozzle tilt angle.
+ double temp = vy;
+ vy = temp * Math.cos(tilt) - vz * Math.sin(tilt);
+ vz = temp * Math.sin(tilt) + vz * Math.cos(tilt);
+
+ // Apply the nozzle orientation angle.
+ temp = vx;
+ vx = temp * Math.cos(orientation) - vy * Math.sin(orientation);
+ vy = temp * Math.sin(orientation) + vy * Math.cos(orientation);
+
+ // Determine where the paint will hit the surface.
+ if (vz < 0.05) {
+ continue;
+ }
+ float pd = (float) (z / vz);
+ float px = (float) (vx * pd);
+ float py = (float) (vy * pd);
+
+ // Throw some paint at this location, relative to the center of the spray.
+ mCanvas.drawCircle(x + px - cx, y + py - cy, 1.0f, paint);
+ }
}
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java b/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
index b98a5b5..b0a1f96 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
@@ -84,7 +84,8 @@
private final void initLabelView() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
- mTextPaint.setTextSize(16);
+ // Must manually scale the desired text size to match screen density
+ mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
mTextPaint.setColor(0xFF000000);
setPadding(3, 3, 3, 3);
}
@@ -104,6 +105,7 @@
* @param size Font size
*/
public void setTextSize(int size) {
+ // This text size has been pre-scaled by the getDimensionPixelOffset method
mTextPaint.setTextSize(size);
requestLayout();
invalidate();
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
index 3ee63e5..464b601 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
@@ -99,9 +99,8 @@
}
private class OverscanState implements State {
public void apply() {
- display("FULLSCREEN + LOW_PROFILE + HIDE_NAVIGATION");
- mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ display("FULLSCREEN + HIDE_NAVIGATION");
+ mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
public State next() {
return new NormalState();
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.java b/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.java
index 8ffa3b9..bffeb96 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/SearchViewActionBar.java
@@ -39,12 +39,9 @@
* This demonstrates the usage of SearchView in an ActionBar as a menu item.
* It sets a SearchableInfo on the SearchView for suggestions and submitting queries to.
*/
-public class SearchViewActionBar extends Activity implements SearchView.OnQueryTextListener,
- SearchView.OnCloseListener, Button.OnClickListener {
+public class SearchViewActionBar extends Activity implements SearchView.OnQueryTextListener {
private SearchView mSearchView;
- private Button mOpenButton;
- private Button mCloseButton;
private TextView mStatusView;
@Override
@@ -55,10 +52,6 @@
setContentView(R.layout.searchview_actionbar);
mStatusView = (TextView) findViewById(R.id.status_text);
- mOpenButton = (Button) findViewById(R.id.open_button);
- mCloseButton = (Button) findViewById(R.id.close_button);
- mOpenButton.setOnClickListener(this);
- mCloseButton.setOnClickListener(this);
}
@Override
@@ -99,7 +92,6 @@
}
mSearchView.setOnQueryTextListener(this);
- mSearchView.setOnCloseListener(this);
}
public boolean onQueryTextChange(String newText) {
@@ -117,14 +109,6 @@
return false;
}
- public void onClick(View view) {
- if (view == mCloseButton) {
- mSearchView.setIconified(true);
- } else if (view == mOpenButton) {
- mSearchView.setIconified(false);
- }
- }
-
protected boolean isAlwaysExpanded() {
return false;
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/Switches.java b/samples/ApiDemos/src/com/example/android/apis/view/Switches.java
new file mode 100644
index 0000000..0f4f48c
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/Switches.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import com.example.android.apis.R;
+
+/**
+ * Demonstrates the use of the toggle switch widget.
+ */
+public class Switches extends Activity implements CompoundButton.OnCheckedChangeListener {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.switches);
+
+ Switch s = (Switch) findViewById(R.id.monitored_switch);
+ if (s != null) {
+ s.setOnCheckedChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Toast.makeText(this, "Monitored switch is " + (isChecked ? "on" : "off"),
+ Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/samples/BrowserPlugin/jni/RenderingThread.cpp b/samples/BrowserPlugin/jni/RenderingThread.cpp
index 91ffb4a..1307e90 100644
--- a/samples/BrowserPlugin/jni/RenderingThread.cpp
+++ b/samples/BrowserPlugin/jni/RenderingThread.cpp
@@ -43,9 +43,14 @@
}
android::status_t RenderingThread::readyToRun() {
+ gLogI.log(kError_ANPLogType, "thread %p acquiring native window...", this);
while (m_ANW == NULL) {
m_ANW = gNativeWindowI.acquireNativeWindow(m_npp);
+ if (!m_ANW)
+ gLogI.log(kError_ANPLogType, "thread %p acquire native window FAILED!", this);
+
}
+ gLogI.log(kError_ANPLogType, "thread %p acquired native window successfully!", this);
#if (!USE_SOFTWARE_RENDERING)
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -165,6 +170,8 @@
const SkBitmap& bitmap)
{
#if USE_SOFTWARE_RENDERING
+ if (bitmap.height() == 0 || bitmap.width() == 0)
+ return;
//STEP 1: lock the ANW, getting a buffer
ANativeWindow_Buffer buffer;
diff --git a/samples/BrowserPlugin/jni/animation/AnimationThread.cpp b/samples/BrowserPlugin/jni/animation/AnimationThread.cpp
index 1388fe3..2729a53 100644
--- a/samples/BrowserPlugin/jni/animation/AnimationThread.cpp
+++ b/samples/BrowserPlugin/jni/animation/AnimationThread.cpp
@@ -45,7 +45,7 @@
m_paint = new SkPaint;
m_paint->setAntiAlias(true);
- m_bitmap = constructBitmap(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ m_bitmap = constructBitmap(0, 0);
m_canvas = new SkCanvas(*m_bitmap);
m_startExecutionTime = 0;
@@ -95,11 +95,6 @@
int width, height;
getDimensions(width, height);
- if (width <= 0)
- width = DEFAULT_WIDTH;
- if (height <= 0)
- height = DEFAULT_HEIGHT;
-
if (m_bitmap->width() != width || m_bitmap->height() != height) {
delete m_canvas;
delete m_bitmap;
diff --git a/samples/BrowserPlugin/jni/animation/AnimationThread.h b/samples/BrowserPlugin/jni/animation/AnimationThread.h
index e95ecce..8222a7e 100644
--- a/samples/BrowserPlugin/jni/animation/AnimationThread.h
+++ b/samples/BrowserPlugin/jni/animation/AnimationThread.h
@@ -59,9 +59,6 @@
SkPaint* m_paint;
SkBitmap* m_bitmap;
SkCanvas* m_canvas;
-
- static const unsigned int DEFAULT_WIDTH = 400;
- static const unsigned int DEFAULT_HEIGHT = 400;
};
diff --git a/samples/HoneycombGallery/AndroidManifest.xml b/samples/HoneycombGallery/AndroidManifest.xml
index 6e6163b..2a99856 100644
--- a/samples/HoneycombGallery/AndroidManifest.xml
+++ b/samples/HoneycombGallery/AndroidManifest.xml
@@ -15,10 +15,16 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.android.hcgallery" android:versionCode="1"
- android:versionName="1.0">
+ package="com.example.android.hcgallery" android:versionCode="2"
+ android:versionName="1.5">
- <uses-sdk android:minSdkVersion="11" />
+ <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="14" />
+
+ <!-- Declares that the app supports devices w/o touch, such as a mouse-driven device
+ or a device that provides only a d-pad for navigation -->
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
+ <!-- The app uses the camera, but checks for it at runtime and will work without it -->
+ <uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -27,8 +33,7 @@
android:icon="@drawable/icon"
android:logo="@drawable/logo"
android:theme="@style/AppTheme.Light"
- android:hardwareAccelerated="true"
- android:debuggable="true">
+ android:hardwareAccelerated="true">
<activity android:name=".MainActivity"
android:label="@string/app_name"
@@ -43,8 +48,11 @@
</intent-filter>
</activity>
- <!-- CameraSample -->
- <activity android:name=".CameraSample"
+ <!-- Holds the ContentFragment for small/normal screens -->
+ <activity android:name=".ContentActivity" />
+
+ <!-- Camera Sample -->
+ <activity android:name=".CameraActivity"
android:label="@string/camera_sample"
android:screenOrientation="landscape" />
</application>
diff --git a/samples/HoneycombGallery/res/layout-port/main.xml b/samples/HoneycombGallery/res/layout-large-port/main.xml
similarity index 82%
rename from samples/HoneycombGallery/res/layout-port/main.xml
rename to samples/HoneycombGallery/res/layout-large-port/main.xml
index a5e9970..206c6ce 100644
--- a/samples/HoneycombGallery/res/layout-port/main.xml
+++ b/samples/HoneycombGallery/res/layout-large-port/main.xml
@@ -21,14 +21,12 @@
android:id="@+id/frags">
<fragment class="com.example.android.hcgallery.TitlesFragment"
- android:id="@+id/frag_title"
- android:visibility="gone"
- android:layout_marginTop="?android:attr/actionBarSize"
+ android:id="@+id/titles_frag"
android:layout_width="match_parent"
- android:layout_height="@dimen/titles_size"/>
+ android:layout_height="@dimen/titles_size" />
<fragment class="com.example.android.hcgallery.ContentFragment"
- android:id="@+id/frag_content"
+ android:id="@+id/content_frag"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/samples/HoneycombGallery/res/layout-port/main.xml b/samples/HoneycombGallery/res/layout-large/main.xml
similarity index 76%
copy from samples/HoneycombGallery/res/layout-port/main.xml
copy to samples/HoneycombGallery/res/layout-large/main.xml
index a5e9970..8322e32 100644
--- a/samples/HoneycombGallery/res/layout-port/main.xml
+++ b/samples/HoneycombGallery/res/layout-large/main.xml
@@ -15,21 +15,19 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+ android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frags">
<fragment class="com.example.android.hcgallery.TitlesFragment"
- android:id="@+id/frag_title"
- android:visibility="gone"
- android:layout_marginTop="?android:attr/actionBarSize"
- android:layout_width="match_parent"
- android:layout_height="@dimen/titles_size"/>
+ android:id="@+id/titles_frag"
+ android:layout_width="@dimen/titles_size"
+ android:layout_height="match_parent"/>
<fragment class="com.example.android.hcgallery.ContentFragment"
- android:id="@+id/frag_content"
+ android:id="@+id/content_frag"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-</LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/HoneycombGallery/res/layout/camera_sample.xml b/samples/HoneycombGallery/res/layout/camera_sample.xml
index dcb8867..9986f05 100644
--- a/samples/HoneycombGallery/res/layout/camera_sample.xml
+++ b/samples/HoneycombGallery/res/layout/camera_sample.xml
@@ -15,10 +15,7 @@
limitations under the License.
-->
-
-
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.android.hcgallery.CameraFragment"
diff --git a/samples/HoneycombGallery/res/layout/action_bar_custom.xml b/samples/HoneycombGallery/res/layout/content_activity.xml
similarity index 67%
rename from samples/HoneycombGallery/res/layout/action_bar_custom.xml
rename to samples/HoneycombGallery/res/layout/content_activity.xml
index 5f111d9..0409a0c 100644
--- a/samples/HoneycombGallery/res/layout/action_bar_custom.xml
+++ b/samples/HoneycombGallery/res/layout/content_activity.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2011 The Android Open Source Project
@@ -14,11 +15,11 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent">Hello Action Bar</TextView>
-</LinearLayout>
+ <fragment class="com.example.android.hcgallery.ContentFragment"
+ android:id="@+id/content_frag"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/samples/HoneycombGallery/res/layout/main.xml b/samples/HoneycombGallery/res/layout/main.xml
index 4987920..a724429 100644
--- a/samples/HoneycombGallery/res/layout/main.xml
+++ b/samples/HoneycombGallery/res/layout/main.xml
@@ -14,22 +14,13 @@
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="horizontal"
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/frags">
-
- <fragment class="com.example.android.hcgallery.TitlesFragment"
- android:id="@+id/frag_title"
- android:visibility="gone"
- android:layout_marginTop="?android:attr/actionBarSize"
- android:layout_width="@dimen/titles_size"
- android:layout_height="match_parent" />
-
- <fragment class="com.example.android.hcgallery.ContentFragment"
- android:id="@+id/frag_content"
+ android:layout_height="match_parent">
+ <fragment
+ class="com.example.android.hcgallery.TitlesFragment"
+ android:id="@+id/titles_frag"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</LinearLayout>
+ android:layout_height="match_parent"/>
+</FrameLayout>
diff --git a/samples/HoneycombGallery/res/layout/notification.xml b/samples/HoneycombGallery/res/layout/notification.xml
index 3da757d..caae928 100644
--- a/samples/HoneycombGallery/res/layout/notification.xml
+++ b/samples/HoneycombGallery/res/layout/notification.xml
@@ -21,8 +21,8 @@
android:orientation="horizontal">
<LinearLayout
- android:paddingLeft="16dip"
- android:layout_width="0dip"
+ android:paddingLeft="16dp"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
@@ -54,7 +54,7 @@
android:src="@drawable/btn_notification_ic_example"
android:background="@null"
android:layout_weight="0"
- android:layout_width="48dip"
+ android:layout_width="48dp"
android:layout_height="match_parent" />
</LinearLayout>
diff --git a/samples/HoneycombGallery/res/menu/camera_menu.xml b/samples/HoneycombGallery/res/menu/camera_menu.xml
index 55184dd..c961d3d 100644
--- a/samples/HoneycombGallery/res/menu/camera_menu.xml
+++ b/samples/HoneycombGallery/res/menu/camera_menu.xml
@@ -16,5 +16,7 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/switch_cam" android:title="Switch Camera" />
+ <item android:id="@+id/menu_switch_cam"
+ android:title="@string/switch_camera"
+ android:showAsAction="ifRoom" />
</menu>
diff --git a/samples/HoneycombGallery/res/menu/main_menu.xml b/samples/HoneycombGallery/res/menu/main_menu.xml
index 0e27cb5..3210aa7 100644
--- a/samples/HoneycombGallery/res/menu/main_menu.xml
+++ b/samples/HoneycombGallery/res/menu/main_menu.xml
@@ -16,25 +16,20 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/camera"
- android:title="Camera"
- android:icon="?attr/menuIconCamera"
- android:showAsAction="ifRoom" />
- <item android:id="@+id/toggleTitles"
- android:icon="?attr/menuIconToggle"
- android:title="Toggle Titles"
- android:showAsAction="ifRoom|withText" />
- <!-- Example of items in the overflow menu -->
- <item android:id="@+id/toggleTheme"
- android:title="Day/Night"
- android:showAsAction="never" />
- <item android:id="@+id/showDialog"
- android:title="Show a dialog"
- android:showAsAction="never" />
- <item android:id="@+id/showStandardNotification"
- android:title="Show a basic notification"
- android:showAsAction="never" />
- <item android:id="@+id/showCustomNotification"
- android:title="Show a custom notification"
- android:showAsAction="never" />
+ <item android:id="@+id/menu_camera"
+ android:title="@string/camera"
+ android:icon="?attr/menuIconCamera"
+ android:showAsAction="ifRoom" />
+ <item android:id="@+id/menu_toggleTitles"
+ android:title="@string/toggle_titles"
+ android:icon="?attr/menuIconToggle"
+ android:showAsAction="ifRoom|withText" />
+ <item android:id="@+id/menu_toggleTheme"
+ android:title="@string/day_night" />
+ <item android:id="@+id/menu_showDialog"
+ android:title="@string/show_dialog" />
+ <item android:id="@+id/menu_showStandardNotification"
+ android:title="@string/basic_notification" />
+ <item android:id="@+id/menu_showCustomNotification"
+ android:title="@string/custom_notification" />
</menu>
diff --git a/samples/HoneycombGallery/res/menu/photo_context_menu.xml b/samples/HoneycombGallery/res/menu/photo_context_menu.xml
index efe26c9..0039bc3 100644
--- a/samples/HoneycombGallery/res/menu/photo_context_menu.xml
+++ b/samples/HoneycombGallery/res/menu/photo_context_menu.xml
@@ -16,8 +16,8 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/share"
- android:title="Share"
- android:icon="?attr/menuIconShare"
- android:showAsAction="always|withText" />
+ <item android:id="@+id/menu_share"
+ android:title="@string/share"
+ android:icon="?attr/menuIconShare"
+ android:showAsAction="always|withText" />
</menu>
diff --git a/samples/HoneycombGallery/res/values-port/dimens.xml b/samples/HoneycombGallery/res/values-large-port/dimens.xml
similarity index 100%
rename from samples/HoneycombGallery/res/values-port/dimens.xml
rename to samples/HoneycombGallery/res/values-large-port/dimens.xml
diff --git a/samples/HoneycombGallery/res/values/strings.xml b/samples/HoneycombGallery/res/values/strings.xml
index 5c3b355..65f7945 100644
--- a/samples/HoneycombGallery/res/values/strings.xml
+++ b/samples/HoneycombGallery/res/values/strings.xml
@@ -25,4 +25,14 @@
<string name="notification_text">Example notification text</string>
<string name="photo_selection_cab_title">Photo selection</string>
-</resources>
+
+ <!-- Menu strings -->
+ <string name="share">Share</string>
+ <string name="camera">Camera</string>
+ <string name="toggle_titles">Toggle Titles</string>
+ <string name="day_night">Day/Night</string>
+ <string name="show_dialog">Show a dialog</string>
+ <string name="basic_notification">Show a basic notification</string>
+ <string name="custom_notification">Show a custom notification</string>
+ <string name="switch_camera">Switch camera</string>
+</resources>
\ No newline at end of file
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraSample.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraActivity.java
similarity index 90%
rename from samples/HoneycombGallery/src/com/example/android/hcgallery/CameraSample.java
rename to samples/HoneycombGallery/src/com/example/android/hcgallery/CameraActivity.java
index 2243858..d87676c 100644
--- a/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraSample.java
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraActivity.java
@@ -19,7 +19,8 @@
import android.app.Activity;
import android.os.Bundle;
-public class CameraSample extends Activity {
+/** Basic shell activity that hosts CameraFragment. */
+public class CameraActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraFragment.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraFragment.java
index 19c4b47..3290f11 100644
--- a/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraFragment.java
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/CameraFragment.java
@@ -16,12 +16,9 @@
package com.example.android.hcgallery;
-import java.io.IOException;
-import java.util.List;
-
-import android.app.Fragment;
-import android.app.Activity;
import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
@@ -38,12 +35,16 @@
import android.view.View;
import android.view.ViewGroup;
+import java.io.IOException;
+import java.util.List;
+
public class CameraFragment extends Fragment {
private Preview mPreview;
Camera mCamera;
int mNumberOfCameras;
- int mCameraCurrentlyLocked;
+ int mCurrentCamera; // Camera ID currently chosen
+ int mCameraCurrentlyLocked; // Camera ID that's actually acquired
// The first rear facing camera
int mDefaultCameraId;
@@ -52,20 +53,18 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- // Create a RelativeLayout container that will hold a SurfaceView,
- // and set it as the content of our activity.
+ // Create a container that will hold a SurfaceView for camera previews
mPreview = new Preview(this.getActivity());
// Find the total number of cameras available
mNumberOfCameras = Camera.getNumberOfCameras();
- // Find the ID of the default camera
+ // Find the ID of the rear-facing ("default") camera
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < mNumberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
- mDefaultCameraId = i;
+ mCurrentCamera = mDefaultCameraId = i;
}
}
setHasOptionsMenu(mNumberOfCameras > 1);
@@ -95,9 +94,10 @@
public void onResume() {
super.onResume();
- // Open the default i.e. the first rear facing camera.
- mCamera = Camera.open(mDefaultCameraId);
- mCameraCurrentlyLocked = mDefaultCameraId;
+ // Use mCurrentCamera to select the camera desired to safely restore
+ // the fragment after the camera has been changed
+ mCamera = Camera.open(mCurrentCamera);
+ mCameraCurrentlyLocked = mCurrentCamera;
mPreview.setCamera(mCamera);
}
@@ -128,7 +128,7 @@
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
- case R.id.switch_cam:
+ case R.id.menu_switch_cam:
// Release this camera -> mCameraCurrentlyLocked
if (mCamera != null) {
mCamera.stopPreview();
@@ -139,10 +139,9 @@
// Acquire the next camera and request Preview to reconfigure
// parameters.
- mCamera = Camera
- .open((mCameraCurrentlyLocked + 1) % mNumberOfCameras);
- mCameraCurrentlyLocked = (mCameraCurrentlyLocked + 1)
- % mNumberOfCameras;
+ mCurrentCamera = (mCameraCurrentlyLocked + 1) % mNumberOfCameras;
+ mCamera = Camera.open(mCurrentCamera);
+ mCameraCurrentlyLocked = mCurrentCamera;
mPreview.switchCamera(mCamera);
// Start the preview
@@ -152,6 +151,7 @@
Intent intent = new Intent(this.getActivity(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
+ return true;
default:
return super.onOptionsItemSelected(item);
@@ -175,6 +175,7 @@
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
+ boolean mSurfaceCreated = false;
Preview(Context context) {
super(context);
@@ -194,7 +195,7 @@
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters()
.getSupportedPreviewSizes();
- requestLayout();
+ if (mSurfaceCreated) requestLayout();
}
}
@@ -205,11 +206,6 @@
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
- Camera.Parameters parameters = camera.getParameters();
- parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
- requestLayout();
-
- camera.setParameters(parameters);
}
@Override
@@ -227,11 +223,18 @@
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
height);
}
+
+ if (mCamera != null) {
+ Camera.Parameters parameters = mCamera.getParameters();
+ parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+
+ mCamera.setParameters(parameters);
+ }
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (changed && getChildCount() > 0) {
+ if (getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
@@ -269,6 +272,8 @@
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
+ if (mPreviewSize == null) requestLayout();
+ mSurfaceCreated = true;
}
public void surfaceDestroyed(SurfaceHolder holder) {
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentActivity.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentActivity.java
new file mode 100644
index 0000000..f8e93d4
--- /dev/null
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentActivity.java
@@ -0,0 +1,48 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.example.android.hcgallery;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/** This is a shell activity that hosts ContentFragment when the device screen
+ * is smaller than "large".
+ */
+public class ContentActivity extends Activity {
+ private int mThemeId = 0;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ // The activity theme is the only state data that the activity needs
+ // to restore. All info about the content displayed is managed by the fragment
+ mThemeId = extras.getInt("theme");
+ } else if (savedInstanceState != null) {
+ // If there's no restore state, get the theme from the intent
+ mThemeId = savedInstanceState.getInt("theme");
+ }
+
+ if (mThemeId != 0) {
+ setTheme(mThemeId);
+ }
+
+ setContentView(R.layout.content_activity);
+
+ if (extras != null) {
+ // Take the info from the intent and deliver it to the fragment so it can update
+ int category = extras.getInt("category");
+ int position = extras.getInt("position");
+ ContentFragment frag = (ContentFragment) getFragmentManager().findFragmentById(R.id.content_frag);
+ frag.updateContentAndRecycleBitmap(category, position);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("theme", mThemeId);
+ }
+}
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentFragment.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentFragment.java
index 8c3323b..b6754f9 100644
--- a/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentFragment.java
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/ContentFragment.java
@@ -17,7 +17,6 @@
package com.example.android.hcgallery;
import android.app.ActionBar;
-import android.app.Activity;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -37,6 +36,8 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
@@ -46,8 +47,16 @@
import java.io.IOException;
import java.util.StringTokenizer;
+/** Fragment that shows the content selected from the TitlesFragment.
+ * When running on a screen size smaller than "large", this fragment is hosted in
+ * ContentActivity. Otherwise, it appears side by side with the TitlesFragment
+ * in MainActivity. */
public class ContentFragment extends Fragment {
private View mContentView;
+ private int mCategory = 0;
+ private int mCurPosition = 0;
+ private boolean mSystemUiVisible = true;
+ private boolean mSoloFragment = false;
// The bitmap currently used by ImageView
private Bitmap mBitmap = null;
@@ -55,11 +64,9 @@
// Current action mode (contextual action bar, a.k.a. CAB)
private ActionMode mCurrentActionMode;
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- }
-
+ /** This is where we initialize the fragment's UI and attach some
+ * event listeners to UI components.
+ */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -67,65 +74,48 @@
final ImageView imageView = (ImageView) mContentView.findViewById(R.id.image);
mContentView.setDrawingCacheEnabled(false);
+ // Handle drag events when a list item is dragged into the view
mContentView.setOnDragListener(new View.OnDragListener() {
- public boolean onDrag(View v, DragEvent event) {
+ public boolean onDrag(View view, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_ENTERED:
- mContentView.setBackgroundColor(
+ view.setBackgroundColor(
getResources().getColor(R.color.drag_active_color));
break;
case DragEvent.ACTION_DRAG_EXITED:
- mContentView.setBackgroundColor(Color.TRANSPARENT);
+ view.setBackgroundColor(Color.TRANSPARENT);
break;
case DragEvent.ACTION_DRAG_STARTED:
return processDragStarted(event);
case DragEvent.ACTION_DROP:
- mContentView.setBackgroundColor(Color.TRANSPARENT);
+ view.setBackgroundColor(Color.TRANSPARENT);
return processDrop(event, imageView);
}
return false;
}
});
- // Keep the action bar visibility in sync with the system status bar. That is, when entering
- // 'lights out mode,' hide the action bar, and when exiting this mode, show the action bar.
-
- final Activity activity = getActivity();
- mContentView.setOnSystemUiVisibilityChangeListener(
- new View.OnSystemUiVisibilityChangeListener() {
- public void onSystemUiVisibilityChange(int visibility) {
- ActionBar actionBar = activity.getActionBar();
- if (actionBar != null) {
- mContentView.setSystemUiVisibility(visibility);
- if (visibility == View.STATUS_BAR_VISIBLE) {
- actionBar.show();
- } else {
- actionBar.hide();
- }
- }
- }
- });
-
- // Show/hide the system status bar when single-clicking a photo. This is also called
- // 'lights out mode.' Activating and deactivating this mode also invokes the listener
- // defined above, which will show or hide the action bar accordingly.
-
+ // Show/hide the system status bar when single-clicking a photo.
mContentView.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if (mContentView.getSystemUiVisibility() == View.STATUS_BAR_VISIBLE) {
- mContentView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+ public void onClick(View view) {
+ if (mCurrentActionMode != null) {
+ // If we're in an action mode, don't toggle the action bar
+ return;
+ }
+
+ if (mSystemUiVisible) {
+ setSystemUiVisible(false);
} else {
- mContentView.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+ setSystemUiVisible(true);
}
}
});
// When long-pressing a photo, activate the action mode for selection, showing the
// contextual action bar (CAB).
-
mContentView.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View view) {
if (mCurrentActionMode != null) {
@@ -134,7 +124,7 @@
mCurrentActionMode = getActivity().startActionMode(
mContentSelectionActionModeCallback);
- mContentView.setSelected(true);
+ view.setSelected(true);
return true;
}
});
@@ -142,6 +132,99 @@
return mContentView;
}
+ /** This is where we perform additional setup for the fragment that's either
+ * not related to the fragment's layout or must be done after the layout is drawn.
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // Set member variable for whether this fragment is the only one in the activity
+ Fragment listFragment = getFragmentManager().findFragmentById(R.id.titles_frag);
+ mSoloFragment = listFragment == null ? true : false;
+
+ if (mSoloFragment) {
+ // The fragment is alone, so enable up navigation
+ getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
+ // Must call in order to get callback to onOptionsItemSelected()
+ setHasOptionsMenu(true);
+ }
+
+ // Current position and UI visibility should survive screen rotations.
+ if (savedInstanceState != null) {
+ setSystemUiVisible(savedInstanceState.getBoolean("systemUiVisible"));
+ if (mSoloFragment) {
+ // Restoring these members is not necessary when this fragment
+ // is combined with the TitlesFragment, because when the TitlesFragment
+ // is restored, it selects the appropriate item and sends the event
+ // to the updateContentAndRecycleBitmap() method itself
+ mCategory = savedInstanceState.getInt("category");
+ mCurPosition = savedInstanceState.getInt("listPosition");
+ updateContentAndRecycleBitmap(mCategory, mCurPosition);
+ }
+ }
+
+ if (mSoloFragment) {
+ String title = Directory.getCategory(mCategory).getEntry(mCurPosition).getName();
+ ActionBar bar = getActivity().getActionBar();
+ bar.setTitle(title);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // This callback is used only when mSoloFragment == true (see onActivityCreated above)
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // App icon in Action Bar clicked; go up
+ Intent intent = new Intent(getActivity(), MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // Reuse the existing instance
+ startActivity(intent);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("listPosition", mCurPosition);
+ outState.putInt("category", mCategory);
+ outState.putBoolean("systemUiVisible", mSystemUiVisible);
+ }
+
+ /** Toggle whether the system UI (status bar / system bar) is visible.
+ * This also toggles the action bar visibility.
+ * @param show True to show the system UI, false to hide it.
+ */
+ void setSystemUiVisible(boolean show) {
+ mSystemUiVisible = show;
+
+ Window window = getActivity().getWindow();
+ WindowManager.LayoutParams winParams = window.getAttributes();
+ View view = getView();
+ ActionBar actionBar = getActivity().getActionBar();
+
+ if (show) {
+ // Show status bar (remove fullscreen flag)
+ window.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ // Show system bar
+ view.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+ // Show action bar
+ actionBar.show();
+ } else {
+ // Add fullscreen flag (hide status bar)
+ window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ // Hide system bar
+ view.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+ // Hide action bar
+ actionBar.hide();
+ }
+ window.setAttributes(winParams);
+ }
+
boolean processDragStarted(DragEvent event) {
// Determine whether to continue processing drag and drop based on the
// plain text mime type.
@@ -176,7 +259,7 @@
updateContentAndRecycleBitmap(category, entryId);
// Update list fragment with selected entry.
TitlesFragment titlesFrag = (TitlesFragment)
- getFragmentManager().findFragmentById(R.id.frag_title);
+ getFragmentManager().findFragmentById(R.id.titles_frag);
titlesFrag.selectPosition(entryId);
return true;
}
@@ -185,7 +268,15 @@
return false;
}
+ /**
+ * Sets the current image visible.
+ * @param category Index position of the image category
+ * @param position Index position of the image
+ */
void updateContentAndRecycleBitmap(int category, int position) {
+ mCategory = category;
+ mCurPosition = position;
+
if (mCurrentActionMode != null) {
mCurrentActionMode.finish();
}
@@ -203,6 +294,9 @@
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
}
+ /** Share the currently selected photo using an AsyncTask to compress the image
+ * and then invoke the appropriate share intent.
+ */
void shareCurrentPhoto() {
File externalCacheDir = getActivity().getExternalCacheDir();
if (externalCacheDir == null) {
@@ -229,6 +323,7 @@
* Compress and write the bitmap to disk on a separate thread.
* @return TRUE if the write was successful, FALSE otherwise.
*/
+ @Override
protected Boolean doInBackground(Void... voids) {
try {
FileOutputStream fo = new FileOutputStream(tempFile, false);
@@ -250,6 +345,7 @@
* After doInBackground completes (either successfully or in failure), we invoke an
* intent to share the photo. This code is run on the main (UI) thread.
*/
+ @Override
protected void onPostExecute(Boolean result) {
if (result != Boolean.TRUE) {
return;
@@ -283,7 +379,7 @@
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
- case R.id.share:
+ case R.id.menu_share:
shareCurrentPhoto();
actionMode.finish();
return true;
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/MainActivity.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/MainActivity.java
index 3cfaf6d..06905b8 100644
--- a/samples/HoneycombGallery/src/com/example/android/hcgallery/MainActivity.java
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/MainActivity.java
@@ -33,6 +33,7 @@
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -43,92 +44,86 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
import android.widget.RemoteViews;
-public class MainActivity extends Activity implements ActionBar.TabListener {
+/** This is the main "launcher" activity.
+ * When running on a "large" or larger screen, this activity displays both the
+ * TitlesFragments and the Content Fragment. When on a smaller screen size, this
+ * activity displays only the TitlesFragment. In which case, selecting a list
+ * item opens the ContentActivity, holds only the ContentFragment. */
+public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
- private static final int NOTIFICATION_DEFAULT = 1;
- private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
-
- private View mActionBarView;
private Animator mCurrentTitlesAnimator;
private String[] mToggleLabels = {"Show Titles", "Hide Titles"};
- private int mLabelIndex = 1;
+ private static final int NOTIFICATION_DEFAULT = 1;
+ private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
private int mThemeId = -1;
+ private boolean mDualFragments = false;
+ private boolean mTitlesHidden = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
- mThemeId = savedInstanceState.getInt("theme");
- this.setTheme(mThemeId);
+ if(savedInstanceState != null) {
+ if (savedInstanceState.getInt("theme", -1) != -1) {
+ mThemeId = savedInstanceState.getInt("theme");
+ this.setTheme(mThemeId);
+ }
+ mTitlesHidden = savedInstanceState.getBoolean("titlesHidden");
}
setContentView(R.layout.main);
- Directory.initializeDirectory();
-
ActionBar bar = getActionBar();
+ bar.setDisplayShowTitleEnabled(false);
- int i;
- for (i = 0; i < Directory.getCategoryCount(); i++) {
- bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
- .setTabListener(this));
+ ContentFragment frag = (ContentFragment) getFragmentManager()
+ .findFragmentById(R.id.content_frag);
+ if (frag != null) mDualFragments = true;
+
+ if (mTitlesHidden) {
+ getFragmentManager().beginTransaction()
+ .hide(getFragmentManager().findFragmentById(R.id.titles_frag)).commit();
}
-
- mActionBarView = getLayoutInflater().inflate(
- R.layout.action_bar_custom, null);
-
- bar.setCustomView(mActionBarView);
- bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO);
- bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
- bar.setDisplayShowHomeEnabled(true);
-
- // If category is not saved to the savedInstanceState,
- // 0 is returned by default.
- if(savedInstanceState != null) {
- int category = savedInstanceState.getInt("category");
- bar.selectTab(bar.getTabAt(category));
- }
- }
-
- public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
- TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
- .findFragmentById(R.id.frag_title);
- titleFrag.populateTitles(tab.getPosition());
-
- titleFrag.selectPosition(0);
- }
-
- public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
- }
-
- public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
+ // If the device doesn't support camera, remove the camera menu item
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ menu.removeItem(R.id.menu_camera);
+ }
return true;
}
@Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // If not showing both fragments, remove the "toggle titles" menu item
+ if (!mDualFragments) {
+ menu.removeItem(R.id.menu_toggleTitles);
+ } else {
+ menu.findItem(R.id.menu_toggleTitles).setTitle(mToggleLabels[mTitlesHidden ? 0 : 1]);
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.camera:
- Intent intent = new Intent(this, CameraSample.class);
+ case R.id.menu_camera:
+ Intent intent = new Intent(this, CameraActivity.class);
intent.putExtra("theme", mThemeId);
startActivity(intent);
return true;
- case R.id.toggleTitles:
+ case R.id.menu_toggleTitles:
toggleVisibleTitles();
return true;
- case R.id.toggleTheme:
+ case R.id.menu_toggleTheme:
if (mThemeId == R.style.AppTheme_Dark) {
mThemeId = R.style.AppTheme_Light;
} else {
@@ -137,15 +132,15 @@
this.recreate();
return true;
- case R.id.showDialog:
+ case R.id.menu_showDialog:
showDialog("This is indeed an awesome dialog.");
return true;
- case R.id.showStandardNotification:
+ case R.id.menu_showStandardNotification:
showNotification(false);
return true;
- case R.id.showCustomNotification:
+ case R.id.menu_showCustomNotification:
showNotification(true);
return true;
@@ -154,13 +149,13 @@
}
}
+ /** Respond to the "toogle titles" item in the action bar */
public void toggleVisibleTitles() {
// Use these for custom animations.
final FragmentManager fm = getFragmentManager();
final TitlesFragment f = (TitlesFragment) fm
- .findFragmentById(R.id.frag_title);
+ .findFragmentById(R.id.titles_frag);
final View titlesView = f.getView();
- mLabelIndex = 1 - mLabelIndex;
// Determine if we're in portrait, and whether we're showing or hiding the titles
// with this toggle.
@@ -207,6 +202,8 @@
@Override
public void onAnimationEnd(Animator animator) {
mCurrentTitlesAnimator = null;
+ mTitlesHidden = false;
+ invalidateOptionsMenu();
}
});
@@ -226,6 +223,8 @@
return;
mCurrentTitlesAnimator = null;
fm.beginTransaction().hide(f).commit();
+ mTitlesHidden = true;
+ invalidateOptionsMenu();
}
});
}
@@ -234,8 +233,6 @@
objectAnimator.start();
mCurrentTitlesAnimator = objectAnimator;
- invalidateOptionsMenu();
-
// Manually trigger onNewIntent to check for ACTION_DIALOG.
onNewIntent(getIntent());
}
@@ -314,21 +311,35 @@
}
@Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.getItem(1).setTitle(mToggleLabels[mLabelIndex]);
- return true;
- }
-
- @Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
- ActionBar bar = getActionBar();
- int category = bar.getSelectedTab().getPosition();
- outState.putInt("category", category);
outState.putInt("theme", mThemeId);
+ outState.putBoolean("titlesHidden", mTitlesHidden);
+ }
+
+ /** Implementation for TitlesFragment.OnItemSelectedListener.
+ * When the TitlesFragment receives an onclick event for a list item,
+ * it's passed back to this activity through this method so that we can
+ * deliver it to the ContentFragment in the manner appropriate */
+ public void onItemSelected(int category, int position) {
+ if (!mDualFragments) {
+ // If showing only the TitlesFragment, start the ContentActivity and
+ // pass it the info about the selected item
+ Intent intent = new Intent(this, ContentActivity.class);
+ intent.putExtra("category", category);
+ intent.putExtra("position", position);
+ intent.putExtra("theme", mThemeId);
+ startActivity(intent);
+ } else {
+ // If showing both fragments, directly update the ContentFragment
+ ContentFragment frag = (ContentFragment) getFragmentManager()
+ .findFragmentById(R.id.content_frag);
+ frag.updateContentAndRecycleBitmap(category, position);
+ }
}
+ /** Dialog implementation that shows a simple dialog as a fragment */
public static class MyDialogFragment extends DialogFragment {
public static MyDialogFragment newInstance(String title) {
diff --git a/samples/HoneycombGallery/src/com/example/android/hcgallery/TitlesFragment.java b/samples/HoneycombGallery/src/com/example/android/hcgallery/TitlesFragment.java
index 85a7b68..2726679 100644
--- a/samples/HoneycombGallery/src/com/example/android/hcgallery/TitlesFragment.java
+++ b/samples/HoneycombGallery/src/com/example/android/hcgallery/TitlesFragment.java
@@ -16,62 +16,172 @@
package com.example.android.hcgallery;
-import android.app.Fragment;
-import android.app.FragmentManager;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.ClipData;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.Paint;
-import android.graphics.Typeface;
import android.os.Bundle;
-import android.text.TextPaint;
-import android.util.TypedValue;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
-import android.widget.ImageView;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.AdapterView.OnItemLongClickListener;
-public class TitlesFragment extends ListFragment {
+/**
+ * Fragment that shows the list of images
+ * As an extension of ListFragment, this fragment uses a default layout
+ * that includes a single ListView, which you can acquire with getListView()
+ * When running on a screen size smaller than "large", this fragment appears alone
+ * in MainActivity. In this case, selecting a list item opens the ContentActivity,
+ * which likewise holds only the ContentFragment.
+ */
+public class TitlesFragment extends ListFragment implements ActionBar.TabListener {
+ OnItemSelectedListener mListener;
private int mCategory = 0;
private int mCurPosition = 0;
+ private boolean mDualFragments = false;
+ /** Container Activity must implement this interface and we ensure
+ * that it does during the onAttach() callback
+ */
+ public interface OnItemSelectedListener {
+ public void onItemSelected(int category, int position);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ // Check that the container activity has implemented the callback interface
+ try {
+ mListener = (OnItemSelectedListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnItemSelectedListener");
+ }
+ }
+
+ /** This is where we perform setup for the fragment that's either
+ * not related to the fragment's layout or must be done after the layout is drawn.
+ * Notice that this fragment does not implement onCreateView(), because it extends
+ * ListFragment, which includes a ListView as the root view by default, so there's
+ * no need to set up the layout.
+ */
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ ContentFragment frag = (ContentFragment) getFragmentManager()
+ .findFragmentById(R.id.content_frag);
+ if (frag != null) mDualFragments = true;
+
+ ActionBar bar = getActivity().getActionBar();
+ bar.setDisplayHomeAsUpEnabled(false);
+ bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ // Must call in order to get callback to onCreateOptionsMenu()
+ setHasOptionsMenu(true);
+
+ Directory.initializeDirectory();
+ for (int i = 0; i < Directory.getCategoryCount(); i++) {
+ bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
+ .setTabListener(this));
+ }
+
//Current position should survive screen rotations.
if (savedInstanceState != null) {
mCategory = savedInstanceState.getInt("category");
mCurPosition = savedInstanceState.getInt("listPosition");
+ bar.selectTab(bar.getTabAt(mCategory));
}
populateTitles(mCategory);
ListView lv = getListView();
- lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- lv.setCacheColorHint(Color.TRANSPARENT);
- lv.setOnItemLongClickListener(new OnItemLongClickListener() {
- public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
- final String title = (String) ((TextView) v).getText();
+ lv.setCacheColorHint(Color.TRANSPARENT); // Improves scrolling performance
- // Set up clip data with the category||entry_id format.
- final String textData = String.format("%d||%d", mCategory, pos);
- ClipData data = ClipData.newPlainText(title, textData);
- v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
- return true;
- }
- });
+ if (mDualFragments) {
+ // Highlight the currently selected item
+ lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ // Enable drag and dropping
+ lv.setOnItemLongClickListener(new OnItemLongClickListener() {
+ public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
+ final String title = (String) ((TextView) v).getText();
- selectPosition(mCurPosition);
+ // Set up clip data with the category||entry_id format.
+ final String textData = String.format("%d||%d", mCategory, pos);
+ ClipData data = ClipData.newPlainText(title, textData);
+ v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
+ return true;
+ }
+ });
+ }
+
+ // If showing both fragments, select the appropriate list item by default
+ if (mDualFragments) selectPosition(mCurPosition);
+
+ // Attach a GlobalLayoutListener so that we get a callback when the layout
+ // has finished drawing. This is necessary so that we can apply top-margin
+ // to the ListView in order to dodge the ActionBar. Ordinarily, that's not
+ // necessary, but we've set the ActionBar to "overlay" mode using our theme,
+ // so the layout does not account for the action bar position on its own.
+ ViewTreeObserver observer = getListView().getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(layoutListener);
}
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ // Always detach ViewTreeObserver listeners when the view tears down
+ getListView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener);
+ }
+
+ /** Attaches an adapter to the fragment's ListView to populate it with items */
+ public void populateTitles(int category) {
+ DirectoryCategory cat = Directory.getCategory(category);
+ String[] items = new String[cat.getEntryCount()];
+ for (int i = 0; i < cat.getEntryCount(); i++)
+ items[i] = cat.getEntry(i).getName();
+ // Convenience method to attach an adapter to ListFragment's ListView
+ setListAdapter(new ArrayAdapter<String>(getActivity(),
+ R.layout.title_list_item, items));
+ mCategory = category;
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ // Send the event to the host activity via OnItemSelectedListener callback
+ mListener.onItemSelected(mCategory, position);
+ mCurPosition = position;
+ }
+
+ /** Called to select an item from the listview */
+ public void selectPosition(int position) {
+ // Only if we're showing both fragments should the item be "highlighted"
+ if (mDualFragments) {
+ ListView lv = getListView();
+ lv.setItemChecked(position, true);
+ }
+ // Calls the parent activity's implementation of the OnItemSelectedListener
+ // so the activity can pass the event to the sibling fragment as appropriate
+ mListener.onItemSelected(mCategory, position);
+ }
+
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("listPosition", mCurPosition);
+ outState.putInt("category", mCategory);
+ }
+
+ /** This defines how the draggable list items appear during a drag event */
private class MyDragShadowBuilder extends View.DragShadowBuilder {
private Drawable mShadow;
@@ -93,38 +203,53 @@
}
}
- public void populateTitles(int category) {
- DirectoryCategory cat = Directory.getCategory(category);
- String[] items = new String[cat.getEntryCount()];
- for (int i = 0; i < cat.getEntryCount(); i++)
- items[i] = cat.getEntry(i).getName();
- setListAdapter(new ArrayAdapter<String>(getActivity(),
- R.layout.title_list_item, items));
- mCategory = category;
+ // Because the fragment doesn't have a reliable callback to notify us when
+ // the activity's layout is completely drawn, this OnGlobalLayoutListener provides
+ // the necessary callback so we can add top-margin to the ListView in order to dodge
+ // the ActionBar. Which is necessary because the ActionBar is in overlay mode, meaning
+ // that it will ordinarily sit on top of the activity layout as a top layer and
+ // the ActionBar height can vary. Specifically, when on a small/normal size screen,
+ // the action bar tabs appear in a second row, making the action bar twice as tall.
+ ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ int barHeight = getActivity().getActionBar().getHeight();
+ ListView listView = getListView();
+ FrameLayout.LayoutParams params = (LayoutParams) listView.getLayoutParams();
+ // The list view top-margin should always match the action bar height
+ if (params.topMargin != barHeight) {
+ params.topMargin = barHeight;
+ listView.setLayoutParams(params);
+ }
+ // The action bar doesn't update its height when hidden, so make top-margin zero
+ if (!getActivity().getActionBar().isShowing()) {
+ params.topMargin = 0;
+ listView.setLayoutParams(params);
+ }
+ }
+ };
+
+
+ /* The following are callbacks implemented for the ActionBar.TabListener,
+ * which this fragment implements to handle events when tabs are selected.
+ */
+
+ public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
+ TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
+ .findFragmentById(R.id.titles_frag);
+ titleFrag.populateTitles(tab.getPosition());
+
+ if (mDualFragments) {
+ titleFrag.selectPosition(0);
+ }
}
- @Override
- public void onListItemClick(ListView l, View v, int position, long id) {
- updateImage(position);
+ /* These must be implemented, but we don't use them */
+
+ public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
- private void updateImage(int position) {
- ContentFragment frag = (ContentFragment) getFragmentManager()
- .findFragmentById(R.id.frag_content);
- frag.updateContentAndRecycleBitmap(mCategory, position);
- mCurPosition = position;
+ public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
- public void selectPosition(int position) {
- ListView lv = getListView();
- lv.setItemChecked(position, true);
- updateImage(position);
- }
-
- @Override
- public void onSaveInstanceState (Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt("listPosition", mCurPosition);
- outState.putInt("category", mCategory);
- }
}
diff --git a/samples/RenderScript/HelloCompute/AndroidManifest.xml b/samples/RenderScript/HelloCompute/AndroidManifest.xml
index a7baf0d..73e1110 100644
--- a/samples/RenderScript/HelloCompute/AndroidManifest.xml
+++ b/samples/RenderScript/HelloCompute/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.rs.hellocompute">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-sdk android:minSdkVersion="11" />
+ <uses-sdk android:minSdkVersion="14" />
<application android:label="RsHelloCompute">
<activity android:name="HelloCompute">
<intent-filter>
diff --git a/samples/SampleSpellCheckerService/Android.mk b/samples/SampleSpellCheckerService/Android.mk
new file mode 100755
index 0000000..adf65d9
--- /dev/null
+++ b/samples/SampleSpellCheckerService/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := SampleSpellChecker
+
+include $(BUILD_PACKAGE)
diff --git a/samples/SampleSpellCheckerService/AndroidManifest.xml b/samples/SampleSpellCheckerService/AndroidManifest.xml
new file mode 100644
index 0000000..fcb1671
--- /dev/null
+++ b/samples/SampleSpellCheckerService/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.samplespellcheckerservice" >
+
+ <uses-sdk android:minSdkVersion="14"/>
+ <application
+ android:label="@string/app_name" >
+ <service
+ android:label="@string/app_name"
+ android:name=".SampleSpellCheckerService"
+ android:permission="android.permission.BIND_TEXT_SERVICE" >
+ <intent-filter >
+ <action android:name="android.service.textservice.SpellCheckerService" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.view.textservice.scs"
+ android:resource="@xml/spellchecker" />
+ </service>
+
+ <activity
+ android:label="@string/sample_settings"
+ android:name="SpellCheckerSettingsActivity" >
+ <intent-filter >
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/samples/SampleSpellCheckerService/_index.html b/samples/SampleSpellCheckerService/_index.html
new file mode 100755
index 0000000..d6dada9
--- /dev/null
+++ b/samples/SampleSpellCheckerService/_index.html
@@ -0,0 +1,5 @@
+<p>A sample showing how to create a spell checker with the <code><a
+href="../../../reference/android/service/textservice/SpellCheckerService.html">SpellCheckerService</a></code>
+APIs introduced in Android 4.0 (API level 14).</p>
+<p>This app does not have a launcher activity. Once you install it, go to the system's input settings and enable
+"Sample correction" as the spelling correction service. </p>
diff --git a/samples/SampleSpellCheckerService/res/values/strings.xml b/samples/SampleSpellCheckerService/res/values/strings.xml
new file mode 100644
index 0000000..79da5c6
--- /dev/null
+++ b/samples/SampleSpellCheckerService/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <string name="app_name">SampleSpellCheckerService</string>
+ <string name="spellchecker_name">Sample correction</string>
+ <string name="subtype_generic" translatable="false">%s</string>
+ <string name="sample_settings">Sample settings</string>
+ <string name="sample_preference">Sample preference</string>
+</resources>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/values/styles.xml b/samples/SampleSpellCheckerService/res/xml/spell_checker_settings.xml
similarity index 61%
copy from samples/SampleSyncAdapter/res/values/styles.xml
copy to samples/SampleSpellCheckerService/res/xml/spell_checker_settings.xml
index 074613e..e04d50e 100644
--- a/samples/SampleSyncAdapter/res/values/styles.xml
+++ b/samples/SampleSpellCheckerService/res/xml/spell_checker_settings.xml
@@ -14,14 +14,11 @@
limitations under the License.
-->
-<resources>
- <!--
- These styles will only be used in Honeycomb and later because
- Android doesn't support third-party contact editing in pre-
- Honeycomb versions.
- -->
- <color name="EditPanelBackgroundColor">#ffffff</color>
- <color name="EditPanelBorderColor">#cccccc</color>
- <style name="ContactEditTheme" parent="android:Theme.Holo.Light">
- </style>
-</resources>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/sample_settings">
+ <CheckBoxPreference
+ android:title="@string/sample_preference"
+ android:persistent="true"
+ android:defaultValue="true" />
+</PreferenceScreen>
diff --git a/samples/SampleSpellCheckerService/res/xml/spellchecker.xml b/samples/SampleSpellCheckerService/res/xml/spellchecker.xml
new file mode 100644
index 0000000..f4601d2
--- /dev/null
+++ b/samples/SampleSpellCheckerService/res/xml/spellchecker.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide the configuration information -->
+<!-- for the spell checker -->
+
+<spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/spellchecker_name"
+ android:settingsActivity="com.example.android.samplespellcheckerservice.SpellCheckerSettingsActivity">
+ <subtype
+ android:label="@string/subtype_generic"
+ android:subtypeLocale="en"
+ />
+</spell-checker>
diff --git a/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java
new file mode 100644
index 0000000..dc85587
--- /dev/null
+++ b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java
@@ -0,0 +1,55 @@
+/*
+ * 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.samplespellcheckerservice;
+
+import android.service.textservice.SpellCheckerService;
+import android.util.Log;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+public class SampleSpellCheckerService extends SpellCheckerService {
+ private static final String TAG = SampleSpellCheckerService.class.getSimpleName();
+ private static final boolean DBG = true;
+ @Override
+ public Session createSession() {
+ return new AndroidSpellCheckerSession();
+ }
+
+ private static class AndroidSpellCheckerSession extends Session {
+ private String mLocale;
+ @Override
+ public void onCreate() {
+ mLocale = getLocale();
+ }
+
+ @Override
+ public SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit) {
+ if (DBG) {
+ Log.d(TAG, "onGetSuggestions: " + textInfo.getText());
+ }
+ final String input = textInfo.getText();
+ final int length = input.length();
+ // Just a fake logic:
+ // length <= 3 for short words that we assume are in the fake dictionary
+ // length > 20 for too long words that we assume can't be recognized (such as CJK words)
+ final int flags = length <= 3 ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+ : length <= 20 ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0;
+ return new SuggestionsInfo(flags,
+ new String[] {"aaa", "bbb", "Candidate for " + input, mLocale});
+ }
+ }
+}
diff --git a/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsActivity.java b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsActivity.java
new file mode 100644
index 0000000..f223ef7
--- /dev/null
+++ b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsActivity.java
@@ -0,0 +1,39 @@
+/**
+ * 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.samplespellcheckerservice;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Spell checker preference screen.
+ */
+public class SpellCheckerSettingsActivity extends PreferenceActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public Intent getIntent() {
+ final Intent modIntent = new Intent(super.getIntent());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SpellCheckerSettingsFragment.class.getName());
+ modIntent.putExtra(EXTRA_NO_HEADERS, true);
+ return modIntent;
+ }
+}
diff --git a/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsFragment.java b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsFragment.java
new file mode 100644
index 0000000..cd2efcc
--- /dev/null
+++ b/samples/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SpellCheckerSettingsFragment.java
@@ -0,0 +1,38 @@
+/**
+ * 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.samplespellcheckerservice;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+/**
+ * Preference screen.
+ */
+public class SpellCheckerSettingsFragment extends PreferenceFragment {
+
+ /**
+ * Empty constructor for fragment generation.
+ */
+ public SpellCheckerSettingsFragment() {
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ addPreferencesFromResource(R.xml.spell_checker_settings);
+ }
+}
diff --git a/samples/SampleSyncAdapter/Android.mk b/samples/SampleSyncAdapter/Android.mk
index 0f87c17..eec507b 100644
--- a/samples/SampleSyncAdapter/Android.mk
+++ b/samples/SampleSyncAdapter/Android.mk
@@ -10,8 +10,6 @@
LOCAL_SDK_VERSION := current
-LOCAL_DX_FLAGS=--target-api=11
-
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
diff --git a/samples/SampleSyncAdapter/AndroidManifest.xml b/samples/SampleSyncAdapter/AndroidManifest.xml
index 25e9e99..285abfb 100644
--- a/samples/SampleSyncAdapter/AndroidManifest.xml
+++ b/samples/SampleSyncAdapter/AndroidManifest.xml
@@ -46,7 +46,7 @@
<uses-permission
android:name="android.permission.WRITE_SYNC_SETTINGS" />
- <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11"/>
+ <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14"/>
<application
android:icon="@drawable/icon"
@@ -100,27 +100,8 @@
</activity>
<activity
- android:name=".editor.ContactEditorActivity"
- android:theme="@style/ContactEditTheme"
- android:windowSoftInputMode="adjustResize">
- <intent-filter>
- <action
- android:name="android.intent.action.INSERT" />
- <data
- android:mimeType="vnd.android.cursor.item/contact" />
- </intent-filter>
-
- <!--
- Note that the editor gets a raw contact URI, but is expected to call
- setResult with the corresponding aggregate contact URI, not raw contact
- URI.
- -->
- <intent-filter>
- <action
- android:name="android.intent.action.EDIT" />
- <data
- android:mimeType="vnd.android.cursor.item/raw_contact" />
- </intent-filter>
+ android:name=".activites.InviteContactActivity"
+ android:theme="@android:style/Theme.Dialog">
<!--
We use the INVITE intent to add a raw contact to an existing contact.
It always comes with a lookup URI.
@@ -132,5 +113,50 @@
android:mimeType="vnd.android.cursor.item/contact" />
</intent-filter>
</activity>
+
+ <activity
+ android:name=".activities.ViewGroupActivity"
+ android:theme="@android:style/Theme.Dialog">
+ <!--
+ We use the VIEW intent to view a group in our app.
+ It always comes with a lookup URI.
+ -->
+ <intent-filter>
+ <action
+ android:name="android.intent.action.VIEW" />
+ <data
+ android:mimeType="vnd.android.cursor.item/group" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".activities.ViewStreamItemActivity"
+ android:theme="@android:style/Theme.Dialog">
+ <!--
+ We use the VIEW intent to view a stream item in our app.
+ It always comes with a lookup URI.
+ -->
+ <intent-filter>
+ <action
+ android:name="android.intent.action.VIEW" />
+ <data
+ android:mimeType="vnd.android.cursor.item/stream_item" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".activities.ViewStreamItemPhotoActivity"
+ android:theme="@android:style/Theme.Dialog">
+ <!--
+ We use the VIEW intent to view a stream item photo in our app.
+ It always comes with a lookup URI.
+ -->
+ <intent-filter>
+ <action
+ android:name="android.intent.action.VIEW" />
+ <data
+ android:mimeType="vnd.android.cursor.item/stream_item_photo" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/samples/SampleSyncAdapter/res/drawable/border.xml b/samples/SampleSyncAdapter/res/drawable/border.xml
deleted file mode 100644
index ab71f2c..0000000
--- a/samples/SampleSyncAdapter/res/drawable/border.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/EditPanelBackgroundColor" />
- <stroke android:width="2dip" android:color="@color/EditPanelBorderColor" />
- <padding android:left="5dip" android:top="5dip" android:right="5dip" android:bottom="5dip" />
-</shape>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png b/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png
deleted file mode 100644
index 3468bbd..0000000
--- a/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png
+++ /dev/null
Binary files differ
diff --git a/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml b/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml
deleted file mode 100644
index 3b7d97b..0000000
--- a/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
-/**
- * Copyright (c) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal">
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="600dip"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@drawable/border">
- <include layout="@layout/editor_header" />
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:paddingTop="20dip"
- android:paddingRight="20dip"
- android:paddingBottom="20dip"
- android:paddingLeft="20dip"
- android:layout_weight="1">
- <include layout="@layout/editor_fields" />
- </ScrollView>
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml b/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml
deleted file mode 100644
index 648e6f9..0000000
--- a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-
-<!-- Account info header -->
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="64dip"
- android:layout_width="match_parent"
- android:background="?android:attr/selectableItemBackground">
-
- <ImageView
- android:id="@+id/header_account_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="7dip"
- android:layout_marginRight="7dip"
- android:layout_centerVertical="true"
- android:layout_alignParentRight="true"
- android:src="@drawable/icon" />
-
- <TextView
- android:id="@+id/header_account_type"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@+id/header_account_icon"
- android:layout_alignTop="@id/header_account_icon"
- android:layout_marginTop="-4dip"
- android:textSize="24sp"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:text="@string/header_account_type" />
-
- <TextView
- android:id="@+id/header_account_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@+id/header_account_icon"
- android:layout_alignBottom="@+id/header_account_icon"
- android:layout_marginBottom="2dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true" />
-
-</RelativeLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/editor_fields.xml b/samples/SampleSyncAdapter/res/layout/editor_fields.xml
deleted file mode 100644
index 31d0128..0000000
--- a/samples/SampleSyncAdapter/res/layout/editor_fields.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
-/**
- * Copyright (c) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:stretchColumns="2">
-
- <TableRow>
- <TextView
- android:layout_column="1"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_name"
- android:padding="3dip" />
- <EditText
- android:layout_column="2"
- android:id="@+id/editor_name"
- android:singleLine="true"
- android:inputType="textPersonName"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minWidth="250dip"
- android:scrollHorizontally="true"
- android:capitalize="none"
- android:textSize="@dimen/contact_name_text_size"
- android:gravity="fill_horizontal"
- android:autoText="false" />
- </TableRow>
- <TableRow>
- <TextView
- android:layout_column="1"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_phone_home"
- android:padding="3dip" />
- <EditText
- android:id="@+id/editor_phone_home"
- android:singleLine="true"
- android:inputType="phone"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minWidth="250dip"
- android:scrollHorizontally="true"
- android:capitalize="none"
- android:gravity="fill_horizontal"
- android:autoText="false" />
- </TableRow>
- <TableRow>
- <TextView
- android:layout_column="1"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_phone_mobile"
- android:padding="3dip" />
- <EditText
- android:id="@+id/editor_phone_mobile"
- android:singleLine="true"
- android:inputType="phone"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minWidth="250dip"
- android:scrollHorizontally="true"
- android:capitalize="none"
- android:gravity="fill_horizontal"
- android:autoText="false" />
- </TableRow>
- <TableRow>
- <TextView
- android:layout_column="1"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_phone_work"
- android:padding="3dip" />
- <EditText
- android:id="@+id/editor_phone_work"
- android:singleLine="true"
- android:phoneNumber="true"
- android:autoText="true"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minWidth="250dip"
- android:scrollHorizontally="true"
- android:capitalize="none"
- android:gravity="fill_horizontal" />
- </TableRow>
- <TableRow>
- <TextView
- android:layout_column="1"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_email"
- android:padding="3dip" />
- <EditText
- android:id="@+id/editor_email"
- android:singleLine="true"
- android:inputType="textEmailAddress"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minWidth="250dip"
- android:scrollHorizontally="true"
- android:capitalize="none"
- android:gravity="fill_horizontal"
- android:autoText="false" />
- </TableRow>
-</TableLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/editor.xml b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
similarity index 61%
rename from samples/SampleSyncAdapter/res/layout/editor.xml
rename to samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
index a0c36d2..1e09d5b 100644
--- a/samples/SampleSyncAdapter/res/layout/editor.xml
+++ b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
@@ -18,19 +18,15 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:paddingTop="20dip"
- android:paddingRight="20dip"
- android:paddingBottom="20dip"
- android:paddingLeft="20dip"
- android:layout_weight="1">
- <include layout="@layout/editor_fields" />
- </ScrollView>
-
-</LinearLayout>
\ No newline at end of file
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:text="@string/invite_contact_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/invite_contact_uri"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/login_activity.xml b/samples/SampleSyncAdapter/res/layout/login_activity.xml
index 7408ffe..6c93f90 100644
--- a/samples/SampleSyncAdapter/res/layout/login_activity.xml
+++ b/samples/SampleSyncAdapter/res/layout/login_activity.xml
@@ -55,7 +55,8 @@
android:scrollHorizontally="true"
android:capitalize="none"
android:autoText="false"
- android:inputType="textEmailAddress" />
+ android:inputType="textEmailAddress"
+ android:text="user" />
<TextView
android:id="@+id/username_fixed"
android:textAppearance="?android:attr/textAppearanceSmall"
@@ -81,7 +82,8 @@
android:capitalize="none"
android:autoText="false"
android:password="true"
- android:inputType="textPassword" />
+ android:inputType="textPassword"
+ android:text="test" />
<TextView
android:id="@+id/message_bottom"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/samples/SampleSyncAdapter/res/layout/view_group_activity.xml b/samples/SampleSyncAdapter/res/layout/view_group_activity.xml
new file mode 100644
index 0000000..45212d9
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_group_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+/**
+ * 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.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:text="@string/view_group_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/view_group_uri"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml
new file mode 100644
index 0000000..a04d07f
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+/**
+ * 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.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:text="@string/view_stream_item_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/view_stream_item_uri"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml
new file mode 100644
index 0000000..ddc09d0
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+/**
+ * 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.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:text="@string/view_stream_item_photo_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/view_stream_item_photo_uri"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/menu/edit.xml b/samples/SampleSyncAdapter/res/menu/edit.xml
deleted file mode 100644
index 1227584..0000000
--- a/samples/SampleSyncAdapter/res/menu/edit.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/menu_done"
- android:alphabeticShortcut="\n"
- android:icon="@drawable/done_menu_icon"
- android:title="@string/menu_done"
- android:showAsAction="always|withText" />
-
- <item
- android:id="@+id/menu_cancel"
- android:alphabeticShortcut="q"
- android:title="@string/menu_cancel"
- android:showAsAction="always|withText" />
-</menu>
diff --git a/samples/SampleSyncAdapter/res/values/strings.xml b/samples/SampleSyncAdapter/res/values/strings.xml
index 7ac95f5..22fe14e 100644
--- a/samples/SampleSyncAdapter/res/values/strings.xml
+++ b/samples/SampleSyncAdapter/res/values/strings.xml
@@ -109,4 +109,34 @@
<!-- The label of the button to add contact to this contact provider -->
<string name="invite_action_label">Add to Sample SyncAdaper</string>
+ <!-- The description for the invite contact flow -->
+ <string name="invite_contact_description">Congratulations! The user wants to add the contact
+ to the amazing Sample SyncAdapter social network. If this was a real app, it should now
+ make best efforts to add the contact to this network. This would probably involve
+ looking up the person on the network, inviting if he is not there already and syncing
+ the new contact down.
+
+ Ideally, when the user gets back to the People app, the new contact should already
+ be there, enriching the original contact.
+
+ This is the information we got to lookup the contact:</string>
+
+ <!-- The label of the button to view a group -->
+ <string name="view_group_action_label">Show sample group details</string>
+
+ <!-- The description for the view group button -->
+ <string name="view_group_description">This would now show the details of the group.
+
+ This is the group uri:</string>
+
+ <!-- The description for the view stream item -->
+ <string name="view_stream_item_description">This would now show the details of the stream item.
+
+ This is the uri of the stream item:</string>
+
+ <!-- The description for the view stream item photo -->
+ <string name="view_stream_item_photo_description">This would now show the details of the stream item photo.
+
+ This is the uri of the photo:</string>
+
</resources>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
index 62dfa80..48cf503 100644
--- a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
@@ -19,8 +19,6 @@
<ContactsAccountType
xmlns:android="http://schemas.android.com/apk/res/android"
- editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
- createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
>
<ContactsDataKind
diff --git a/samples/SampleSyncAdapter/res/xml-v14/contacts.xml b/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
index 5517729..48e079a 100644
--- a/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
@@ -19,11 +19,13 @@
<ContactsAccountType
xmlns:android="http://schemas.android.com/apk/res/android"
- editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
- createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
- inviteContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
+ inviteContactActivity="com.example.android.samplesync.activities.InviteContactActivity"
inviteContactActionLabel="@string/invite_action_label"
viewContactNotifyService="com.example.android.samplesync.notifier.NotifierService"
+ viewGroupActivity="com.example.android.samplesync.activities.ViewGroupActivity"
+ viewGroupActionLabel="@string/view_group_action_label"
+ viewStreamItemActivity="com.example.android.samplesync.activities.ViewStreamItemActivity"
+ viewStreamItemPhotoActivity="com.example.android.samplesync.activities.ViewStreamItemPhotoActivity"
>
<ContactsDataKind
diff --git a/samples/SampleSyncAdapter/res/xml/contacts.xml b/samples/SampleSyncAdapter/res/xml/contacts.xml
index b46257d..06ecf6a 100644
--- a/samples/SampleSyncAdapter/res/xml/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml/contacts.xml
@@ -19,8 +19,6 @@
<ContactsSource
xmlns:android="http://schemas.android.com/apk/res/android"
- editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
- createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
>
<ContactsDataKind
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java
new file mode 100644
index 0000000..1923fc2
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle the invite-intent. In a real app, this would look up the user on the network
+ * and either connect ("add as friend", "follow") or invite them to the network
+ */
+public class InviteContactActivity extends Activity {
+ private static final String TAG = "InviteContactActivity";
+
+ private TextView mUriTextView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.invite_contact_activity);
+
+ mUriTextView = (TextView) findViewById(R.id.invite_contact_uri);
+ mUriTextView.setText(getIntent().getDataString());
+ }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java
new file mode 100644
index 0000000..1b32784
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle the view-group action. In a real app, this would show a rich view of the
+ * group, like members, updates etc.
+ */
+public class ViewGroupActivity extends Activity {
+ private static final String TAG = "ViewGroupActivity";
+
+ private TextView mUriTextView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_group_activity);
+
+ mUriTextView = (TextView) findViewById(R.id.view_group_uri);
+ mUriTextView.setText(getIntent().getDataString());
+ }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java
new file mode 100644
index 0000000..6d54f31
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle view a stream-item. In a real app, this would show a rich view of the
+ * item.
+ */
+public class ViewStreamItemActivity extends Activity {
+ private static final String TAG = "ViewStreamItemActivity";
+
+ private TextView mUriTextView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_stream_item_activity);
+
+ mUriTextView = (TextView) findViewById(R.id.view_stream_item_uri);
+ mUriTextView.setText(getIntent().getDataString());
+ }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java
new file mode 100644
index 0000000..962bc70
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to view a stream-item-photo. In a real app, this would show a fullscreen view of the
+ * photo, potentially with ways to interact with it
+ */
+public class ViewStreamItemPhotoActivity extends Activity {
+ private static final String TAG = "ViewStreamItemPhotoActivity";
+
+ private TextView mUriTextView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_stream_item_photo_activity);
+
+ mUriTextView = (TextView) findViewById(R.id.view_stream_item_photo_uri);
+ mUriTextView.setText(getIntent().getDataString());
+ }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
index 2a3c0fc..5649f6d 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
@@ -110,7 +110,7 @@
mMessage = (TextView) findViewById(R.id.message);
mUsernameEdit = (EditText) findViewById(R.id.username_edit);
mPasswordEdit = (EditText) findViewById(R.id.password_edit);
- mUsernameEdit.setText(mUsername);
+ if (!TextUtils.isEmpty(mUsername)) mUsernameEdit.setText(mUsername);
mMessage.setText(getMessage());
}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
index bebcd72..c2ebd19 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
@@ -16,16 +16,6 @@
package com.example.android.samplesync.client;
-import android.accounts.Account;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.example.android.samplesync.authenticator.AuthenticatorActivity;
-
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -41,9 +31,15 @@
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
-import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.accounts.Account;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+import android.util.Log;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
@@ -54,11 +50,8 @@
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.TimeZone;
/**
* Provides utility methods for communicating with the server.
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java
deleted file mode 100644
index efda0cc..0000000
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.example.android.samplesync.editor;
-
-import com.example.android.samplesync.Constants;
-import com.example.android.samplesync.R;
-import com.example.android.samplesync.client.RawContact;
-import com.example.android.samplesync.platform.BatchOperation;
-import com.example.android.samplesync.platform.ContactManager;
-import com.example.android.samplesync.platform.ContactManager.ContactQuery;
-import com.example.android.samplesync.platform.ContactManager.EditorQuery;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * Implements a sample editor for a contact that belongs to a remote contact service.
- * The editor can be invoked for an existing SampleSyncAdapter contact, or it can
- * be used to create a brand new SampleSyncAdapter contact. We look at the Intent
- * object to figure out whether this is a "new" or "edit" operation.
- */
-public class ContactEditorActivity extends Activity {
- private static final String TAG = "SampleSyncAdapter";
-
- // Keep track of whether we're inserting a new contact or editing an
- // existing contact.
- private boolean mIsInsert;
-
- // The name of the external account we're syncing this contact to.
- private String mAccountName;
-
- // For existing contacts, this is the URI to the contact data.
- private Uri mRawContactUri;
-
- // The raw clientId for this contact
- private long mRawContactId;
-
- // Make sure we only attempt to save the contact once if the
- // user presses the "done" button multiple times...
- private boolean mSaveInProgress = false;
-
- // Keep track of the controls used to edit contact values, so we can get/set
- // those values easily.
- private EditText mNameEditText;
- private EditText mHomePhoneEditText;
- private EditText mMobilePhoneEditText;
- private EditText mWorkPhoneEditText;
- private EditText mEmailEditText;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.editor);
-
- mNameEditText = (EditText)findViewById(R.id.editor_name);
- mHomePhoneEditText = (EditText)findViewById(R.id.editor_phone_home);
- mMobilePhoneEditText = (EditText)findViewById(R.id.editor_phone_mobile);
- mWorkPhoneEditText = (EditText)findViewById(R.id.editor_phone_work);
- mEmailEditText = (EditText)findViewById(R.id.editor_email);
-
- // Figure out whether we're creating a new contact (ACTION_INSERT), editing
- // an existing contact, or adding a new one to existing contact (INVITE_CONTACT).
- Intent intent = getIntent();
- String action = intent.getAction();
- if (Intent.ACTION_INSERT.equals(action)) {
- // We're inserting a new contact, so save off the external account name
- // which should have been added to the intent we were passed.
- mIsInsert = true;
- String accountName = intent.getStringExtra(RawContacts.ACCOUNT_NAME);
- if (accountName == null) {
- Log.e(TAG, "Account name is required");
- finish();
- }
- setAccountName(accountName);
- } else if (ContactsContract.Intents.INVITE_CONTACT.equals(action)) {
- // Adding to an existing contact.
- mIsInsert = true;
- // Use the first account found.
- Account[] myAccounts = AccountManager.get(this).getAccountsByType(
- Constants.ACCOUNT_TYPE);
- if (myAccounts.length == 0) {
- Log.e(TAG, "Account not configured");
- finish();
- }
- setAccountName(myAccounts[0].name);
-
- Uri lookupUri = intent.getData();
- if (lookupUri == null) {
- Log.e(TAG, "Contact lookup URI is required");
- finish();
- }
- startLoadContactEntity(lookupUri);
- } else {
- // We're editing an existing contact. Load in the data from the contact
- // so that the user can edit it.
- mIsInsert = false;
- mRawContactUri = intent.getData();
- if (mRawContactUri == null) {
- Log.e(TAG, "Raw contact URI is required");
- finish();
- }
- startLoadRawContactEntity();
- }
- }
-
- @Override
- public void onBackPressed() {
- // This method will have been called if the user presses the "Back" button
- // in the ActionBar. We treat that the same way as the "Done" button in
- // the ActionBar.
- save();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // This method gets called so that we can place items in the main Options menu -
- // for example, the ActionBar items. We add our menus from the res/menu/edit.xml
- // file.
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.edit, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- case R.id.menu_done:
- // The user pressed the "Home" button or our "Done" button - both
- // in the ActionBar. In both cases, we want to save the contact
- // and exit.
- save();
- return true;
- case R.id.menu_cancel:
- // The user pressed the Cancel menu item in the ActionBar.
- // Close the editor without saving any changes.
- finish();
- return true;
- }
- return false;
- }
-
- /**
- * Create an AsyncTask to load the contact from the Contacts data provider
- */
- private void startLoadRawContactEntity() {
- Uri uri = Uri.withAppendedPath(mRawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
- new LoadRawContactTask().execute(uri);
- }
-
- /**
- * Called by the LoadRawContactTask when the contact information has been
- * successfully loaded from the Contacts data provider.
- */
- public void onRawContactEntityLoaded(Cursor cursor) {
- if (cursor.moveToFirst()) {
- String mimetype = cursor.getString(EditorQuery.COLUMN_MIMETYPE);
- if (StructuredName.CONTENT_ITEM_TYPE.equals(mimetype)) {
- setAccountName(cursor.getString(EditorQuery.COLUMN_ACCOUNT_NAME));
- mRawContactId = cursor.getLong(EditorQuery.COLUMN_RAW_CONTACT_ID);
- mNameEditText.setText(cursor.getString(EditorQuery.COLUMN_FULL_NAME));
- } else if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
- final int type = cursor.getInt(EditorQuery.COLUMN_PHONE_TYPE);
- if (type == Phone.TYPE_HOME) {
- mHomePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
- } else if (type == Phone.TYPE_MOBILE) {
- mMobilePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
- } else if (type == Phone.TYPE_WORK) {
- mWorkPhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
- }
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
- mEmailEditText.setText(cursor.getString(EditorQuery.COLUMN_DATA1));
- }
- }
- }
-
- /**
- * Create an AsyncTask to load the contact from the Contacts data provider
- */
- private void startLoadContactEntity(Uri lookupUri) {
- new LoadContactTask().execute(lookupUri);
- }
-
- /**
- * Called by the LoadContactTask when the contact information has been
- * successfully loaded from the Contacts data provider.
- */
- public void onContactEntityLoaded(Cursor cursor) {
- if (cursor.moveToFirst()) {
- mNameEditText.setText(cursor.getString(ContactQuery.COLUMN_DISPLAY_NAME));
- }
- }
-
- /**
- * Save the updated contact data. We actually take two different actions
- * depending on whether we are creating a new contact or editing an
- * existing contact.
- */
- public void save() {
- // If we're already saving this contact, don't kick-off yet
- // another save - the user probably just pressed the "Done"
- // button multiple times...
- if (mSaveInProgress) {
- return;
- }
-
- mSaveInProgress = true;
- if (mIsInsert) {
- saveNewContact();
- } else {
- saveChanges();
- }
- }
-
- /**
- * Save off the external contacts provider account name. We show the account name
- * in the header section of the edit panel, and we also need it later when we
- * save off a brand new contact.
- */
- private void setAccountName(String accountName) {
- mAccountName = accountName;
- Log.i(TAG, "account=" + mAccountName);
- if (accountName != null) {
- TextView accountNameLabel = (TextView)findViewById(R.id.header_account_name);
- if (accountNameLabel != null) {
- accountNameLabel.setText(accountName);
- }
- }
- }
-
- /**
- * Save a new contact using the Contacts content provider. The actual insertion
- * is performed in an AsyncTask.
- */
- @SuppressWarnings("unchecked")
- private void saveNewContact() {
- new InsertContactTask().execute(buildRawContact());
- }
-
- /**
- * Save changes to an existing contact. The actual update is performed in
- * an AsyncTask.
- */
- @SuppressWarnings("unchecked")
- private void saveChanges() {
- new UpdateContactTask().execute(buildRawContact());
- }
-
- /**
- * Build a RawContact object from the data in the user-editable form
- * @return a new RawContact object representing the edited user
- */
- private RawContact buildRawContact() {
- return RawContact.create(mNameEditText.getText().toString(),
- null,
- null,
- mMobilePhoneEditText.getText().toString(),
- mWorkPhoneEditText.getText().toString(),
- mHomePhoneEditText.getText().toString(),
- mEmailEditText.getText().toString(),
- null,
- false,
- mRawContactId,
- -1);
- }
-
- /**
- * Called after a contact is saved - both for edited contacts and new contacts.
- * We set the final result of the activity to be "ok", and then close the activity
- * by calling finish().
- */
- public void onContactSaved(Uri result) {
- if (result != null) {
- Intent intent = new Intent();
- intent.setData(result);
- setResult(RESULT_OK, intent);
- finish();
- }
- mSaveInProgress = false;
- }
-
- /**
- * Represents an asynchronous task used to load a contact from
- * the Contacts content provider.
- *
- */
- public class LoadRawContactTask extends AsyncTask<Uri, Void, Cursor> {
-
- @Override
- protected Cursor doInBackground(Uri... params) {
- // Our background task is to load the contact from the Contacts provider
- return getContentResolver().query(params[0], EditorQuery.PROJECTION, null, null, null);
- }
-
- @Override
- protected void onPostExecute(Cursor cursor) {
- if (cursor == null) return;
- // After we've successfully loaded the contact, call back into
- // the ContactEditorActivity so we can update the UI
- try {
- onRawContactEntityLoaded(cursor);
- } finally {
- cursor.close();
- }
- }
- }
-
- /**
- * Represents an asynchronous task used to save a new contact
- * into the contacts database.
- */
- public class InsertContactTask extends AsyncTask<RawContact, Void, Uri> {
-
- @Override
- protected Uri doInBackground(RawContact... params) {
- try {
- final RawContact rawContact = params[0];
- final Context context = getApplicationContext();
- final ContentResolver resolver = getContentResolver();
- final BatchOperation batchOperation = new BatchOperation(context, resolver);
- ContactManager.addContact(context, mAccountName, rawContact, false, batchOperation);
- Uri rawContactUri = batchOperation.execute();
-
- // Convert the raw contact URI to a contact URI
- if (rawContactUri != null) {
- return RawContacts.getContactLookupUri(resolver, rawContactUri);
- } else {
- Log.e(TAG, "Could not save new contact");
- return null;
- }
- } catch (Exception e) {
- Log.e(TAG, "An error occurred while saving new contact", e);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Uri result) {
- // Tell the UI that the contact has been successfully saved
- onContactSaved(result);
- }
- }
-
-
- /**
- * Represents an asynchronous task used to save an updated contact
- * into the contacts database.
- */
- public class UpdateContactTask extends AsyncTask<RawContact, Void, Uri> {
-
- @Override
- protected Uri doInBackground(RawContact... params) {
- try {
- final RawContact rawContact = params[0];
- final Context context = getApplicationContext();
- final ContentResolver resolver = getContentResolver();
- final BatchOperation batchOperation = new BatchOperation(context, resolver);
- ContactManager.updateContact(context, resolver, rawContact, false, false, false,
- false, rawContact.getRawContactId(), batchOperation);
- batchOperation.execute();
-
- // Convert the raw contact URI to a contact URI
- return RawContacts.getContactLookupUri(resolver, mRawContactUri);
- } catch (Exception e) {
- Log.e(TAG, "Could not save changes", e);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Uri result) {
- // Tell the UI that the contact has been successfully saved
- onContactSaved(result);
- }
- }
-
- /**
- * Loads contact information by a lookup URI.
- */
- public class LoadContactTask extends AsyncTask<Uri, Void, Cursor> {
-
- @Override
- protected Cursor doInBackground(Uri... params) {
- return getContentResolver().query(params[0], ContactQuery.PROJECTION, null, null, null);
- }
-
- @Override
- protected void onPostExecute(Cursor cursor) {
- if (cursor == null) return;
- try {
- onContactEntityLoaded(cursor);
- } finally {
- cursor.close();
- }
- }
- }
-}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
index 035c976..6b2dfb1 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
@@ -34,6 +34,7 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
@@ -54,6 +55,41 @@
private static final String TAG = "ContactManager";
+ public static final String SAMPLE_GROUP_NAME = "Sample Group";
+
+ public static long ensureSampleGroupExists(Context context, Account account) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ // Lookup the sample group
+ long groupId = 0;
+ final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
+ Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " +
+ Groups.TITLE + "=?",
+ new String[] { account.name, account.type, SAMPLE_GROUP_NAME }, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ groupId = cursor.getLong(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ if (groupId == 0) {
+ // Sample group doesn't exist yet, so create it
+ final ContentValues contentValues = new ContentValues();
+ contentValues.put(Groups.ACCOUNT_NAME, account.name);
+ contentValues.put(Groups.ACCOUNT_TYPE, account.type);
+ contentValues.put(Groups.TITLE, SAMPLE_GROUP_NAME);
+ contentValues.put(Groups.GROUP_IS_READ_ONLY, true);
+
+ final Uri newGroupUri = resolver.insert(Groups.CONTENT_URI, contentValues);
+ groupId = ContentUris.parseId(newGroupUri);
+ }
+ return groupId;
+ }
+
/**
* Take a list of updated contacts and apply those changes to the
* contacts database. Typically this list of contacts would have been
@@ -67,7 +103,7 @@
* sync request.
*/
public static synchronized long updateContacts(Context context, String account,
- List<RawContact> rawContacts, long lastSyncMarker) {
+ List<RawContact> rawContacts, long groupId, long lastSyncMarker) {
long currentSyncMarker = lastSyncMarker;
final ContentResolver resolver = context.getContentResolver();
@@ -112,7 +148,7 @@
Log.d(TAG, "In addContact");
if (!rawContact.isDeleted()) {
newUsers.add(rawContact);
- addContact(context, account, rawContact, true, batchOperation);
+ addContact(context, account, rawContact, groupId, true, batchOperation);
}
}
// A sync adapter should batch operations on multiple contacts,
@@ -235,12 +271,13 @@
* @param context the Authenticator Activity context
* @param accountName the account the contact belongs to
* @param rawContact the sample SyncAdapter User object
+ * @param groupId the id of the sample group
* @param inSync is the add part of a client-server sync?
* @param batchOperation allow us to batch together multiple operations
* into a single provider call
*/
public static void addContact(Context context, String accountName, RawContact rawContact,
- boolean inSync, BatchOperation batchOperation) {
+ long groupId, boolean inSync, BatchOperation batchOperation) {
// Put the data in the contacts provider
final ContactOperations contactOp = ContactOperations.createNewContact(
@@ -252,6 +289,7 @@
.addPhone(rawContact.getCellPhone(), Phone.TYPE_MOBILE)
.addPhone(rawContact.getHomePhone(), Phone.TYPE_HOME)
.addPhone(rawContact.getOfficePhone(), Phone.TYPE_WORK)
+ .addGroupMembership(groupId)
.addAvatar(rawContact.getAvatarUrl());
// If we have a serverId, then go ahead and create our status profile.
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
index cb8e97b..1445e55 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
@@ -25,6 +25,7 @@
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
@@ -185,6 +186,20 @@
return this;
}
+ /**
+ * Adds a group membership
+ *
+ * @param id The id of the group to assign
+ * @return instance of ContactOperations
+ */
+ public ContactOperations addGroupMembership(long groupId) {
+ mValues.clear();
+ mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
+ mValues.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+ addInsertOp();
+ return this;
+ }
+
public ContactOperations addAvatar(String avatarUrl) {
if (avatarUrl != null) {
byte[] avatarBuffer = NetworkUtilities.downloadAvatar(avatarUrl);
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
index 0ca8dee..0f570cd 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
@@ -15,6 +15,15 @@
*/
package com.example.android.samplesync.syncadapter;
+import com.example.android.samplesync.Constants;
+import com.example.android.samplesync.client.NetworkUtilities;
+import com.example.android.samplesync.client.RawContact;
+import com.example.android.samplesync.platform.ContactManager;
+
+import org.apache.http.ParseException;
+import org.apache.http.auth.AuthenticationException;
+import org.json.JSONException;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
@@ -27,18 +36,7 @@
import android.text.TextUtils;
import android.util.Log;
-import com.example.android.samplesync.Constants;
-import com.example.android.samplesync.client.NetworkUtilities;
-import com.example.android.samplesync.client.RawContact;
-import com.example.android.samplesync.platform.ContactManager;
-
-import org.apache.http.ParseException;
-import org.apache.http.auth.AuthenticationException;
-import org.json.JSONException;
-
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
/**
@@ -91,6 +89,9 @@
final String authtoken = mAccountManager.blockingGetAuthToken(account,
Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE);
+ // Make sure that the sample group exists
+ final long groupId = ContactManager.ensureSampleGroupExists(mContext, account);
+
// Find the local 'dirty' contacts that we need to tell the server about...
// Find the local users that need to be sync'd to the server...
dirtyContacts = ContactManager.getDirtyContacts(mContext, account);
@@ -106,6 +107,7 @@
long newSyncState = ContactManager.updateContacts(mContext,
account.name,
updatedContacts,
+ groupId,
lastSyncMarker);
// This is a demo of how you can update IM-style status messages
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.java
index 75498fd..cf734d7 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.java
@@ -24,7 +24,9 @@
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.TabHost;
+import android.widget.TabWidget;
import java.util.ArrayList;
@@ -159,7 +161,16 @@
@Override
public void onPageSelected(int position) {
+ // Unfortunately when TabHost changes the current tab, it kindly
+ // also takes care of putting focus on it when not in touch mode.
+ // The jerk.
+ // This hack tries to prevent this from pulling focus out of our
+ // ViewPager.
+ TabWidget widget = mTabHost.getTabWidget();
+ int oldFocusability = widget.getDescendantFocusability();
+ widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
+ widget.setDescendantFocusability(oldFocusability);
}
@Override
diff --git a/samples/TtsEngine/res/xml/general_settings.xml b/samples/TtsEngine/res/xml/general_settings.xml
index 1cd6fd9..bd963fd 100644
--- a/samples/TtsEngine/res/xml/general_settings.xml
+++ b/samples/TtsEngine/res/xml/general_settings.xml
@@ -16,7 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
- <CheckboxPreference
+ <CheckBoxPreference
android:key="robot_speak_whisper"
android:title="@string/whisper_title"
android:summary="@string/whisper_summary"
diff --git a/samples/VoicemailProviderDemo/AndroidManifest.xml b/samples/VoicemailProviderDemo/AndroidManifest.xml
index 558dbf7..f70c248 100644
--- a/samples/VoicemailProviderDemo/AndroidManifest.xml
+++ b/samples/VoicemailProviderDemo/AndroidManifest.xml
@@ -23,8 +23,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1" android:versionName="1.0" package="com.example.android.voicemail">
- <uses-sdk android:minSdkVersion="13"
- android:targetSdkVersion="13" />
+ <uses-sdk android:minSdkVersion="14"
+ android:targetSdkVersion="14" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
diff --git a/samples/WiFiDirectDemo/Android.mk b/samples/WiFiDirectDemo/Android.mk
new file mode 100644
index 0000000..f77bb85
--- /dev/null
+++ b/samples/WiFiDirectDemo/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WiFiDirectDemo
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/WiFiDirectDemo/AndroidManifest.xml b/samples/WiFiDirectDemo/AndroidManifest.xml
new file mode 100644
index 0000000..993716f
--- /dev/null
+++ b/samples/WiFiDirectDemo/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.wifidirect"
+ android:versionCode="1" android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="14" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <!-- Market filtering -->
+ <uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo">
+ <activity
+ android:name=".WiFiDirectActivity"
+ android:label="@string/app_name" android:launchMode="singleTask">
+ <intent-filter>
+ <action
+ android:name="android.intent.action.MAIN" />
+ <category
+ android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- Used for transferring files after a successful connection -->
+ <service android:enabled="true" android:name=".FileTransferService" />
+
+ </application>
+</manifest>
diff --git a/samples/WiFiDirectDemo/_index.html b/samples/WiFiDirectDemo/_index.html
new file mode 100644
index 0000000..07fb190
--- /dev/null
+++ b/samples/WiFiDirectDemo/_index.html
@@ -0,0 +1,49 @@
+<p>This is a demo application highlighting how to make peer to peer network connections with
+the Wi-Fi Direct APIs. The application allows you to transfer jpeg images from the gallery after a succesful connection.</p>
+
+<p>The source code for this demo app shows how to accomplish three key things
+with Wi-Fi Direct APIs: Discovering peers, connecting using Wi-Fi Direct APIs, and finding group details post connection, so that a
+TCP socket can be opened to transfer files.</p>
+
+<p>The application includes:<p>
+<ul> <li><a
+ href="src/com/example/android/wifidirect/WiFiDirectActivity.html"><code>WiFiDirectActivity</code></a>
+ — the main <code>Activity</code> that contains two fragments to handle app's UI and peer lifecycle. It also registers a broadcast receiver for Wi-Fi Direct related events.</li> <li><a
+ href="src/com/example/android/wifidirect/WiFiDirectBroadcastReceiver.html"><code>
+ WiFiDirectBroadcastReceiver</code></a> — a <code>BroadcastReceiver</code>
+ that listens for Wi-Fi Direct related events and passes them to
+ <code>WiFiDirectActivity</code> and it's fragments for neccesary action.</li> <li><a
+ href="src/com/example/android/wifidirect/DeviceListFragment.html"><code>DeviceListFragment</code></a>
+ — a <code>ListFragment</code> that displays available peers and their status. </li>
+<li><a href="src/com/example/android/wifidirect/DeviceDetailFragment.html"><code>DeviceDetailFragment</code></a>
+ — a <code>Fragment</code> that displays the details of the selected device and also drives the connection, disonnection and data transfer functionality of the demo. </li>
+<li><a href="src/com/example/android/wifidirect/FileTransferService.html"><code>FileTransferService</code></a>
+ — an <code>IntentService</code> that services file transfer requests from the application by using TCP sockets. </li> </ul>
+<p>If you are developing an application that uses the Wi-Fi Direct APIs, remember that the
+feature is supported only on Android 4.0 (API level 14) and higher versions of
+the platform. To ensure that your application can only
+be installed on devices that are capable of supporting Wi-Fi Direct mode, remember to add the
+following to the application's manifest before publishing to Android Market:</p>
+<ul> <li><code><uses-sdk android:minSdkVersion="14" /></code>, which
+ indicates to Android Market and the platform that your application requires
+ Android 4.0 or higher. For more information, see <a
+ href="../../../guide/appendix/api-levels.html">API Levels</a> and the
+ documentation for the <a
+ href="../../../guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
+ element.</li> </ul> <p>To control how Android Market filters your application
+from devices that do not support Wi-Fi Direct mode, remember to add the following to the
+application's manifest <ul> <li><code><uses-feature
+ android:name="android.hardware.wifi.direct" /></code>, which tells Android
+ Market that your application uses the Wi-Fi Direct API. The declaration should include
+ an <code>android:required</code> attribute that indicates whether you want
+ Android Market to filter the application from devices that do not offer Wi-Fi Direct support. Other <code><uses-feature></code> declarations may also be
+ needed, depending on your implementation. For more information, see the
+ documentation for the <a
+ href="../../../guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a>
+ element.</li> </ul>
+<p>For more information about using the Wi-Fi Direct API, see the <a
+ href="../../../reference/android/net/wifi/p2p/package-summary.html"><code>android.net.wifi.p2p </a></code>
+documentation. </p>
+
+<img alt="" src="../images/WifiDirect.png" />
+
diff --git a/samples/WiFiDirectDemo/assets/sample_file.txt b/samples/WiFiDirectDemo/assets/sample_file.txt
new file mode 100644
index 0000000..504dbf5
--- /dev/null
+++ b/samples/WiFiDirectDemo/assets/sample_file.txt
@@ -0,0 +1,3 @@
+This is a sample text file for wifi_direct demo.
+
+Once the devices are connected, the server i.e. groupOwner will listen for incoming connections and write this file.
\ No newline at end of file
diff --git a/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_discover.png b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_discover.png
new file mode 100644
index 0000000..98241d7
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_discover.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_on_off.png b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_on_off.png
new file mode 100644
index 0000000..5eabf08
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_action_on_off.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-hdpi/ic_launcher.png b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..f2a2f25
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-hdpi/icon.png b/samples/WiFiDirectDemo/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_discover.png b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_discover.png
new file mode 100644
index 0000000..12849ed
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_discover.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_on_off.png b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_on_off.png
new file mode 100644
index 0000000..e84700a
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_action_on_off.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-ldpi/ic_launcher.png b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..002dae2
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-ldpi/icon.png b/samples/WiFiDirectDemo/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_discover.png b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_discover.png
new file mode 100644
index 0000000..bd71a10
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_discover.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_on_off.png b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_on_off.png
new file mode 100644
index 0000000..8e49066
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_action_on_off.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-mdpi/ic_launcher.png b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c3792d0
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-mdpi/icon.png b/samples/WiFiDirectDemo/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_discover.png b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_discover.png
new file mode 100644
index 0000000..543e2a0
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_discover.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_on_off.png b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_on_off.png
new file mode 100644
index 0000000..3d30ac9
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_action_on_off.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_launcher.png b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..c6d8a02
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable/details_view.xml b/samples/WiFiDirectDemo/res/drawable/details_view.xml
new file mode 100644
index 0000000..06fb95a
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable/details_view.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <solid android:color="@color/layout_border_color"/>
+ <stroke
+ android:width="4dp"
+ android:color="@android:color/background_dark" />
+ <padding
+ android:left="10dp"
+ android:top="2dp"
+ android:right="10dp"
+ android:bottom="2dp" />
+</shape>
diff --git a/samples/WiFiDirectDemo/res/drawable/machine.png b/samples/WiFiDirectDemo/res/drawable/machine.png
new file mode 100644
index 0000000..d61609a
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable/machine.png
Binary files differ
diff --git a/samples/WiFiDirectDemo/res/drawable/section_header.xml b/samples/WiFiDirectDemo/res/drawable/section_header.xml
new file mode 100644
index 0000000..22344dc
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/drawable/section_header.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:width="2dp"
+ android:color="@color/layout_border_color" />
+
+ <padding
+ android:left="10dp"
+ android:top="5dp"
+ android:right="10dp"
+ android:bottom="2dp" />
+ <corners
+ android:radius="2dp" />
+</shape>
diff --git a/samples/WiFiDirectDemo/res/layout-land/main.xml b/samples/WiFiDirectDemo/res/layout-land/main.xml
new file mode 100644
index 0000000..55b0d09
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout-land/main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:id="@+id/linearLayout1"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <fragment
+ class="com.example.android.wifidirect.DeviceListFragment"
+ android:id="@+id/frag_list"
+ android:layout_width="@dimen/phone_list_height"
+ android:layout_height="match_parent">
+ <!-- Preview: layout=@layout/row_devices -->
+ </fragment>
+ <fragment
+ class="com.example.android.wifidirect.DeviceDetailFragment"
+ android:id="@+id/frag_detail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- Preview: layout=@layout/device_detail -->
+ </fragment>
+ </LinearLayout>
+</LinearLayout>
diff --git a/samples/WiFiDirectDemo/res/layout-large/main.xml b/samples/WiFiDirectDemo/res/layout-large/main.xml
new file mode 100644
index 0000000..b278d64
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout-large/main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1"
+ android:layout_height="fill_parent" android:orientation="horizontal">
+ <fragment class="com.example.android.wifidirect.DeviceListFragment"
+ android:id="@+id/frag_list" android:layout_width="@dimen/tablet_list_width"
+ android:layout_height="match_parent">
+ <!-- Preview: layout=@layout/row_devices -->
+ </fragment>
+ <fragment class="com.example.android.wifidirect.DeviceDetailFragment"
+ android:id="@+id/frag_detail" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- Preview: layout=@layout/device_detail -->
+ </fragment>
+ </LinearLayout>
+</LinearLayout>
diff --git a/samples/WiFiDirectDemo/res/layout/device_detail.xml b/samples/WiFiDirectDemo/res/layout/device_detail.xml
new file mode 100644
index 0000000..40509b1
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout/device_detail.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:background="@drawable/details_view"
+ android:visibility="gone">
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/btn_connect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/connect_peer_button" />
+ <Button
+ android:id="@+id/btn_disconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/disconnect_peer_button" />
+ <Button
+ android:id="@+id/btn_start_client"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_file_button"
+ android:visibility="gone" />
+ </LinearLayout>
+ <TextView
+ android:id="@+id/device_address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/device_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/group_owner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/group_ip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:id="@+id/status_bar"
+ android:orientation="vertical"
+ android:layout_gravity="bottom"
+ android:layout_height="37dp"
+ android:layout_marginBottom="3dp"
+ android:background="@android:color/background_dark">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_margin="5dp"
+ android:textColor="@android:color/white"
+ android:id="@+id/status_text">
+ </TextView>
+ </LinearLayout>
+</FrameLayout>
diff --git a/samples/WiFiDirectDemo/res/layout/device_list.xml b/samples/WiFiDirectDemo/res/layout/device_list.xml
new file mode 100644
index 0000000..2d91566
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout/device_list.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="3dp">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:text="@string/label_me" />
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:gravity="center_vertical"
+ android:background="@android:color/holo_blue_light" />
+
+ <!-- Self information -->
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:padding="3dip">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginRight="2dp"
+ android:src="@drawable/machine" />
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/my_name"
+ android:layout_width="fill_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+ <TextView
+ android:id="@+id/my_status"
+ android:layout_width="fill_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:singleLine="true"
+ android:ellipsize="marquee" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:text="@string/label_peers" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:gravity="center_vertical"
+ android:background="@android:color/holo_blue_light" />
+
+ <!-- Available peers -->
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false" />
+
+ <TextView
+ android:id="@id/android:empty"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:layout_height="match_parent"
+ android:text="@string/empty_message" />
+</LinearLayout>
diff --git a/samples/WiFiDirectDemo/res/layout/main.xml b/samples/WiFiDirectDemo/res/layout/main.xml
new file mode 100644
index 0000000..70151d5
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout/main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:id="@+id/linearLayout1"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <fragment
+ class="com.example.android.wifidirect.DeviceListFragment"
+ android:id="@+id/frag_list"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/phone_list_height">
+ <!-- Preview: layout=@layout/row_devices -->
+ </fragment>
+ <fragment
+ class="com.example.android.wifidirect.DeviceDetailFragment"
+ android:id="@+id/frag_detail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- Preview: layout=@layout/device_detail -->
+ </fragment>
+ </LinearLayout>
+</LinearLayout>
diff --git a/samples/WiFiDirectDemo/res/layout/row_devices.xml b/samples/WiFiDirectDemo/res/layout/row_devices.xml
new file mode 100644
index 0000000..f5b68fa
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/layout/row_devices.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:padding="6dip">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginRight="2dip"
+ android:src="@drawable/machine" />
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/device_name"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/device_details"
+ android:singleLine="true"
+ android:ellipsize="marquee" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/WiFiDirectDemo/res/menu/action_items.xml b/samples/WiFiDirectDemo/res/menu/action_items.xml
new file mode 100644
index 0000000..d2d3c7c
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/menu/action_items.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/atn_direct_enable"
+ android:icon="@drawable/ic_action_on_off"
+ android:title="@string/enable_p2p_button"
+ android:showAsAction="ifRoom|withText" />
+
+ <item
+ android:id="@+id/atn_direct_discover"
+ android:icon="@drawable/ic_action_discover"
+ android:title="@string/discover_peers_button"
+ android:showAsAction="ifRoom|withText" />
+</menu>
diff --git a/samples/WiFiDirectDemo/res/values/colors.xml b/samples/WiFiDirectDemo/res/values/colors.xml
new file mode 100644
index 0000000..6dae474
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/values/colors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="status_bar_color">#FF000000</color>
+ <color name="layout_border_color">#FF111111</color>
+</resources>
diff --git a/samples/HoneycombGallery/res/layout/action_bar_custom.xml b/samples/WiFiDirectDemo/res/values/dimens.xml
similarity index 63%
copy from samples/HoneycombGallery/res/layout/action_bar_custom.xml
copy to samples/WiFiDirectDemo/res/values/dimens.xml
index 5f111d9..9ff9f12 100644
--- a/samples/HoneycombGallery/res/layout/action_bar_custom.xml
+++ b/samples/WiFiDirectDemo/res/values/dimens.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2011 The Android Open Source Project
@@ -13,12 +14,8 @@
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="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent">Hello Action Bar</TextView>
-</LinearLayout>
+<resources>
+ <dimen name="tablet_list_width">300dp</dimen>
+ <dimen name="phone_list_height">200dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/WiFiDirectDemo/res/values/strings.xml b/samples/WiFiDirectDemo/res/values/strings.xml
new file mode 100644
index 0000000..96042b6
--- /dev/null
+++ b/samples/WiFiDirectDemo/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">WiFi Direct</string>
+ <string name="enable_p2p_button">P2P On/Off</string>
+ <string name="discover_peers_button">Discover</string>
+ <string name="connect_peer_button">Connect</string>
+ <string name="disconnect_peer_button">Disconnect</string>
+ <string name="get_file_button">Launch Gallery</string>
+ <string name="empty"></string>
+ <string name="yes">yes</string>
+ <string name="no">no</string>
+ <string name="client_text">This device will act as a client. Click on Gallery button to pick a local(stored) file</string>
+ <string name="empty_message">No devices found. Turn on P2P and perform discovery from the Action Bar</string>
+ <string name="p2p_off_warning">Enable P2P from action bar button above or system settings</string>
+ <string name="group_owner_text">"Am I the Group Owner? "</string>
+ <string name="label_me">ME</string>
+ <string name="label_peers">PEERS</string>
+</resources>
diff --git a/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceDetailFragment.java b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceDetailFragment.java
new file mode 100644
index 0000000..f74afae
--- /dev/null
+++ b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceDetailFragment.java
@@ -0,0 +1,295 @@
+/*
+ * 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.wifidirect;
+
+import android.app.Fragment;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * A fragment that manages a particular peer and allows interaction with device
+ * i.e. setting up network connection and transferring data.
+ */
+public class DeviceDetailFragment extends Fragment implements ConnectionInfoListener {
+
+ protected static final int CHOOSE_FILE_RESULT_CODE = 20;
+ private View mContentView = null;
+ private WifiP2pDevice device;
+ private WifiP2pInfo info;
+ ProgressDialog progressDialog = null;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ mContentView = inflater.inflate(R.layout.device_detail, null);
+ mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = device.deviceAddress;
+ config.wps.setup = WpsInfo.PBC;
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",
+ "Connecting to :" + device.deviceAddress, true, true
+// new DialogInterface.OnCancelListener() {
+//
+// @Override
+// public void onCancel(DialogInterface dialog) {
+// ((DeviceActionListener) getActivity()).cancelDisconnect();
+// }
+// }
+ );
+ ((DeviceActionListener) getActivity()).connect(config);
+
+ }
+ });
+
+ mContentView.findViewById(R.id.btn_disconnect).setOnClickListener(
+ new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ ((DeviceActionListener) getActivity()).disconnect();
+ }
+ });
+
+ mContentView.findViewById(R.id.btn_start_client).setOnClickListener(
+ new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // Allow user to pick an image from Gallery or other
+ // registered apps
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE);
+ }
+ });
+
+ return mContentView;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+
+ // User has picked an image. Transfer it to group owner i.e peer using
+ // FileTransferService.
+ Uri uri = data.getData();
+ TextView statusText = (TextView) mContentView.findViewById(R.id.status_text);
+ statusText.setText("Sending: " + uri);
+ Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri);
+ Intent serviceIntent = new Intent(getActivity(), FileTransferService.class);
+ serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE);
+ serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());
+ serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,
+ info.groupOwnerAddress.getHostAddress());
+ serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);
+ getActivity().startService(serviceIntent);
+ }
+
+ @Override
+ public void onConnectionInfoAvailable(final WifiP2pInfo info) {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ this.info = info;
+ this.getView().setVisibility(View.VISIBLE);
+
+ // The owner IP is now known.
+ TextView view = (TextView) mContentView.findViewById(R.id.group_owner);
+ view.setText(getResources().getString(R.string.group_owner_text)
+ + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)
+ : getResources().getString(R.string.no)));
+
+ // InetAddress from WifiP2pInfo struct.
+ view = (TextView) mContentView.findViewById(R.id.device_info);
+ view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());
+
+ // After the group negotiation, we assign the group owner as the file
+ // server. The file server is single threaded, single connection server
+ // socket.
+ if (info.groupFormed && info.isGroupOwner) {
+ new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text))
+ .execute();
+ } else if (info.groupFormed) {
+ // The other device acts as the client. In this case, we enable the
+ // get file button.
+ mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
+ ((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources()
+ .getString(R.string.client_text));
+ }
+
+ // hide the connect button
+ mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE);
+ }
+
+ /**
+ * Updates the UI with device data
+ *
+ * @param device the device to be displayed
+ */
+ public void showDetails(WifiP2pDevice device) {
+ this.device = device;
+ this.getView().setVisibility(View.VISIBLE);
+ TextView view = (TextView) mContentView.findViewById(R.id.device_address);
+ view.setText(device.deviceAddress);
+ view = (TextView) mContentView.findViewById(R.id.device_info);
+ view.setText(device.toString());
+
+ }
+
+ /**
+ * Clears the UI fields after a disconnect or direct mode disable operation.
+ */
+ public void resetViews() {
+ mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE);
+ TextView view = (TextView) mContentView.findViewById(R.id.device_address);
+ view.setText(R.string.empty);
+ view = (TextView) mContentView.findViewById(R.id.device_info);
+ view.setText(R.string.empty);
+ view = (TextView) mContentView.findViewById(R.id.group_owner);
+ view.setText(R.string.empty);
+ view = (TextView) mContentView.findViewById(R.id.status_text);
+ view.setText(R.string.empty);
+ mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE);
+ this.getView().setVisibility(View.GONE);
+ }
+
+ /**
+ * A simple server socket that accepts connection and writes some data on
+ * the stream.
+ */
+ public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> {
+
+ private Context context;
+ private TextView statusText;
+
+ /**
+ * @param context
+ * @param statusText
+ */
+ public FileServerAsyncTask(Context context, View statusText) {
+ this.context = context;
+ this.statusText = (TextView) statusText;
+ }
+
+ @Override
+ protected String doInBackground(Void... params) {
+ try {
+ ServerSocket serverSocket = new ServerSocket(8988);
+ Log.d(WiFiDirectActivity.TAG, "Server: Socket opened");
+ Socket client = serverSocket.accept();
+ Log.d(WiFiDirectActivity.TAG, "Server: connection done");
+ final File f = new File(Environment.getExternalStorageDirectory() + "/"
+ + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()
+ + ".jpg");
+
+ File dirs = new File(f.getParent());
+ if (!dirs.exists())
+ dirs.mkdirs();
+ f.createNewFile();
+
+ Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString());
+ InputStream inputstream = client.getInputStream();
+ copyFile(inputstream, new FileOutputStream(f));
+ serverSocket.close();
+ return f.getAbsolutePath();
+ } catch (IOException e) {
+ Log.e(WiFiDirectActivity.TAG, e.getMessage());
+ return null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
+ */
+ @Override
+ protected void onPostExecute(String result) {
+ if (result != null) {
+ statusText.setText("File copied - " + result);
+ Intent intent = new Intent();
+ intent.setAction(android.content.Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse("file://" + result), "image/*");
+ context.startActivity(intent);
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.os.AsyncTask#onPreExecute()
+ */
+ @Override
+ protected void onPreExecute() {
+ statusText.setText("Opening a server socket");
+ }
+
+ }
+
+ public static boolean copyFile(InputStream inputStream, OutputStream out) {
+ byte buf[] = new byte[1024];
+ int len;
+ try {
+ while ((len = inputStream.read(buf)) != -1) {
+ out.write(buf, 0, len);
+
+ }
+ out.close();
+ inputStream.close();
+ } catch (IOException e) {
+ Log.d(WiFiDirectActivity.TAG, e.toString());
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceListFragment.java b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceListFragment.java
new file mode 100644
index 0000000..bfdbd6b
--- /dev/null
+++ b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/DeviceListFragment.java
@@ -0,0 +1,207 @@
+/*
+ * 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.wifidirect;
+
+import android.app.ListFragment;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A ListFragment that displays available peers on discovery and requests the
+ * parent activity to handle user interaction events
+ */
+public class DeviceListFragment extends ListFragment implements PeerListListener {
+
+ private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
+ ProgressDialog progressDialog = null;
+ View mContentView = null;
+ private WifiP2pDevice device;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ this.setListAdapter(new WiFiPeerListAdapter(getActivity(), R.layout.row_devices, peers));
+
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ mContentView = inflater.inflate(R.layout.device_list, null);
+ return mContentView;
+ }
+
+ /**
+ * @return this device
+ */
+ public WifiP2pDevice getDevice() {
+ return device;
+ }
+
+ private static String getDeviceStatus(int deviceStatus) {
+ Log.d(WiFiDirectActivity.TAG, "Peer status :" + deviceStatus);
+ switch (deviceStatus) {
+ case WifiP2pDevice.AVAILABLE:
+ return "Available";
+ case WifiP2pDevice.INVITED:
+ return "Invited";
+ case WifiP2pDevice.CONNECTED:
+ return "Connected";
+ case WifiP2pDevice.FAILED:
+ return "Failed";
+ case WifiP2pDevice.UNAVAILABLE:
+ return "Unavailable";
+ default:
+ return "Unknown";
+
+ }
+ }
+
+ /**
+ * Initiate a connection with the peer.
+ */
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ WifiP2pDevice device = (WifiP2pDevice) getListAdapter().getItem(position);
+ ((DeviceActionListener) getActivity()).showDetails(device);
+ }
+
+ /**
+ * Array adapter for ListFragment that maintains WifiP2pDevice list.
+ */
+ private class WiFiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {
+
+ private List<WifiP2pDevice> items;
+
+ /**
+ * @param context
+ * @param textViewResourceId
+ * @param objects
+ */
+ public WiFiPeerListAdapter(Context context, int textViewResourceId,
+ List<WifiP2pDevice> objects) {
+ super(context, textViewResourceId, objects);
+ items = objects;
+
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = convertView;
+ if (v == null) {
+ LayoutInflater vi = (LayoutInflater) getActivity().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ v = vi.inflate(R.layout.row_devices, null);
+ }
+ WifiP2pDevice device = items.get(position);
+ if (device != null) {
+ TextView top = (TextView) v.findViewById(R.id.device_name);
+ TextView bottom = (TextView) v.findViewById(R.id.device_details);
+ if (top != null) {
+ top.setText(device.deviceName);
+ }
+ if (bottom != null) {
+ bottom.setText(getDeviceStatus(device.status));
+ }
+ }
+
+ return v;
+
+ }
+ }
+
+ /**
+ * Update UI for this device.
+ *
+ * @param device WifiP2pDevice object
+ */
+ public void updateThisDevice(WifiP2pDevice device) {
+ this.device = device;
+ TextView view = (TextView) mContentView.findViewById(R.id.my_name);
+ view.setText(device.deviceName);
+ view = (TextView) mContentView.findViewById(R.id.my_status);
+ view.setText(getDeviceStatus(device.status));
+ }
+
+ @Override
+ public void onPeersAvailable(WifiP2pDeviceList peerList) {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ peers.clear();
+ peers.addAll(peerList.getDeviceList());
+ ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
+ if (peers.size() == 0) {
+ Log.d(WiFiDirectActivity.TAG, "No devices found");
+ return;
+ }
+
+ }
+
+ public void clearPeers() {
+ peers.clear();
+ ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
+ }
+
+ /**
+ *
+ */
+ public void onInitiateDiscovery() {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", "finding peers", true,
+ true, new DialogInterface.OnCancelListener() {
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+
+ }
+ });
+ }
+
+ /**
+ * An interface-callback for the activity to listen to fragment interaction
+ * events.
+ */
+ public interface DeviceActionListener {
+
+ void showDetails(WifiP2pDevice device);
+
+ void cancelDisconnect();
+
+ void connect(WifiP2pConfig config);
+
+ void disconnect();
+ }
+
+}
diff --git a/samples/WiFiDirectDemo/src/com/example/android/wifidirect/FileTransferService.java b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/FileTransferService.java
new file mode 100644
index 0000000..cfd3160
--- /dev/null
+++ b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/FileTransferService.java
@@ -0,0 +1,86 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.example.android.wifidirect;
+
+import android.app.IntentService;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * A service that process each file transfer request i.e Intent by opening a
+ * socket connection with the WiFi Direct Group Owner and writing the file
+ */
+public class FileTransferService extends IntentService {
+
+ private static final int SOCKET_TIMEOUT = 5000;
+ public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE";
+ public static final String EXTRAS_FILE_PATH = "file_url";
+ public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
+ public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";
+
+ public FileTransferService(String name) {
+ super(name);
+ }
+
+ public FileTransferService() {
+ super("FileTransferService");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.app.IntentService#onHandleIntent(android.content.Intent)
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+
+ Context context = getApplicationContext();
+ if (intent.getAction().equals(ACTION_SEND_FILE)) {
+ String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);
+ String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
+ Socket socket = new Socket();
+ int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
+
+ try {
+ Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");
+ socket.bind(null);
+ socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
+
+ Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());
+ OutputStream stream = socket.getOutputStream();
+ ContentResolver cr = context.getContentResolver();
+ InputStream is = null;
+ try {
+ is = cr.openInputStream(Uri.parse(fileUri));
+ } catch (FileNotFoundException e) {
+ Log.d(WiFiDirectActivity.TAG, e.toString());
+ }
+ DeviceDetailFragment.copyFile(is, stream);
+ Log.d(WiFiDirectActivity.TAG, "Client: Data written");
+ } catch (IOException e) {
+ Log.e(WiFiDirectActivity.TAG, e.getMessage());
+ } finally {
+ if (socket != null) {
+ if (socket.isConnected()) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // Give up
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectActivity.java b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectActivity.java
new file mode 100644
index 0000000..2e51c3d
--- /dev/null
+++ b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectActivity.java
@@ -0,0 +1,266 @@
+/*
+ * 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.wifidirect;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager.ActionListener;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.ChannelListener;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;
+
+/**
+ * An activity that uses WiFi Direct APIs to discover and connect with available
+ * devices. WiFi Direct APIs are asynchronous and rely on callback mechanism
+ * using interfaces to notify the application of operation success or failure.
+ * The application should also register a BroadcastReceiver for notification of
+ * WiFi state related events.
+ */
+public class WiFiDirectActivity extends Activity implements ChannelListener, DeviceActionListener {
+
+ public static final String TAG = "wifidirectdemo";
+ private WifiP2pManager manager;
+ private boolean isWifiP2pEnabled = false;
+ private boolean retryChannel = false;
+
+ private final IntentFilter intentFilter = new IntentFilter();
+ private Channel channel;
+ private BroadcastReceiver receiver = null;
+
+ /**
+ * @param isWifiP2pEnabled the isWifiP2pEnabled to set
+ */
+ public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {
+ this.isWifiP2pEnabled = isWifiP2pEnabled;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // add necessary intent values to be matched.
+
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+
+ manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+ channel = manager.initialize(this, getMainLooper(), null);
+ }
+
+ /** register the BroadcastReceiver with the intent values to be matched */
+ @Override
+ public void onResume() {
+ super.onResume();
+ receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
+ registerReceiver(receiver, intentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(receiver);
+ }
+
+ /**
+ * Remove all peers and clear all fields. This is called on
+ * BroadcastReceiver receiving a state change event.
+ */
+ public void resetData() {
+ DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_list);
+ DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_detail);
+ if (fragmentList != null) {
+ fragmentList.clearPeers();
+ }
+ if (fragmentDetails != null) {
+ fragmentDetails.resetViews();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.action_items, menu);
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.atn_direct_enable:
+ if (manager != null && channel != null) {
+
+ // Since this is the system wireless settings activity, it's
+ // not going to send us a result. We will be notified by
+ // WiFiDeviceBroadcastReceiver instead.
+
+ startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
+ } else {
+ Log.e(TAG, "channel or manager is null");
+ }
+ return true;
+
+ case R.id.atn_direct_discover:
+ if (!isWifiP2pEnabled) {
+ Toast.makeText(WiFiDirectActivity.this, R.string.p2p_off_warning,
+ Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_list);
+ fragment.onInitiateDiscovery();
+ manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(int reasonCode) {
+ Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void showDetails(WifiP2pDevice device) {
+ DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_detail);
+ fragment.showDetails(device);
+
+ }
+
+ @Override
+ public void connect(WifiP2pConfig config) {
+ manager.connect(channel, config, new ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ @Override
+ public void disconnect() {
+ final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_detail);
+ fragment.resetViews();
+ manager.removeGroup(channel, new ActionListener() {
+
+ @Override
+ public void onFailure(int reasonCode) {
+ Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);
+
+ }
+
+ @Override
+ public void onSuccess() {
+ fragment.getView().setVisibility(View.GONE);
+ }
+
+ });
+ }
+
+ @Override
+ public void onChannelDisconnected() {
+ // we will try once more
+ if (manager != null && !retryChannel) {
+ Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show();
+ resetData();
+ retryChannel = true;
+ manager.initialize(this, getMainLooper(), this);
+ } else {
+ Toast.makeText(this,
+ "Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void cancelDisconnect() {
+
+ /*
+ * A cancel abort request by user. Disconnect i.e. removeGroup if
+ * already connected. Else, request WifiP2pManager to abort the ongoing
+ * request
+ */
+ if (manager != null) {
+ final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_list);
+ if (fragment.getDevice() == null
+ || fragment.getDevice().status == WifiP2pDevice.CONNECTED) {
+ disconnect();
+ } else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE
+ || fragment.getDevice().status == WifiP2pDevice.INVITED) {
+
+ manager.cancelConnect(channel, new ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ Toast.makeText(WiFiDirectActivity.this, "Aborting connection",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(int reasonCode) {
+ Toast.makeText(WiFiDirectActivity.this,
+ "Connect abort request failed. Reason Code: " + reasonCode,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ }
+}
diff --git a/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectBroadcastReceiver.java b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectBroadcastReceiver.java
new file mode 100644
index 0000000..8bec5b4
--- /dev/null
+++ b/samples/WiFiDirectDemo/src/com/example/android/wifidirect/WiFiDirectBroadcastReceiver.java
@@ -0,0 +1,111 @@
+/*
+ * 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.wifidirect;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
+import android.util.Log;
+
+/**
+ * A BroadcastReceiver that notifies of important wifi p2p events.
+ */
+public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
+
+ private WifiP2pManager manager;
+ private Channel channel;
+ private WiFiDirectActivity activity;
+
+ /**
+ * @param manager WifiP2pManager system service
+ * @param channel Wifi p2p channel
+ * @param activity activity associated with the receiver
+ */
+ public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
+ WiFiDirectActivity activity) {
+ super();
+ this.manager = manager;
+ this.channel = channel;
+ this.activity = activity;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.content.BroadcastReceiver#onReceive(android.content.Context,
+ * android.content.Intent)
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
+
+ // UI update to indicate wifi p2p status.
+ int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
+ if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
+ // Wifi Direct mode is enabled
+ activity.setIsWifiP2pEnabled(true);
+ } else {
+ activity.setIsWifiP2pEnabled(false);
+ activity.resetData();
+
+ }
+ Log.d(WiFiDirectActivity.TAG, "P2P state changed - " + state);
+ } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+
+ // request available peers from the wifi p2p manager. This is an
+ // asynchronous call and the calling activity is notified with a
+ // callback on PeerListListener.onPeersAvailable()
+ if (manager != null) {
+ manager.requestPeers(channel, (PeerListListener) activity.getFragmentManager()
+ .findFragmentById(R.id.frag_list));
+ }
+ Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
+ } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+ if (manager == null) {
+ return;
+ }
+
+ NetworkInfo networkInfo = (NetworkInfo) intent
+ .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
+
+ if (networkInfo.isConnected()) {
+
+ // we are connected with the other device, request connection
+ // info to find group owner IP
+
+ DeviceDetailFragment fragment = (DeviceDetailFragment) activity
+ .getFragmentManager().findFragmentById(R.id.frag_detail);
+ manager.requestConnectionInfo(channel, fragment);
+ } else {
+ // It's a disconnect
+ activity.resetData();
+ }
+ } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
+ DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
+ .findFragmentById(R.id.frag_list);
+ fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
+ WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
+
+ }
+ }
+}
diff --git a/samples/source.properties b/samples/source.properties
index 795c25a..7cb1d33 100644
--- a/samples/source.properties
+++ b/samples/source.properties
@@ -1,4 +1,4 @@
Pkg.UserSrc=false
Pkg.Revision=1
-AndroidVersion.ApiLevel=13
-#AndroidVersion.CodeName=Honeycomb
+AndroidVersion.ApiLevel=14
+#AndroidVersion.CodeName=
diff --git a/sdk/compatibility_source.properties b/sdk/compatibility_source.properties
deleted file mode 100644
index f23e52a..0000000
--- a/sdk/compatibility_source.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-Pkg.UserSrc=false
-Pkg.Revision=3
diff --git a/sdk/doc_source.properties b/sdk/doc_source.properties
index 5f493e4..acf5d3b 100644
--- a/sdk/doc_source.properties
+++ b/sdk/doc_source.properties
@@ -1,5 +1,5 @@
Pkg.UserSrc=false
Pkg.Revision=1
-AndroidVersion.ApiLevel=13
+AndroidVersion.ApiLevel=14
#AndroidVersion.CodeName=
diff --git a/sdk/images_armeabi-v7a_source.properties b/sdk/images_armeabi-v7a_source.properties
new file mode 100644
index 0000000..aa7d1aa
--- /dev/null
+++ b/sdk/images_armeabi-v7a_source.properties
@@ -0,0 +1,6 @@
+Pkg.Desc=Android SDK Platform 4.0
+Pkg.UserSrc=false
+Pkg.Revision=1
+AndroidVersion.ApiLevel=14
+#AndroidVersion.CodeName=
+SystemImage.Abi=armeabi-v7a
diff --git a/sdk/images_armeabi_source.properties b/sdk/images_armeabi_source.properties
new file mode 100644
index 0000000..192ecde
--- /dev/null
+++ b/sdk/images_armeabi_source.properties
@@ -0,0 +1,6 @@
+Pkg.Desc=Android SDK Platform 4.0
+Pkg.UserSrc=false
+Pkg.Revision=1
+AndroidVersion.ApiLevel=14
+#AndroidVersion.CodeName=
+SystemImage.Abi=armeabi
diff --git a/sdk/images_x86_source.properties b/sdk/images_x86_source.properties
new file mode 100644
index 0000000..caccb2d
--- /dev/null
+++ b/sdk/images_x86_source.properties
@@ -0,0 +1,6 @@
+Pkg.Desc=Android SDK Platform 4.0
+Pkg.UserSrc=false
+Pkg.Revision=1
+AndroidVersion.ApiLevel=14
+#AndroidVersion.CodeName=
+SystemImage.Abi=x86
diff --git a/sdk/llvm-rs-cc.txt b/sdk/llvm-rs-cc.txt
deleted file mode 100644
index 34f81ef..0000000
--- a/sdk/llvm-rs-cc.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# map versions of the llvm-rs-cc compiler with the min API level.
-# <compiler version>:<api leve>
-# the compiler version number maps to the filename llvm-rs-cc-<version>[.exe]
-# except for version 1 which is simply llvm-rs-cc[.exe]
-1:11
-2:12
\ No newline at end of file
diff --git a/sdk/platform_source.properties b/sdk/platform_source.properties
index 7b6a869..5e3ef13 100644
--- a/sdk/platform_source.properties
+++ b/sdk/platform_source.properties
@@ -1,6 +1,8 @@
-Pkg.Desc=Android SDK Platform 3.2
+Pkg.Desc=Android SDK Platform 4.0
Pkg.UserSrc=false
-Platform.Version=3.2
+Platform.Version=4.0
Pkg.Revision=1
-AndroidVersion.ApiLevel=13
+AndroidVersion.ApiLevel=14
#AndroidVersion.CodeName=
+Layoutlib.Api=7
+Layoutlib.Revision=1
diff --git a/sdk/source_source.properties b/sdk/source_source.properties
new file mode 100644
index 0000000..7cb1d33
--- /dev/null
+++ b/sdk/source_source.properties
@@ -0,0 +1,4 @@
+Pkg.UserSrc=false
+Pkg.Revision=1
+AndroidVersion.ApiLevel=14
+#AndroidVersion.CodeName=
diff --git a/sdk/compatibility_README.txt b/sdk/support_README.txt
similarity index 81%
rename from sdk/compatibility_README.txt
rename to sdk/support_README.txt
index 7752b3b..2d3ba25 100644
--- a/sdk/compatibility_README.txt
+++ b/sdk/support_README.txt
@@ -1,8 +1,9 @@
-Compatibility Libraries for Android.
+Support Libraries for Android.
This SDK component contains static libraries providing access to newer APIs
-on older platforms. To use those libraries, simply copy them as static libraries
-into your project.
+on older platforms and various helper classes.
+
+To use those libraries, simply copy them as static libraries into your project.
Each library is called v<api>, indicating the minimum API level that they require.
@@ -21,7 +22,7 @@
*** V13 ***
-Provides the same features as v4, plus:
+v13/android-support-v13.jar provides the same features as v4, plus:
- FragmentPagerAdapter: Implementation of PagerAdapter that represents each page as a Fragment.
v13/src/ is the source code for the compatibility library, not including the v4 source
diff --git a/sdk/support_source.properties b/sdk/support_source.properties
new file mode 100644
index 0000000..16d5cd8
--- /dev/null
+++ b/sdk/support_source.properties
@@ -0,0 +1,6 @@
+Pkg.UserSrc=false
+Pkg.Revision=4
+Extra.Vendor=android
+Extra.Path=support
+Extra.OldPaths=compatibility
+
diff --git a/sdk_overlay/frameworks/base/core/res/res/values/config.xml b/sdk_overlay/frameworks/base/core/res/res/values/config.xml
index 3296ed1..22cc16e 100644
--- a/sdk_overlay/frameworks/base/core/res/res/values/config.xml
+++ b/sdk_overlay/frameworks/base/core/res/res/values/config.xml
@@ -23,6 +23,5 @@
<!-- Component name of the service providing geocoder API support. -->
<string name="config_geocodeProvider">com.google.android.location.GeocodeProvider</string>
- <!-- This device is not "voice capable"; it's data-only. -->
- <bool name="config_voice_capable">false</bool>
+ <bool name="config_voice_capable">true</bool>
</resources>
diff --git a/testrunner/coverage_targets.xml b/testrunner/coverage_targets.xml
index 406d5e5..8966700 100644
--- a/testrunner/coverage_targets.xml
+++ b/testrunner/coverage_targets.xml
@@ -84,6 +84,8 @@
<coverage_target name="Mms" build_path="packages/apps/Mms" type="APPS" />
<coverage_target name="Music" build_path="packages/apps/Music"
type="APPS" />
+ <coverage_target name="SystemUI" build_path="frameworks/base/packages/SystemUI"
+ type="APPS" />
<coverage_target name="VoiceDialer" build_path="packages/apps/VoiceDialer"
type="APPS" />
@@ -104,8 +106,6 @@
build_path="frameworks/base/packages/SettingsProvider" type="APPS" />
<coverage_target name="TelephonyProvider"
build_path="packages/providers/telephony" type="APPS" />
- <coverage_target name="VoicemailProvider"
- build_path="packages/providers/VoicemailProvider" type="APPS" />
<!-- input methods -->
<coverage_target name="LatinIME" build_path="packages/inputmethods/LatinIME"
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index d773973..cbe75b7 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -446,12 +446,6 @@
coverage_target="CalendarProvider"
continuous="true" />
-<test name="voicemailprovider"
- build_path="packages/providers/VoicemailProvider"
- package="com.android.providers.voicemail.tests"
- coverage_target="VoicemailProvider"
- continuous="true" />
-
<test name="contactsprov"
build_path="packages/providers/ContactsProvider"
package="com.android.providers.contacts.tests"
@@ -519,6 +513,13 @@
package="com.android.quicksearchbox.tests"
coverage_target="QuickSearchBox" />
+<test name="systemui"
+ build_path="frameworks/base/packages/SystemUI"
+ package="com.android.systemui.tests"
+ coverage_target="SystemUI"
+ continuous="true"
+ description="SystemUI tests" />
+
<!-- native tests -->
<!-- Bionic C++ -->
diff --git a/tools/a3dconvert/ColladaConditioner.cpp b/tools/a3dconvert/ColladaConditioner.cpp
index 0b0f694..0a99e0e 100644
--- a/tools/a3dconvert/ColladaConditioner.cpp
+++ b/tools/a3dconvert/ColladaConditioner.cpp
@@ -200,4 +200,40 @@
}
return convertSuceeded;
-}
\ No newline at end of file
+}
+
+bool ColladaConditioner::stripGeometry(DAE *dae) {
+ bool convertSuceeded = true;
+ int geometryElementCount = (int)(dae->getDatabase()->getElementCount(NULL,
+ "library_geometries" ));
+
+ for(int currentGeometry = 0; currentGeometry < geometryElementCount; currentGeometry++) {
+
+ daeElement * element = 0;
+ int error = dae->getDatabase()->getElement(&element, currentGeometry,
+ NULL, "library_geometries");
+ daeBool removed = daeElement::removeFromParent(element);
+ convertSuceeded = convertSuceeded && removed;
+ }
+ return convertSuceeded;
+}
+
+bool ColladaConditioner::stripGeometry(const char *inputFile) {
+ DAE dae;
+ bool convertSuceeded = true;
+ domCOLLADA* root = dae.open(inputFile);
+
+ if (!root) {
+ printf("Failed to read file %s.\n", inputFile);
+ return false;
+ }
+
+ stripGeometry(&dae);
+
+ dae.writeAll();
+ if(!convertSuceeded) {
+ printf("Encountered errors\n");
+ }
+
+ return convertSuceeded;
+}
diff --git a/tools/a3dconvert/ColladaConditioner.h b/tools/a3dconvert/ColladaConditioner.h
index 470b7b6..bd5e5c9 100644
--- a/tools/a3dconvert/ColladaConditioner.h
+++ b/tools/a3dconvert/ColladaConditioner.h
@@ -23,6 +23,8 @@
public:
bool triangulate(DAE *dae);
bool triangulate(const char *inputFile);
+ bool stripGeometry(DAE *dae);
+ bool stripGeometry(const char *inputFile);
};
#endif //COLLADA_CONDITIONER
diff --git a/tools/a3dconvert/ColladaLoader.cpp b/tools/a3dconvert/ColladaLoader.cpp
index 32d6250..3fc954d 100644
--- a/tools/a3dconvert/ColladaLoader.cpp
+++ b/tools/a3dconvert/ColladaLoader.cpp
@@ -26,6 +26,9 @@
}
ColladaLoader::~ColladaLoader() {
+ if (mDae) {
+ delete mDae;
+ }
clearGeometry();
}
@@ -37,13 +40,16 @@
}
bool ColladaLoader::init(const char *colladaFile) {
- DAE dae;
-
+ if (mDae) {
+ delete mDae;
+ }
clearGeometry();
+ mDae = new DAE();
+
bool convertSuceeded = true;
- domCOLLADA* root = dae.open(colladaFile);
+ domCOLLADA* root = mDae->open(colladaFile);
if (!root) {
fprintf(stderr, "Failed to read file %s.\n", colladaFile);
return false;
@@ -51,7 +57,7 @@
// We only want to deal with triangulated meshes since rendering complex polygons is not feasible
ColladaConditioner conditioner;
- conditioner.triangulate(&dae);
+ conditioner.triangulate(mDae);
domLibrary_geometries *allGeometry = daeSafeCast<domLibrary_geometries>(root->getDescendant("library_geometries"));
@@ -102,3 +108,18 @@
return convertSuceeded;
}
+
+bool ColladaLoader::stripGeometryAndSave() {
+
+ ColladaConditioner conditioner;
+ bool convertSuceeded = conditioner.stripGeometry(mDae);
+
+ mDae->writeAll();
+ if(!convertSuceeded) {
+ printf("Encountered errors\n");
+ } else {
+ printf("Stripped geometry data from collada file\n");
+ }
+
+ return convertSuceeded;
+}
diff --git a/tools/a3dconvert/ColladaLoader.h b/tools/a3dconvert/ColladaLoader.h
index 3d7bb7d..b1a6e3b 100644
--- a/tools/a3dconvert/ColladaLoader.h
+++ b/tools/a3dconvert/ColladaLoader.h
@@ -19,6 +19,7 @@
#include <vector>
+class DAE;
class domLibrary_geometries;
class domGeometry;
class ColladaGeometry;
@@ -36,8 +37,10 @@
virtual uint32_t getNumMeshes() const {
return mGeometries.size();
}
+ bool stripGeometryAndSave();
private:
+ DAE *mDae;
void clearGeometry();
std::vector<ColladaGeometry*> mGeometries;
diff --git a/tools/a3dconvert/SimpleMesh.h b/tools/a3dconvert/SimpleMesh.h
index 15205cb..c87bb7d 100644
--- a/tools/a3dconvert/SimpleMesh.h
+++ b/tools/a3dconvert/SimpleMesh.h
@@ -75,22 +75,28 @@
}
// Generate the element that describes our channel layout
- rsc->mStateElement.elementBuilderBegin();
+ Element::Builder vtxBuilder;
for (uint32_t c = 0; c < mChannels.size(); c ++) {
// Skip empty channels
if (mChannels[c].mData.size() == 0) {
continue;
}
- const Element *subElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, mChannels[c].mStride);
- rsc->mStateElement.elementBuilderAdd(subElem, mChannels[c].mName.c_str(), 1);
+ ObjectBaseRef<const Element> subElem = Element::createRef(rsc,
+ RS_TYPE_FLOAT_32,
+ RS_KIND_USER,
+ false,
+ mChannels[c].mStride);
+ vtxBuilder.add(subElem.get(), mChannels[c].mName.c_str(), 1);
}
- const Element *vertexDataElem = rsc->mStateElement.elementBuilderCreate(rsc);
+ ObjectBaseRef<const Element> vertexDataElem = vtxBuilder.create(rsc);
uint32_t numVerts = mChannels[0].mData.size()/mChannels[0].mStride;
- Type *vertexDataType = Type::getType(rsc, vertexDataElem, numVerts, 0, 0, false, false);
+ ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(rsc, vertexDataElem.get(),
+ numVerts, 0, 0, false, false);
vertexDataType->compute();
- Allocation *vertexAlloc = Allocation::createAllocation(rsc, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT);
+ Allocation *vertexAlloc = Allocation::createAllocation(rsc, vertexDataType.get(),
+ RS_ALLOCATION_USAGE_SCRIPT);
uint32_t vertexSize = vertexDataElem->getSizeBytes()/sizeof(float);
// Fill this allocation with some data
@@ -112,7 +118,8 @@
}
// Now lets write index data
- const Element *indexElem = Element::create(rsc, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
+ ObjectBaseRef<const Element> indexElem = Element::createRef(rsc, RS_TYPE_UNSIGNED_16,
+ RS_KIND_USER, false, 1);
Mesh *mesh = new Mesh(rsc, 1, mTriangleLists.size());
mesh->setName(mName.c_str());
@@ -122,11 +129,13 @@
for (uint32_t pCount = 0; pCount < mTriangleLists.size(); pCount ++) {
uint32_t numIndicies = mTriangleLists[pCount].size();
- Type *indexType = Type::getType(rsc, indexElem, numIndicies, 0, 0, false, false );
+ ObjectBaseRef<Type> indexType = Type::getTypeRef(rsc, indexElem.get(),
+ numIndicies, 0, 0, false, false );
indexType->compute();
- Allocation *indexAlloc = Allocation::createAllocation(rsc, indexType, RS_ALLOCATION_USAGE_SCRIPT);
+ Allocation *indexAlloc = Allocation::createAllocation(rsc, indexType.get(),
+ RS_ALLOCATION_USAGE_SCRIPT);
uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
const std::vector<uint32_t> &indexList = mTriangleLists[pCount];
uint32_t numTries = numIndicies / 3;
diff --git a/tools/a3dconvert/a3dconvert.cpp b/tools/a3dconvert/a3dconvert.cpp
index 562af7f..3535b17 100644
--- a/tools/a3dconvert/a3dconvert.cpp
+++ b/tools/a3dconvert/a3dconvert.cpp
@@ -87,7 +87,7 @@
const char *objExt = ".obj";
const char *daeExt = ".dae";
- if(argc != 3) {
+ if(argc != 3 && argc != 4) {
printf("-----------------------------------------------------------------\n");
printf("Usage:\n");
printf("a3dconvert input_file a3d_output_file\n");
@@ -105,10 +105,17 @@
return 1;
}
+ bool stripColladaGeo = false;
GeometryLoader *loader = NULL;
std::string ext = filename.substr(dotPos);
if (ext == daeExt) {
loader = new ColladaLoader();
+ if (argc == 4) {
+ std::string option = argv[3];
+ if (option == "-d") {
+ stripColladaGeo = true;
+ }
+ }
} else if (ext == objExt) {
loader = new ObjLoader();
} else {
@@ -121,6 +128,11 @@
isSuccessful = convertToA3D(loader, argv[2]);
}
+ if (isSuccessful && stripColladaGeo) {
+ ColladaLoader *colladaLoader = (ColladaLoader*)loader;
+ colladaLoader->stripGeometryAndSave();
+ }
+
delete loader;
if(isSuccessful) {
diff --git a/tools/emulator/opengl/Android.mk b/tools/emulator/opengl/Android.mk
index cd5b1b7..2c1cb64 100644
--- a/tools/emulator/opengl/Android.mk
+++ b/tools/emulator/opengl/Android.mk
@@ -24,6 +24,10 @@
#
EMUGL_COMMON_CFLAGS := -DWITH_GLES2
+# Uncomment the following line if you want to enable debug traces
+# in the GLES emulation libraries.
+# EMUGL_COMMON_CFLAGS += -DEMUGL_DEBUG=1
+
# Include common definitions used by all the modules included later
# in this build file. This contains the definition of all useful
# emugl-xxxx functions.
@@ -57,15 +61,13 @@
include $(EMUGL_PATH)/system/GLESv2_enc/Android.mk
include $(EMUGL_PATH)/system/renderControl_enc/Android.mk
include $(EMUGL_PATH)/system/OpenglSystemCommon/Android.mk
-include $(EMUGL_PATH)/tests/ut_rendercontrol_enc/Android.mk
# System shared libraries
include $(EMUGL_PATH)/system/GLESv1/Android.mk
include $(EMUGL_PATH)/system/GLESv2/Android.mk
-include $(EMUGL_PATH)/system/egl/Android.mk
-include $(EMUGL_PATH)/tests/gles_android_wrapper/Android.mk
include $(EMUGL_PATH)/system/gralloc/Android.mk
+include $(EMUGL_PATH)/system/egl/Android.mk
# Host static libraries
include $(EMUGL_PATH)/host/libs/GLESv1_dec/Android.mk
@@ -83,12 +85,7 @@
# Host executables
include $(EMUGL_PATH)/host/renderer/Android.mk
-# Host unit-test for the renderer. this one uses its own small
-# EGL host wrapper.
-include $(EMUGL_PATH)/tests/event_injector/Android.mk
-include $(EMUGL_PATH)/tests/EGL_host_wrapper/Android.mk
-include $(EMUGL_PATH)/tests/emulator_test_renderer/Android.mk
-include $(EMUGL_PATH)/tests/ut_renderer/Android.mk
+# Host unit-test for the renderer.
include $(EMUGL_PATH)/tests/translator_tests/MacCommon/Android.mk
include $(EMUGL_PATH)/tests/translator_tests/GLES_CM/Android.mk
diff --git a/tools/emulator/opengl/common.mk b/tools/emulator/opengl/common.mk
index b0b7e5c..82f652d 100644
--- a/tools/emulator/opengl/common.mk
+++ b/tools/emulator/opengl/common.mk
@@ -109,6 +109,7 @@
$(eval _mod = $(_emugl_MODULE)) \
$(eval _emugl.$(_mod).type := $(patsubst HOST_%,%,$2))\
$(eval _emugl.$(_mod).imports :=) \
+ $(eval _emugl,$(_mod).moved :=) \
$(foreach _type,$(EMUGL_EXPORT_TYPES),\
$(eval _emugl.$(_mod).export.$(_type) :=)\
)
@@ -156,7 +157,9 @@
$(eval LOCAL_STATIC_LIBRARIES := $(1:HOST_%=%) $(LOCAL_STATIC_LIBRARIES))\
)\
$(if $(filter SHARED_LIBRARY,$(_emugl.$1.type)),\
- $(eval LOCAL_SHARED_LIBRARIES := $(1:HOST_%=%) $(LOCAL_SHARED_LIBRARIES))\
+ $(if $(_emugl.$1.moved),,\
+ $(eval LOCAL_SHARED_LIBRARIES := $(1:HOST_%=%) $(LOCAL_SHARED_LIBRARIES))\
+ )\
)\
)\
)
@@ -332,5 +335,7 @@
# For example: $(call emugl-set-shared-library-subpath,egl)
emugl-set-shared-library-subpath = \
$(eval LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/$1)\
+ $(eval LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/$1)\
+ $(eval _emugl.$(LOCAL_MODULE).moved := true)\
$(call emugl-export-outer,ADDITIONAL_DEPENDENCIES,$(LOCAL_MODULE_PATH)/$(LOCAL_MODULE)$(TARGET_SHLIB_SUFFIX))
diff --git a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
index 41d8023..445ec17 100644
--- a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
+++ b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
@@ -34,6 +34,7 @@
virtual int commitBuffer(size_t size) = 0;
virtual const unsigned char *readFully( void *buf, size_t len) = 0;
virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
+ virtual int writeFully(const void* buf, size_t len) = 0;
virtual ~IOStream() {
@@ -82,6 +83,7 @@
return readFully(buf, len);
}
+
private:
unsigned char *m_buf;
size_t m_bufsize;
diff --git a/tools/emulator/opengl/host/include/libOpenglRender/render_api.h b/tools/emulator/opengl/host/include/libOpenglRender/render_api.h
index 324ae1f..bb33192 100644
--- a/tools/emulator/opengl/host/include/libOpenglRender/render_api.h
+++ b/tools/emulator/opengl/host/include/libOpenglRender/render_api.h
@@ -22,20 +22,73 @@
#include "render_api_platform_types.h"
+// initLibrary - initialize the library and tries to load the corresponding
+// GLES translator libraries. This function must be called before anything
+// else to ensure that everything works. If it returns an error, then
+// you cannot use the library at all (this can happen under certain
+// environments where the desktop GL libraries are not available)
+//
+// returns true if the library could be initialized successfully;
+//
+bool initLibrary(void);
+
+// list of constants to be passed to setStreamMode, which determines
+// which
+#define STREAM_MODE_DEFAULT 0
+#define STREAM_MODE_TCP 1
+#define STREAM_MODE_UNIX 2
+#define STREAM_MODE_PIPE 3
+
+// Change the stream mode. This must be called before initOpenGLRenderer
+int setStreamMode(int mode);
+
//
// initOpenGLRenderer - initialize the OpenGL renderer process.
-// window is the native window to be used as the framebuffer.
-// x,y,width,height are the dimensions of the rendering subwindow.
// portNum is the tcp port number the renderer is listening to.
+// width and height are the framebuffer dimensions that will be
+// reported to the guest display driver.
//
-// returns true if renderer has been starter successfully;
+// returns true if renderer has been started successfully;
//
// This function is *NOT* thread safe and should be called first
-// to initialize the renderer.
+// to initialize the renderer after initLibrary().
//
-bool initOpenGLRenderer(FBNativeWindowType window,
- int x, int y, int width, int height,
- int portNum);
+bool initOpenGLRenderer(int width, int height, int portNum);
+
+
+//
+// createOpenGLSubwindow -
+// Create a native subwindow which is a child of 'window'
+// to be used for framebuffer display.
+// Framebuffer will not get displayed if a subwindow is not
+// created.
+// x,y,width,height are the dimensions of the rendering subwindow.
+// zRot is the rotation to apply on the framebuffer display image.
+//
+bool createOpenGLSubwindow(FBNativeWindowType window,
+ int x, int y, int width, int height, float zRot);
+
+//
+// destroyOpenGLSubwindow -
+// destroys the created native subwindow. Once destroyed,
+// Framebuffer content will not be visible until a new
+// subwindow will be created.
+//
+bool destroyOpenGLSubwindow();
+
+//
+// setOpenGLDisplatRotation -
+// set the framebuffer display image rotation in units
+// of degrees around the z axis
+//
+void setOpenGLDisplayRotation(float zRot);
+
+//
+// repaintOpenGLDisplay -
+// causes the OpenGL subwindow to get repainted with the
+// latest framebuffer content.
+//
+void repaintOpenGLDisplay();
//
// stopOpenGLRenderer - stops the OpenGL renderer process.
diff --git a/tools/emulator/opengl/host/libs/GLESv1_dec/Android.mk b/tools/emulator/opengl/host/libs/GLESv1_dec/Android.mk
index ba62949..5f134fc 100644
--- a/tools/emulator/opengl/host/libs/GLESv1_dec/Android.mk
+++ b/tools/emulator/opengl/host/libs/GLESv1_dec/Android.mk
@@ -1,6 +1,6 @@
LOCAL_PATH := $(call my-dir)
-$(call emugl-begin-host-shared-library,libGLESv1_dec)
+$(call emugl-begin-host-static-library,libGLESv1_dec)
$(call emugl-import, libOpenglCodecCommon libOpenglOsUtils)
$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
diff --git a/tools/emulator/opengl/host/libs/GLESv2_dec/Android.mk b/tools/emulator/opengl/host/libs/GLESv2_dec/Android.mk
index 009d47b..a66c6ce 100644
--- a/tools/emulator/opengl/host/libs/GLESv2_dec/Android.mk
+++ b/tools/emulator/opengl/host/libs/GLESv2_dec/Android.mk
@@ -1,6 +1,6 @@
LOCAL_PATH := $(call my-dir)
-$(call emugl-begin-host-shared-library,libGLESv2_dec)
+$(call emugl-begin-host-static-library,libGLESv2_dec)
$(call emugl-import, libOpenglCodecCommon libOpenglOsUtils)
$(call emugl-gen-decoder,$(EMUGL_PATH)/system/GLESv2_enc,gl2)
diff --git a/tools/emulator/opengl/host/libs/GLESv2_dec/GL2Decoder.cpp b/tools/emulator/opengl/host/libs/GLESv2_dec/GL2Decoder.cpp
index 0457fdc..e867cf8 100644
--- a/tools/emulator/opengl/host/libs/GLESv2_dec/GL2Decoder.cpp
+++ b/tools/emulator/opengl/host/libs/GLESv2_dec/GL2Decoder.cpp
@@ -43,7 +43,7 @@
m_GL2library = osUtils::dynLibrary::open(libname);
if (m_GL2library == NULL) {
- fprintf(stderr, "Couldn't find %s \n", libname);
+ fprintf(stderr, "%s: Couldn't find %s \n", __FUNCTION__, libname);
return -1;
}
this->initDispatchByName(s_getProc, this);
@@ -77,7 +77,7 @@
int nFormats;
ctx->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &nFormats);
if (nFormats > count) {
- fprintf(stderr, "GetCompressedTextureFormats: The requested number of formats does not match the number that is reported by OpenGL\n");
+ fprintf(stderr, "%s: GetCompressedTextureFormats: The requested number of formats does not match the number that is reported by OpenGL\n", __FUNCTION__);
} else {
ctx->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
}
diff --git a/tools/emulator/opengl/host/libs/Translator/EGL/EglContext.cpp b/tools/emulator/opengl/host/libs/Translator/EGL/EglContext.cpp
index c2ea4a7..bc33f1f 100644
--- a/tools/emulator/opengl/host/libs/Translator/EGL/EglContext.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/EGL/EglContext.cpp
@@ -38,7 +38,7 @@
m_mngr(mngr)
{
m_shareGroup = shared_context.Ptr()?
- mngr->attachShareGroup(context,shared_context.Ptr()->getShareGroup().Ptr()):
+ mngr->attachShareGroup(context,shared_context->nativeType()):
mngr->createShareGroup(context);
m_hndl = ++s_nextContextHndl;
}
diff --git a/tools/emulator/opengl/host/libs/Translator/EGL/EglWindowsApi.cpp b/tools/emulator/opengl/host/libs/Translator/EGL/EglWindowsApi.cpp
index 76820d6..c11c547 100644
--- a/tools/emulator/opengl/host/libs/Translator/EGL/EglWindowsApi.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/EGL/EglWindowsApi.cpp
@@ -17,6 +17,7 @@
#include <windows.h>
#include <wingdi.h>
#include <GL/wglext.h>
+#include <stdio.h>
#define IS_TRUE(a) \
if(a != true) return false;
@@ -30,25 +31,40 @@
bool isPixelFormatSet;
};
+struct TlsData {
+ std::map<int,DisplayInfo> m_map;
+};
+
+static DWORD s_tlsIndex = 0;
+
+static TlsData *getTLS() {
+ TlsData *tls = (TlsData *)TlsGetValue(s_tlsIndex);
+ if (!tls) {
+ tls = new TlsData();
+ TlsSetValue(s_tlsIndex, tls);
+ }
+ return tls;
+}
+
class WinDisplay{
public:
typedef enum {
DEFAULT_DISPLAY = 0
};
WinDisplay(){};
- DisplayInfo& getInfo(int configurationIndex){ return m_map[configurationIndex];}
- HDC getDC(int configId){return m_map[configId].dc;}
+ DisplayInfo& getInfo(int configurationIndex){ return getTLS()->m_map[configurationIndex];}
+ HDC getDC(int configId){return getTLS()->m_map[configId].dc;}
void setInfo(int configurationIndex,const DisplayInfo& info);
- bool isPixelFormatSet(int cfgId){ return m_map[cfgId].isPixelFormatSet;}
- void pixelFormatWasSet(int cfgId){m_map[cfgId].isPixelFormatSet = true;}
+ bool isPixelFormatSet(int cfgId){ return getTLS()->m_map[cfgId].isPixelFormatSet;}
+ void pixelFormatWasSet(int cfgId){getTLS()->m_map[cfgId].isPixelFormatSet = true;}
bool infoExists(int configurationIndex);
void releaseAll();
-private:
- std::map<int,DisplayInfo> m_map;
};
void WinDisplay::releaseAll(){
- for(std::map<int,DisplayInfo>::iterator it = m_map.begin(); it != m_map.end();it++){
+ TlsData * tls = getTLS();
+
+ for(std::map<int,DisplayInfo>::iterator it = tls->m_map.begin(); it != tls->m_map.end();it++){
if((*it).second.hwnd){
DestroyWindow((*it).second.hwnd);
}
@@ -57,11 +73,11 @@
}
bool WinDisplay::infoExists(int configurationIndex){
- return m_map.find(configurationIndex) == m_map.end();
+ return getTLS()->m_map.find(configurationIndex) != getTLS()->m_map.end();
}
void WinDisplay::setInfo(int configurationIndex,const DisplayInfo& info){
- m_map[configurationIndex] = info;
+ getTLS()->m_map[configurationIndex] = info;
}
struct WglExtProcs{
@@ -193,6 +209,7 @@
}
EGLNativeInternalDisplayType getDefaultDisplay() {
+ if (!s_tlsIndex) s_tlsIndex = TlsAlloc();
WinDisplay* dpy = new WinDisplay();
HWND hwnd = createDummyWindow();
@@ -202,6 +219,7 @@
}
EGLNativeInternalDisplayType getInternalDisplay(EGLNativeDisplayType display){
+ if (!s_tlsIndex) s_tlsIndex = TlsAlloc();
WinDisplay* dpy = new WinDisplay();
dpy->setInfo(WinDisplay::DEFAULT_DISPLAY,DisplayInfo(display,NULL));
return dpy;
@@ -210,7 +228,7 @@
static HDC getDummyDC(EGLNativeInternalDisplayType display,int cfgId){
HDC dpy = NULL;
- if(display->infoExists(cfgId)){
+ if(!display->infoExists(cfgId)){
HWND hwnd = createDummyWindow();
dpy = GetDC(hwnd);
display->setInfo(cfgId,DisplayInfo(dpy,hwnd));
diff --git a/tools/emulator/opengl/host/libs/Translator/EGL/MacNative.m b/tools/emulator/opengl/host/libs/Translator/EGL/MacNative.m
index 225a79f..6828655 100644
--- a/tools/emulator/opengl/host/libs/Translator/EGL/MacNative.m
+++ b/tools/emulator/opengl/host/libs/Translator/EGL/MacNative.m
@@ -18,6 +18,50 @@
#include <OpenGL/OpenGL.h>
#include "MacPixelFormatsAttribs.h"
+//
+// EmuGLContext inherit from NSOpenGLContext
+// and adds binding state for the context to know
+// if it was last bounded to a pbuffer or a window.
+// This is because after the context was bounded to
+// a Pbuffer, before we bind it to a window we must
+// release it form the pbuffer by calling the
+// clearDrawable method. We do not want to call clearDrawable
+// more than really needed since when it is called at a time
+// that a window is bounded to the context it will clear the
+// window content causing flickering effect.
+// Thererfore we call clearDrawable only when we bind the context
+// to a window and it was previously bound to a Pbuffer.
+//
+@interface EmuGLContext : NSOpenGLContext {
+ @private
+ int boundToPbuffer;
+ int boundToWin;
+}
+
+- (id) initWithFormat:(NSOpenGLPixelFormat *)pixelFormat shareContext:(NSOpenGLContext *)share;
+- (void) preBind:(int)forPbuffer;
+@end
+
+@implementation EmuGLContext
+- (id) initWithFormat:(NSOpenGLPixelFormat *)pixelFormat shareContext:(NSOpenGLContext *)share
+{
+ self = [super initWithFormat:pixelFormat shareContext:share];
+ if (self != nil) {
+ boundToPbuffer = 0;
+ boundToWin = 0;
+ }
+ return self;
+}
+
+- (void) preBind:(int)forPbuffer
+{
+ if ((!forPbuffer && boundToPbuffer)) {
+ [self clearDrawable];
+ }
+ boundToPbuffer = forPbuffer;
+ boundToWin = !boundToPbuffer;
+}
+@end
int getNumPixelFormats(){
int size;
@@ -38,17 +82,17 @@
void* nsCreateContext(void* format,void* share){
NSOpenGLPixelFormat* frmt = (NSOpenGLPixelFormat*)format;
- return [[NSOpenGLContext alloc] initWithFormat:frmt shareContext:share];
+ return [[EmuGLContext alloc] initWithFormat:frmt shareContext:share];
}
void nsPBufferMakeCurrent(void* context,void* nativePBuffer,int level){
- NSOpenGLContext* ctx = (NSOpenGLContext *)context;
+ EmuGLContext* ctx = (EmuGLContext *)context;
NSOpenGLPixelBuffer* pbuff = (NSOpenGLPixelBuffer *)nativePBuffer;
if(ctx == nil){
[NSOpenGLContext clearCurrentContext];
} else {
if(pbuff != nil){
- [ctx clearDrawable];
+ [ctx preBind:1];
[ctx setPixelBuffer:pbuff cubeMapFace:0 mipMapLevel:level currentVirtualScreen:0];
[ctx makeCurrentContext];
}
@@ -56,11 +100,12 @@
}
void nsWindowMakeCurrent(void* context,void* nativeWin){
- NSOpenGLContext* ctx = (NSOpenGLContext *)context;
+ EmuGLContext* ctx = (EmuGLContext *)context;
NSView* win = (NSView *)nativeWin;
if(ctx == nil){
[NSOpenGLContext clearCurrentContext];
} else if (win != nil) {
+ [ctx preBind:0];
[ctx setView: win];
[ctx makeCurrentContext];
}
@@ -82,7 +127,7 @@
void nsDestroyContext(void* context){
- NSOpenGLContext *ctx = (NSOpenGLContext*)context;
+ EmuGLContext *ctx = (EmuGLContext*)context;
if(ctx != nil){
[ctx release];
}
diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp b/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp
index 76b0e8a..3c48a43 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp
@@ -317,6 +317,7 @@
GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus(GLenum target){
GET_CTX_RET(GL_FRAMEBUFFER_COMPLETE);
RET_AND_SET_ERROR_IF(!GLESv2Validate::framebufferTarget(target),GL_INVALID_ENUM,GL_FRAMEBUFFER_COMPLETE);
+ ctx->drawValidate();
return ctx->dispatcher().glCheckFramebufferStatusEXT(target);
}
@@ -346,11 +347,19 @@
GL_APICALL void GL_APIENTRY glCompileShader(GLuint shader){
GET_CTX();
if(ctx->shareGroup().Ptr()) {
- const GLuint globalShaderName = ctx->shareGroup()->getGlobalName(SHADER,shader);
+ const GLuint globalShaderName = ctx->shareGroup()->getGlobalName(SHADER,shader);
SET_ERROR_IF(globalShaderName==0, GL_INVALID_VALUE);
ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,shader);
SET_ERROR_IF(objData.Ptr()->getDataType()!= SHADER_DATA,GL_INVALID_OPERATION);
- ctx->dispatcher().glCompileShader(globalShaderName);
+ ShaderParser* sp = (ShaderParser*)objData.Ptr();
+ ctx->dispatcher().glCompileShader(globalShaderName);
+
+ GLsizei infoLogLength=0;
+ GLchar* infoLog;
+ ctx->dispatcher().glGetShaderiv(globalShaderName,GL_INFO_LOG_LENGTH,&infoLogLength);
+ infoLog = new GLchar[infoLogLength+1];
+ ctx->dispatcher().glGetShaderInfoLog(globalShaderName,infoLogLength,NULL,infoLog);
+ sp->setInfoLog(infoLog);
}
}
@@ -1151,6 +1160,16 @@
params[0] = GL_FALSE;
}
break;
+ case GL_INFO_LOG_LENGTH:
+ {
+ ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,program);
+ SET_ERROR_IF(!objData.Ptr() ,GL_INVALID_OPERATION);
+ SET_ERROR_IF(objData.Ptr()->getDataType()!=PROGRAM_DATA,GL_INVALID_OPERATION);
+ ProgramData* programData = (ProgramData*)objData.Ptr();
+ GLint logLength = strlen(programData->getInfoLog());
+ params[0] = (logLength>0) ? logLength+1 : 0;
+ }
+ break;
default:
ctx->dispatcher().glGetProgramiv(globalProgramName,pname,params);
}
@@ -1162,7 +1181,30 @@
if(ctx->shareGroup().Ptr()) {
const GLuint globalProgramName = ctx->shareGroup()->getGlobalName(SHADER,program);
SET_ERROR_IF(globalProgramName==0, GL_INVALID_VALUE);
- ctx->dispatcher().glGetProgramInfoLog(globalProgramName,bufsize,length,infolog);
+ ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,program);
+ SET_ERROR_IF(!objData.Ptr() ,GL_INVALID_OPERATION);
+ SET_ERROR_IF(objData.Ptr()->getDataType()!=PROGRAM_DATA,GL_INVALID_OPERATION);
+ ProgramData* programData = (ProgramData*)objData.Ptr();
+
+ if (bufsize==0) {
+ if (length) {
+ *length = 0;
+ }
+ return;
+ }
+
+ GLsizei logLength;
+ logLength = strlen(programData->getInfoLog());
+
+ GLsizei returnLength=0;
+ if (infolog) {
+ returnLength = bufsize-1 < logLength ? bufsize-1 : logLength;
+ strncpy(infolog,programData->getInfoLog(),returnLength+1);
+ infolog[returnLength] = '\0';
+ }
+ if (length) {
+ *length = returnLength;
+ }
}
}
@@ -1171,16 +1213,53 @@
if(ctx->shareGroup().Ptr()) {
const GLuint globalShaderName = ctx->shareGroup()->getGlobalName(SHADER,shader);
SET_ERROR_IF(globalShaderName==0, GL_INVALID_VALUE);
- ctx->dispatcher().glGetShaderiv(globalShaderName,pname,params);
+ switch(pname) {
+ case GL_INFO_LOG_LENGTH:
+ {
+ ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,shader);
+ SET_ERROR_IF(!objData.Ptr() ,GL_INVALID_OPERATION);
+ SET_ERROR_IF(objData.Ptr()->getDataType()!=SHADER_DATA,GL_INVALID_OPERATION);
+ ShaderParser* sp = (ShaderParser*)objData.Ptr();
+ GLint logLength = strlen(sp->getInfoLog());
+ params[0] = (logLength>0) ? logLength+1 : 0;
+ }
+ break;
+ default:
+ ctx->dispatcher().glGetShaderiv(globalShaderName,pname,params);
+ }
}
}
+
GL_APICALL void GL_APIENTRY glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog){
GET_CTX();
if(ctx->shareGroup().Ptr()) {
const GLuint globalShaderName = ctx->shareGroup()->getGlobalName(SHADER,shader);
SET_ERROR_IF(globalShaderName==0, GL_INVALID_VALUE);
- ctx->dispatcher().glGetShaderInfoLog(globalShaderName,bufsize,length,infolog);
+ ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,shader);
+ SET_ERROR_IF(!objData.Ptr() ,GL_INVALID_OPERATION);
+ SET_ERROR_IF(objData.Ptr()->getDataType()!=SHADER_DATA,GL_INVALID_OPERATION);
+ ShaderParser* sp = (ShaderParser*)objData.Ptr();
+
+ if (bufsize==0) {
+ if (length) {
+ *length = 0;
+ }
+ return;
+ }
+
+ GLsizei logLength;
+ logLength = strlen(sp->getInfoLog());
+
+ GLsizei returnLength=0;
+ if (infolog) {
+ returnLength = bufsize-1 <logLength ? bufsize-1 : logLength;
+ strncpy(infolog,sp->getInfoLog(),returnLength+1);
+ infolog[returnLength] = '\0';
+ }
+ if (length) {
+ *length = returnLength;
+ }
}
}
@@ -1218,11 +1297,16 @@
SET_ERROR_IF(!objData.Ptr(),GL_INVALID_OPERATION);
SET_ERROR_IF(objData.Ptr()->getDataType()!=SHADER_DATA,GL_INVALID_OPERATION);
const char* src = ((ShaderParser*)objData.Ptr())->getOriginalSrc();
- int srcLength = strlen(src);
+ int srcLength = 0;
+ if (src) {
+ srcLength = strlen(src);
+ }
int returnLength = bufsize<srcLength ? bufsize-1 : srcLength;
- strncpy(source,src, returnLength);
- source[returnLength] = '\0';
+ if (returnLength) {
+ strncpy(source,src, returnLength);
+ source[returnLength] = '\0';
+ }
if (length)
*length = returnLength;
@@ -1501,6 +1585,13 @@
}
}
programData->setLinkStatus(linkStatus);
+
+ GLsizei infoLogLength=0;
+ GLchar* infoLog;
+ ctx->dispatcher().glGetProgramiv(globalProgramName,GL_INFO_LOG_LENGTH,&infoLogLength);
+ infoLog = new GLchar[infoLogLength+1];
+ ctx->dispatcher().glGetProgramInfoLog(globalProgramName,infoLogLength,NULL,infoLog);
+ programData->setInfoLog(infoLog);
}
}
@@ -1834,7 +1925,15 @@
SET_ERROR_IF(globalProgramName==0, GL_INVALID_VALUE);
ObjectDataPtr objData = ctx->shareGroup()->getObjectData(SHADER,program);
SET_ERROR_IF(objData.Ptr()->getDataType()!=PROGRAM_DATA,GL_INVALID_OPERATION);
+ ProgramData* programData = (ProgramData*)objData.Ptr();
ctx->dispatcher().glValidateProgram(globalProgramName);
+
+ GLsizei infoLogLength=0;
+ GLchar* infoLog;
+ ctx->dispatcher().glGetProgramiv(globalProgramName,GL_INFO_LOG_LENGTH,&infoLogLength);
+ infoLog = new GLchar[infoLogLength+1];
+ ctx->dispatcher().glGetProgramInfoLog(globalProgramName,infoLogLength,NULL,infoLog);
+ programData->setInfoLog(infoLog);
}
}
diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.cpp b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.cpp
index b3e4a1e..656c782 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.cpp
@@ -20,9 +20,23 @@
ProgramData::ProgramData() : ObjectData(PROGRAM_DATA),
AttachedVertexShader(0),
AttachedFragmentShader(0),
- LinkStatus(GL_FALSE) {}
+ LinkStatus(GL_FALSE) {
+ infoLog = new GLchar[1];
+ infoLog[0] = '\0';
+}
-ProgramData::~ProgramData () {};
+ProgramData::~ProgramData () {
+ delete[] infoLog;
+};
+
+void ProgramData::setInfoLog(GLchar* log) {
+ delete[] infoLog;
+ infoLog = log;
+}
+
+GLchar* ProgramData::getInfoLog() {
+ return infoLog;
+}
GLuint ProgramData::getAttachedVertexShader() {
return AttachedVertexShader;
diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.h b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.h
index 2bf7524..a79574a 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.h
+++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ProgramData.h
@@ -32,9 +32,13 @@
void setLinkStatus(GLint status);
GLint getLinkStatus();
+ void setInfoLog(GLchar *log);
+ GLchar* getInfoLog();
+
private:
GLuint AttachedVertexShader;
GLuint AttachedFragmentShader;
GLint LinkStatus;
+ GLchar* infoLog;
};
#endif
diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.cpp b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.cpp
index 2f27639..82cf5b5 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.cpp
@@ -3,16 +3,30 @@
ShaderParser::ShaderParser():ObjectData(SHADER_DATA),
m_type(0),
- m_parsedLines(NULL) {};
+ m_originalSrc(NULL),
+ m_parsedLines(NULL) {
+ m_infoLog = new GLchar[1];
+ m_infoLog[0] = '\0';
+};
ShaderParser::ShaderParser(GLenum type):ObjectData(SHADER_DATA),
m_type(type),
- m_parsedLines(NULL) {};
+ m_originalSrc(NULL),
+ m_parsedLines(NULL) {
+
+ m_infoLog = new GLchar[1];
+ m_infoLog[0] = '\0';
+};
void ShaderParser::setSrc(const Version& ver,GLsizei count,const GLchar** strings,const GLint* length){
for(int i = 0;i<count;i++){
m_src.append(strings[i]);
}
+ //store original source
+ if (m_originalSrc)
+ free(m_originalSrc);
+ m_originalSrc = strdup(m_src.c_str());
+
clearParsedSrc();
// parseGLSLversion must be called first since #version should be the
@@ -35,7 +49,7 @@
//XXX: Until proved otherwise, glsl doesn't know/use those precision macros, so we omit then
parseOmitPrecision();
#endif
-
+ parseLineNumbers();
parseOriginalSrc();
}
const GLchar** ShaderParser::parsedLines() {
@@ -44,7 +58,12 @@
};
const char* ShaderParser::getOriginalSrc(){
- return m_src.c_str();
+ return m_originalSrc;
+}
+
+void ShaderParser::parseLineNumbers()
+{
+ m_parsedSrc += "#line 1\n";
}
void ShaderParser::parseOriginalSrc() {
@@ -166,9 +185,107 @@
"#define lowp \n"
"#define mediump \n"
"#define highp \n"
- "#define precision \n"
};
m_parsedSrc+=defines;
+
+ //
+ // parse the source and blank out precision statements
+ // which has the following syntax:
+ // precision {qualifier} {type};
+ // where {qualifier} is one of lowp,mediump or hightp
+ // type is any valid GLES defined type (we do not check that here!)
+ // NOTE: This is needed in order to workaround driver bug in
+ // Intel/Linux where the compiler does not get statement like
+ // "float;", otherwise we could just define a macro named
+ // precision to be empty.
+ //
+ const char *src = m_src.c_str();
+
+ enum {
+ PRECISION,
+ QUALIFIER,
+ SEMICOLON
+ } statementState = PRECISION;
+ const char *precision = NULL;
+ const char *delimiter = NULL;
+
+ enum {
+ PARSE_NONE,
+ PARSE_IN_C_COMMENT,
+ PARSE_IN_LINE_COMMENT
+ } parseState = PARSE_NONE;
+ const char *c = src;
+ const char *t = NULL;
+
+ #define IS_DELIMITER(c) ( (c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' )
+ #define IS_TOKEN_START(c) ( ((c) >= 'a' && (c) <='z') || ((c) >= 'A' && (c) <= 'Z') )
+ #define IS_TOKEN_DELIMITER(c) ( IS_DELIMITER(c) || (c) == ';' )
+
+ while( c && *c != '\0') {
+ if (parseState == PARSE_IN_C_COMMENT) {
+ if (*c == '*' && *(c+1) == '/') {
+ parseState = PARSE_NONE;
+ c += 2;
+ }
+ else c++;
+ }
+ else if (parseState == PARSE_IN_LINE_COMMENT) {
+ if (*c == '\n') {
+ parseState = PARSE_NONE;
+ }
+ c++;
+ }
+ else if (*c == '/' && *(c+1) == '/') {
+ parseState = PARSE_IN_LINE_COMMENT;
+ c += 2;
+ }
+ else if (*c == '/' && *(c+1) == '*') {
+ parseState = PARSE_IN_C_COMMENT;
+ c += 2;
+ }
+ else if (t && IS_TOKEN_DELIMITER(*c)) {
+ int tokenLen = c - t;
+ switch (statementState) {
+ case PRECISION:
+ if (tokenLen == 9 && !strncmp(t,"precision",9)) {
+ statementState = QUALIFIER;
+ precision = t;
+ }
+ break;
+ case QUALIFIER:
+ if ((tokenLen == 4 && !strncmp(t,"lowp",4)) ||
+ (tokenLen == 7 && !strncmp(t,"mediump",7)) ||
+ (tokenLen == 5 && !strncmp(t,"highp",5))) {
+ statementState = SEMICOLON;
+ }
+ else {
+ statementState = PRECISION;
+ }
+ break;
+ case SEMICOLON:
+ if (*c == ';') {
+ for (char *r = (char *)precision; r<=c ; ++r) {
+ *r = ' '; //blank the character
+ }
+ }
+ statementState = PRECISION; //search for the next precision line
+ break;
+ default:
+ break;
+ }
+ c++;
+ t = NULL;
+ }
+ else if (IS_DELIMITER(*c)) {
+ c++;
+ }
+ else {
+ if (!t && IS_TOKEN_START(*c)) {
+ t = c;
+ }
+ c++;
+ }
+ }
}
void ShaderParser::parseExtendDefaultPrecision(){
@@ -191,6 +308,20 @@
return m_type;
}
+void ShaderParser::setInfoLog(GLchar* infoLog)
+{
+ delete[] m_infoLog;
+ m_infoLog = infoLog;
+}
+
+GLchar* ShaderParser::getInfoLog()
+{
+ return m_infoLog;
+}
+
ShaderParser::~ShaderParser(){
clearParsedSrc();
+ if (m_originalSrc)
+ free(m_originalSrc);
+ delete[] m_infoLog;
}
diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.h b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.h
index 32d3b5f..8c0ff3e 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.h
+++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/ShaderParser.h
@@ -16,17 +16,23 @@
GLenum getType();
~ShaderParser();
+ void setInfoLog(GLchar * infoLog);
+ GLchar* getInfoLog();
+
private:
void parseOriginalSrc();
void parseGLSLversion();
void parseBuiltinConstants();
void parseOmitPrecision();
void parseExtendDefaultPrecision();
+ void parseLineNumbers();
void clearParsedSrc();
GLenum m_type;
+ char* m_originalSrc;
std::string m_src;
std::string m_parsedSrc;
GLchar* m_parsedLines;
+ GLchar* m_infoLog;
};
#endif
diff --git a/tools/emulator/opengl/host/libs/Translator/GLcommon/GLDispatch.cpp b/tools/emulator/opengl/host/libs/Translator/GLcommon/GLDispatch.cpp
index 956220d..abed760 100644
--- a/tools/emulator/opengl/host/libs/Translator/GLcommon/GLDispatch.cpp
+++ b/tools/emulator/opengl/host/libs/Translator/GLcommon/GLDispatch.cpp
@@ -37,7 +37,7 @@
static osUtils::dynLibrary* libGL = osUtils::dynLibrary::open("opengl32");
ret = (GL_FUNC_PTR)wglGetProcAddress(funcName);
#elif defined(__APPLE__)
- static osUtils::dynLibrary* libGL = osUtils::dynLibrary::open("libGL.dylib");
+ static osUtils::dynLibrary* libGL = osUtils::dynLibrary::open("/System/Library/Frameworks/OpenGL.framework/OpenGL");
#endif
if(!ret && libGL){
ret = libGL->findSymbol(funcName);
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/Android.mk b/tools/emulator/opengl/host/libs/libOpenglRender/Android.mk
index 1096e10..3beee95 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/Android.mk
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/Android.mk
@@ -39,6 +39,7 @@
RenderServer.cpp
$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include)
+$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
# use Translator's egl/gles headers
LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/Translator/include
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
index b0a1ba2..950abab 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
@@ -72,15 +72,37 @@
s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ //
+ // create another texture for that colorbuffer for blit
+ //
+ s_gl.glGenTextures(1, &cb->m_blitTex);
+ s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
+ s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat,
+ p_width, p_height, 0,
+ texInternalFormat,
+ GL_UNSIGNED_BYTE, NULL);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
cb->m_width = p_width;
cb->m_height = p_height;
+ cb->m_internalFormat = texInternalFormat;
if (fb->getCaps().has_eglimage_texture_2d) {
cb->m_eglImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
- fb->getContext(),
+ s_egl.eglGetCurrentContext(),
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)cb->m_tex,
NULL);
+
+ cb->m_blitEGLImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
+ s_egl.eglGetCurrentContext(),
+ EGL_GL_TEXTURE_2D_KHR,
+ (EGLClientBuffer)cb->m_blitTex,
+ NULL);
}
fb->unbind_locked();
@@ -90,7 +112,8 @@
ColorBuffer::ColorBuffer() :
m_tex(0),
m_eglImage(NULL),
- m_fbo(0)
+ m_fbo(0),
+ m_internalFormat(0)
{
}
@@ -156,8 +179,8 @@
return false;
}
- s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -176,6 +199,92 @@
return true;
}
+bool ColorBuffer::blitFromCurrentReadBuffer()
+{
+ RenderThreadInfo *tInfo = getRenderThreadInfo();
+ if (!tInfo->currContext.Ptr()) {
+ // no Current context
+ return false;
+ }
+
+ //
+ // Create a temporary texture inside the current context
+ // from the blit_texture EGLImage and copy the pixels
+ // from the current read buffer to that texture
+ //
+ GLuint tmpTex;
+ GLint currTexBind;
+ if (tInfo->currContext->isGL2()) {
+ s_gl2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+ s_gl2.glGenTextures(1,&tmpTex);
+ s_gl2.glBindTexture(GL_TEXTURE_2D, tmpTex);
+ s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+ s_gl2.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
+ 0, 0, m_width, m_height, 0);
+ }
+ else {
+ s_gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+ s_gl.glGenTextures(1,&tmpTex);
+ s_gl.glBindTexture(GL_TEXTURE_2D, tmpTex);
+ s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+ s_gl.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
+ 0, 0, m_width, m_height, 0);
+ }
+
+
+ //
+ // Now bind the frame buffer context and blit from
+ // m_blitTex into m_tex
+ //
+ FrameBuffer *fb = FrameBuffer::getFB();
+ if (fb->bind_locked()) {
+
+ //
+ // bind FBO object which has this colorbuffer as render target
+ //
+ if (bind_fbo()) {
+
+ //
+ // save current viewport and match it to the current
+ // colorbuffer size
+ //
+ GLint vport[4];
+ s_gl.glGetIntegerv(GL_VIEWPORT, vport);
+ s_gl.glViewport(0, 0, m_width, m_height);
+
+ // render m_blitTex
+ s_gl.glBindTexture(GL_TEXTURE_2D, m_blitTex);
+ s_gl.glEnable(GL_TEXTURE_2D);
+ s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ drawTexQuad(); // this will render the texture flipped
+
+ // unbind the fbo
+ s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+
+ // restrore previous viewport
+ s_gl.glViewport(vport[0], vport[1], vport[2], vport[3]);
+ }
+
+ // unbind from the FrameBuffer context
+ fb->unbind_locked();
+ }
+
+ //
+ // delete the temporary texture and restore the texture binding
+ // inside the current context
+ //
+ if (tInfo->currContext->isGL2()) {
+ s_gl2.glDeleteTextures(1, &tmpTex);
+ s_gl2.glBindTexture(GL_TEXTURE_2D, currTexBind);
+ }
+ else {
+ s_gl.glDeleteTextures(1, &tmpTex);
+ s_gl.glBindTexture(GL_TEXTURE_2D, currTexBind);
+ }
+
+ return true;
+}
+
bool ColorBuffer::bindToTexture()
{
if (m_eglImage) {
@@ -246,6 +355,7 @@
{
s_gl.glBindTexture(GL_TEXTURE_2D, m_tex);
s_gl.glEnable(GL_TEXTURE_2D);
+ s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
drawTexQuad();
return true;
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
index 4bc495a..5e01dcc 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
@@ -38,6 +38,7 @@
bool post();
bool bindToTexture();
bool bindToRenderbuffer();
+ bool blitFromCurrentReadBuffer();
private:
ColorBuffer();
@@ -46,10 +47,13 @@
private:
GLuint m_tex;
+ GLuint m_blitTex;
EGLImageKHR m_eglImage;
+ EGLImageKHR m_blitEGLImage;
GLuint m_width;
GLuint m_height;
GLuint m_fbo;
+ GLenum m_internalFormat;
};
typedef SmartPtr<ColorBuffer> ColorBufferPtr;
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.cpp
index 4622161..089f1da 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.cpp
@@ -87,88 +87,22 @@
s_egl.eglGetConfigs(dpy, configs, nConfigs, &nConfigs);
//
- // Find number of usable configs which support pbuffer rendering
- // for each ES and ES2 as well as number of configs supporting
- // EGL_BIND_TO_TEXTURE_RGBA for each of ES and ES2.
+ // copy the config attributes, filter out
+ // configs we do not want to support.
//
- const int GL = 0;
- const int GL1 = 1;
- const int GL2 = 2;
- int numPbuf[3] = {0, 0, 0};
- int numBindToTexture[3] = {0, 0, 0};
- for (int i=0; i<nConfigs; i++) {
- GLint depthSize, stencilSize;
- GLint renderType, surfaceType;
- GLint bindToTexture;
-
- s_egl.eglGetConfigAttrib(dpy, configs[i], EGL_DEPTH_SIZE, &depthSize);
- s_egl.eglGetConfigAttrib(dpy, configs[i], EGL_STENCIL_SIZE, &stencilSize);
- s_egl.eglGetConfigAttrib(dpy, configs[i], EGL_RENDERABLE_TYPE, &renderType);
- s_egl.eglGetConfigAttrib(dpy, configs[i], EGL_SURFACE_TYPE, &surfaceType);
- if (depthSize > 0 && stencilSize > 0 &&
- (surfaceType & EGL_PBUFFER_BIT) != 0) {
-
- numPbuf[GL]++;
-
- if ((renderType & EGL_OPENGL_ES_BIT) != 0) {
- numPbuf[GL1]++;
- }
-
- if ((renderType & EGL_OPENGL_ES2_BIT) != 0) {
- numPbuf[GL2]++;
- }
-
- s_egl.eglGetConfigAttrib(dpy, configs[i],
- EGL_BIND_TO_TEXTURE_RGBA, &bindToTexture);
- if (bindToTexture) {
- numBindToTexture[GL]++;
- if ((renderType & EGL_OPENGL_ES_BIT) != 0) {
- numBindToTexture[GL1]++;
- }
-
- if ((renderType & EGL_OPENGL_ES2_BIT) != 0) {
- numBindToTexture[GL2]++;
- }
- }
- }
- }
-
- bool useOnlyPbuf = false;
- bool useOnlyBindToTexture = false;
- int numConfigs = nConfigs;
- if ( numPbuf[GL1] > 0 &&
- (!caps.hasGL2 || numPbuf[GL2] > 0)) {
- useOnlyPbuf = true;
- numConfigs = numPbuf[GL];
- }
- if (useOnlyPbuf &&
- !(caps.has_eglimage_texture_2d &&
- caps.has_eglimage_renderbuffer) &&
- numBindToTexture[GL1] > 0 &&
- (!caps.hasGL2 || numBindToTexture[GL2] > 0)) {
- useOnlyBindToTexture = true;
- numConfigs = numBindToTexture[GL];
- ret = INIT_CONFIG_HAS_BIND_TO_TEXTURE;
- }
- else {
- ret = INIT_CONFIG_PASSED;
- }
-
int j = 0;
s_fbConfigs = new FBConfig*[nConfigs];
for (int i=0; i<nConfigs; i++) {
- if (useOnlyBindToTexture) {
- EGLint bindToTexture;
- s_egl.eglGetConfigAttrib(dpy, configs[i],
- EGL_BIND_TO_TEXTURE_RGBA, &bindToTexture);
- if (!bindToTexture) continue;
- }
- else if (useOnlyPbuf) {
- EGLint surfaceType;
- s_egl.eglGetConfigAttrib(dpy, configs[i],
- EGL_SURFACE_TYPE, &surfaceType);
- if (!(surfaceType & EGL_PBUFFER_BIT)) continue;
- }
+
+ //
+ // filter out configs which does not support pbuffers.
+ // we only support pbuffer configs since we use a pbuffer
+ // handle to bind a guest created window object.
+ //
+ EGLint surfaceType;
+ s_egl.eglGetConfigAttrib(dpy, configs[i],
+ EGL_SURFACE_TYPE, &surfaceType);
+ if (!(surfaceType & EGL_PBUFFER_BIT)) continue;
//
// Filter out not RGB configs
@@ -184,7 +118,8 @@
s_numConfigs = j;
delete[] configs;
- return ret;
+
+ return s_numConfigs > 0 ? INIT_CONFIG_PASSED : INIT_CONFIG_FAILED;
}
const FBConfig *FBConfig::get(int p_config)
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.h b/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.h
index 8e0356a..6388549 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/FBConfig.h
@@ -23,8 +23,7 @@
enum InitConfigStatus {
INIT_CONFIG_FAILED = 0,
- INIT_CONFIG_PASSED = 1,
- INIT_CONFIG_HAS_BIND_TO_TEXTURE = 2
+ INIT_CONFIG_PASSED = 1
};
class FBConfig
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp
index 50ee9cb..ef38bec 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp
@@ -21,19 +21,19 @@
#include "GL2Dispatch.h"
#include "ThreadInfo.h"
#include <stdio.h>
+#include "TimeUtils.h"
FrameBuffer *FrameBuffer::s_theFrameBuffer = NULL;
HandleType FrameBuffer::s_nextHandle = 0;
#ifdef WITH_GLES2
-static const char *getGLES2ExtensionString(EGLDisplay p_dpy,
- EGLNativeWindowType p_window)
+static const char *getGLES2ExtensionString(EGLDisplay p_dpy)
{
EGLConfig config;
EGLSurface surface;
GLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
@@ -44,9 +44,13 @@
return NULL;
}
- surface = s_egl.eglCreateWindowSurface(p_dpy, config,
- p_window,
- NULL);
+ EGLint pbufAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ surface = s_egl.eglCreatePbufferSurface(p_dpy, config, pbufAttribs);
if (surface == EGL_NO_SURFACE) {
return NULL;
}
@@ -85,51 +89,28 @@
void FrameBuffer::finalize(){
if(s_theFrameBuffer){
+ s_theFrameBuffer->removeSubWindow();
s_theFrameBuffer->m_colorbuffers.clear();
s_theFrameBuffer->m_windows.clear();
s_theFrameBuffer->m_contexts.clear();
s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
- s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_eglSurface);
s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_eglContext);
- if (s_theFrameBuffer->m_subWin) {
- destroySubWindow(s_theFrameBuffer->m_subWinDisplay,
- s_theFrameBuffer->m_subWin);
- }
- delete s_theFrameBuffer;
+ s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufContext);
+ s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufSurface);
s_theFrameBuffer = NULL;
}
}
-bool FrameBuffer::initialize(FBNativeWindowType p_window,
- int p_x, int p_y,
- int p_width, int p_height)
+bool FrameBuffer::initialize(int width, int height)
{
if (s_theFrameBuffer != NULL) {
return true;
}
//
- // Load EGL Plugin
- //
- if (!init_egl_dispatch()) {
- // Failed to load EGL
- printf("Failed to init_egl_dispatch\n");
- return false;
- }
-
- //
- // Load GLES Plugin
- //
- if (!init_gl_dispatch()) {
- // Failed to load GLES
- ERR("Failed to init_gl_dispatch\n");
- return false;
- }
-
- //
// allocate space for the FrameBuffer object
//
- FrameBuffer *fb = new FrameBuffer(p_x, p_y, p_width, p_height);
+ FrameBuffer *fb = new FrameBuffer(width, height);
if (!fb) {
ERR("Failed to create fb\n");
return false;
@@ -143,7 +124,7 @@
fb->m_caps.hasGL2 = false;
}
else {
- fb->m_caps.hasGL2 = init_gl2_dispatch();
+ fb->m_caps.hasGL2 = s_gl2_enabled;
}
#else
fb->m_caps.hasGL2 = false;
@@ -168,10 +149,6 @@
DBG("egl: %d %d\n", fb->m_caps.eglMajor, fb->m_caps.eglMinor);
s_egl.eglBindAPI(EGL_OPENGL_ES_API);
- fb->m_nativeWindow = p_window;
-
- fb->m_subWin = createSubWindow(p_window,&fb->m_subWinDisplay,p_x,p_y,p_width,p_height);
-
//
// if GLES2 plugin has loaded - try to make GLES2 context and
// get GLES2 extension string
@@ -179,7 +156,7 @@
const char *gl2Extensions = NULL;
#ifdef WITH_GLES2
if (fb->m_caps.hasGL2) {
- gl2Extensions = getGLES2ExtensionString(fb->m_eglDisplay, fb->m_subWin);
+ gl2Extensions = getGLES2ExtensionString(fb->m_eglDisplay);
if (!gl2Extensions) {
// Could not create GLES2 context - drop GL2 capability
fb->m_caps.hasGL2 = false;
@@ -188,8 +165,7 @@
#endif
//
- // Create EGL context and Surface attached to the native window, for
- // framebuffer post rendering.
+ // Create EGL context for framebuffer post rendering.
//
#if 0
GLint configAttribs[] = {
@@ -202,35 +178,25 @@
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_NONE
};
#endif
- EGLConfig eglConfig;
+
int n;
if (!s_egl.eglChooseConfig(fb->m_eglDisplay, configAttribs,
- &eglConfig, 1, &n)) {
+ &fb->m_eglConfig, 1, &n)) {
ERR("Failed on eglChooseConfig\n");
delete fb;
return false;
}
- EGLNativeDisplayType dpy;
- fb->m_eglSurface = s_egl.eglCreateWindowSurface(fb->m_eglDisplay, eglConfig,
- fb->m_subWin,
- NULL);
- if (fb->m_eglSurface == EGL_NO_SURFACE) {
- ERR("Failed to create surface\n");
- delete fb;
- return false;
- }
-
GLint glContextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 1,
EGL_NONE
};
- fb->m_eglContext = s_egl.eglCreateContext(fb->m_eglDisplay, eglConfig,
+ fb->m_eglContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
EGL_NO_CONTEXT,
glContextAttribs);
if (fb->m_eglContext == EGL_NO_CONTEXT) {
@@ -239,6 +205,43 @@
return false;
}
+ //
+ // Create another context which shares with the eglContext to be used
+ // when we bind the pbuffer. That prevent switching drawable binding
+ // back and forth on framebuffer context.
+ // The main purpose of it is to solve a "blanking" behaviour we see on
+ // on Mac platform when switching binded drawable for a context however
+ // it is more efficient on other platforms as well.
+ //
+ fb->m_pbufContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
+ fb->m_eglContext,
+ glContextAttribs);
+ if (fb->m_pbufContext == EGL_NO_CONTEXT) {
+ printf("Failed to create Pbuffer Context 0x%x\n", s_egl.eglGetError());
+ delete fb;
+ return false;
+ }
+
+ //
+ // create a 1x1 pbuffer surface which will be used for binding
+ // the FB context.
+ // The FB output will go to a subwindow, if one exist.
+ //
+ EGLint pbufAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ fb->m_pbufSurface = s_egl.eglCreatePbufferSurface(fb->m_eglDisplay,
+ fb->m_eglConfig,
+ pbufAttribs);
+ if (fb->m_pbufSurface == EGL_NO_SURFACE) {
+ printf("Failed to create pbuf surface for FB 0x%x\n", s_egl.eglGetError());
+ delete fb;
+ return false;
+ }
+
// Make the context current
if (!fb->bind_locked()) {
ERR("Failed to make current\n");
@@ -274,6 +277,18 @@
}
//
+ // Fail initialization if not all of the following extensions
+ // exist:
+ // EGL_KHR_gl_texture_2d_image
+ // GL_OES_EGL_IMAGE (by both GLES implementations [1 and 2])
+ //
+ if (!fb->m_caps.has_eglimage_texture_2d) {
+ ERR("Failed: Missing egl_image related extension(s)\n");
+ delete fb;
+ return false;
+ }
+
+ //
// Initialize set of configs
//
InitConfigStatus configStatus = FBConfig::initConfigList(fb);
@@ -315,20 +330,9 @@
}
//
- // update Pbuffer bind to texture capability based on configs
+ // Initialize some GL state in the pbuffer context
//
- fb->m_caps.has_BindToTexture =
- (configStatus == INIT_CONFIG_HAS_BIND_TO_TEXTURE);
-
-
- //
- // Initialize some GL state
- //
- s_gl.glMatrixMode(GL_PROJECTION);
- s_gl.glLoadIdentity();
- s_gl.glOrthof(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
- s_gl.glMatrixMode(GL_MODELVIEW);
- s_gl.glLoadIdentity();
+ fb->initGLState();
// release the FB context
fb->unbind_locked();
@@ -340,26 +344,99 @@
return true;
}
-FrameBuffer::FrameBuffer(int p_x, int p_y, int p_width, int p_height) :
- m_x(p_x),
- m_y(p_y),
+FrameBuffer::FrameBuffer(int p_width, int p_height) :
m_width(p_width),
m_height(p_height),
m_eglDisplay(EGL_NO_DISPLAY),
m_eglSurface(EGL_NO_SURFACE),
m_eglContext(EGL_NO_CONTEXT),
+ m_pbufContext(EGL_NO_CONTEXT),
m_prevContext(EGL_NO_CONTEXT),
m_prevReadSurf(EGL_NO_SURFACE),
m_prevDrawSurf(EGL_NO_SURFACE),
m_subWin(NULL),
- m_subWinDisplay(NULL)
+ m_subWinDisplay(NULL),
+ m_lastPostedColorBuffer(0),
+ m_zRot(0.0f),
+ m_eglContextInitialized(false),
+ m_statsNumFrames(0),
+ m_statsStartTime(0LL)
{
+ m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
}
FrameBuffer::~FrameBuffer()
{
}
+bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window,
+ int p_x, int p_y,
+ int p_width, int p_height, float zRot)
+{
+ bool success = false;
+
+ if (s_theFrameBuffer) {
+ s_theFrameBuffer->m_lock.lock();
+ FrameBuffer *fb = s_theFrameBuffer;
+ if (!fb->m_subWin) {
+
+ // create native subwindow for FB display output
+ fb->m_subWin = createSubWindow(p_window,
+ &fb->m_subWinDisplay,
+ p_x,p_y,p_width,p_height);
+ if (fb->m_subWin) {
+ fb->m_nativeWindow = p_window;
+
+ // create EGLSurface from the generated subwindow
+ fb->m_eglSurface = s_egl.eglCreateWindowSurface(fb->m_eglDisplay,
+ fb->m_eglConfig,
+ fb->m_subWin,
+ NULL);
+
+ if (fb->m_eglSurface == EGL_NO_SURFACE) {
+ ERR("Failed to create surface\n");
+ destroySubWindow(fb->m_subWinDisplay, fb->m_subWin);
+ fb->m_subWin = NULL;
+ }
+ else if (fb->bindSubwin_locked()) {
+ // Subwin creation was successfull,
+ // update viewport and z rotation and draw
+ // the last posted color buffer.
+ s_gl.glViewport(0, 0, p_width, p_height);
+ fb->m_zRot = zRot;
+ fb->post( fb->m_lastPostedColorBuffer, false );
+ fb->unbind_locked();
+ success = true;
+ }
+ }
+ }
+ s_theFrameBuffer->m_lock.unlock();
+ }
+
+ return success;
+}
+
+bool FrameBuffer::removeSubWindow()
+{
+ bool removed = false;
+ if (s_theFrameBuffer) {
+ s_theFrameBuffer->m_lock.lock();
+ if (s_theFrameBuffer->m_subWin) {
+ s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
+ s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,
+ s_theFrameBuffer->m_eglSurface);
+ destroySubWindow(s_theFrameBuffer->m_subWinDisplay,
+ s_theFrameBuffer->m_subWin);
+
+ s_theFrameBuffer->m_eglSurface = EGL_NO_SURFACE;
+ s_theFrameBuffer->m_subWin = NULL;
+ removed = true;
+ }
+ s_theFrameBuffer->m_lock.unlock();
+ }
+ return removed;
+}
+
HandleType FrameBuffer::genHandle()
{
HandleType id;
@@ -616,12 +693,38 @@
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+ if (!s_egl.eglMakeCurrent(m_eglDisplay, m_pbufSurface,
+ m_pbufSurface, m_pbufContext)) {
+ ERR("eglMakeCurrent failed\n");
+ return false;
+ }
+
+ m_prevContext = prevContext;
+ m_prevReadSurf = prevReadSurf;
+ m_prevDrawSurf = prevDrawSurf;
+ return true;
+}
+
+bool FrameBuffer::bindSubwin_locked()
+{
+ EGLContext prevContext = s_egl.eglGetCurrentContext();
+ EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
+ EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+
if (!s_egl.eglMakeCurrent(m_eglDisplay, m_eglSurface,
m_eglSurface, m_eglContext)) {
ERR("eglMakeCurrent failed\n");
return false;
}
+ //
+ // initialize GL state in eglContext if not yet initilaized
+ //
+ if (!m_eglContextInitialized) {
+ initGLState();
+ m_eglContextInitialized = true;
+ }
+
m_prevContext = prevContext;
m_prevReadSurf = prevReadSurf;
m_prevDrawSurf = prevDrawSurf;
@@ -641,22 +744,81 @@
return true;
}
-bool FrameBuffer::post(HandleType p_colorbuffer)
+bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock)
{
- android::Mutex::Autolock mutex(m_lock);
+ if (needLock) m_lock.lock();
bool ret = false;
ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
if (c != m_colorbuffers.end()) {
- if (!bind_locked()) {
+
+ m_lastPostedColorBuffer = p_colorbuffer;
+ if (!m_subWin) {
+ // no subwindow created for the FB output
+ // cannot post the colorbuffer
+ if (needLock) m_lock.unlock();
+ return ret;
+ }
+
+
+ // bind the subwindow eglSurface
+ if (!bindSubwin_locked()) {
+ ERR("FrameBuffer::post eglMakeCurrent failed\n");
+ if (needLock) m_lock.unlock();
return false;
}
+
+ //
+ // render the color buffer to the window
+ //
+ s_gl.glPushMatrix();
+ s_gl.glRotatef(m_zRot, 0.0f, 0.0f, 1.0f);
+ if (m_zRot != 0.0f) {
+ s_gl.glClear(GL_COLOR_BUFFER_BIT);
+ }
ret = (*c).second->post();
+ s_gl.glPopMatrix();
+
if (ret) {
+
+ //
+ // output FPS statistics
+ //
+ if (m_fpsStats) {
+ long long currTime = GetCurrentTimeMS();
+ m_statsNumFrames++;
+ if (currTime - m_statsStartTime >= 1000) {
+ float dt = (float)(currTime - m_statsStartTime) / 1000.0f;
+ printf("FPS: %5.3f\n", (float)m_statsNumFrames / dt);
+ m_statsStartTime = currTime;
+ m_statsNumFrames = 0;
+ }
+ }
+
s_egl.eglSwapBuffers(m_eglDisplay, m_eglSurface);
}
+
+ // restore previous binding
unbind_locked();
}
+ if (needLock) m_lock.unlock();
return ret;
}
+
+bool FrameBuffer::repost()
+{
+ if (m_lastPostedColorBuffer) {
+ return post( m_lastPostedColorBuffer );
+ }
+ return false;
+}
+
+void FrameBuffer::initGLState()
+{
+ s_gl.glMatrixMode(GL_PROJECTION);
+ s_gl.glLoadIdentity();
+ s_gl.glOrthof(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
+ s_gl.glMatrixMode(GL_MODELVIEW);
+ s_gl.glLoadIdentity();
+}
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h
index d2e7730..4725317 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h
@@ -35,7 +35,6 @@
bool hasGL2;
bool has_eglimage_texture_2d;
bool has_eglimage_renderbuffer;
- bool has_BindToTexture;
EGLint eglMajor;
EGLint eglMinor;
};
@@ -43,9 +42,11 @@
class FrameBuffer
{
public:
- static bool initialize(FBNativeWindowType p_window,
- int x, int y,
- int width, int height);
+ static bool initialize(int width, int height);
+ static bool setupSubWindow(FBNativeWindowType p_window,
+ int x, int y,
+ int width, int height, float zRot);
+ static bool removeSubWindow();
static void finalize();
static FrameBuffer *getFB() { return s_theFrameBuffer; }
@@ -70,18 +71,25 @@
int x, int y, int width, int height,
GLenum format, GLenum type, void *pixels);
- bool post(HandleType p_colorbuffer);
+ bool post(HandleType p_colorbuffer, bool needLock = true);
+ bool repost();
EGLDisplay getDisplay() const { return m_eglDisplay; }
- EGLContext getContext() const { return m_eglContext; }
EGLNativeWindowType getSubWindow() const { return m_subWin; }
bool bind_locked();
bool unbind_locked();
+ void setDisplayRotation(float zRot) {
+ m_zRot = zRot;
+ repost();
+ }
+
private:
- FrameBuffer(int p_x, int p_y, int p_width, int p_height);
+ FrameBuffer(int p_width, int p_height);
~FrameBuffer();
HandleType genHandle();
+ bool bindSubwin_locked();
+ void initGLState();
private:
static FrameBuffer *s_theFrameBuffer;
@@ -100,11 +108,21 @@
EGLSurface m_eglSurface;
EGLContext m_eglContext;
+ EGLSurface m_pbufSurface;
+ EGLContext m_pbufContext;
EGLContext m_prevContext;
EGLSurface m_prevReadSurf;
EGLSurface m_prevDrawSurf;
EGLNativeWindowType m_subWin;
EGLNativeDisplayType m_subWinDisplay;
+ EGLConfig m_eglConfig;
+ HandleType m_lastPostedColorBuffer;
+ float m_zRot;
+ bool m_eglContextInitialized;
+
+ int m_statsNumFrames;
+ long long m_statsStartTime;
+ bool m_fpsStats;
};
#endif
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.cpp
index a1b22d7..b461807 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.cpp
@@ -20,6 +20,7 @@
#include "osDynLibrary.h"
gl2_decoder_context_t s_gl2;
+int s_gl2_enabled;
static osUtils::dynLibrary *s_gles2_lib = NULL;
@@ -50,6 +51,7 @@
// init the GLES dispatch table
//
s_gl2.initDispatchByName( gl2_dispatch_get_proc_func, NULL );
+ s_gl2_enabled = true;
return true;
}
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.h b/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.h
index 7ad19bb..89f3651 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/GL2Dispatch.h
@@ -24,6 +24,7 @@
void *gl2_dispatch_get_proc_func(const char *name, void *userData);
extern gl2_decoder_context_t s_gl2;
+extern int s_gl2_enabled;
#endif
#endif
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/NativeLinuxSubWindow.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/NativeLinuxSubWindow.cpp
index 6e2636e..ff335df 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/NativeLinuxSubWindow.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/NativeLinuxSubWindow.cpp
@@ -15,17 +15,34 @@
*/
#include "NativeSubWindow.h"
+static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg)
+{
+ if (e->type == MapNotify && e->xmap.window == (Window)arg) {
+ return 1;
+ }
+ return 0;
+}
+
+static Display *s_display = NULL;
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
EGLNativeDisplayType* display_out,
int x, int y,int width, int height){
- *display_out = XOpenDisplay(NULL);
- Window win = XCreateWindow(*display_out,p_window,x,y, width,height,0,CopyFromParent,CopyFromParent,CopyFromParent,0,NULL);
+
+ // The call to this function is protected by a lock
+ // in FrameBuffer so it is safe to check and initialize s_display here
+ if (!s_display) s_display = XOpenDisplay(NULL);
+ *display_out = s_display;
+
+ XSetWindowAttributes wa;
+ wa.event_mask = StructureNotifyMask;
+ Window win = XCreateWindow(*display_out,p_window,x,y, width,height,0,CopyFromParent,CopyFromParent,CopyFromParent,CWEventMask,&wa);
XMapWindow(*display_out,win);
- XSync(*display_out,False);
+ XEvent e;
+ XIfEvent(*display_out, &e, WaitForMapNotify, (char *)win);
return win;
}
void destroySubWindow(EGLNativeDisplayType dis,EGLNativeWindowType win){
- XCloseDisplay(dis);
+ XDestroyWindow(dis, win);
}
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/NativeMacSubWindow.m b/tools/emulator/opengl/host/libs/libOpenglRender/NativeMacSubWindow.m
index 269b7ea..2ac39e8 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/NativeMacSubWindow.m
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/NativeMacSubWindow.m
@@ -50,6 +50,7 @@
void destroySubWindow(EGLNativeDisplayType dis,EGLNativeWindowType win){
if(win){
NSView *glView = (NSView *)win;
+ [glView removeFromSuperview];
[glView release];
}
}
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
index 3046db7..78f305c 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.cpp
@@ -15,6 +15,11 @@
*/
#include "RenderServer.h"
#include "TcpStream.h"
+#ifdef _WIN32
+#include "Win32PipeStream.h"
+#else
+#include "UnixStream.h"
+#endif
#include "RenderThread.h"
#include "FrameBuffer.h"
#include <set>
@@ -27,6 +32,8 @@
{
}
+extern "C" int gRendererStreamMode;
+
RenderServer *RenderServer::create(int port)
{
RenderServer *server = new RenderServer();
@@ -34,7 +41,16 @@
return NULL;
}
- server->m_listenSock = new TcpStream();
+ if (gRendererStreamMode == STREAM_MODE_TCP) {
+ server->m_listenSock = new TcpStream();
+ } else {
+#ifdef _WIN32
+ server->m_listenSock = new Win32PipeStream();
+#else
+ server->m_listenSock = new UnixStream();
+#endif
+ }
+
if (server->m_listenSock->listen(port) < 0) {
ERR("RenderServer::create failed to listen on port %d\n", port);
delete server;
@@ -49,7 +65,7 @@
RenderThreadsSet threads;
while(1) {
- TcpStream *stream = m_listenSock->accept();
+ SocketStream *stream = m_listenSock->accept();
if (!stream) {
fprintf(stderr,"Error accepting connection, aborting\n");
break;
@@ -103,7 +119,7 @@
// insert the added thread to the list
threads.insert(rt);
- printf("Started new RenderThread\n");
+ DBG("Started new RenderThread\n");
}
//
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.h b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
index 1de4053..98e9890 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/RenderServer.h
@@ -16,7 +16,7 @@
#ifndef _LIB_OPENGL_RENDER_RENDER_SERVER_H
#define _LIB_OPENGL_RENDER_RENDER_SERVER_H
-#include "TcpStream.h"
+#include "SocketStream.h"
#include "osThread.h"
class RenderServer : public osUtils::Thread
@@ -31,7 +31,7 @@
RenderServer();
private:
- TcpStream *m_listenSock;
+ SocketStream *m_listenSock;
bool m_exiting;
};
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
index 36fb704..b8d1bd2 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
@@ -35,9 +35,7 @@
m_width(0),
m_height(0),
m_pbufWidth(0),
- m_pbufHeight(0),
- m_useEGLImage(false),
- m_useBindToTexture(false)
+ m_pbufHeight(0)
{
}
@@ -64,31 +62,10 @@
const FrameBufferCaps &caps = fb->getCaps();
//
- // We can use eglimage and prevent copies if:
- // GL_KHR_gl_texture_2D_image is present.
- // and either there is no need for depth or stencil buffer
- // or GL_KHR_gl_renderbuffer_image present.
+ // Create a pbuffer to be used as the egl surface
+ // for that window.
//
-#if 0
- //XXX: This path should be implemented
- win->m_useEGLImage =
- (caps.has_eglimage_texture_2d &&
- (caps.has_eglimage_renderbuffer ||
- (fbconf->getDepthSize() + fbconf->getStencilSize() == 0)) );
-#else
- win->m_useEGLImage = false;
-#endif
-
- if (win->m_useEGLImage) {
- }
- else if (0 != (fbconf->getSurfaceType() & EGL_PBUFFER_BIT)) {
- if (!win->resizePbuffer(p_width, p_height)) {
- delete win;
- return NULL;
- }
- }
- else {
- // no EGLImage support and not Pbuffer support - fail
+ if (!win->resizePbuffer(p_width, p_height)) {
delete win;
return NULL;
}
@@ -108,19 +85,8 @@
{
if (m_attachedColorBuffer.Ptr() != NULL) {
- if (!m_useEGLImage) {
- bool copied = false;
- if (m_useBindToTexture) {
- copied = m_attachedColorBuffer->blitFromPbuffer(m_eglSurface);
- }
-
- if (!copied) {
- copyToColorBuffer();
- }
- }
- else {
- //TODO: EGLImage
- }
+ //copyToColorBuffer();
+ blitToColorBuffer();
}
}
@@ -174,9 +140,6 @@
return; // bad param
}
- if (m_useEGLImage) {
- // XXX: should be implemented
- }
}
void WindowSurface::copyToColorBuffer()
@@ -257,6 +220,36 @@
}
+void WindowSurface::blitToColorBuffer()
+{
+ if (!m_width && !m_height) return;
+
+ if (m_attachedColorBuffer->getWidth() != m_width ||
+ m_attachedColorBuffer->getHeight() != m_height) {
+ // XXX: should never happen - how this needs to be handled?
+ return;
+ }
+
+ //
+ // Make the surface current
+ //
+ EGLContext prevContext = s_egl.eglGetCurrentContext();
+ EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
+ EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+ FrameBuffer *fb = FrameBuffer::getFB();
+ if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
+ m_eglSurface, m_drawContext->getEGLContext())) {
+ return;
+ }
+
+ m_attachedColorBuffer->blitFromCurrentReadBuffer();
+
+ // restore current context/surface
+ s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
+ prevReadSurf, prevContext);
+
+}
+
bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
{
if (m_eglSurface &&
@@ -292,27 +285,14 @@
const FrameBufferCaps &caps = fb->getCaps();
//
- // Create pbuffer surface, if possible
- // set it such that it will be able to be bound to a texture
- // later to prevent readback.
+ // Create pbuffer surface.
//
- EGLint pbufAttribs[12];
+ EGLint pbufAttribs[5];
pbufAttribs[0] = EGL_WIDTH;
pbufAttribs[1] = p_width;
pbufAttribs[2] = EGL_HEIGHT;
pbufAttribs[3] = p_height;
-
- if (caps.has_BindToTexture) {
- pbufAttribs[4] = EGL_TEXTURE_FORMAT;
- pbufAttribs[5] = EGL_TEXTURE_RGBA;
- pbufAttribs[6] = EGL_TEXTURE_TARGET;
- pbufAttribs[7] = EGL_TEXTURE_2D;
- pbufAttribs[8] = EGL_NONE;
- m_useBindToTexture = true;
- }
- else {
- pbufAttribs[4] = EGL_NONE;
- }
+ pbufAttribs[4] = EGL_NONE;
m_eglSurface = s_egl.eglCreatePbufferSurface(fb->getDisplay(),
m_fbconf->getEGLConfig(),
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
index de9bc7b..6c08954 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
@@ -45,6 +45,7 @@
WindowSurface();
void copyToColorBuffer(); // copy pbuffer content with readback+download
+ void blitToColorBuffer(); // copy pbuffer content with texload and blit
bool resizePbuffer(unsigned int p_width, unsigned int p_height);
private:
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
index fa04235..8a5f464 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp
@@ -20,6 +20,17 @@
#include "osProcess.h"
#include "TimeUtils.h"
+#include "TcpStream.h"
+#ifdef _WIN32
+#include "Win32PipeStream.h"
+#else
+#include "UnixStream.h"
+#endif
+
+#include "EGLDispatch.h"
+#include "GLDispatch.h"
+#include "GL2Dispatch.h"
+
static osUtils::childProcess *s_renderProc = NULL;
static RenderServer *s_renderThread = NULL;
static int s_renderPort = 0;
@@ -27,13 +38,45 @@
static IOStream *createRenderThread(int p_stream_buffer_size,
unsigned int clientFlags);
-#ifdef __APPLE__
+//
+// For now run the renderer as a thread inside the calling
+// process instead as running it in a separate process for all
+// platforms.
+// at the future we want it to run as a seperate process except for
+// Mac OS X since it is imposibble on this platform to make one process
+// render to a window created by another process.
+//
+//#ifdef __APPLE__
#define RENDER_API_USE_THREAD
-#endif
+//#endif
-bool initOpenGLRenderer(FBNativeWindowType window,
- int x, int y, int width, int height,
- int portNum)
+bool initLibrary(void)
+{
+ //
+ // Load EGL Plugin
+ //
+ if (!init_egl_dispatch()) {
+ // Failed to load EGL
+ printf("Failed to init_egl_dispatch\n");
+ return false;
+ }
+
+ //
+ // Load GLES Plugin
+ //
+ if (!init_gl_dispatch()) {
+ // Failed to load GLES
+ ERR("Failed to init_gl_dispatch\n");
+ return false;
+ }
+
+ /* failure to init the GLES2 dispatch table is not fatal */
+ init_gl2_dispatch();
+
+ return true;
+}
+
+bool initOpenGLRenderer(int width, int height, int portNum)
{
//
@@ -50,7 +93,7 @@
// initialize the renderer and listen to connections
// on a thread in the current process.
//
- bool inited = FrameBuffer::initialize(window, x, y, width, height);
+ bool inited = FrameBuffer::initialize(width, height);
if (!inited) {
return false;
}
@@ -169,15 +212,98 @@
return ret;
}
+bool createOpenGLSubwindow(FBNativeWindowType window,
+ int x, int y, int width, int height, float zRot)
+{
+ if (s_renderThread) {
+ return FrameBuffer::setupSubWindow(window,x,y,width,height, zRot);
+ }
+ else {
+ //
+ // XXX: should be implemented by sending the renderer process
+ // a request
+ ERR("%s not implemented for separate renderer process !!!\n",
+ __FUNCTION__);
+ }
+ return false;
+}
+
+bool destroyOpenGLSubwindow()
+{
+ if (s_renderThread) {
+ return FrameBuffer::removeSubWindow();
+ }
+ else {
+ //
+ // XXX: should be implemented by sending the renderer process
+ // a request
+ ERR("%s not implemented for separate renderer process !!!\n",
+ __FUNCTION__);
+ return false;
+ }
+}
+
+void setOpenGLDisplayRotation(float zRot)
+{
+ if (s_renderThread) {
+ FrameBuffer *fb = FrameBuffer::getFB();
+ if (fb) {
+ fb->setDisplayRotation(zRot);
+ }
+ }
+ else {
+ //
+ // XXX: should be implemented by sending the renderer process
+ // a request
+ ERR("%s not implemented for separate renderer process !!!\n",
+ __FUNCTION__);
+ }
+}
+
+void repaintOpenGLDisplay()
+{
+ if (s_renderThread) {
+ FrameBuffer *fb = FrameBuffer::getFB();
+ if (fb) {
+ fb->repost();
+ }
+ }
+ else {
+ //
+ // XXX: should be implemented by sending the renderer process
+ // a request
+ ERR("%s not implemented for separate renderer process !!!\n",
+ __FUNCTION__);
+ }
+}
+
+
+/* NOTE: For now, always use TCP mode by default, until the emulator
+ * has been updated to support Unix and Win32 pipes
+ */
+#define DEFAULT_STREAM_MODE STREAM_MODE_TCP
+
+int gRendererStreamMode = DEFAULT_STREAM_MODE;
+
IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags)
{
- TcpStream *stream = new TcpStream(p_stream_buffer_size);
+ SocketStream* stream = NULL;
+
+ if (gRendererStreamMode == STREAM_MODE_TCP) {
+ stream = new TcpStream(p_stream_buffer_size);
+ } else {
+#ifdef _WIN32
+ stream = new Win32PipeStream(p_stream_buffer_size);
+#else /* !_WIN32 */
+ stream = new UnixStream(p_stream_buffer_size);
+#endif
+ }
+
if (!stream) {
ERR("createRenderThread failed to create stream\n");
return NULL;
}
-
- if (stream->connect("localhost", s_renderPort) < 0) {
+ if (stream->connect(s_renderPort) < 0) {
ERR("createRenderThread failed to connect\n");
delete stream;
return NULL;
@@ -186,10 +312,36 @@
//
// send clientFlags to the renderer
//
- unsigned int *pClientFlags =
+ unsigned int *pClientFlags =
(unsigned int *)stream->allocBuffer(sizeof(unsigned int));
*pClientFlags = clientFlags;
stream->commitBuffer(sizeof(unsigned int));
return stream;
}
+
+int
+setStreamMode(int mode)
+{
+ switch (mode) {
+ case STREAM_MODE_DEFAULT:
+ mode = DEFAULT_STREAM_MODE;
+ break;
+
+ case STREAM_MODE_TCP:
+ break;
+
+#ifndef _WIN32
+ case STREAM_MODE_UNIX:
+ break;
+#else /* _WIN32 */
+ case STREAM_MODE_PIPE:
+ break;
+#endif /* _WIN32 */
+ default:
+ // Invalid stream mode
+ return -1;
+ }
+ gRendererStreamMode = mode;
+ return 0;
+}
diff --git a/tools/emulator/opengl/host/libs/renderControl_dec/Android.mk b/tools/emulator/opengl/host/libs/renderControl_dec/Android.mk
index 889b5dd..4d07938 100644
--- a/tools/emulator/opengl/host/libs/renderControl_dec/Android.mk
+++ b/tools/emulator/opengl/host/libs/renderControl_dec/Android.mk
@@ -1,6 +1,6 @@
LOCAL_PATH := $(call my-dir)
-$(call emugl-begin-host-shared-library,lib_renderControl_dec)
+$(call emugl-begin-host-static-library,lib_renderControl_dec)
$(call emugl-import,libOpenglCodecCommon)
$(call emugl-gen-decoder,$(EMUGL_PATH)/system/renderControl_enc,renderControl)
# For renderControl_types.h
diff --git a/tools/emulator/opengl/host/renderer/Android.mk b/tools/emulator/opengl/host/renderer/Android.mk
index 934931b..55fcb80 100644
--- a/tools/emulator/opengl/host/renderer/Android.mk
+++ b/tools/emulator/opengl/host/renderer/Android.mk
@@ -1,31 +1,14 @@
LOCAL_PATH:=$(call my-dir)
# host renderer process ###########################
-include $(CLEAR_VARS)
+$(call emugl-begin-host-executable,emulator_renderer)
+$(call emugl-import,libOpenglRender)
+LOCAL_SRC_FILES := main.cpp
+LOCAL_CFLAGS += -O0 -g
-emulatorOpengl := $(LOCAL_PATH)/../..
+#ifeq ($(HOST_OS),windows)
+#LOCAL_LDLIBS += -lws2_32
+#endif
-LOCAL_MODULE := emulator_renderer
-LOCAL_MODULE_TAGS := debug
+$(call emugl-end-module)
-LOCAL_SRC_FILES := \
- main.cpp
-
-LOCAL_CFLAGS += -g -O0
-
-LOCAL_C_INCLUDES := $(emulatorOpengl)/host/include \
- $(emulatorOpengl)/host/include/libOpenglRender \
- $(emulatorOpengl)/shared/OpenglOsUtils \
- $(emulatorOpengl)/shared/OpenglCodecCommon \
- $(emulatorOpengl)/host/libs/libOpenglRender
-
-ifeq ($(HOST_OS),windows)
-LOCAL_LDLIBS += -lws2_32
-endif
-
-LOCAL_SHARED_LIBRARIES := libOpenglRender \
- libGLESv1_dec \
- libGLESv2_dec \
- lib_renderControl_dec
-
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/emulator/opengl/host/renderer/main.cpp b/tools/emulator/opengl/host/renderer/main.cpp
index fb20454..4549c56 100644
--- a/tools/emulator/opengl/host/renderer/main.cpp
+++ b/tools/emulator/opengl/host/renderer/main.cpp
@@ -120,13 +120,19 @@
//
// initialize Framebuffer
//
- bool inited = FrameBuffer::initialize(windowId,
- winX, winY, winWidth, winHeight);
+ bool inited = FrameBuffer::initialize(winWidth, winHeight);
if (!inited) {
fprintf(stderr,"Failed to initialize Framebuffer\n");
return -1;
}
+ inited = FrameBuffer::setupSubWindow(windowId,
+ winX, winY, winWidth, winHeight, 0.0);
+ if (!inited) {
+ fprintf(stderr,"Failed to create subwindow Framebuffer\n");
+ return -1;
+ }
+
//
// Create and run a render server listening to the given port number
//
diff --git a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
index 9e95604..4265301 100644
--- a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
@@ -21,6 +21,15 @@
#include <errno.h>
#include <sys/types.h>
+/* Define this to 1 to enable support for the 'isLarge' variable flag
+ * that instructs the encoder to send large data buffers by a direct
+ * write through the pipe (i.e. without copying it into a temporary
+ * buffer. This has definite performance benefits when using a QEMU Pipe.
+ *
+ * Set to 0 otherwise.
+ */
+#define WITH_LARGE_SUPPORT 1
+
EntryPoint * ApiGen::findEntryByName(const std::string & name)
{
EntryPoint * entry = NULL;
@@ -338,6 +347,104 @@
return 0;
}
+// Format the byte length expression for a given variable into a user-provided buffer
+// If the variable type is not a pointer, this is simply its size as a decimal constant
+// If the variable is a pointer, this will be an expression provided by the .attrib file
+// through the 'len' attribute.
+//
+// Returns 1 if the variable is a pointer, 0 otherwise
+//
+static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen)
+{
+ int ret = 0;
+ if (!var.isPointer()) {
+ snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
+ } else {
+ ret = 1;
+ const char* lenExpr = var.lenExpression().c_str();
+ const char* varname = var.name().c_str();
+ if (e != NULL && lenExpr[0] == '\0') {
+ fprintf(stderr, "%s: data len is undefined for '%s'\n",
+ e->name().c_str(), varname);
+ }
+ if (var.nullAllowed()) {
+ snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
+ } else {
+ snprintf(buff, bufflen, "%s", lenExpr);
+ }
+ }
+ return ret;
+}
+
+static int writeVarEncodingSize(Var& var, FILE* fp)
+{
+ int ret = 0;
+ if (!var.isPointer()) {
+ fprintf(fp, "%u", (unsigned int) var.type()->bytes());
+ } else {
+ ret = 1;
+ fprintf(fp, "__size_%s", var.name().c_str());
+ }
+ return ret;
+}
+
+
+
+static void writeVarEncodingExpression(Var& var, FILE* fp)
+{
+ const char* varname = var.name().c_str();
+
+ if (var.isPointer()) {
+ // encode a pointer header
+ fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
+
+ Var::PointerDir dir = var.pointerDir();
+ if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
+ if (var.nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ", varname);
+ } else {
+ fprintf(fp, "\t");
+ }
+
+ if (var.packExpression().size() != 0) {
+ fprintf(fp, "%s;", var.packExpression().c_str());
+ } else {
+ fprintf(fp, "memcpy(ptr, %s, __size_%s);",
+ varname, varname);
+ }
+
+ fprintf(fp, "ptr += __size_%s;\n", varname);
+ }
+ } else {
+ // encode a non pointer variable
+ if (!var.isVoid()) {
+ fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
+ var.type()->name().c_str(), varname,
+ (uint) var.type()->bytes());
+ }
+ }
+}
+
+#if WITH_LARGE_SUPPORT
+static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
+{
+ const char* varname = var.name().c_str();
+
+ fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
+ if (var.nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ", varname);
+ } else {
+ fprintf(fp, "\t");
+ }
+ if (var.writeExpression() != "") {
+ fprintf(fp, "%s", var.writeExpression().c_str());
+ } else {
+ fprintf(fp, "stream->writeFully(%s, __size_%s)", varname, varname);
+ }
+ fprintf(fp, ";\n");
+}
+#endif /* WITH_LARGE_SUPPORT */
+
int ApiGen::genEncoderImpl(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "wt");
@@ -368,46 +475,139 @@
fprintf(fp, "{\n");
// fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
- fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
+ fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
classname.c_str(),
classname.c_str());
-
- // size calculation ;
- fprintf(fp, "\t size_t packetSize = ");
-
+ fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
VarsArray & evars = e->vars();
+ size_t maxvars = evars.size();
+ size_t j;
+
+ char buff[256];
+
+ // Define the __size_XXX variables that contain the size of data
+ // associated with pointers.
+ for (j = 0; j < maxvars; j++) {
+ Var& var = evars[j];
+
+ if (!var.isPointer())
+ continue;
+
+ const char* varname = var.name().c_str();
+ fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
+
+ getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
+ fprintf(fp, "%s;\n", buff);
+ }
+
+#if WITH_LARGE_SUPPORT
+ // We need to take care of 'isLarge' variable in a special way
+ // Anything before an isLarge variable can be packed into a single
+ // buffer, which is then commited. Each isLarge variable is a pointer
+ // to data that can be written to directly through the pipe, which
+ // will be instant when using a QEMU pipe
+
+ size_t nvars = 0;
+ size_t npointers = 0;
+
+ // First, compute the total size, 8 bytes for the opcode + payload size
+ fprintf(fp, "\t unsigned char *ptr;\n");
+ fprintf(fp, "\t const size_t packetSize = 8");
+
+ for (j = 0; j < maxvars; j++) {
+ fprintf(fp, " + ");
+ npointers += writeVarEncodingSize(evars[j], fp);
+ }
+ if (npointers > 0) {
+ fprintf(fp, " + %u*4", npointers);
+ }
+ fprintf(fp, ";\n");
+
+ // We need to divide the packet into fragments. Each fragment contains
+ // either copied arguments to a temporary buffer, or direct writes for
+ // large variables.
+ //
+ // The first fragment must also contain the opcode+payload_size
+ //
+ nvars = 0;
+ while (nvars < maxvars || maxvars == 0) {
+
+ // Skip over non-large fields
+ for (j = nvars; j < maxvars; j++) {
+ if (evars[j].isLarge())
+ break;
+ }
+
+ // Write a fragment if needed.
+ if (nvars == 0 || j > nvars) {
+ const char* plus = "";
+
+ if (nvars == 0 && j == maxvars) {
+ // Simple shortcut for the common case where we don't have large variables;
+ fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
+
+ } else {
+ // allocate buffer from the stream until the first large variable
+ fprintf(fp, "\tptr = stream->alloc(");
+ plus = "";
+
+ if (nvars == 0) {
+ fprintf(fp,"8"); plus = " + ";
+ }
+ if (j > nvars) {
+ npointers = 0;
+ for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+ fprintf(fp, "%s", plus); plus = " + ";
+ npointers += writeVarEncodingSize(evars[j], fp);
+ }
+ if (npointers > 0) {
+ fprintf(fp, "%s%u*4", plus, npointers); plus = " + ";
+ }
+ }
+ fprintf(fp,");\n");
+ }
+
+ // encode packet header if needed.
+ if (nvars == 0) {
+ fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
+ fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n");
+ }
+
+ if (maxvars == 0)
+ break;
+
+ // encode non-large fields in this fragment
+ for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+ writeVarEncodingExpression(evars[j],fp);
+ }
+
+ // Ensure the fragment is commited if it is followed by a large variable
+ if (j < maxvars) {
+ fprintf(fp, "\tstream->flush();\n");
+ }
+ }
+
+ // If we have one or more large variables, write them directly.
+ // As size + data
+ for ( ; j < maxvars && evars[j].isLarge(); j++) {
+ writeVarLargeEncodingExpression(evars[j], fp);
+ }
+
+ nvars = j;
+ }
+
+#else /* !WITH_LARGE_SUPPORT */
size_t nvars = evars.size();
size_t npointers = 0;
+ fprintf(fp, "\t const size_t packetSize = 8");
for (size_t j = 0; j < nvars; j++) {
- fprintf(fp, "%s ", j == 0 ? "" : " +");
- if (evars[j].isPointer()) {
- npointers++;
-
- if (evars[j].lenExpression() == "") {
- fprintf(stderr, "%s: data len is undefined for '%s'\n",
- e->name().c_str(), evars[j].name().c_str());
- }
-
- if (evars[j].nullAllowed()) {
- fprintf(fp, "(%s != NULL ? %s : 0)",
- evars[j].name().c_str(),
- evars[j].lenExpression().c_str());
- } else {
- if (evars[j].pointerDir() == Var::POINTER_IN ||
- evars[j].pointerDir() == Var::POINTER_INOUT) {
- fprintf(fp, "%s", evars[j].lenExpression().c_str());
- } else {
- fprintf(fp, "0");
- }
- }
- } else {
- fprintf(fp, "%u", (unsigned int) evars[j].type()->bytes());
- }
+ npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
+ fprintf(fp, " + %s", buff);
}
- fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
+ fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
// allocate buffer from the stream;
- fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
+ fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n");
// encode into the stream;
fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
@@ -415,62 +615,23 @@
// out variables
for (size_t j = 0; j < nvars; j++) {
- if (evars[j].isPointer()) {
- // encode a pointer header
- if (evars[j].nullAllowed()) {
- fprintf(fp, "\t*(unsigned int *)(ptr) = (%s != NULL) ? %s : 0; ptr += 4; \n",
- evars[j].name().c_str(), evars[j].lenExpression().c_str());
- } else {
- fprintf(fp, "\t*(unsigned int *)(ptr) = %s; ptr += 4; \n",
- evars[j].lenExpression().c_str());
- }
-
- Var::PointerDir dir = evars[j].pointerDir();
- if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
- if (evars[j].nullAllowed()) {
- fprintf(fp, "\tif (%s != NULL) ", evars[j].name().c_str());
- } else {
- fprintf(fp, "\t");
- }
-
- if (evars[j].packExpression().size() != 0) {
- fprintf(fp, "%s;", evars[j].packExpression().c_str());
- } else {
- fprintf(fp, "memcpy(ptr, %s, %s);",
- evars[j].name().c_str(),
- evars[j].lenExpression().c_str());
- }
-
- if (evars[j].nullAllowed()) {
- fprintf(fp, "ptr += %s == NULL ? 0 : %s; \n", evars[j].name().c_str(), evars[j].lenExpression().c_str());
- } else {
- fprintf(fp, "ptr += %s;\n", evars[j].lenExpression().c_str());
- }
- }
- } else {
- // encode a non pointer variable
- if (!evars[j].isVoid()) {
- fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
- evars[j].type()->name().c_str(), evars[j].name().c_str(),
- (uint) evars[j].type()->bytes());
- }
- }
+ writeVarEncodingExpression(evars[j], fp);
}
+#endif /* !WITH_LARGE_SUPPORT */
+
// in variables;
for (size_t j = 0; j < nvars; j++) {
if (evars[j].isPointer()) {
Var::PointerDir dir = evars[j].pointerDir();
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
+ const char* varname = evars[j].name().c_str();
if (evars[j].nullAllowed()) {
- fprintf(fp, "\tif (%s != NULL) ctx->m_stream->readback(%s, %s);\n",
- evars[j].name().c_str(),
- evars[j].name().c_str(),
- evars[j].lenExpression().c_str());
+ fprintf(fp, "\tif (%s != NULL) ",varname);
} else {
- fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
- evars[j].name().c_str(),
- evars[j].lenExpression().c_str());
+ fprintf(fp, "\t");
}
+ fprintf(fp, "stream->readback(%s, __size_%s);\n",
+ varname, varname);
}
}
}
@@ -482,7 +643,7 @@
fprintf(fp, "\t return NULL;\n");
} else if (e->retval().type()->name() != "void") {
fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
- fprintf(fp, "\tctx->m_stream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
+ fprintf(fp, "\tstream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
fprintf(fp, "\treturn retval;\n");
}
fprintf(fp, "}\n\n");
diff --git a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
index 413b56a..43b904b 100644
--- a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
@@ -119,7 +119,7 @@
fprintf(stderr, "UNKNOWN retval: %s\n", linestr.c_str());
}
- m_retval.init(std::string(""), theType, std::string(""), Var::POINTER_OUT, std::string(""));
+ m_retval.init(std::string(""), theType, std::string(""), Var::POINTER_OUT, std::string(""), std::string(""));
// function name
m_name = getNextToken(linestr, pos, &last, ",)");
@@ -146,7 +146,7 @@
varname = oss.str();
}
- m_vars.push_back(Var(varname, v, std::string(""), Var::POINTER_IN, ""));
+ m_vars.push_back(Var(varname, v, std::string(""), Var::POINTER_IN, "", ""));
}
pos = last + 1;
}
@@ -286,22 +286,36 @@
(unsigned int)lc, varname.c_str(), name().c_str());
return -2;
}
- pos = last;
- std::string flag = getNextToken(line, pos, &last, WHITESPACE);
- if (flag.size() == 0) {
- fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
- return -3;
- }
-
- if (flag == "nullAllowed") {
- if (v->isPointer()) {
- v->setNullAllowed(true);
- } else {
- fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
- (unsigned int) lc, v->name().c_str());
+ int count = 0;
+ for (;;) {
+ pos = last;
+ std::string flag = getNextToken(line, pos, &last, WHITESPACE);
+ if (flag.size() == 0) {
+ if (count == 0) {
+ fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
+ return -3;
+ }
+ break;
}
- } else {
- fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+ count++;
+
+ if (flag == "nullAllowed") {
+ if (v->isPointer()) {
+ v->setNullAllowed(true);
+ } else {
+ fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
+ (unsigned int) lc, v->name().c_str());
+ }
+ } else if (flag == "isLarge") {
+ if (v->isPointer()) {
+ v->setIsLarge(true);
+ } else {
+ fprintf(stderr, "WARNING: %u: setting isLarge flag for a non-pointer variable %s\n",
+ (unsigned int) lc, v->name().c_str());
+ }
+ } else {
+ fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+ }
}
} else if (token == "custom_pack") {
pos = last;
@@ -320,6 +334,23 @@
// set the size expression into var
pos = last;
v->setPackExpression(line.substr(pos));
+ } else if (token == "custom_write") {
+ pos = last;
+ std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+
+ if (varname.size() == 0) {
+ fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_write' attribute\n", (unsigned int)lc);
+ return -1;
+ }
+ Var * v = var(varname);
+ if (v == NULL) {
+ fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
+ (unsigned int)lc, varname.c_str(), name().c_str());
+ return -2;
+ }
+ // set the size expression into var
+ pos = last;
+ v->setWriteExpression(line.substr(pos));
} else if (token == "flag") {
pos = last;
std::string flag = getNextToken(line, pos, &last, WHITESPACE);
diff --git a/tools/emulator/opengl/host/tools/emugen/README b/tools/emulator/opengl/host/tools/emugen/README
index 4d2c28d..5df11a3 100644
--- a/tools/emulator/opengl/host/tools/emugen/README
+++ b/tools/emulator/opengl/host/tools/emugen/README
@@ -313,7 +313,10 @@
var_flag
description : set variable flags
- format: var_flag <varname> < nullAllowed | ... >
+ format: var_flag <varname> < nullAllowed | isLarge | ... >
+
+ nullAllowed -> for pointer variables, indicates that NULL is a valid value
+ isLarge -> for pointer variables, indicates that the data should be sent without an intermediate copy
flag
description: set entry point flag;
diff --git a/tools/emulator/opengl/host/tools/emugen/Var.h b/tools/emulator/opengl/host/tools/emugen/Var.h
index c9735c7..322c66a 100644
--- a/tools/emulator/opengl/host/tools/emugen/Var.h
+++ b/tools/emulator/opengl/host/tools/emugen/Var.h
@@ -30,7 +30,9 @@
m_lenExpression(""),
m_pointerDir(POINTER_IN),
m_nullAllowed(false),
+ m_isLarge(false),
m_packExpression(""),
+ m_writeExpression(""),
m_paramCheckExpression("")
{
@@ -40,26 +42,33 @@
const VarType * vartype,
const std::string & lenExpression,
PointerDir dir,
- const std::string &packExpression) :
+ const std::string &packExpression,
+ const std::string &writeExpression) :
m_name(name),
m_type(const_cast<VarType *>(vartype)),
m_lenExpression(lenExpression),
m_pointerDir(dir),
m_nullAllowed(false),
+ m_isLarge(false),
m_packExpression(packExpression),
- m_paramCheckExpression("")
+ m_writeExpression(writeExpression),
+ m_paramCheckExpression("")
{
}
void init(const std::string name, const VarType * vartype,
std::string lenExpression,
- PointerDir dir, std::string packExpression) {
+ PointerDir dir,
+ std::string packExpression,
+ std::string writeExpression) {
m_name = name;
m_type = vartype;
m_lenExpression = lenExpression;
m_packExpression = packExpression;
+ m_writeExpression = writeExpression;
m_pointerDir = dir;
m_nullAllowed = false;
+ m_isLarge = false;
}
@@ -69,14 +78,18 @@
bool isVoid() const { return ((m_type->bytes() == 0) && (!m_type->isPointer())); }
const std::string & lenExpression() const { return m_lenExpression; }
const std::string & packExpression() const { return(m_packExpression); }
+ const std::string & writeExpression() const { return(m_writeExpression); }
const std::string & paramCheckExpression() const { return m_paramCheckExpression; }
void setLenExpression(const std::string & lenExpression) { m_lenExpression = lenExpression; }
void setPackExpression(const std::string & packExpression) { m_packExpression = packExpression; }
+ void setWriteExpression(const std::string & writeExpression) { m_writeExpression = writeExpression; }
void setParamCheckExpression(const std::string & paramCheckExpression) { m_paramCheckExpression = paramCheckExpression; }
void setPointerDir(PointerDir dir) { m_pointerDir = dir; }
PointerDir pointerDir() { return m_pointerDir; }
void setNullAllowed(bool state) { m_nullAllowed = state; }
+ void setIsLarge(bool state) { m_isLarge = state; }
bool nullAllowed() const { return m_nullAllowed; }
+ bool isLarge() const { return m_isLarge; }
void printType(FILE *fp) { fprintf(fp, "%s", m_type->name().c_str()); }
void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); }
@@ -87,7 +100,9 @@
std::string m_lenExpression; // an expression to calcualte a pointer data size
PointerDir m_pointerDir;
bool m_nullAllowed;
+ bool m_isLarge;
std::string m_packExpression; // an expression to pack data into the stream
+ std::string m_writeExpression; // an expression to write data into the stream
std::string m_paramCheckExpression; //an expression to check parameter value
};
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/Android.mk b/tools/emulator/opengl/shared/OpenglCodecCommon/Android.mk
index fa1b248..2f7da79 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/Android.mk
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/Android.mk
@@ -3,16 +3,19 @@
#
LOCAL_PATH := $(call my-dir)
-### CodecCommon guest ##############################################
-$(call emugl-begin-static-library,libOpenglCodecCommon)
-
-LOCAL_SRC_FILES := \
+commonSources := \
GLClientState.cpp \
GLSharedGroup.cpp \
glUtils.cpp \
+ SocketStream.cpp \
TcpStream.cpp \
TimeUtils.cpp
+### CodecCommon guest ##############################################
+$(call emugl-begin-static-library,libOpenglCodecCommon)
+
+LOCAL_SRC_FILES := $(commonSources)
+
LOCAL_CFLAGS += -DLOG_TAG=\"eglCodecCommon\"
$(call emugl-export,SHARED_LIBRARIES,libcutils libutils)
@@ -22,11 +25,13 @@
### OpenglCodecCommon host ##############################################
$(call emugl-begin-host-static-library,libOpenglCodecCommon)
-LOCAL_SRC_FILES := \
- GLClientState.cpp \
- glUtils.cpp \
- TcpStream.cpp \
- TimeUtils.cpp
+LOCAL_SRC_FILES := $(commonSources)
+
+ifeq ($(HOST_OS),windows)
+ LOCAL_SRC_FILES += Win32PipeStream.cpp
+else
+ LOCAL_SRC_FILES += UnixStream.cpp
+endif
$(call emugl-export,STATIC_LIBRARIES,libcutils)
$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/ErrorLog.h b/tools/emulator/opengl/shared/OpenglCodecCommon/ErrorLog.h
index 8502bb0..5e9c7b3 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/ErrorLog.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/ErrorLog.h
@@ -19,11 +19,19 @@
#if (HAVE_ANDROID_OS == 1)
# include <cutils/log.h>
# define ERR(...) LOGE(__VA_ARGS__)
-# define DBG(...) LOGD(__VA_ARGS__)
+# ifdef EMUGL_DEBUG
+# define DBG(...) LOGD(__VA_ARGS__)
+# else
+# define DBG(...) ((void)0)
+# endif
#else
# include <stdio.h>
# define ERR(...) fprintf(stderr, __VA_ARGS__)
-# define DBG(...) fprintf(stderr, __VA_ARGS__)
+# ifdef EMUGL_DEBUG
+# define DBG(...) fprintf(stderr, __VA_ARGS__)
+# else
+# define DBG(...) ((void)0)
+# endif
#endif
#endif
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp
index f46889b..ff48c9d 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp
@@ -45,19 +45,30 @@
m_Indexes[index].base = base;
m_Indexes[index].size = size;
m_Indexes[index].type = type;
+ if (index > 0) {
+ m_Indexes[index].appBase = m_Indexes[index-1].appBase +
+ m_Indexes[index-1].size;
+ }
+ else {
+ m_Indexes[index].appBase = 0;
+ }
+ m_Indexes[index].hostLocsPerElement = 1;
}
GLuint ProgramData::getIndexForLocation(GLint location)
{
- GLuint i=0;
- for (i=0;i<m_numIndexes;++i)
+ GLuint index = m_numIndexes;
+ GLint minDist = -1;
+ for (GLuint i=0;i<m_numIndexes;++i)
{
- GLint low = m_Indexes[i].base;;
- GLint high = low + m_Indexes[i].size;
- if (location >= low && location < high)
- break;
+ GLint dist = location - m_Indexes[i].base;
+ if (dist >= 0 &&
+ (minDist < 0 || dist < minDist)) {
+ index = i;
+ minDist = dist;
+ }
}
- return i;
+ return index;
}
GLenum ProgramData::getTypeForLocation(GLint location)
@@ -72,24 +83,44 @@
void ProgramData::setupLocationShiftWAR()
{
m_locShiftWAR = false;
- for (int i=0; i<m_numIndexes; i++) {
+ for (GLuint i=0; i<m_numIndexes; i++) {
if (0 != (m_Indexes[i].base & 0xffff)) {
return;
}
}
- m_locShiftWAR = true;
+ // if we have one uniform at location 0, we do not need the WAR.
+ if (m_numIndexes > 1) {
+ m_locShiftWAR = true;
+ }
}
-GLint ProgramData::locationWARHostToApp(GLint hostLoc)
+GLint ProgramData::locationWARHostToApp(GLint hostLoc, GLint arrIndex)
{
- if (m_locShiftWAR && hostLoc>0) return hostLoc>>16;
- else return hostLoc;
+ if (!m_locShiftWAR) return hostLoc;
+
+ GLuint index = getIndexForLocation(hostLoc);
+ if (index<m_numIndexes) {
+ if (arrIndex > 0) {
+ m_Indexes[index].hostLocsPerElement =
+ (hostLoc - m_Indexes[index].base) / arrIndex;
+ }
+ return m_Indexes[index].appBase + arrIndex;
+ }
+ return -1;
}
GLint ProgramData::locationWARAppToHost(GLint appLoc)
{
- if (m_locShiftWAR && appLoc>0) return appLoc<<16;
- else return appLoc;
+ if (!m_locShiftWAR) return appLoc;
+
+ for(GLuint i=0; i<m_numIndexes; i++) {
+ GLint elemIndex = appLoc - m_Indexes[i].appBase;
+ if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) {
+ return m_Indexes[i].base +
+ elemIndex * m_Indexes[i].hostLocsPerElement;
+ }
+ }
+ return -1;
}
@@ -222,11 +253,11 @@
if (pData) pData->setupLocationShiftWAR();
}
-GLint GLSharedGroup::locationWARHostToApp(GLuint program, GLint hostLoc)
+GLint GLSharedGroup::locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex)
{
android::AutoMutex _lock(m_lock);
ProgramData* pData = m_programs.valueFor(program);
- if (pData) return pData->locationWARHostToApp(hostLoc);
+ if (pData) return pData->locationWARHostToApp(hostLoc, arrIndex);
else return hostLoc;
}
@@ -238,6 +269,14 @@
else return appLoc;
}
+bool GLSharedGroup::needUniformLocationWAR(GLuint program)
+{
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData = m_programs.valueFor(program);
+ if (pData) return pData->needUniformLocationWAR();
+ return false;
+}
+
void GLSharedGroup::addShaderData(GLuint shader)
{
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h
index 2443562..7104550 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h
@@ -49,7 +49,9 @@
GLint base;
GLint size;
GLenum type;
- }IndexInfo;
+ GLint appBase;
+ GLint hostLocsPerElement;
+ } IndexInfo;
GLuint m_numIndexes;
IndexInfo* m_Indexes;
@@ -64,8 +66,9 @@
GLuint getIndexForLocation(GLint location);
GLenum getTypeForLocation(GLint location);
+ bool needUniformLocationWAR() const { return m_locShiftWAR; }
void setupLocationShiftWAR();
- GLint locationWARHostToApp(GLint hostLoc);
+ GLint locationWARHostToApp(GLint hostLoc, GLint arrIndex);
GLint locationWARAppToHost(GLint appLoc);
};
@@ -93,8 +96,9 @@
void setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type);
GLenum getProgramUniformType(GLuint program, GLint location);
void setupLocationShiftWAR(GLuint program);
- GLint locationWARHostToApp(GLuint program, GLint hostLoc);
+ GLint locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex);
GLint locationWARAppToHost(GLuint program, GLint appLoc);
+ bool needUniformLocationWAR(GLuint program);
void addShaderData(GLuint shader);
bool isShader(GLuint shader);
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
new file mode 100644
index 0000000..ddc56d0
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
@@ -0,0 +1,168 @@
+/*
+* 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.
+*/
+#include "SocketStream.h"
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#else
+#include <ws2tcpip.h>
+#endif
+
+SocketStream::SocketStream(size_t bufSize) :
+ IOStream(bufSize),
+ m_sock(-1),
+ m_bufsize(bufSize),
+ m_buf(NULL)
+{
+}
+
+SocketStream::SocketStream(int sock, size_t bufSize) :
+ IOStream(bufSize),
+ m_sock(sock),
+ m_bufsize(bufSize),
+ m_buf(NULL)
+{
+}
+
+SocketStream::~SocketStream()
+{
+ if (m_sock >= 0) {
+#ifdef _WIN32
+ closesocket(m_sock);
+#else
+ ::close(m_sock);
+#endif
+ }
+ if (m_buf != NULL) {
+ free(m_buf);
+ m_buf = NULL;
+ }
+}
+
+
+void *SocketStream::allocBuffer(size_t minSize)
+{
+ size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
+ if (!m_buf) {
+ m_buf = (unsigned char *)malloc(allocSize);
+ }
+ else if (m_bufsize < allocSize) {
+ unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
+ if (p != NULL) {
+ m_buf = p;
+ m_bufsize = allocSize;
+ } else {
+ ERR("%s: realloc (%d) failed\n", __FUNCTION__, allocSize);
+ free(m_buf);
+ m_buf = NULL;
+ m_bufsize = 0;
+ }
+ }
+
+ return m_buf;
+};
+
+int SocketStream::commitBuffer(size_t size)
+{
+ return writeFully(m_buf, size);
+}
+
+int SocketStream::writeFully(const void* buffer, size_t size)
+{
+ if (!valid()) return -1;
+
+ size_t res = size;
+ int retval = 0;
+
+ while (res > 0) {
+ ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
+ if (stat < 0) {
+ if (errno != EINTR) {
+ retval = stat;
+ ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno));
+ break;
+ }
+ } else {
+ res -= stat;
+ }
+ }
+ return retval;
+}
+
+const unsigned char *SocketStream::readFully(void *buf, size_t len)
+{
+ const unsigned char* ret = NULL;
+ if (!valid()) return NULL;
+ if (!buf) {
+ return NULL; // do not allow NULL buf in that implementation
+ }
+ size_t res = len;
+ while (res > 0) {
+ ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0);
+ if (stat > 0) {
+ res -= stat;
+ continue;
+ }
+ if (stat == 0 || errno != EINTR) { // client shutdown or error
+ return NULL;
+ }
+ }
+ return (const unsigned char *)buf;
+}
+
+const unsigned char *SocketStream::read( void *buf, size_t *inout_len)
+{
+ if (!valid()) return NULL;
+ if (!buf) {
+ return NULL; // do not allow NULL buf in that implementation
+ }
+
+ int n;
+ do {
+ n = recv(buf, *inout_len);
+ } while( n < 0 && errno == EINTR );
+
+ if (n > 0) {
+ *inout_len = n;
+ return (const unsigned char *)buf;
+ }
+
+ return NULL;
+}
+
+int SocketStream::recv(void *buf, size_t len)
+{
+ if (!valid()) return int(ERR_INVALID_SOCKET);
+ int res = 0;
+ while(true) {
+ res = ::recv(m_sock, (char *)buf, len, 0);
+ if (res < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+ break;
+ }
+ return res;
+}
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
new file mode 100644
index 0000000..3a501b4
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
@@ -0,0 +1,50 @@
+/*
+* 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.
+*/
+#ifndef __SOCKET_STREAM_H
+#define __SOCKET_STREAM_H
+
+#include <stdlib.h>
+#include "IOStream.h"
+
+class SocketStream : public IOStream {
+public:
+ typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError;
+
+ explicit SocketStream(size_t bufsize = 10000);
+ virtual ~SocketStream();
+
+ virtual int listen(unsigned short port) = 0;
+ virtual SocketStream *accept() = 0;
+ virtual int connect(unsigned short port) = 0;
+
+ virtual void *allocBuffer(size_t minSize);
+ virtual int commitBuffer(size_t size);
+ virtual const unsigned char *readFully(void *buf, size_t len);
+ virtual const unsigned char *read(void *buf, size_t *inout_len);
+
+ bool valid() { return m_sock >= 0; }
+ virtual int recv(void *buf, size_t len);
+ virtual int writeFully(const void *buf, size_t len);
+
+protected:
+ int m_sock;
+ size_t m_bufsize;
+ unsigned char *m_buf;
+
+ SocketStream(int sock, size_t bufSize);
+};
+
+#endif /* __SOCKET_STREAM_H */
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.cpp
index 4e947da..4da2cec 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.cpp
@@ -23,52 +23,39 @@
#ifndef _WIN32
#include <netinet/in.h>
+#include <netinet/tcp.h>
+#else
+#include <ws2tcpip.h>
#endif
TcpStream::TcpStream(size_t bufSize) :
- IOStream(bufSize),
- m_sock(-1),
- m_bufsize(bufSize),
- m_buf(NULL)
+ SocketStream(bufSize)
{
}
TcpStream::TcpStream(int sock, size_t bufSize) :
- IOStream(bufSize),
- m_sock(sock),
- m_bufsize(bufSize),
- m_buf(NULL)
+ SocketStream(sock, bufSize)
{
-}
-
-TcpStream::~TcpStream()
-{
- if (m_sock >= 0) {
+ // disable Nagle algorithm to improve bandwidth of small
+ // packets which are quite common in our implementation.
#ifdef _WIN32
- closesocket(m_sock);
+ DWORD flag;
#else
- ::close(m_sock);
+ int flag;
#endif
- }
- if (m_buf != NULL) {
- free(m_buf);
- }
+ flag = 1;
+ setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(flag) );
}
-
-int TcpStream::listen(unsigned short port, bool localhost_only)
+int TcpStream::listen(unsigned short port)
{
- if (localhost_only) {
- m_sock = socket_loopback_server(port, SOCK_STREAM);
- } else {
- m_sock = socket_inaddr_any_server(port, SOCK_STREAM);
- }
+ m_sock = socket_loopback_server(port, SOCK_STREAM);
if (!valid()) return int(ERR_INVALID_SOCKET);
return 0;
}
-TcpStream * TcpStream::accept()
+SocketStream * TcpStream::accept()
{
int clientSock = -1;
@@ -91,120 +78,14 @@
return clientStream;
}
+int TcpStream::connect(unsigned short port)
+{
+ return connect("127.0.0.1",port);
+}
-int TcpStream::connect(const char *hostname, unsigned short port)
+int TcpStream::connect(const char* hostname, unsigned short port)
{
m_sock = socket_network_client(hostname, port, SOCK_STREAM);
if (!valid()) return -1;
return 0;
}
-
-void *TcpStream::allocBuffer(size_t minSize)
-{
- size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
- if (!m_buf) {
- m_buf = (unsigned char *)malloc(allocSize);
- }
- else if (m_bufsize < allocSize) {
- unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
- if (p != NULL) {
- m_buf = p;
- m_bufsize = allocSize;
- } else {
- ERR("realloc (%d) failed\n", allocSize);
- free(m_buf);
- m_buf = NULL;
- m_bufsize = 0;
- }
- }
-
- return m_buf;
-};
-
-int TcpStream::commitBuffer(size_t size)
-{
- return writeFully(m_buf, size);
-}
-
-int TcpStream::writeFully(const void *buf, size_t len)
-{
- if (!valid()) return -1;
-
- size_t res = len;
- int retval = 0;
-
- while (res > 0) {
- ssize_t stat = ::send(m_sock, (const char *)(buf) + (len - res), res, 0);
- if (stat < 0) {
- if (errno != EINTR) {
- retval = stat;
- ERR("TcpStream::writeFully failed: %s\n", strerror(errno));
- break;
- }
- } else {
- res -= stat;
- }
- }
- return retval;
-}
-
-const unsigned char *TcpStream::readFully(void *buf, size_t len)
-{
- if (!valid()) return NULL;
- if (!buf) {
- return NULL; // do not allow NULL buf in that implementation
- }
- size_t res = len;
- while (res > 0) {
- ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0);
- if (stat == 0) {
- // client shutdown;
- return NULL;
- } else if (stat < 0) {
- if (errno == EINTR) {
- continue;
- } else {
- return NULL;
- }
- } else {
- res -= stat;
- }
- }
- return (const unsigned char *)buf;
-}
-
-const unsigned char *TcpStream::read( void *buf, size_t *inout_len)
-{
- if (!valid()) return NULL;
- if (!buf) {
- return NULL; // do not allow NULL buf in that implementation
- }
-
- int n;
- do {
- n = recv(buf, *inout_len);
- } while( n < 0 && errno == EINTR );
-
- if (n > 0) {
- *inout_len = n;
- return (const unsigned char *)buf;
- }
-
- return NULL;
-}
-
-int TcpStream::recv(void *buf, size_t len)
-{
- if (!valid()) return int(ERR_INVALID_SOCKET);
- int res = 0;
- while(true) {
- res = ::recv(m_sock, (char *)buf, len, 0);
- if (res < 0) {
- if (errno == EINTR) {
- continue;
- }
- }
- break;
- }
- return res;
-}
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.h
index 7521843..811a871 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/TcpStream.h
@@ -16,35 +16,16 @@
#ifndef __TCP_STREAM_H
#define __TCP_STREAM_H
-#include <stdlib.h>
-#include "IOStream.h"
+#include "SocketStream.h"
-
-class TcpStream : public IOStream {
+class TcpStream : public SocketStream {
public:
- typedef enum { ERR_INVALID_SOCKET = -1000 } TcpStreamError;
-
explicit TcpStream(size_t bufsize = 10000);
- ~TcpStream();
- int listen(unsigned short port, bool localhost_only = true);
- TcpStream *accept();
- int connect(const char *hostname, unsigned short port);
-
- virtual void *allocBuffer(size_t minSize);
- virtual int commitBuffer(size_t size);
- virtual const unsigned char *readFully( void *buf, size_t len);
- virtual const unsigned char *read( void *buf, size_t *inout_len);
-
- bool valid() { return m_sock >= 0; }
- int recv(void *buf, size_t len);
-
+ virtual int listen(unsigned short port);
+ virtual SocketStream *accept();
+ virtual int connect(unsigned short port);
+ int connect(const char* hostname, unsigned short port);
private:
- int writeFully(const void *buf, size_t len);
-
-private:
- int m_sock;
- size_t m_bufsize;
- unsigned char *m_buf;
TcpStream(int sock, size_t bufSize);
};
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.cpp
new file mode 100644
index 0000000..8e463a3
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.cpp
@@ -0,0 +1,137 @@
+/*
+* 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.
+*/
+#include "UnixStream.h"
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+/* Not all systems define PATH_MAX, those who don't generally don't
+ * have a limit on the maximum path size, so use a value that is
+ * large enough for our very limited needs.
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 128
+#endif
+
+UnixStream::UnixStream(size_t bufSize) :
+ SocketStream(bufSize)
+{
+}
+
+UnixStream::UnixStream(int sock, size_t bufSize) :
+ SocketStream(sock, bufSize)
+{
+}
+
+/* Initialize a sockaddr_un with the appropriate values corresponding
+ * to a given 'virtual port'. Returns 0 on success, -1 on error.
+ */
+static int
+make_unix_path(char *path, size_t pathlen, int port_number)
+{
+ char tmp[PATH_MAX]; // temp directory
+ int ret = 0;
+
+ // First, create user-specific temp directory if needed
+ const char* user = getenv("USER");
+ if (user != NULL) {
+ struct stat st;
+ snprintf(tmp, sizeof(tmp), "/tmp/android-%s", user);
+ do {
+ ret = ::lstat(tmp, &st);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0 && errno == ENOENT) {
+ do {
+ ret = ::mkdir(tmp, 0766);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ ERR("Could not create temp directory: %s", tmp);
+ user = NULL; // will fall-back to /tmp
+ }
+ }
+ else if (ret < 0) {
+ user = NULL; // will fallback to /tmp
+ }
+ }
+
+ if (user == NULL) { // fallback to /tmp in case of error
+ snprintf(tmp, sizeof(tmp), "/tmp");
+ }
+
+ // Now, initialize it properly
+ snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number);
+ return 0;
+}
+
+
+int UnixStream::listen(unsigned short port)
+{
+ char path[PATH_MAX];
+
+ if (make_unix_path(path, sizeof(path), port) < 0) {
+ return -1;
+ }
+
+ m_sock = socket_local_server(path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ if (!valid()) return int(ERR_INVALID_SOCKET);
+
+ return 0;
+}
+
+SocketStream * UnixStream::accept()
+{
+ int clientSock = -1;
+
+ while (true) {
+ struct sockaddr_un addr;
+ socklen_t len = sizeof(addr);
+ clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
+
+ if (clientSock < 0 && errno == EINTR) {
+ continue;
+ }
+ break;
+ }
+
+ UnixStream *clientStream = NULL;
+
+ if (clientSock >= 0) {
+ clientStream = new UnixStream(clientSock, m_bufsize);
+ }
+ return clientStream;
+}
+
+int UnixStream::connect(unsigned short port)
+{
+ char path[PATH_MAX];
+
+ if (make_unix_path(path, sizeof(path), port) < 0)
+ return -1;
+
+ m_sock = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ if (!valid()) return -1;
+
+ return 0;
+}
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.h
new file mode 100644
index 0000000..c184b19
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/UnixStream.h
@@ -0,0 +1,31 @@
+/*
+* 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.
+*/
+#ifndef __UNIX_STREAM_H
+#define __UNIX_STREAM_H
+
+#include "SocketStream.h"
+
+class UnixStream : public SocketStream {
+public:
+ explicit UnixStream(size_t bufsize = 10000);
+ virtual int listen(unsigned short port);
+ virtual SocketStream *accept();
+ virtual int connect(unsigned short port);
+private:
+ UnixStream(int sock, size_t bufSize);
+};
+
+#endif
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp
new file mode 100644
index 0000000..e1a0b9b
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.cpp
@@ -0,0 +1,239 @@
+/*
+* 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.
+*/
+#include "Win32PipeStream.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <windows.h>
+
+#ifndef _WIN32
+#error ONLY BUILD THIS SOURCE FILE FOR WINDOWS!
+#endif
+
+/* The official documentation states that the name of a given named
+ * pipe cannot be more than 256 characters long.
+ */
+#define NAMED_PIPE_MAX 256
+
+Win32PipeStream::Win32PipeStream(size_t bufSize) :
+ SocketStream(bufSize),
+ m_pipe(INVALID_HANDLE_VALUE)
+{
+}
+
+Win32PipeStream::Win32PipeStream(HANDLE pipe, size_t bufSize) :
+ SocketStream(-1, bufSize),
+ m_pipe(pipe)
+{
+}
+
+Win32PipeStream::~Win32PipeStream()
+{
+ if (m_pipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(m_pipe);
+ m_pipe = INVALID_HANDLE_VALUE;
+ }
+}
+
+/* Initialize the pipe name corresponding to a given port
+ */
+static void
+make_pipe_name(char *path, size_t pathlen, int port_number)
+{
+ snprintf(path, pathlen, "\\\\.\\pipe\\qemu-gles-%d", port_number);
+}
+
+
+/* Technical note: Named pipes work differently from BSD Sockets.
+ * One does not create/bind a pipe, and collect a new handle each
+ * time a client connects with accept().
+ *
+ * Instead, the server creates a new pipe instance each time it wants
+ * to get a new client connection, then calls ConnectNamedPipe() to
+ * wait for a connection.
+ *
+ * So listen() is a no-op, and accept() really creates the pipe handle.
+ *
+ * Also, connect() must create a pipe handle with CreateFile() and
+ * wait for a server instance with WaitNamedPipe()
+ */
+int Win32PipeStream::listen(unsigned short port)
+{
+ // just save the port number for accept()
+ m_port = port;
+ return 0;
+}
+
+SocketStream * Win32PipeStream::accept()
+{
+ char path[NAMED_PIPE_MAX+1];
+ SocketStream* clientStream;
+ HANDLE pipe;
+
+ make_pipe_name(path, sizeof(path), m_port);
+
+ pipe = ::CreateNamedPipe(
+ path, // pipe name
+ PIPE_ACCESS_DUPLEX, // read-write access
+ PIPE_TYPE_BYTE | // byte-oriented writes
+ PIPE_READMODE_BYTE | // byte-oriented reads
+ PIPE_WAIT, // blocking operations
+ PIPE_UNLIMITED_INSTANCES, // no limit on clients
+ 4096, // input buffer size
+ 4096, // output buffer size
+ 0, // client time-out
+ NULL); // default security attributes
+
+ if (pipe == INVALID_HANDLE_VALUE) {
+ ERR("%s: CreateNamedPipe failed %d\n", __FUNCTION__, (int)GetLastError());
+ return NULL;
+ }
+
+ // Stupid Win32 API design: If a client is already connected, then
+ // ConnectNamedPipe will return 0, and GetLastError() will return
+ // ERROR_PIPE_CONNECTED. This is not an error! It just means that the
+ // function didn't have to wait.
+ //
+ if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) {
+ ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
+ CloseHandle(pipe);
+ return NULL;
+ }
+
+ clientStream = new Win32PipeStream(pipe, m_bufsize);
+ return clientStream;
+}
+
+int Win32PipeStream::connect(unsigned short port)
+{
+ char path[NAMED_PIPE_MAX+1];
+ HANDLE pipe;
+ int tries = 10;
+
+ make_pipe_name(path, sizeof(path), port);
+
+ /* We're going to loop in order to wait for the pipe server to
+ * be setup properly.
+ */
+ for (; tries > 0; tries--) {
+ pipe = ::CreateFile(
+ path, // pipe name
+ GENERIC_READ | GENERIC_WRITE, // read & write
+ 0, // no sharing
+ NULL, // default security attrs
+ OPEN_EXISTING, // open existing pipe
+ 0, // default attributes
+ NULL); // no template file
+
+ /* If we have a valid pipe handle, break from the loop */
+ if (pipe != INVALID_HANDLE_VALUE) {
+ break;
+ }
+
+ /* We can get here if the pipe is busy, i.e. if the server hasn't
+ * create a new pipe instance to service our request. In which case
+ * GetLastError() will return ERROR_PIPE_BUSY.
+ *
+ * If so, then use WaitNamedPipe() to wait for a decent time
+ * to try again.
+ */
+ if (GetLastError() != ERROR_PIPE_BUSY) {
+ /* Not ERROR_PIPE_BUSY */
+ ERR("%s: CreateFile failed: %d\n", __FUNCTION__, (int)GetLastError());
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Wait for 5 seconds */
+ if ( !WaitNamedPipe(path, 5000) ) {
+ ERR("%s: WaitNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ m_pipe = pipe;
+ return 0;
+}
+
+/* Special buffer methods, since we can't use socket functions here */
+
+int Win32PipeStream::commitBuffer(size_t size)
+{
+ if (m_pipe == INVALID_HANDLE_VALUE)
+ return -1;
+
+ size_t res = size;
+ int retval = 0;
+
+ while (res > 0) {
+ DWORD written;
+ if (! ::WriteFile(m_pipe, (const char *)m_buf + (size - res), res, &written, NULL)) {
+ retval = -1;
+ ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError());
+ break;
+ }
+ res -= written;
+ }
+ return retval;
+}
+
+const unsigned char *Win32PipeStream::readFully(void *buf, size_t len)
+{
+ const unsigned char* ret = NULL;
+
+ if (m_pipe == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ if (!buf) {
+ return NULL; // do not allow NULL buf in that implementation
+ }
+
+ size_t res = len;
+ while (res > 0) {
+ DWORD readcount = 0;
+ if (! ::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) {
+ errno = (int)GetLastError();
+ return NULL;
+ }
+ res -= readcount;
+ }
+ return (const unsigned char *)buf;
+}
+
+const unsigned char *Win32PipeStream::read( void *buf, size_t *inout_len)
+{
+ size_t len = *inout_len;
+ DWORD readcount;
+
+ if (m_pipe == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ if (!buf) {
+ return NULL; // do not allow NULL buf in that implementation
+ }
+
+ if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) {
+ errno = (int)GetLastError();
+ return NULL;
+ }
+
+ *inout_len = (size_t)readcount;
+ return (const unsigned char *)buf;
+}
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h
new file mode 100644
index 0000000..4114545
--- /dev/null
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/Win32PipeStream.h
@@ -0,0 +1,41 @@
+/*
+* 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.
+*/
+#ifndef __WIN32_PIPE_STREAM_H
+#define __WIN32_PIPE_STREAM_H
+
+#include "SocketStream.h"
+#include <windows.h>
+
+class Win32PipeStream : public SocketStream {
+public:
+ explicit Win32PipeStream(size_t bufsize = 10000);
+ virtual ~Win32PipeStream();
+ virtual int listen(unsigned short port);
+ virtual SocketStream *accept();
+ virtual int connect(unsigned short port);
+
+ virtual int commitBuffer(size_t size);
+ virtual const unsigned char *readFully(void *buf, size_t len);
+ virtual const unsigned char *read(void *buf, size_t *inout_len);
+
+private:
+ Win32PipeStream(HANDLE pipe, size_t bufSize);
+ HANDLE m_pipe;
+ int m_port;
+};
+
+
+#endif
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
index b0702a7..ae70598 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
@@ -16,6 +16,7 @@
#include "glUtils.h"
#include <string.h>
#include "ErrorLog.h"
+#include <IOStream.h>
size_t glSizeof(GLenum type)
{
@@ -344,6 +345,25 @@
}
}
+void glUtilsWritePackPointerData(void* _stream, unsigned char *src,
+ int size, GLenum type, unsigned int stride,
+ unsigned int datalen)
+{
+ IOStream* stream = reinterpret_cast<IOStream*>(_stream);
+
+ unsigned int vsize = size * glSizeof(type);
+ if (stride == 0) stride = vsize;
+
+ if (stride == vsize) {
+ stream->writeFully(src, datalen);
+ } else {
+ for (unsigned int i = 0; i < datalen; i += vsize) {
+ stream->writeFully(src, (size_t)vsize);
+ src += stride;
+ }
+ }
+}
+
int glUtilsPixelBitSize(GLenum format, GLenum type)
{
int components = 0;
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
index c66c568..f8857f1 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
@@ -51,6 +51,9 @@
void glUtilsPackPointerData(unsigned char *dst, unsigned char *str,
int size, GLenum type, unsigned int stride,
unsigned int datalen);
+ void glUtilsWritePackPointerData(void* stream, unsigned char *src,
+ int size, GLenum type, unsigned int stride,
+ unsigned int datalen);
int glUtilsPixelBitSize(GLenum format, GLenum type);
void glUtilsPackStrings(char *ptr, char **strings, GLint *length, GLsizei count);
int glUtilsCalcShaderSourceLen(char **strings, GLint *length, GLsizei count);
diff --git a/tools/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp b/tools/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
index 0cddb64..66611ee 100644
--- a/tools/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
+++ b/tools/emulator/opengl/shared/OpenglOsUtils/osThreadUnix.cpp
@@ -27,6 +27,7 @@
Thread::~Thread()
{
+ pthread_mutex_destroy(&m_lock);
}
bool
diff --git a/tools/emulator/opengl/system/GLESv1/Android.mk b/tools/emulator/opengl/system/GLESv1/Android.mk
index 9d45694..97356b7 100644
--- a/tools/emulator/opengl/system/GLESv1/Android.mk
+++ b/tools/emulator/opengl/system/GLESv1/Android.mk
@@ -10,47 +10,3 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
$(call emugl-end-module)
-
-#
-# include $(CLEAR_VARS)
-#
-# # add additional depencies to ensure that the generated code that we depend on
-# # is generated
-# LOCAL_ADDITIONAL_DEPENDENCIES := \
-# $(TARGET_OUT_SHARED_LIBRARIES)/lib_renderControl_enc$(TARGET_SHLIB_SUFFIX) \
-# $(TARGET_OUT_SHARED_LIBRARIES)/libGLESv1_enc$(TARGET_SHLIB_SUFFIX)
-#
-# LOCAL_SRC_FILES := \
-# gl.cpp
-#
-#
-# LOCAL_PRELINK_MODULE := false
-# LOCAL_CFLAGS += -DLOG_TAG=\"GLES_emulation\" -DGL_GLEXT_PROTOTYPES
-# LOCAL_C_INCLUDES += \
-# $(emulatorOpengl)/host/include/libOpenglRender \
-# $(emulatorOpengl)/shared/OpenglCodecCommon \
-# $(emulatorOpengl)/system/OpenglSystemCommon \
-# $(emulatorOpengl)/system/GLESv1_enc \
-# $(emulatorOpengl)/system/renderControl_enc \
-# $(call intermediates-dir-for, SHARED_LIBRARIES, lib_renderControl_enc) \
-# $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_enc)
-#
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
-# LOCAL_MODULE := libGLESv1_CM_emulation
-# LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-#
-#
-# LOCAL_STATIC_LIBRARIES := \
-# libOpenglCodecCommon \
-#
-# LOCAL_SHARED_LIBRARIES := \
-# libcutils \
-# libutils \
-# libdl \
-# libOpenglSystemCommon \
-# libGLESv1_enc \
-# lib_renderControl_enc
-#
-#
-# include $(BUILD_SHARED_LIBRARY)
diff --git a/tools/emulator/opengl/system/GLESv1/gl.cpp b/tools/emulator/opengl/system/GLESv1/gl.cpp
index 2ab6986..142c218 100644
--- a/tools/emulator/opengl/system/GLESv1/gl.cpp
+++ b/tools/emulator/opengl/system/GLESv1/gl.cpp
@@ -6,10 +6,11 @@
#include "ErrorLog.h"
#include <private/ui/android_natives_priv.h>
#include "gralloc_cb.h"
+#include "ThreadInfo.h"
//XXX: fix this macro to get the context from fast tls path
-#define GET_CONTEXT gl_client_context_t * ctx = HostConnection::get()->glEncoder();
+#define GET_CONTEXT gl_client_context_t * ctx = getEGLThreadInfo()->hostConn->glEncoder();
#include "gl_entry.cpp"
diff --git a/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp b/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
index 6d12fe3..43391f0 100644
--- a/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
+++ b/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
@@ -544,7 +544,7 @@
m_glBufferData_enc = set_glBufferData(s_glBufferData);
m_glBufferSubData_enc = set_glBufferSubData(s_glBufferSubData);
m_glDeleteBuffers_enc = set_glDeleteBuffers(s_glDeleteBuffers);
-
+
m_glEnableClientState_enc = set_glEnableClientState(s_glEnableClientState);
m_glDisableClientState_enc = set_glDisableClientState(s_glDisableClientState);
m_glIsEnabled_enc = set_glIsEnabled(s_glIsEnabled);
@@ -558,7 +558,7 @@
GLEncoder::~GLEncoder()
{
- delete m_compressedTextureFormats;
+ delete [] m_compressedTextureFormats;
}
size_t GLEncoder::pixelDataSize(GLsizei width, GLsizei height, GLenum format, GLenum type, int pack)
diff --git a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
index fed0f7a..9b84f89 100644
--- a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
+++ b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
@@ -240,7 +240,7 @@
glTexImage2D
dir pixels in
len pixels pixelDataSize(self, width, height, format, type, 0)
- var_flag pixels nullAllowed
+ var_flag pixels nullAllowed isLarge
#void glTexParameteriv(GLenum target, GLenum pname, GLint *params)
glTexParameteriv
@@ -253,6 +253,7 @@
#void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
glTexSubImage2D
len pixels pixelDataSize(self, width, height, format, type, 0)
+ var_flag pixels isLarge
#void glVertexPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)
# we treat the pointer as an offset to a VBO
diff --git a/tools/emulator/opengl/system/GLESv2/gl2.cpp b/tools/emulator/opengl/system/GLESv2/gl2.cpp
index f4157d7..06db296 100644
--- a/tools/emulator/opengl/system/GLESv2/gl2.cpp
+++ b/tools/emulator/opengl/system/GLESv2/gl2.cpp
@@ -6,10 +6,10 @@
#include "ErrorLog.h"
#include <private/ui/android_natives_priv.h>
#include "gralloc_cb.h"
-
+#include "ThreadInfo.h"
//XXX: fix this macro to get the context from fast tls path
-#define GET_CONTEXT gl2_client_context_t * ctx = HostConnection::get()->gl2Encoder();
+#define GET_CONTEXT gl2_client_context_t * ctx = getEGLThreadInfo()->hostConn->gl2Encoder();
#include "gl2_entry.cpp"
diff --git a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp
index 5781dd4..61b19c1 100644
--- a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp
+++ b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp
@@ -534,9 +534,30 @@
int GL2Encoder::s_glGetUniformLocation(void *self, GLuint program, const GLchar *name)
{
+ if (!name) return -1;
+
GL2Encoder *ctx = (GL2Encoder*)self;
+
+ // if we need the uniform location WAR
+ // parse array index from the end of the name string
+ int arrIndex = 0;
+ bool needLocationWAR = ctx->m_shared->needUniformLocationWAR(program);
+ if (needLocationWAR) {
+ int namelen = strlen(name);
+ if (name[namelen-1] == ']') {
+ char *brace = strrchr(name,'[');
+ if (!brace || sscanf(brace+1,"%d",&arrIndex) != 1) {
+ return -1;
+ }
+
+ }
+ }
+
int hostLoc = ctx->m_glGetUniformLocation_enc(self, program, name);
- return ctx->m_shared->locationWARHostToApp(program, hostLoc);
+ if (hostLoc >= 0 && needLocationWAR) {
+ return ctx->m_shared->locationWARHostToApp(program, hostLoc, arrIndex);
+ }
+ return hostLoc;
}
void GL2Encoder::s_glUseProgram(void *self, GLuint program)
diff --git a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
index 538c453..7fe9a66 100644
--- a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
+++ b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
@@ -9,20 +9,22 @@
#void glBufferData(GLenum target, GLsizeiptr size, GLvoid *data, GLenum usage)
glBufferData
len data size
- var_flag data nullAllowed
+ var_flag data nullAllowed isLarge
#void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data)
glBufferSubData
len data size
+ var_flag data isLarge
#void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid *data)
glCompressedTexImage2D
len data imageSize
- var_flag data nullAllowed
+ var_flag data nullAllowed isLarge
#void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid *data)
glCompressedTexSubImage2D
len data imageSize
+ var_flag data isLarge
#void glDeleteBuffers(GLsizei n, GLuint *buffers)
glDeleteBuffers
@@ -243,7 +245,7 @@
glTexImage2D
dir pixels in
len pixels pixelDataSize(self, width, height, format, type, 0)
- var_flag pixels nullAllowed
+ var_flag pixels nullAllowed isLarge
#void glTexParameterfv(GLenum target, GLenum pname, GLfloat *params)
glTexParameterfv
@@ -255,6 +257,7 @@
#void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
glTexSubImage2D
len pixels pixelDataSize(self, width, height, format, type, 0)
+ var_flag pixels isLarge
#void glUniform1fv(GLint location, GLsizei count, GLfloat *v)
glUniform1fv
@@ -333,20 +336,23 @@
#void glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid *pixels)
glTexImage3DOES
- len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)
- var_flag pixels nullAllowed
+ len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)
+ var_flag pixels nullAllowed isLarge
#void glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *pixels)
glTexSubImage3DOES
len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)
+ var_flag pixels isLarge
#void glCompressedTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid *data)
glCompressedTexImage3DOES
len data imageSize
+ var_flag data isLarge
#void glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, GLvoid *data)
glCompressedTexSubImage3DOES
len data imageSize
+ var_flag data isLarge
#void glDeleteVertexArraysOES(GLsizei n, GLuint *arrays)
glDeleteVertexArraysOES
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/Android.mk b/tools/emulator/opengl/system/OpenglSystemCommon/Android.mk
index a04d19c..6198748 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/Android.mk
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/Android.mk
@@ -8,6 +8,6 @@
QemuPipeStream.cpp \
ThreadInfo.cpp
-$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
+$(call emugl-export,C_INCLUDES,$(LOCAL_PATH) bionic/libc/private)
$(call emugl-end-module)
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/HostConnection.cpp b/tools/emulator/opengl/system/OpenglSystemCommon/HostConnection.cpp
index c967b4f..2927341 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/HostConnection.cpp
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/HostConnection.cpp
@@ -60,22 +60,6 @@
return NULL;
}
-#if 0
- TcpStream *stream = new TcpStream(STREAM_BUFFER_SIZE);
- if (stream) {
- if (stream->connect("10.0.2.2", STREAM_PORT_NUM) < 0) {
- LOGE("Failed to connect to host (TcpStream)!!!\n");
- delete stream;
- stream = NULL;
- }
- else {
- con->m_stream = stream;
- LOGE("Established TCP connection with HOST\n");
- }
- }
- if (!stream)
-#endif
-
if (useQemuPipe) {
QemuPipeStream *stream = new QemuPipeStream(STREAM_BUFFER_SIZE);
if (!stream) {
@@ -108,11 +92,11 @@
}
// send zero 'clientFlags' to the host.
- unsigned int *pClientFlags =
+ unsigned int *pClientFlags =
(unsigned int *)con->m_stream->allocBuffer(sizeof(unsigned int));
*pClientFlags = 0;
con->m_stream->commitBuffer(sizeof(unsigned int));
-
+
LOGD("HostConnection::get() New Host Connection established %p, tid %d\n", con, gettid());
tinfo->hostConn = con;
}
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
index db36286..57ee399 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
@@ -39,8 +39,7 @@
bool valid() { return m_sock >= 0; }
int recv(void *buf, size_t len);
-private:
- int writeFully(const void *buf, size_t len);
+ virtual int writeFully(const void *buf, size_t len);
private:
int m_sock;
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.cpp b/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.cpp
index 3ab42a3..75da8f2 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.cpp
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.cpp
@@ -27,7 +27,7 @@
}
}
-EGLThreadInfo *getEGLThreadInfo()
+EGLThreadInfo *slow_getEGLThreadInfo()
{
EGLThreadInfo *ti = (EGLThreadInfo *)thread_store_get(&s_tls);
if (ti) return ti;
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.h b/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.h
index 98101f0..0328733 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.h
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/ThreadInfo.h
@@ -17,6 +17,10 @@
#define _THREAD_INFO_H
#include "HostConnection.h"
+#include <pthread.h>
+#ifdef HAVE_ANDROID_OS
+#include <bionic_tls.h>
+#endif
struct EGLContext_t;
@@ -30,5 +34,26 @@
};
-EGLThreadInfo *getEGLThreadInfo();
+EGLThreadInfo *slow_getEGLThreadInfo();
+
+#ifdef HAVE_ANDROID_OS
+ // We have a dedicated TLS slot in bionic
+ inline EGLThreadInfo* getEGLThreadInfo() {
+ EGLThreadInfo *tInfo =
+ (EGLThreadInfo *)(((unsigned *)__get_tls())[TLS_SLOT_OPENGL]);
+ if (!tInfo) {
+ tInfo = slow_getEGLThreadInfo();
+ ((uint32_t *)__get_tls())[TLS_SLOT_OPENGL] = (uint32_t)tInfo;
+ }
+ return tInfo;
+ }
+#else
+ inline EGLThreadInfo* getEGLThreadInfo() {
+ return slow_getEGLThreadInfo();
+ }
#endif
+
+
+
+
+#endif // of _THREAD_INFO_H
diff --git a/tools/emulator/opengl/system/egl/Android.mk b/tools/emulator/opengl/system/egl/Android.mk
index a84c9bc..867773f 100644
--- a/tools/emulator/opengl/system/egl/Android.mk
+++ b/tools/emulator/opengl/system/egl/Android.mk
@@ -1,56 +1,24 @@
-ifneq (,$(BUILD_EMULATOR_OPENGL_DRIVER))
+ifneq (false,$(BUILD_EMULATOR_OPENGL_DRIVER))
LOCAL_PATH := $(call my-dir)
-emulatorOpengl := $(LOCAL_PATH)/../..
-### EGL implementation ###########################################
-include $(CLEAR_VARS)
+$(call emugl-begin-shared-library,libEGL_emulation)
+$(call emugl-import,libOpenglSystemCommon)
+$(call emugl-set-shared-library-subpath,egl)
-# add additional depencies to ensure that the generated code that we depend on
-# is generated
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(TARGET_OUT_SHARED_LIBRARIES)/lib_renderControl_enc$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/libGLESv1_enc$(TARGET_SHLIB_SUFFIX)
+LOCAL_CFLAGS += -DLOG_TAG=\"EGL_emulation\" -DEGL_EGLEXT_PROTOTYPES -DWITH_GLES2
LOCAL_SRC_FILES := \
- eglDisplay.cpp \
- egl.cpp \
- ClientAPIExts.cpp
+ eglDisplay.cpp \
+ egl.cpp \
+ ClientAPIExts.cpp
+LOCAL_SHARED_LIBRARIES += libdl
-LOCAL_PRELINK_MODULE := false
-LOCAL_CFLAGS += -DLOG_TAG=\"EGL_emulation\" -DEGL_EGLEXT_PROTOTYPES -DWITH_GLES2
-LOCAL_C_INCLUDES += \
- $(emulatorOpengl)/host/include/libOpenglRender \
- $(emulatorOpengl)/shared/OpenglCodecCommon \
- $(emulatorOpengl)/system/OpenglSystemCommon \
- $(emulatorOpengl)/system/GLESv1_enc \
- $(emulatorOpengl)/system/GLESv2_enc \
- $(emulatorOpengl)/system/renderControl_enc \
- $(call intermediates-dir-for, SHARED_LIBRARIES, lib_renderControl_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv2_enc)
+# Used to access the Bionic private OpenGL TLS slot
+LOCAL_C_INCLUDES += bionic/libc/private
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
-LOCAL_MODULE := libEGL_emulation
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_STATIC_LIBRARIES := \
- libOpenglCodecCommon
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libdl \
- libGLESv1_enc \
- libGLESv2_enc \
- libOpenglSystemCommon \
- lib_renderControl_enc
-
-
-include $(BUILD_SHARED_LIBRARY)
+$(call emugl-end-module)
#### egl.cfg ####
@@ -69,6 +37,6 @@
LOCAL_MODULE_CLASS := ETC
include $(BUILD_PREBUILT)
-endif # TARGET_PRODUCT in 'full sdk full_x86 sdk_x86'
+endif # TARGET_PRODUCT in 'full sdk full_x86 sdk_x86)
-endif # of ifneq (,$(BUILD_EMULATOR_OPENGL_DRIVER))
+endif # BUILD_EMULATOR_OPENGL_DRIVER != false
diff --git a/tools/emulator/opengl/system/egl/ClientAPIExts.cpp b/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
index 888c231..5e81afe 100644
--- a/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
+++ b/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
@@ -129,7 +129,7 @@
#define API_ENTRY_RET(rtype,fname,params,args) \
API_ENTRY(fname,params,args)
-static struct _client_ext_funcs {
+static const struct _client_ext_funcs {
const char *fname;
void* proc;
} s_client_ext_funcs[] = {
diff --git a/tools/emulator/opengl/system/egl/ClientAPIExts.in b/tools/emulator/opengl/system/egl/ClientAPIExts.in
index c3162eb..5850701 100644
--- a/tools/emulator/opengl/system/egl/ClientAPIExts.in
+++ b/tools/emulator/opengl/system/egl/ClientAPIExts.in
@@ -2,7 +2,7 @@
// Each extension function should have one of the following
// macro definitions:
// API_ENTRY(funcname, paramlist, arglist)
-// -or- (in case funciton has return value)
+// -or- (if the function has a return value)
// API_ENTRY_RET(return_type,funcname, paramlist, arglist)
//
API_ENTRY(glEGLImageTargetTexture2DOES,
diff --git a/tools/emulator/opengl/system/egl/egl.cfg b/tools/emulator/opengl/system/egl/egl.cfg
index 9d3f2dc..951a2e8 100644
--- a/tools/emulator/opengl/system/egl/egl.cfg
+++ b/tools/emulator/opengl/system/egl/egl.cfg
@@ -1 +1,2 @@
-0 0 emulation
+0 1 emulation
+0 0 android
diff --git a/tools/emulator/opengl/system/egl/eglDisplay.cpp b/tools/emulator/opengl/system/egl/eglDisplay.cpp
index 78025b1..7beeb8e 100644
--- a/tools/emulator/opengl/system/egl/eglDisplay.cpp
+++ b/tools/emulator/opengl/system/egl/eglDisplay.cpp
@@ -19,17 +19,17 @@
static const int systemEGLVersionMajor = 1;
static const int systemEGLVersionMinor = 4;
-static const char *systemEGLVendor = "Google Android emulator";
+static const char systemEGLVendor[] = "Google Android emulator";
// list of extensions supported by this EGL implementation
// NOTE that each extension name should be suffixed with space
-static const char *systemStaticEGLExtensions =
+static const char systemStaticEGLExtensions[] =
"EGL_ANDROID_image_native_buffer ";
// list of extensions supported by this EGL implementation only if supported
// on the host implementation.
// NOTE that each extension name should be suffixed with space
-static const char *systemDynamicEGLExtensions =
+static const char systemDynamicEGLExtensions[] =
"EGL_KHR_image_base "
"EGL_KHR_gl_texture_2d_image ";
@@ -70,6 +70,11 @@
pthread_mutex_init(&m_lock, NULL);
}
+eglDisplay::~eglDisplay()
+{
+ pthread_mutex_destroy(&m_lock);
+}
+
bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
{
pthread_mutex_lock(&m_lock);
diff --git a/tools/emulator/opengl/system/egl/eglDisplay.h b/tools/emulator/opengl/system/egl/eglDisplay.h
index 934c699..9d979d9 100644
--- a/tools/emulator/opengl/system/egl/eglDisplay.h
+++ b/tools/emulator/opengl/system/egl/eglDisplay.h
@@ -33,6 +33,7 @@
{
public:
eglDisplay();
+ ~eglDisplay();
bool initialize(EGLClient_eglInterface *eglIface);
void terminate();
diff --git a/tools/emulator/opengl/system/egl/egl_ftable.h b/tools/emulator/opengl/system/egl/egl_ftable.h
index ee40585..2c9d19a 100644
--- a/tools/emulator/opengl/system/egl/egl_ftable.h
+++ b/tools/emulator/opengl/system/egl/egl_ftable.h
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-static struct _egl_funcs_by_name {
+static const struct _egl_funcs_by_name {
const char *name;
void *proc;
} egl_funcs_by_name[] = {
@@ -63,4 +63,4 @@
{"eglSetSwapRectangleANDROID", (void *)eglSetSwapRectangleANDROID}
};
-static int egl_num_funcs = sizeof(egl_funcs_by_name) / sizeof(struct _egl_funcs_by_name);
+static const int egl_num_funcs = sizeof(egl_funcs_by_name) / sizeof(struct _egl_funcs_by_name);
diff --git a/tools/emulator/opengl/system/gralloc/Android.mk b/tools/emulator/opengl/system/gralloc/Android.mk
index 52ee26c..8705602 100644
--- a/tools/emulator/opengl/system/gralloc/Android.mk
+++ b/tools/emulator/opengl/system/gralloc/Android.mk
@@ -1,44 +1,19 @@
-ifneq (,$(BUILD_EMULATOR_OPENGL_DRIVER))
+ifneq (false,$(BUILD_EMULATOR_OPENGL_DRIVER))
LOCAL_PATH := $(call my-dir)
-emulatorOpengl := $(LOCAL_PATH)/../..
-### OpenglSystemCommon ##############################################
-include $(CLEAR_VARS)
+$(call emugl-begin-shared-library,gralloc.goldfish)
+$(call emugl-import,libGLESv1_enc lib_renderControl_enc libOpenglSystemCommon)
+$(call emugl-set-shared-library-subpath,hw)
-# add additional depencies to ensure that the generated code that we depend on
-# is generated
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(TARGET_OUT_SHARED_LIBRARIES)/lib_renderControl_enc$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/libGLESv1_enc$(TARGET_SHLIB_SUFFIX)
+LOCAL_CFLAGS += -DLOG_TAG=\"gralloc_goldfish\"
-LOCAL_SRC_FILES := \
- gralloc.cpp
+LOCAL_SRC_FILES := gralloc.cpp
-LOCAL_C_INCLUDES += \
- $(emulatorOpengl)/host/include/libOpenglRender \
- $(emulatorOpengl)/shared/OpenglCodecCommon \
- $(emulatorOpengl)/system/OpenglSystemCommon \
- $(emulatorOpengl)/system/GLESv1_enc \
- $(emulatorOpengl)/system/renderControl_enc \
- $(call intermediates-dir-for, SHARED_LIBRARIES, lib_renderControl_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_enc)
+# Need to access the special OPENGL TLS Slot
+LOCAL_C_INCLUDES += bionic/libc/private
+LOCAL_SHARED_LIBRARIES += libdl
-LOCAL_MODULE_TAGS := debug
-LOCAL_PRELINK_MODULE := false
-LOCAL_CFLAGS:= -DLOG_TAG=\"gralloc_goldfish\"
-LOCAL_MODULE_PATH = $(TARGET_OUT_SHARED_LIBRARIES)/hw
-LOCAL_MODULE := gralloc.goldfish
+$(call emugl-end-module)
-LOCAL_STATIC_LIBRARIES := \
- libOpenglCodecCommon
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libOpenglSystemCommon \
- libGLESv1_enc \
- lib_renderControl_enc
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif # of ifneq (,$(GEODON_DRIVER))
+endif # BUILD_EMULATOR_OPENGL_DRIVER != false
diff --git a/tools/emulator/opengl/system/gralloc/gralloc.cpp b/tools/emulator/opengl/system/gralloc/gralloc.cpp
index 8362596..b27eaa3 100644
--- a/tools/emulator/opengl/system/gralloc/gralloc.cpp
+++ b/tools/emulator/opengl/system/gralloc/gralloc.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "gralloc_cb.h"
+#include <string.h>
#include <pthread.h>
#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define
# include <asm/page.h>
@@ -23,11 +23,28 @@
#include <cutils/ashmem.h>
#include <unistd.h>
#include <errno.h>
+#include <dlfcn.h>
#include <sys/mman.h>
+#include "gralloc_cb.h"
#include "HostConnection.h"
#include "glUtils.h"
#include <cutils/log.h>
+#include <cutils/properties.h>
+/* Set to 1 or 2 to enable debug traces */
+#define DEBUG 0
+
+#if DEBUG >= 1
+# define D(...) LOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+#if DEBUG >= 2
+# define DD(...) LOGD(__VA_ARGS__)
+#else
+# define DD(...) ((void)0)
+#endif
#define DBG_FUNC DBG("%s\n", __FUNCTION__)
//
@@ -37,6 +54,16 @@
gralloc_module_t base;
};
+/* If not NULL, this is a pointer to the fallback module.
+ * This really is gralloc.default, which we'll use if we detect
+ * that the emulator we're running in does not support GPU emulation.
+ */
+static gralloc_module_t* sFallback;
+static pthread_once_t sFallbackOnce = PTHREAD_ONCE_INIT;
+
+static void fallback_init(void); // forward
+
+
typedef struct _alloc_list_node {
buffer_handle_t handle;
_alloc_list_node *next;
@@ -103,7 +130,7 @@
int w, int h, int format, int usage,
buffer_handle_t* pHandle, int* pStride)
{
- LOGD("gralloc_alloc w=%d h=%d usage=0x%x\n", w, h, usage);
+ D("gralloc_alloc w=%d h=%d usage=0x%x\n", w, h, usage);
gralloc_device_t *grdev = (gralloc_device_t *)dev;
if (!grdev || !pHandle || !pStride)
@@ -170,7 +197,7 @@
*pStride = bpr / bpp;
}
- LOGD("gralloc_alloc ashmem_size=%d, tid %d\n", ashmem_size, gettid());
+ D("gralloc_alloc ashmem_size=%d, tid %d\n", ashmem_size, gettid());
//
// Allocate space in ashmem if needed
@@ -182,7 +209,7 @@
fd = ashmem_create_region("gralloc-buffer", ashmem_size);
if (fd < 0) {
- LOGE("gralloc_alloc failed to create ashmem region err=%d\n", errno);
+ LOGE("gralloc_alloc failed to create ashmem region: %s\n", strerror(errno));
return -errno;
}
}
@@ -212,7 +239,7 @@
DEFINE_HOST_CONNECTION;
if (hostCon && rcEnc) {
cb->hostHandle = rcEnc->rcCreateColorBuffer(rcEnc, w, h, glFormat);
- LOGD("Created host ColorBuffer 0x%x\n", cb->hostHandle);
+ D("Created host ColorBuffer 0x%x\n", cb->hostHandle);
}
if (!cb->hostHandle) {
@@ -252,7 +279,7 @@
if (cb->hostHandle != 0) {
DEFINE_AND_VALIDATE_HOST_CONNECTION;
-
+ D("Destroying host ColorBuffer 0x%x\n", cb->hostHandle);
rcEnc->rcDestroyColorBuffer(rcEnc, cb->hostHandle);
}
@@ -358,7 +385,7 @@
DEFINE_AND_VALIDATE_HOST_CONNECTION;
// send request to host
- // XXX - should be implemented
+ // TODO: XXX - should be implemented
//rcEnc->rc_XXX
return 0;
@@ -387,9 +414,7 @@
{
fb_device_t *fbdev = (fb_device_t *)dev;
- if (fbdev) {
- delete fbdev;
- }
+ delete fbdev;
return 0;
}
@@ -401,10 +426,17 @@
static int gralloc_register_buffer(gralloc_module_t const* module,
buffer_handle_t handle)
{
+ pthread_once(&sFallbackOnce, fallback_init);
+ if (sFallback != NULL) {
+ return sFallback->registerBuffer(sFallback, handle);
+ }
+
+ D("gralloc_register_buffer(%p) called", handle);
+
private_module_t *gr = (private_module_t *)module;
cb_handle_t *cb = (cb_handle_t *)handle;
if (!gr || !cb_handle_t::validate(cb)) {
- ERR("gralloc_register_buffer: invalid buffer");
+ ERR("gralloc_register_buffer(%p): invalid buffer", cb);
return -EINVAL;
}
@@ -416,7 +448,7 @@
void *vaddr;
int err = map_buffer(cb, &vaddr);
if (err) {
- ERR("gralloc_register_buffer: map failed");
+ ERR("gralloc_register_buffer(%p): map failed: %s", cb, strerror(-err));
return -err;
}
cb->mappedPid = getpid();
@@ -428,10 +460,14 @@
static int gralloc_unregister_buffer(gralloc_module_t const* module,
buffer_handle_t handle)
{
+ if (sFallback != NULL) {
+ return sFallback->unregisterBuffer(sFallback, handle);
+ }
+
private_module_t *gr = (private_module_t *)module;
cb_handle_t *cb = (cb_handle_t *)handle;
if (!gr || !cb_handle_t::validate(cb)) {
- ERR("gralloc_unregister_buffer: invalid buffer");
+ ERR("gralloc_unregister_buffer(%p): invalid buffer", cb);
return -EINVAL;
}
@@ -443,13 +479,15 @@
void *vaddr;
int err = munmap((void *)cb->ashmemBase, cb->ashmemSize);
if (err) {
- ERR("gralloc_unregister_buffer: unmap failed");
+ ERR("gralloc_unregister_buffer(%p): unmap failed", cb);
return -EINVAL;
}
cb->ashmemBase = NULL;
cb->mappedPid = 0;
}
+ D("gralloc_unregister_buffer(%p) done\n", cb);
+
return 0;
}
@@ -458,6 +496,10 @@
int l, int t, int w, int h,
void** vaddr)
{
+ if (sFallback != NULL) {
+ return sFallback->lock(sFallback, handle, usage, l, t, w, h, vaddr);
+ }
+
private_module_t *gr = (private_module_t *)module;
cb_handle_t *cb = (cb_handle_t *)handle;
if (!gr || !cb_handle_t::validate(cb)) {
@@ -546,6 +588,10 @@
static int gralloc_unlock(gralloc_module_t const* module,
buffer_handle_t handle)
{
+ if (sFallback != NULL) {
+ return sFallback->unlock(sFallback, handle);
+ }
+
private_module_t *gr = (private_module_t *)module;
cb_handle_t *cb = (cb_handle_t *)handle;
if (!gr || !cb_handle_t::validate(cb)) {
@@ -603,13 +649,19 @@
return 0;
}
+
static int gralloc_device_open(const hw_module_t* module,
const char* name,
hw_device_t** device)
{
int status = -EINVAL;
- LOGD("gralloc_device_open %s\n", name);
+ D("gralloc_device_open %s\n", name);
+
+ pthread_once( &sFallbackOnce, fallback_init );
+ if (sFallback != NULL) {
+ return sFallback->common.methods->open(&sFallback->common, name, device);
+ }
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
@@ -653,16 +705,21 @@
//
// Query the host for Framebuffer attributes
//
- LOGD("gralloc: query Frabuffer attribs\n");
+ D("gralloc: query Frabuffer attribs\n");
EGLint width = rcEnc->rcGetFBParam(rcEnc, FB_WIDTH);
- LOGD("gralloc: width=%d\n", width);
+ D("gralloc: width=%d\n", width);
EGLint height = rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT);
- LOGD("gralloc: height=%d\n", height);
+ D("gralloc: height=%d\n", height);
EGLint xdpi = rcEnc->rcGetFBParam(rcEnc, FB_XDPI);
+ D("gralloc: xdpi=%d\n", xdpi);
EGLint ydpi = rcEnc->rcGetFBParam(rcEnc, FB_YDPI);
+ D("gralloc: ydpi=%d\n", ydpi);
EGLint fps = rcEnc->rcGetFBParam(rcEnc, FB_FPS);
+ D("gralloc: fps=%d\n", fps);
EGLint min_si = rcEnc->rcGetFBParam(rcEnc, FB_MIN_SWAP_INTERVAL);
+ D("gralloc: min_swap=%d\n", min_si);
EGLint max_si = rcEnc->rcGetFBParam(rcEnc, FB_MAX_SWAP_INTERVAL);
+ D("gralloc: max_swap=%d\n", max_si);
//
// Allocate memory for the framebuffer device
@@ -682,7 +739,7 @@
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
- dev->device.setUpdateRect = fb_setUpdateRect;
+ dev->device.setUpdateRect = 0; //fb_setUpdateRect;
dev->device.compositionComplete = fb_compositionComplete; //XXX: this is a dummy
const_cast<uint32_t&>(dev->device.flags) = 0;
@@ -731,3 +788,32 @@
reserved_proc : {NULL, }
}
};
+
+/* This function is called once to detect whether the emulator supports
+ * GPU emulation (this is done by looking at the qemu.gles kernel
+ * parameter, which must be > 0 if this is the case).
+ *
+ * If not, then load gralloc.default instead as a fallback.
+ */
+static void
+fallback_init(void)
+{
+ char prop[PROPERTY_VALUE_MAX];
+ void* module;
+
+ property_get("ro.kernel.qemu.gles", prop, "0");
+ if (atoi(prop) > 0) {
+ return;
+ }
+ LOGD("Emulator without GPU emulation detected.");
+ module = dlopen("/system/lib/hw/gralloc.default.so", RTLD_LAZY|RTLD_LOCAL);
+ if (module != NULL) {
+ sFallback = reinterpret_cast<gralloc_module_t*>(dlsym(module, HAL_MODULE_INFO_SYM_AS_STR));
+ if (sFallback == NULL) {
+ dlclose(module);
+ }
+ }
+ if (sFallback == NULL) {
+ LOGE("Could not find software fallback module!?");
+ }
+}
diff --git a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
index c51ae0e..8b9972f 100644
--- a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
+++ b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
@@ -26,10 +26,10 @@
rcChooseConfig
dir attribs in
- len attribs attribs_size
- dir configs out
+ len attribs attribs_size
+ dir configs out
var_flag configs nullAllowed
- len configs configs_size*sizeof(uint32_t)
+ len configs configs_size*sizeof(uint32_t)
rcReadColorBuffer
dir pixels out
@@ -38,3 +38,4 @@
rcUpdateColorBuffer
dir pixels in
len pixels (((glUtilsPixelBitSize(format, type) * width) >> 3) * height)
+ var_flag pixels isLarge
diff --git a/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk b/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk
index 12a09f1..52a1c8e 100644
--- a/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk
+++ b/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk
@@ -6,12 +6,12 @@
LOCAL_SRC_FILES := main.cpp
PREBUILT := $(HOST_PREBUILT_TAG)
-SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
-SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
-SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
+LOCAL_SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
+LOCAL_SDL_CFLAGS := $(shell $(LOCAL_SDL_CONFIG) --cflags)
+LOCAL_SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(LOCAL_SDL_CONFIG) --static-libs))
-LOCAL_CFLAGS += $(SDL_CFLAGS) -g -O0
-LOCAL_LDLIBS += $(SDL_LDLIBS)
+LOCAL_CFLAGS += $(LOCAL_SDL_CFLAGS) -g -O0
+LOCAL_LDLIBS += $(LOCAL_SDL_LDLIBS)
ifeq ($(HOST_OS),windows)
LOCAL_LDLIBS += -lws2_32
diff --git a/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp b/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp
index c0b5535..06abce7 100644
--- a/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp
+++ b/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp
@@ -23,7 +23,11 @@
static int convert_keysym(int sym); // forward
+#ifdef __linux__
+#include <X11/Xlib.h>
+#endif
#ifdef _WIN32
+
#include <winsock2.h>
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
#else
@@ -45,6 +49,14 @@
winHeight = height;
}
+#ifdef __linux__
+ // some OpenGL implementations may call X functions
+ // it is safer to synchronize all X calls made by all the
+ // rendering threads. (although the calls we do are locked
+ // in the FrameBuffer singleton object).
+ XInitThreads();
+#endif
+
//
// Inialize SDL window
//
@@ -80,13 +92,23 @@
//
// initialize OpenGL renderer to render in our window
//
- bool inited = initOpenGLRenderer(windowId, 0, 0,
- winWidth, winHeight, portNum);
+ bool inited = initOpenGLRenderer(winWidth, winHeight, portNum);
if (!inited) {
return -1;
}
printf("renderer process started\n");
+ float zRot = 0.0f;
+ inited = createOpenGLSubwindow(windowId, 0, 0,
+ winWidth, winHeight, zRot);
+ if (!inited) {
+ printf("failed to create OpenGL subwindow\n");
+ stopOpenGLRenderer();
+ return -1;
+ }
+ int subwinWidth = winWidth;
+ int subwinHeight = winHeight;
+
injector = new EventInjector(consolePort);
// Just wait until the window is closed
@@ -103,7 +125,7 @@
injector->sendMouseDown(ev.button.x, ev.button.y);
mouseDown = 1;
}
- break;
+ break;
case SDL_MOUSEBUTTONUP:
if (mouseDown) {
injector->sendMouseUp(ev.button.x,ev.button.y);
@@ -123,7 +145,41 @@
goto EXIT;
}
#endif
- injector->sendKeyDown(convert_keysym(ev.key.keysym.sym));
+ injector->sendKeyDown(convert_keysym(ev.key.keysym.sym));
+
+ if (ev.key.keysym.sym == SDLK_KP_MINUS) {
+ subwinWidth /= 2;
+ subwinHeight /= 2;
+
+ bool stat = destroyOpenGLSubwindow();
+ printf("destroy subwin returned %d\n", stat);
+ stat = createOpenGLSubwindow(windowId,
+ (winWidth - subwinWidth) / 2,
+ (winHeight - subwinHeight) / 2,
+ subwinWidth, subwinHeight,
+ zRot);
+ printf("create subwin returned %d\n", stat);
+ }
+ else if (ev.key.keysym.sym == SDLK_KP_PLUS) {
+ subwinWidth *= 2;
+ subwinHeight *= 2;
+
+ bool stat = destroyOpenGLSubwindow();
+ printf("destroy subwin returned %d\n", stat);
+ stat = createOpenGLSubwindow(windowId,
+ (winWidth - subwinWidth) / 2,
+ (winHeight - subwinHeight) / 2,
+ subwinWidth, subwinHeight,
+ zRot);
+ printf("create subwin returned %d\n", stat);
+ }
+ else if (ev.key.keysym.sym == SDLK_KP_MULTIPLY) {
+ zRot += 10.0f;
+ setOpenGLDisplayRotation(zRot);
+ }
+ else if (ev.key.keysym.sym == SDLK_KP_ENTER) {
+ repaintOpenGLDisplay();
+ }
break;
case SDL_KEYUP:
injector->sendKeyUp(convert_keysym(ev.key.keysym.sym));
@@ -164,4 +220,3 @@
}
return sym;
}
-
diff --git a/tools/emulator/opengl/tests/event_injector/sockets.c b/tools/emulator/opengl/tests/event_injector/sockets.c
index 93dde30..a2cc334 100644
--- a/tools/emulator/opengl/tests/event_injector/sockets.c
+++ b/tools/emulator/opengl/tests/event_injector/sockets.c
@@ -33,6 +33,7 @@
#ifdef _WIN32
# define xxWIN32_LEAN_AND_MEAN
+# define _WIN32_WINNT 0x501
# include <windows.h>
# include <winsock2.h>
# include <ws2tcpip.h>
@@ -171,9 +172,7 @@
}
if (result == NULL) {
- result = tempstr_format(
- "Unkown socket error (Winsock=0x%08x) errno=%d: %s",
- winsock_error, errno, strerror(errno));
+ result = "Unknown socket error";
}
return result;
}
@@ -1227,25 +1226,6 @@
#ifdef _WIN32
-static void
-socket_close_handler( void* _fd )
-{
- int fd = (int)_fd;
- int ret;
- char buff[64];
-
- /* we want to drain the read side of the socket before closing it */
- do {
- ret = recv( fd, buff, sizeof(buff), 0 );
- } while (ret < 0 && WSAGetLastError() == WSAEINTR);
-
- if (ret < 0 && WSAGetLastError() == EWOULDBLOCK)
- return;
-
- qemu_set_fd_handler( fd, NULL, NULL, NULL );
- closesocket( fd );
-}
-
void
socket_close( int fd )
{
@@ -1253,7 +1233,8 @@
shutdown( fd, SD_BOTH );
/* we want to drain the socket before closing it */
- qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd );
+ //qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd );
+ closesocket(fd);
errno = old_errno;
}
diff --git a/tools/emulator/opengl/tests/gles_android_wrapper/Android.mk b/tools/emulator/opengl/tests/gles_android_wrapper/Android.mk
index 8aee204..f7c8fed 100644
--- a/tools/emulator/opengl/tests/gles_android_wrapper/Android.mk
+++ b/tools/emulator/opengl/tests/gles_android_wrapper/Android.mk
@@ -16,43 +16,6 @@
## comment for no debug
#debugFlags = -g -O0
-#### libGLESv1_CM_emul.so
-# include $(CLEAR_VARS)
-#
-#
-#
-# LOCAL_MODULE := libGLESv1_CM_emul
-# LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
-# LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-# LOCAL_SRC_FILES := glesv1_emul_ifc.cpp
-#
-# LOCAL_PRELINK_MODULE := false
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_SHARED_LIBRARIES := libdl libcutils
-# LOCAL_CFLAGS += $(debugFlags)
-#
-# LOCAL_C_INCLUDES += \
-# $(emulatorOpengl)/system/GLESv1_enc \
-# $(emulatorOpengl)/shared/OpenglCodecCommon
-#
-# glesv1_emul_intermediates := $(local-intermediates-dir)
-#
-# GEN_GLESv1_emul := \
-# $(glesv1_emul_intermediates)/gl_wrapper_entry.cpp \
-# $(glesv1_emul_intermediates)/gl_wrapper_context.cpp
-# $(GEN_GLESv1_emul) : PRIVATE_PATH := $(LOCAL_PATH)
-# $(GEN_GLESv1_emul) : PRIVATE_CUSTOM_TOOL := \
-# $(EMUGEN) -W $(glesv1_emul_intermediates) -i $(emulatorOpengl)/system/GLESv1_enc gl
-# $(GEN_GLESv1_emul) : $(EMUGEN) \
-# $(emulatorOpengl)/system/GLESv1_enc/gl.in \
-# $(emulatorOpengl)/system/GLESv1_enc/gl.attrib \
-# $(emulatorOpengl)/system/GLESv1_enc/gl.types
-# $(transform-generated-source)
-#
-# LOCAL_GENERATED_SOURCES += $(GEN_GLESv1_emul)
-#
-# include $(BUILD_SHARED_LIBRARY)
-
#### libGLESv2_CM_emul.so
$(call emugl-begin-shared-library, libGLESv2_emul)
$(call emugl-import,libGLESv2_enc)
@@ -61,61 +24,15 @@
$(call emugl-set-shared-library-subpath,egl)
$(call emugl-end-module)
-# include $(CLEAR_VARS)
-#
-# LOCAL_MODULE := libGLESv2_emul
-# LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
-# LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-# LOCAL_SRC_FILES := glesv2_emul_ifc.cpp
-#
-# LOCAL_PRELINK_MODULE := false
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_SHARED_LIBRARIES := libdl libcutils
-# LOCAL_CFLAGS += $(debugFlags)
-#
-# LOCAL_C_INCLUDES += \
-# $(emulatorOpengl)/system/GLESv2_enc \
-# $(emulatorOpengl)/shared/OpenglCodecCommon
-#
-# glesv2_emul_intermediates := $(local-intermediates-dir)
-#
-# GEN_GLESv2_emul := \
-# $(glesv2_emul_intermediates)/gl2_wrapper_entry.cpp \
-# $(glesv2_emul_intermediates)/gl2_wrapper_context.cpp
-#
-# $(GEN_GLESv2_emul) : PRIVATE_PATH := $(LOCAL_PATH)
-# $(GEN_GLESv2_emul) : PRIVATE_CUSTOM_TOOL := \
-# $(EMUGEN) -W $(glesv2_emul_intermediates) -i $(emulatorOpengl)/system/GLESv2_enc gl2
-# $(GEN_GLESv2_emul) : $(EMUGEN) \
-# $(emulatorOpengl)/system/GLESv2_enc/gl2.in \
-# $(emulatorOpengl)/system/GLESv2_enc/gl2.attrib \
-# $(emulatorOpengl)/system/GLESv2_enc/gl2.types
-# $(transform-generated-source)
-#
-# LOCAL_GENERATED_SOURCES += $(GEN_GLESv2_emul)
-#
-# include $(BUILD_SHARED_LIBRARY)
-
-
##### libEGL_emul.so ###########
# THE FOLLOWING DOESN'T WORK YET
#
-# $(call emugl-begin-shared-library,libEGL_emul)
-# $(call emugl-import,libut_rendercontrol_enc libGLESv1_enc libGLESv2_enc libOpenglSystemCommon)
-#
-# $(call emugl-set-shared-library-subpath,egl)
-# LOCAL_CFLAGS += $(logTag)
-#
-# LOCAL_SRC_FILES := \
-# egl.cpp \
-# egl_dispatch.cpp \
-# ServerConnection.cpp \
-# ThreadInfo.cpp
-#
-# $(call emugl-end-module)
+$(call emugl-begin-shared-library,libEGL_emul)
+$(call emugl-import,libut_rendercontrol_enc libGLESv1_CM_emul libGLESv2_emul libOpenglSystemCommon)
-include $(CLEAR_VARS)
+$(call emugl-set-shared-library-subpath,egl)
+LOCAL_CFLAGS += $(logTag)
LOCAL_SRC_FILES := \
egl.cpp \
@@ -123,50 +40,7 @@
ServerConnection.cpp \
ThreadInfo.cpp
-# add additional depencies to ensure that the generated code that we depend on
-# is generated
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(TARGET_OUT_SHARED_LIBRARIES)/libut_rendercontrol_enc$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/libGLESv1_enc$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/libGLESv2_enc$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/egl/libGLESv1_CM_emul$(TARGET_SHLIB_SUFFIX) \
- $(TARGET_OUT_SHARED_LIBRARIES)/egl/libGLESv2_emul$(TARGET_SHLIB_SUFFIX)
-
-
-
-LOCAL_C_INCLUDES := $(emulatorOpengl)/shared/OpenglCodecCommon \
- $(emulatorOpengl)/host/include/libOpenglRender \
- $(emulatorOpengl)/system/OpenglSystemCommon \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libut_rendercontrol_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv2_enc) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_CM_emul) \
- $(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv2_emul) \
- $(emulatorOpengl)/system/GLESv1_enc \
- $(emulatorOpengl)/system/GLESv2_enc \
- $(emulatorOpengl)/tests/ut_rendercontrol_enc
-
-
-LOCAL_CFLAGS := $(logTag)
-LOCAL_CFLAGS += $(debugFlags)
-
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
-LOCAL_MODULE := libEGL_emul
-LOCAL_MODULE_TAGS := debug
-LOCAL_PRELINK_MODULE := false
-
-#LOCAL_LDLIBS := -lpthread -ldl
-LOCAL_SHARED_LIBRARIES := libdl \
- libcutils \
- libGLESv1_enc \
- libGLESv2_enc \
- libOpenglSystemCommon \
- libut_rendercontrol_enc
-
-LOCAL_STATIC_LIBRARIES := libOpenglCodecCommon
-
-include $(BUILD_SHARED_LIBRARY)
+$(call emugl-end-module)
#### egl.cfg ####
diff --git a/tools/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk b/tools/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk
index c1cda0a..deb11b6 100644
--- a/tools/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk
+++ b/tools/emulator/opengl/tests/translator_tests/GLES_CM/Android.mk
@@ -4,15 +4,15 @@
$(call emugl-import,libEGL_translator libGLES_CM_translator)
PREBUILT := $(HOST_PREBUILT_TAG)
-SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
-SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
-SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
+LOCAL_SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
+LOCAL_SDL_CFLAGS := $(shell $(LOCAL_SDL_CONFIG) --cflags)
+LOCAL_SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(LOCAL_SDL_CONFIG) --static-libs))
LOCAL_SRC_FILES:= \
triangleCM.cpp
-LOCAL_CFLAGS += $(SDL_CFLAGS) -g -O0
-LOCAL_LDLIBS += $(SDL_LDLIBS)
+LOCAL_CFLAGS += $(LOCAL_SDL_CFLAGS) -g -O0
+LOCAL_LDLIBS += $(LOCAL_SDL_LDLIBS)
LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
@@ -21,40 +21,3 @@
endif
$(call emugl-end-module)
-
-# include $(CLEAR_VARS)
-#
-# translator_path := $(LOCAL_PATH)/../../../host/libs/Translator
-#
-# PREBUILT := $(HOST_PREBUILT_TAG)
-# SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
-# SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
-# SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
-#
-# LOCAL_SRC_FILES:= \
-# triangleCM.cpp
-#
-#
-# LOCAL_SHARED_LIBRARIES := \
-# libEGL_translator \
-# libGLES_CM_translator
-#
-# LOCAL_CFLAGS += $(SDL_CFLAGS) -g -O0
-# LOCAL_LDLIBS += $(SDL_LDLIBS)
-#
-#
-# LOCAL_MODULE:= triangleCM
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
-#
-# ifeq ($(HOST_OS),darwin)
-#
-# LOCAL_LDLIBS += -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit
-# LOCAL_STATIC_LIBRARIES += libMac_view
-# LOCAL_C_INCLUDES += \
-# $(LOCAL_PATH)/../MacCommon \
-# $(translator_path)/include
-# endif
-#
-# include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tools/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk b/tools/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk
index 8edea7a..ae0064f 100644
--- a/tools/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk
+++ b/tools/emulator/opengl/tests/translator_tests/GLES_V2/Android.mk
@@ -4,15 +4,15 @@
$(call emugl-import,libEGL_translator libGLES_V2_translator)
PREBUILT := $(HOST_PREBUILT_TAG)
-SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
-SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
-SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
+LOCAL_SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
+LOCAL_SDL_CFLAGS := $(shell $(LOCAL_SDL_CONFIG) --cflags)
+LOCAL_SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(LOCAL_SDL_CONFIG) --static-libs))
LOCAL_SRC_FILES:= \
triangleV2.cpp
-LOCAL_CFLAGS += $(SDL_CFLAGS) -g -O0
-LOCAL_LDLIBS += $(SDL_LDLIBS)
+LOCAL_CFLAGS += $(LOCAL_SDL_CFLAGS) -g -O0
+LOCAL_LDLIBS += $(LOCAL_SDL_LDLIBS)
LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
@@ -22,40 +22,3 @@
$(call emugl-end-module)
-# include $(CLEAR_VARS)
-#
-# translator_path := $(LOCAL_PATH)/../../../host/libs/Translator
-#
-# PREBUILT := $(HOST_PREBUILT_TAG)
-# SDL_CONFIG ?= prebuilt/$(PREBUILT)/sdl/bin/sdl-config
-# SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
-# SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
-#
-# LOCAL_SRC_FILES:= \
-# triangleV2.cpp
-#
-#
-# LOCAL_SHARED_LIBRARIES := \
-# libEGL_translator \
-# libGLES_V2_translator
-#
-# LOCAL_CFLAGS += $(SDL_CFLAGS) -g -O0
-# LOCAL_LDLIBS += $(SDL_LDLIBS)
-#
-#
-# LOCAL_MODULE:= triangleV2
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
-#
-# ifeq ($(HOST_OS),darwin)
-#
-# LOCAL_LDLIBS += -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit
-# LOCAL_C_INCLUDES += \
-# $(translator_path)/include \
-# $(LOCAL_PATH)/../MacCommon
-# LOCAL_STATIC_LIBRARIES += libMac_view
-#
-# endif
-#
-# include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tools/emulator/opengl/tests/translator_tests/MacCommon/Android.mk b/tools/emulator/opengl/tests/translator_tests/MacCommon/Android.mk
index 6e849a1..4c4ae6b 100644
--- a/tools/emulator/opengl/tests/translator_tests/MacCommon/Android.mk
+++ b/tools/emulator/opengl/tests/translator_tests/MacCommon/Android.mk
@@ -11,20 +11,3 @@
LOCAL_CFLAGS += -g -O0
$(call emugl-end-module)
endif # HOST_OS == darwin
-
-# include $(CLEAR_VARS)
-#
-#
-# LOCAL_LDLIBS := -Wl,-framework,AppKit
-#
-# LOCAL_SRC_FILES := setup_gl.m
-#
-#
-#
-# LOCAL_CFLAGS := -g -O0
-# LOCAL_MODULE_TAGS := debug
-# LOCAL_MODULE := libMac_view
-#
-#
-# include $(BUILD_HOST_STATIC_LIBRARY)
-# endif
diff --git a/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.cpp b/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.cpp
index 661b4cc..e0073c0 100644
--- a/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.cpp
+++ b/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.cpp
@@ -17,7 +17,7 @@
#include <string.h>
#include <assert.h>
-ReadBuffer::ReadBuffer(TcpStream *stream, size_t bufsize)
+ReadBuffer::ReadBuffer(SocketStream *stream, size_t bufsize)
{
m_size = bufsize;
m_stream = stream;
diff --git a/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.h b/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.h
index 1b8f6fd..b039b19 100644
--- a/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.h
+++ b/tools/emulator/opengl/tests/ut_renderer/ReadBuffer.h
@@ -16,11 +16,11 @@
#ifndef _READ_BUFFER_H
#define _READ_BUFFER_H
-#include "TcpStream.h"
+#include "SocketStream.h"
class ReadBuffer {
public:
- ReadBuffer(TcpStream *stream, size_t bufSize);
+ ReadBuffer(SocketStream *stream, size_t bufSize);
~ReadBuffer();
int getData(); // get fresh data from the stream
unsigned char *buf() { return m_readPtr; } // return the next read location
@@ -31,6 +31,6 @@
unsigned char *m_readPtr;
size_t m_size;
size_t m_validData;
- TcpStream *m_stream;
+ SocketStream *m_stream;
};
#endif
diff --git a/tools/emulator/opengl/tests/ut_renderer/RenderingThread.cpp b/tools/emulator/opengl/tests/ut_renderer/RenderingThread.cpp
index ddd8405..70eee20 100644
--- a/tools/emulator/opengl/tests/ut_renderer/RenderingThread.cpp
+++ b/tools/emulator/opengl/tests/ut_renderer/RenderingThread.cpp
@@ -255,7 +255,7 @@
}
-RenderingThread::RenderingThread(TcpStream *stream) :
+RenderingThread::RenderingThread(SocketStream *stream) :
m_stream(stream),
m_currentContext(NULL)
{
diff --git a/tools/emulator/opengl/tests/ut_renderer/RenderingThread.h b/tools/emulator/opengl/tests/ut_renderer/RenderingThread.h
index 549d4af..0b4ebe6 100644
--- a/tools/emulator/opengl/tests/ut_renderer/RenderingThread.h
+++ b/tools/emulator/opengl/tests/ut_renderer/RenderingThread.h
@@ -16,7 +16,7 @@
#ifndef _RENDERING_THREAD_H_
#define _RENDERING_THREAD_H_
-#include "TcpStream.h"
+#include "SocketStream.h"
#include "GLDecoder.h"
#include "GL2Decoder.h"
#include "ut_rendercontrol_dec.h"
@@ -38,7 +38,7 @@
class RenderingThread {
public:
- RenderingThread(TcpStream *stream);
+ RenderingThread(SocketStream *stream);
int start();
void *thread();
RendererContext *currentContext() { return m_currentContext; }
@@ -54,7 +54,7 @@
ut_rendercontrol_decoder_context_t m_utDec;
GL2Decoder m_gl2Dec;
- TcpStream *m_stream;
+ SocketStream *m_stream;
pthread_t m_thread;
RendererContext * m_currentContext;
diff --git a/tools/emulator/opengl/tests/ut_renderer/ut_renderer.cpp b/tools/emulator/opengl/tests/ut_renderer/ut_renderer.cpp
index 2137a82..f2b2bc3 100644
--- a/tools/emulator/opengl/tests/ut_renderer/ut_renderer.cpp
+++ b/tools/emulator/opengl/tests/ut_renderer/ut_renderer.cpp
@@ -20,22 +20,33 @@
#include "codec_defs.h"
#include "RenderingThread.h"
#include "TcpStream.h"
+#ifndef _WIN32
+#include "UnixStream.h"
+#endif
int main(int argc, char **argv)
{
-
- TcpStream *socket = new TcpStream;
+#ifdef _WIN32
+ TcpStream *socket = new TcpStream();
if (socket->listen(CODEC_SERVER_PORT) < 0) {
perror("listen");
exit(1);
}
+#else
+ UnixStream *socket = new UnixStream();
+
+ if (socket->listen(CODEC_SERVER_PORT) < 0) {
+ perror("listen");
+ exit(1);
+ }
+#endif
printf("waiting for client connection on port: %d\n", CODEC_SERVER_PORT);
while (1) {
// wait for client connection
- TcpStream *glStream = socket->accept();
+ SocketStream *glStream = socket->accept();
if (glStream == NULL) {
printf("failed to get client.. aborting\n");
exit(3);
diff --git a/tools/emulator/skins/WVGA800/hardware.ini b/tools/emulator/skins/WVGA800/hardware.ini
index 19e3b35..96dee4f 100644
--- a/tools/emulator/skins/WVGA800/hardware.ini
+++ b/tools/emulator/skins/WVGA800/hardware.ini
@@ -1,4 +1,4 @@
# skin-specific hardware values
hw.lcd.density=240
vm.heapSize=24
-hw.ramSize=256
\ No newline at end of file
+hw.ramSize=512
\ No newline at end of file
diff --git a/tools/emulator/skins/WVGA854/hardware.ini b/tools/emulator/skins/WVGA854/hardware.ini
index 9434288..6ac3d82 100644
--- a/tools/emulator/skins/WVGA854/hardware.ini
+++ b/tools/emulator/skins/WVGA854/hardware.ini
@@ -1,4 +1,4 @@
# skin-specific hardware values
hw.lcd.density=240
vm.heapSize=24
-hw.ramSize=256
+hw.ramSize=512
diff --git a/tools/emulator/skins/WXGA/arrow_down.png b/tools/emulator/skins/WXGA720/arrow_down.png
similarity index 100%
rename from tools/emulator/skins/WXGA/arrow_down.png
rename to tools/emulator/skins/WXGA720/arrow_down.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_left.png b/tools/emulator/skins/WXGA720/arrow_left.png
similarity index 100%
rename from tools/emulator/skins/WXGA/arrow_left.png
rename to tools/emulator/skins/WXGA720/arrow_left.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_right.png b/tools/emulator/skins/WXGA720/arrow_right.png
similarity index 100%
rename from tools/emulator/skins/WXGA/arrow_right.png
rename to tools/emulator/skins/WXGA720/arrow_right.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_up.png b/tools/emulator/skins/WXGA720/arrow_up.png
similarity index 100%
rename from tools/emulator/skins/WXGA/arrow_up.png
rename to tools/emulator/skins/WXGA720/arrow_up.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA720/background_land.png b/tools/emulator/skins/WXGA720/background_land.png
new file mode 100755
index 0000000..2aba27e
--- /dev/null
+++ b/tools/emulator/skins/WXGA720/background_land.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA720/background_port.png b/tools/emulator/skins/WXGA720/background_port.png
new file mode 100755
index 0000000..9561ca8
--- /dev/null
+++ b/tools/emulator/skins/WXGA720/background_port.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/button.png b/tools/emulator/skins/WXGA720/button.png
similarity index 100%
rename from tools/emulator/skins/WXGA/button.png
rename to tools/emulator/skins/WXGA720/button.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/controls.png b/tools/emulator/skins/WXGA720/controls.png
similarity index 100%
rename from tools/emulator/skins/WXGA/controls.png
rename to tools/emulator/skins/WXGA720/controls.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA720/hardware.ini b/tools/emulator/skins/WXGA720/hardware.ini
new file mode 100755
index 0000000..3be263c
--- /dev/null
+++ b/tools/emulator/skins/WXGA720/hardware.ini
@@ -0,0 +1,8 @@
+# skin-specific hardware values
+hw.lcd.density=320
+vm.heapSize=48
+hw.ramSize=1024
+hw.keyboard.lid=no
+hw.mainKeys=no
+
+
diff --git a/tools/emulator/skins/WXGA/key.png b/tools/emulator/skins/WXGA720/key.png
similarity index 100%
rename from tools/emulator/skins/WXGA/key.png
rename to tools/emulator/skins/WXGA720/key.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/keyboard.png b/tools/emulator/skins/WXGA720/keyboard.png
similarity index 100%
rename from tools/emulator/skins/WXGA/keyboard.png
rename to tools/emulator/skins/WXGA720/keyboard.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA720/layout b/tools/emulator/skins/WXGA720/layout
new file mode 100755
index 0000000..8332bdc
--- /dev/null
+++ b/tools/emulator/skins/WXGA720/layout
@@ -0,0 +1,88 @@
+parts {
+ portrait {
+ background {
+ image background_port.png
+ }
+ }
+ landscape {
+ background {
+ image background_land.png
+ }
+ }
+
+ device {
+ display {
+ width 1280
+ height 720
+ x 0
+ y 0
+ }
+ }
+
+}
+
+layouts {
+
+ landscape {
+ width 1333
+ height 775
+ color 0xe0e0e0
+ event EV_SW:0:1
+
+ part1 {
+ name portrait
+ x 1400
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
+ name device
+ x 26
+ y 29
+ }
+ }
+
+ portrait {
+ width 773
+ height 1334
+ color 0xe0e0e0
+ event EV_SW:0:0
+
+ dpad-rotation 3
+
+ part1 {
+ name portrait
+ x 0
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 1400
+ y 0
+ }
+
+ part3 {
+ name device
+ x 27
+ y 1307
+ rotation 3
+ }
+ }
+
+}
+
+keyboard {
+ charmap qwerty2
+}
+
+network {
+ speed full
+ delay none
+}
diff --git a/tools/emulator/skins/WXGA/select.png b/tools/emulator/skins/WXGA720/select.png
similarity index 100%
rename from tools/emulator/skins/WXGA/select.png
rename to tools/emulator/skins/WXGA720/select.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/spacebar.png b/tools/emulator/skins/WXGA720/spacebar.png
similarity index 100%
rename from tools/emulator/skins/WXGA/spacebar.png
rename to tools/emulator/skins/WXGA720/spacebar.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_down.png b/tools/emulator/skins/WXGA800/arrow_down.png
similarity index 100%
copy from tools/emulator/skins/WXGA/arrow_down.png
copy to tools/emulator/skins/WXGA800/arrow_down.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_left.png b/tools/emulator/skins/WXGA800/arrow_left.png
similarity index 100%
copy from tools/emulator/skins/WXGA/arrow_left.png
copy to tools/emulator/skins/WXGA800/arrow_left.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_right.png b/tools/emulator/skins/WXGA800/arrow_right.png
similarity index 100%
copy from tools/emulator/skins/WXGA/arrow_right.png
copy to tools/emulator/skins/WXGA800/arrow_right.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/arrow_up.png b/tools/emulator/skins/WXGA800/arrow_up.png
similarity index 100%
copy from tools/emulator/skins/WXGA/arrow_up.png
copy to tools/emulator/skins/WXGA800/arrow_up.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/background_land.png b/tools/emulator/skins/WXGA800/background_land.png
similarity index 100%
rename from tools/emulator/skins/WXGA/background_land.png
rename to tools/emulator/skins/WXGA800/background_land.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/background_port.png b/tools/emulator/skins/WXGA800/background_port.png
similarity index 100%
rename from tools/emulator/skins/WXGA/background_port.png
rename to tools/emulator/skins/WXGA800/background_port.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/button.png b/tools/emulator/skins/WXGA800/button.png
similarity index 100%
copy from tools/emulator/skins/WXGA/button.png
copy to tools/emulator/skins/WXGA800/button.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/controls.png b/tools/emulator/skins/WXGA800/controls.png
similarity index 100%
copy from tools/emulator/skins/WXGA/controls.png
copy to tools/emulator/skins/WXGA800/controls.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/hardware.ini b/tools/emulator/skins/WXGA800/hardware.ini
similarity index 84%
rename from tools/emulator/skins/WXGA/hardware.ini
rename to tools/emulator/skins/WXGA800/hardware.ini
index c777f9e..81eac11 100755
--- a/tools/emulator/skins/WXGA/hardware.ini
+++ b/tools/emulator/skins/WXGA800/hardware.ini
@@ -1,6 +1,6 @@
# skin-specific hardware values
hw.lcd.density=160
vm.heapSize=48
-hw.ramSize=512
+hw.ramSize=1024
hw.keyboard.lid=no
diff --git a/tools/emulator/skins/WXGA/key.png b/tools/emulator/skins/WXGA800/key.png
similarity index 100%
copy from tools/emulator/skins/WXGA/key.png
copy to tools/emulator/skins/WXGA800/key.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/keyboard.png b/tools/emulator/skins/WXGA800/keyboard.png
similarity index 100%
copy from tools/emulator/skins/WXGA/keyboard.png
copy to tools/emulator/skins/WXGA800/keyboard.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/layout b/tools/emulator/skins/WXGA800/layout
similarity index 100%
rename from tools/emulator/skins/WXGA/layout
rename to tools/emulator/skins/WXGA800/layout
diff --git a/tools/emulator/skins/WXGA/select.png b/tools/emulator/skins/WXGA800/select.png
similarity index 100%
copy from tools/emulator/skins/WXGA/select.png
copy to tools/emulator/skins/WXGA800/select.png
Binary files differ
diff --git a/tools/emulator/skins/WXGA/spacebar.png b/tools/emulator/skins/WXGA800/spacebar.png
similarity index 100%
copy from tools/emulator/skins/WXGA/spacebar.png
copy to tools/emulator/skins/WXGA800/spacebar.png
Binary files differ
diff --git a/tools/emulator/system/camera/Android.mk b/tools/emulator/system/camera/Android.mk
new file mode 100755
index 0000000..2e5c19c
--- /dev/null
+++ b/tools/emulator/system/camera/Android.mk
@@ -0,0 +1,65 @@
+# 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.
+
+ifndef BUILD_EMULATOR_CAMERA_HAL
+BUILD_EMULATOR_CAMERA_HAL := true
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_CFLAGS += -fno-short-enums -DQEMU_HARDWARE
+LOCAL_SHARED_LIBRARIES:= \
+ libbinder \
+ libutils \
+ libcutils \
+ libcamera_client \
+ libui \
+
+# JPEG conversion libraries and includes.
+LOCAL_SHARED_LIBRARIES += \
+ libjpeg \
+ libskia \
+ libandroid_runtime \
+
+LOCAL_C_INCLUDES += external/jpeg \
+ external/skia/include/core/ \
+ frameworks/base/core/jni/android/graphics
+
+LOCAL_SRC_FILES := \
+ EmulatedCameraHal.cpp \
+ EmulatedCameraFactory.cpp \
+ EmulatedCamera.cpp \
+ EmulatedCameraDevice.cpp \
+ EmulatedQemuCamera.cpp \
+ EmulatedQemuCameraDevice.cpp \
+ EmulatedFakeCamera.cpp \
+ EmulatedFakeCameraDevice.cpp \
+ Converters.cpp \
+ PreviewWindow.cpp \
+ CallbackNotifier.cpp \
+ QemuClient.cpp \
+ JpegCompressor.cpp
+
+ifeq ($(TARGET_PRODUCT),vbox_x86)
+LOCAL_MODULE := camera.vbox_x86
+else
+LOCAL_MODULE := camera.goldfish
+endif
+
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_SHARED_LIBRARY)
+
+endif # BUILD_EMULATOR_CAMERA_HAL
diff --git a/tools/emulator/system/camera/CallbackNotifier.cpp b/tools/emulator/system/camera/CallbackNotifier.cpp
new file mode 100755
index 0000000..a877b40
--- /dev/null
+++ b/tools/emulator/system/camera/CallbackNotifier.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class CallbackNotifier that manages callbacks set
+ * via set_callbacks, enable_msg_type, and disable_msg_type camera HAL API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_CallbackNotifier"
+#include <cutils/log.h>
+#include <media/stagefright/MetadataBufferType.h>
+#include "EmulatedCameraDevice.h"
+#include "CallbackNotifier.h"
+#include "JpegCompressor.h"
+
+namespace android {
+
+/* String representation of camera messages. */
+static const char* lCameraMessages[] =
+{
+ "CAMERA_MSG_ERROR",
+ "CAMERA_MSG_SHUTTER",
+ "CAMERA_MSG_FOCUS",
+ "CAMERA_MSG_ZOOM",
+ "CAMERA_MSG_PREVIEW_FRAME",
+ "CAMERA_MSG_VIDEO_FRAME",
+ "CAMERA_MSG_POSTVIEW_FRAME",
+ "CAMERA_MSG_RAW_IMAGE",
+ "CAMERA_MSG_COMPRESSED_IMAGE",
+ "CAMERA_MSG_RAW_IMAGE_NOTIFY",
+ "CAMERA_MSG_PREVIEW_METADATA"
+};
+static const int lCameraMessagesNum = sizeof(lCameraMessages) / sizeof(char*);
+
+/* Builds an array of strings for the given set of messages.
+ * Param:
+ * msg - Messages to get strings for,
+ * strings - Array where to save strings
+ * max - Maximum number of entries in the array.
+ * Return:
+ * Number of strings saved into the 'strings' array.
+ */
+static int GetMessageStrings(uint32_t msg, const char** strings, int max)
+{
+ int index = 0;
+ int out = 0;
+ while (msg != 0 && out < max && index < lCameraMessagesNum) {
+ while ((msg & 0x1) == 0 && index < lCameraMessagesNum) {
+ msg >>= 1;
+ index++;
+ }
+ if ((msg & 0x1) != 0 && index < lCameraMessagesNum) {
+ strings[out] = lCameraMessages[index];
+ out++;
+ msg >>= 1;
+ index++;
+ }
+ }
+
+ return out;
+}
+
+/* Logs messages, enabled by the mask. */
+static void PrintMessages(uint32_t msg)
+{
+ const char* strs[lCameraMessagesNum];
+ const int translated = GetMessageStrings(msg, strs, lCameraMessagesNum);
+ for (int n = 0; n < translated; n++) {
+ LOGV(" %s", strs[n]);
+ }
+}
+
+CallbackNotifier::CallbackNotifier()
+ : mNotifyCB(NULL),
+ mDataCB(NULL),
+ mDataCBTimestamp(NULL),
+ mGetMemoryCB(NULL),
+ mCBOpaque(NULL),
+ mLastFrameTimestamp(0),
+ mFrameRefreshFreq(0),
+ mMessageEnabler(0),
+ mJpegQuality(90),
+ mVideoRecEnabled(false),
+ mTakingPicture(false)
+{
+}
+
+CallbackNotifier::~CallbackNotifier()
+{
+}
+
+/****************************************************************************
+ * Camera API
+ ***************************************************************************/
+
+void CallbackNotifier::setCallbacks(camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user)
+{
+ LOGV("%s: %p, %p, %p, %p (%p)",
+ __FUNCTION__, notify_cb, data_cb, data_cb_timestamp, get_memory, user);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mNotifyCB = notify_cb;
+ mDataCB = data_cb;
+ mDataCBTimestamp = data_cb_timestamp;
+ mGetMemoryCB = get_memory;
+ mCBOpaque = user;
+}
+
+void CallbackNotifier::enableMessage(uint msg_type)
+{
+ LOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
+ PrintMessages(msg_type);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mMessageEnabler |= msg_type;
+ LOGV("**** Currently enabled messages:");
+ PrintMessages(mMessageEnabler);
+}
+
+void CallbackNotifier::disableMessage(uint msg_type)
+{
+ LOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
+ PrintMessages(msg_type);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mMessageEnabler &= ~msg_type;
+ LOGV("**** Currently enabled messages:");
+ PrintMessages(mMessageEnabler);
+}
+
+status_t CallbackNotifier::enableVideoRecording(int fps)
+{
+ LOGV("%s: FPS = %d", __FUNCTION__, fps);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mVideoRecEnabled = true;
+ mLastFrameTimestamp = 0;
+ mFrameRefreshFreq = 1000000000LL / fps;
+
+ return NO_ERROR;
+}
+
+void CallbackNotifier::disableVideoRecording()
+{
+ LOGV("%s:", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mVideoRecEnabled = false;
+ mLastFrameTimestamp = 0;
+ mFrameRefreshFreq = 0;
+}
+
+void CallbackNotifier::releaseRecordingFrame(const void* opaque)
+{
+ /* We don't really have anything to release here, since we report video
+ * frames by copying them directly to the camera memory. */
+}
+
+status_t CallbackNotifier::storeMetaDataInBuffers(bool enable)
+{
+ /* Return INVALID_OPERATION means HAL does not support metadata. So HAL will
+ * return actual frame data with CAMERA_MSG_VIDEO_FRRAME. Return
+ * INVALID_OPERATION to mean metadata is not supported. */
+ return INVALID_OPERATION;
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+void CallbackNotifier::cleanupCBNotifier()
+{
+ Mutex::Autolock locker(&mObjectLock);
+ mMessageEnabler = 0;
+ mNotifyCB = NULL;
+ mDataCB = NULL;
+ mDataCBTimestamp = NULL;
+ mGetMemoryCB = NULL;
+ mCBOpaque = NULL;
+ mLastFrameTimestamp = 0;
+ mFrameRefreshFreq = 0;
+ mJpegQuality = 90;
+ mVideoRecEnabled = false;
+ mTakingPicture = false;
+}
+
+void CallbackNotifier::onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev)
+{
+ if (isMessageEnabled(CAMERA_MSG_VIDEO_FRAME) && isVideoRecordingEnabled() &&
+ isNewVideoFrameTime(timestamp)) {
+ camera_memory_t* cam_buff =
+ mGetMemoryCB(-1, camera_dev->getFrameBufferSize(), 1, NULL);
+ if (NULL != cam_buff && NULL != cam_buff->data) {
+ memcpy(cam_buff->data, frame, camera_dev->getFrameBufferSize());
+ mDataCBTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME,
+ cam_buff, 0, mCBOpaque);
+ } else {
+ LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+ }
+ }
+
+ if (mTakingPicture) {
+ /* This happens just once. */
+ mTakingPicture = false;
+ /* The sequence of callbacks during picture taking is:
+ * - CAMERA_MSG_SHUTTER
+ * - CAMERA_MSG_RAW_IMAGE_NOTIFY
+ * - CAMERA_MSG_COMPRESSED_IMAGE
+ */
+ if (isMessageEnabled(CAMERA_MSG_SHUTTER)) {
+ mNotifyCB(CAMERA_MSG_SHUTTER, 0, 0, mCBOpaque);
+ }
+ if (isMessageEnabled(CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
+ mNotifyCB(CAMERA_MSG_RAW_IMAGE_NOTIFY, 0, 0, mCBOpaque);
+ }
+ if (isMessageEnabled(CAMERA_MSG_COMPRESSED_IMAGE)) {
+ /* Compress the frame to JPEG. Note that when taking pictures, we
+ * have requested camera device to provide us with NV21 frames. */
+ NV21JpegCompressor compressor;
+ status_t res =
+ compressor.compressRawImage(frame, camera_dev->getFrameWidth(),
+ camera_dev->getFrameHeight(),
+ mJpegQuality);
+ if (res == NO_ERROR) {
+ camera_memory_t* jpeg_buff =
+ mGetMemoryCB(-1, compressor.getCompressedSize(), 1, NULL);
+ if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
+ compressor.getCompressedImage(jpeg_buff->data);
+ mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
+ jpeg_buff->release(jpeg_buff);
+ } else {
+ LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+ }
+ } else {
+ LOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+ }
+ }
+ }
+}
+
+void CallbackNotifier::onCameraDeviceError(int err)
+{
+ if (isMessageEnabled(CAMERA_MSG_ERROR) && mNotifyCB != NULL) {
+ mNotifyCB(CAMERA_MSG_ERROR, err, 0, mCBOpaque);
+ }
+}
+
+/****************************************************************************
+ * Private API
+ ***************************************************************************/
+
+bool CallbackNotifier::isNewVideoFrameTime(nsecs_t timestamp)
+{
+ Mutex::Autolock locker(&mObjectLock);
+ if ((timestamp - mLastFrameTimestamp) >= mFrameRefreshFreq) {
+ mLastFrameTimestamp = timestamp;
+ return true;
+ }
+ return false;
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/CallbackNotifier.h b/tools/emulator/system/camera/CallbackNotifier.h
new file mode 100755
index 0000000..63301d2
--- /dev/null
+++ b/tools/emulator/system/camera/CallbackNotifier.h
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H
+#define HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H
+
+/*
+ * Contains declaration of a class CallbackNotifier that manages callbacks set
+ * via set_callbacks, enable_msg_type, and disable_msg_type camera HAL API.
+ */
+
+namespace android {
+
+class EmulatedCameraDevice;
+
+/* Manages callbacks set via set_callbacks, enable_msg_type, and disable_msg_type
+ * camera HAL API.
+ *
+ * Objects of this class are contained in EmulatedCamera objects, and handle
+ * relevant camera API callbacks.
+ * Locking considerations. Apparently, it's not allowed to call callbacks
+ * registered in this class, while holding a lock: recursion is quite possible,
+ * which will cause a deadlock.
+ */
+class CallbackNotifier {
+public:
+ /* Constructs CallbackNotifier instance. */
+ CallbackNotifier();
+
+ /* Destructs CallbackNotifier instance. */
+ ~CallbackNotifier();
+
+ /****************************************************************************
+ * Camera API
+ ***************************************************************************/
+
+public:
+ /* Actual handler for camera_device_ops_t::set_callbacks callback.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::set_callbacks callback.
+ */
+ void setCallbacks(camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user);
+
+ /* Actual handler for camera_device_ops_t::enable_msg_type callback.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::enable_msg_type callback.
+ */
+ void enableMessage(uint msg_type);
+
+ /* Actual handler for camera_device_ops_t::disable_msg_type callback.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::disable_msg_type callback.
+ */
+ void disableMessage(uint msg_type);
+
+ /* Actual handler for camera_device_ops_t::store_meta_data_in_buffers
+ * callback. This method is called by the containing emulated camera object
+ * when it is handing the camera_device_ops_t::store_meta_data_in_buffers
+ * callback.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ status_t storeMetaDataInBuffers(bool enable);
+
+ /* Enables video recording.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::start_recording callback.
+ * Param:
+ * fps - Video frame frequency. This parameter determins when a frame
+ * received via onNextFrameAvailable call will be pushed through the
+ * callback.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ status_t enableVideoRecording(int fps);
+
+ /* Disables video recording.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::stop_recording callback.
+ */
+ void disableVideoRecording();
+
+ /* Releases video frame, sent to the framework.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::release_recording_frame callback.
+ */
+ void releaseRecordingFrame(const void* opaque);
+
+ /* Actual handler for camera_device_ops_t::msg_type_enabled callback.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::msg_type_enabled callback.
+ * Note: this method doesn't grab a lock while checking message status, since
+ * upon exit the status would be undefined anyway. So, grab a lock before
+ * calling this method if you care about persisting a defined message status.
+ * Return:
+ * 0 if message is disabled, or non-zero value, if message is enabled.
+ */
+ inline int isMessageEnabled(uint msg_type)
+ {
+ return mMessageEnabler & msg_type;
+ }
+
+ /* Checks id video recording is enabled.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::recording_enabled callback.
+ * Note: this method doesn't grab a lock while checking video recordin status,
+ * since upon exit the status would be undefined anyway. So, grab a lock
+ * before calling this method if you care about persisting of a defined video
+ * recording status.
+ * Return:
+ * true if video recording is enabled, or false if it is disabled.
+ */
+ inline bool isVideoRecordingEnabled()
+ {
+ return mVideoRecEnabled;
+ }
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Resets the callback notifier. */
+ void cleanupCBNotifier();
+
+ /* Next frame is available in the camera device.
+ * This is a notification callback that is invoked by the camera device when
+ * a new frame is available.
+ * Note that most likely this method is called in context of a worker thread
+ * that camera device has created for frame capturing.
+ * Param:
+ * frame - Captured frame, or NULL if camera device didn't pull the frame
+ * yet. If NULL is passed in this parameter use GetCurrentFrame method
+ * of the camera device class to obtain the next frame. Also note that
+ * the size of the frame that is passed here (as well as the frame
+ * returned from the GetCurrentFrame method) is defined by the current
+ * frame settings (width + height + pixel format) for the camera device.
+ * timestamp - Frame's timestamp.
+ * camera_dev - Camera device instance that delivered the frame.
+ */
+ void onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev);
+
+ /* Entry point for notifications that occur in camera device.
+ * Param:
+ * err - CAMERA_ERROR_XXX error code.
+ */
+ void onCameraDeviceError(int err);
+
+ /* Sets, or resets taking picture state.
+ * This state control whether or not to notify the framework about compressed
+ * image, shutter, and other picture related events.
+ */
+ void setTakingPicture(bool taking)
+ {
+ mTakingPicture = taking;
+ }
+
+ /* Sets JPEG quality used to compress frame during picture taking. */
+ void setJpegQuality(int jpeg_quality)
+ {
+ mJpegQuality = jpeg_quality;
+ }
+
+ /****************************************************************************
+ * Private API
+ ***************************************************************************/
+
+protected:
+ /* Checks if it's time to push new video frame.
+ * Note that this method must be called while object is locked.
+ * Param:
+ * timestamp - Timestamp for the new frame. */
+ bool isNewVideoFrameTime(nsecs_t timestamp);
+
+ /****************************************************************************
+ * Data members
+ ***************************************************************************/
+
+protected:
+ /* Locks this instance for data change. */
+ Mutex mObjectLock;
+
+ /*
+ * Callbacks, registered in set_callbacks.
+ */
+
+ camera_notify_callback mNotifyCB;
+ camera_data_callback mDataCB;
+ camera_data_timestamp_callback mDataCBTimestamp;
+ camera_request_memory mGetMemoryCB;
+ void* mCBOpaque;
+
+ /* Timestamp when last frame has been delivered to the framework. */
+ nsecs_t mLastFrameTimestamp;
+
+ /* Video frequency in nanosec. */
+ nsecs_t mFrameRefreshFreq;
+
+ /* Message enabler. */
+ uint32_t mMessageEnabler;
+
+ /* JPEG quality used to compress frame during picture taking. */
+ int mJpegQuality;
+
+ /* Video recording status. */
+ bool mVideoRecEnabled;
+
+ /* Picture taking status. */
+ bool mTakingPicture;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H */
diff --git a/tools/emulator/system/camera/Converters.cpp b/tools/emulator/system/camera/Converters.cpp
new file mode 100755
index 0000000..f63f67f
--- /dev/null
+++ b/tools/emulator/system/camera/Converters.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implemenation of framebuffer conversion routines.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Converter"
+#include <cutils/log.h>
+#include "Converters.h"
+
+namespace android {
+
+static void _YUV420SToRGB565(const uint8_t* Y,
+ const uint8_t* U,
+ const uint8_t* V,
+ int dUV,
+ uint16_t* rgb,
+ int width,
+ int height)
+{
+ const uint8_t* U_pos = U;
+ const uint8_t* V_pos = V;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+ const uint8_t nU = *U;
+ const uint8_t nV = *V;
+ *rgb = YUVToRGB565(*Y, nU, nV);
+ Y++; rgb++;
+ *rgb = YUVToRGB565(*Y, nU, nV);
+ Y++; rgb++;
+ }
+ if (y & 0x1) {
+ U_pos = U;
+ V_pos = V;
+ } else {
+ U = U_pos;
+ V = V_pos;
+ }
+ }
+}
+
+static void _YUV420SToRGB32(const uint8_t* Y,
+ const uint8_t* U,
+ const uint8_t* V,
+ int dUV,
+ uint32_t* rgb,
+ int width,
+ int height)
+{
+ const uint8_t* U_pos = U;
+ const uint8_t* V_pos = V;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+ const uint8_t nU = *U;
+ const uint8_t nV = *V;
+ *rgb = YUVToRGB32(*Y, nU, nV);
+ Y++; rgb++;
+ *rgb = YUVToRGB32(*Y, nU, nV);
+ Y++; rgb++;
+ }
+ if (y & 0x1) {
+ U_pos = U;
+ V_pos = V;
+ } else {
+ U = U_pos;
+ V = V_pos;
+ }
+ }
+}
+
+void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+ const uint8_t* U = Y + pix_total;
+ const uint8_t* V = U + pix_total / 4;
+ _YUV420SToRGB565(Y, U, V, 1, reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+ const uint8_t* V = Y + pix_total;
+ const uint8_t* U = V + pix_total / 4;
+ _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* Y = reinterpret_cast<const uint8_t*>(yu12);
+ const uint8_t* U = Y + pix_total;
+ const uint8_t* V = U + pix_total / 4;
+ _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB565.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB565(const uint8_t* Y,
+ const uint8_t* U,
+ const uint8_t* V,
+ uint16_t* rgb,
+ int width,
+ int height)
+{
+ _YUV420SToRGB565(Y, U, V, 2, rgb, width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB32.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB32(const uint8_t* Y,
+ const uint8_t* U,
+ const uint8_t* V,
+ uint32_t* rgb,
+ int width,
+ int height)
+{
+ _YUV420SToRGB32(Y, U, V, 2, rgb, width, height);
+}
+
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+ _NVXXToRGB565(y, y + pix_total, y + pix_total + 1,
+ reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+ _NVXXToRGB32(y, y + pix_total, y + pix_total + 1,
+ reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+ _NVXXToRGB565(y, y + pix_total + 1, y + pix_total,
+ reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height)
+{
+ const int pix_total = width * height;
+ const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+ _NVXXToRGB32(y, y + pix_total + 1, y + pix_total,
+ reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/Converters.h b/tools/emulator/system/camera/Converters.h
new file mode 100755
index 0000000..13e2a85
--- /dev/null
+++ b/tools/emulator/system/camera/Converters.h
@@ -0,0 +1,314 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_CONVERTERS_H
+#define HW_EMULATOR_CAMERA_CONVERTERS_H
+
+#include <endian.h>
+
+#ifndef __BYTE_ORDER
+#error "could not determine byte order"
+#endif
+
+/*
+ * Contains declaration of framebuffer conversion routines.
+ *
+ * NOTE: RGB and big/little endian considerations. Wherewer in this code RGB
+ * pixels are represented as WORD, or DWORD, the color order inside the
+ * WORD / DWORD matches the one that would occur if that WORD / DWORD would have
+ * been read from the typecasted framebuffer:
+ *
+ * const uint32_t rgb = *reinterpret_cast<const uint32_t*>(framebuffer);
+ *
+ * So, if this code runs on the little endian CPU, red color in 'rgb' would be
+ * masked as 0x000000ff, and blue color would be masked as 0x00ff0000, while if
+ * the code runs on a big endian CPU, the red color in 'rgb' would be masked as
+ * 0xff000000, and blue color would be masked as 0x0000ff00,
+ */
+
+namespace android {
+
+/*
+ * RGB565 color masks
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static const uint16_t kRed5 = 0x001f;
+static const uint16_t kGreen6 = 0x07e0;
+static const uint16_t kBlue5 = 0xf800;
+#else // __BYTE_ORDER
+static const uint16_t kRed5 = 0xf800;
+static const uint16_t kGreen6 = 0x07e0;
+static const uint16_t kBlue5 = 0x001f;
+#endif // __BYTE_ORDER
+static const uint32_t kBlack16 = 0x0000;
+static const uint32_t kWhite16 = kRed5 | kGreen6 | kBlue5;
+
+/*
+ * RGB32 color masks
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static const uint32_t kRed8 = 0x000000ff;
+static const uint32_t kGreen8 = 0x0000ff00;
+static const uint32_t kBlue8 = 0x00ff0000;
+#else // __BYTE_ORDER
+static const uint32_t kRed8 = 0x00ff0000;
+static const uint32_t kGreen8 = 0x0000ff00;
+static const uint32_t kBlue8 = 0x000000ff;
+#endif // __BYTE_ORDER
+static const uint32_t kBlack32 = 0x00000000;
+static const uint32_t kWhite32 = kRed8 | kGreen8 | kBlue8;
+
+/*
+ * Extracting, and saving color bytes from / to WORD / DWORD RGB.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+/* Extract red, green, and blue bytes from RGB565 word. */
+#define R16(rgb) static_cast<uint8_t>(rgb & kRed5)
+#define G16(rgb) static_cast<uint8_t>((rgb & kGreen6) >> 5)
+#define B16(rgb) static_cast<uint8_t>((rgb & kBlue5) >> 11)
+/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
+#define R16_32(rgb) static_cast<uint8_t>(((rgb & kRed5) << 3) | ((rgb & kRed5) >> 2))
+#define G16_32(rgb) static_cast<uint8_t>(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
+#define B16_32(rgb) static_cast<uint8_t>(((rgb & kBlue5) >> 8) | ((rgb & kBlue5) >> 14))
+/* Extract red, green, and blue bytes from RGB32 dword. */
+#define R32(rgb) static_cast<uint8_t>(rgb & kRed8)
+#define G32(rgb) static_cast<uint8_t>(((rgb & kGreen8) >> 8) & 0xff)
+#define B32(rgb) static_cast<uint8_t>(((rgb & kBlue8) >> 16) & 0xff)
+/* Build RGB565 word from red, green, and blue bytes. */
+#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(b) << 6) | g) << 5) | r)
+/* Build RGB32 dword from red, green, and blue bytes. */
+#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(b) << 8) | g) << 8) | r)
+#else // __BYTE_ORDER
+/* Extract red, green, and blue bytes from RGB565 word. */
+#define R16(rgb) static_cast<uint8_t>((rgb & kRed5) >> 11)
+#define G16(rgb) static_cast<uint8_t>((rgb & kGreen6) >> 5)
+#define B16(rgb) static_cast<uint8_t>(rgb & kBlue5)
+/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
+#define R16_32(rgb) static_cast<uint8_t>(((rgb & kRed5) >> 8) | ((rgb & kRed5) >> 14))
+#define G16_32(rgb) static_cast<uint8_t>(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
+#define B16_32(rgb) static_cast<uint8_t>(((rgb & kBlue5) << 3) | ((rgb & kBlue5) >> 2))
+/* Extract red, green, and blue bytes from RGB32 dword. */
+#define R32(rgb) static_cast<uint8_t>((rgb & kRed8) >> 16)
+#define G32(rgb) static_cast<uint8_t>((rgb & kGreen8) >> 8)
+#define B32(rgb) static_cast<uint8_t>(rgb & kBlue8)
+/* Build RGB565 word from red, green, and blue bytes. */
+#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(r) << 6) | g) << 5) | b)
+/* Build RGB32 dword from red, green, and blue bytes. */
+#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(r) << 8) | g) << 8) | b)
+#endif // __BYTE_ORDER
+
+/* An union that simplifies breaking 32 bit RGB into separate R, G, and B colors.
+ */
+typedef union RGB32_t {
+ uint32_t color;
+ struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t r; uint8_t g; uint8_t b; uint8_t a;
+#else // __BYTE_ORDER
+ uint8_t a; uint8_t b; uint8_t g; uint8_t r;
+#endif // __BYTE_ORDER
+ };
+} RGB32_t;
+
+
+/* Clips a value to the unsigned 0-255 range, treating negative values as zero.
+ */
+static __inline__ int
+clamp(int x)
+{
+ if (x > 255) return 255;
+ if (x < 0) return 0;
+ return x;
+}
+
+/********************************************************************************
+ * Basics of RGB -> YUV conversion
+ *******************************************************************************/
+
+/*
+ * RGB -> YUV conversion macros
+ */
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128)
+
+/* Converts R8 G8 B8 color to YUV. */
+static __inline__ void
+R8G8B8ToYUV(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+ *y = RGB2Y((int)r, (int)g, (int)b);
+ *u = RGB2U((int)r, (int)g, (int)b);
+ *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+/* Converts RGB565 color to YUV. */
+static __inline__ void
+RGB565ToYUV(uint16_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+ R8G8B8ToYUV(R16_32(rgb), G16_32(rgb), B16_32(rgb), y, u, v);
+}
+
+/* Converts RGB32 color to YUV. */
+static __inline__ void
+RGB32ToYUV(uint32_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+ RGB32_t rgb_c;
+ rgb_c.color = rgb;
+ R8G8B8ToYUV(rgb_c.r, rgb_c.g, rgb_c.b, y, u, v);
+}
+
+/********************************************************************************
+ * Basics of YUV -> RGB conversion.
+ * Note that due to the fact that guest uses RGB only on preview window, and the
+ * RGB format that is used is RGB565, we can limit YUV -> RGB conversions to
+ * RGB565 only.
+ *******************************************************************************/
+
+/*
+ * YUV -> RGB conversion macros
+ */
+
+/* "Optimized" macros that take specialy prepared Y, U, and V values:
+ * C = Y - 16
+ * D = U - 128
+ * E = V - 128
+ */
+#define YUV2RO(C, D, E) clamp((298 * (C) + 409 * (E) + 128) >> 8)
+#define YUV2GO(C, D, E) clamp((298 * (C) - 100 * (D) - 208 * (E) + 128) >> 8)
+#define YUV2BO(C, D, E) clamp((298 * (C) + 516 * (D) + 128) >> 8)
+
+/*
+ * Main macros that take the original Y, U, and V values
+ */
+#define YUV2R(y, u, v) clamp((298 * ((y)-16) + 409 * ((v)-128) + 128) >> 8)
+#define YUV2G(y, u, v) clamp((298 * ((y)-16) - 100 * ((u)-128) - 208 * ((v)-128) + 128) >> 8)
+#define YUV2B(y, u, v) clamp((298 * ((y)-16) + 516 * ((u)-128) + 128) >> 8)
+
+
+/* Converts YUV color to RGB565. */
+static __inline__ uint16_t
+YUVToRGB565(int y, int u, int v)
+{
+ /* Calculate C, D, and E values for the optimized macro. */
+ y -= 16; u -= 128; v -= 128;
+ const uint16_t r = (YUV2RO(y,u,v) >> 3) & 0x1f;
+ const uint16_t g = (YUV2GO(y,u,v) >> 2) & 0x3f;
+ const uint16_t b = (YUV2BO(y,u,v) >> 3) & 0x1f;
+ return RGB565(r, g, b);
+}
+
+/* Converts YUV color to RGB32. */
+static __inline__ uint32_t
+YUVToRGB32(int y, int u, int v)
+{
+ /* Calculate C, D, and E values for the optimized macro. */
+ y -= 16; u -= 128; v -= 128;
+ RGB32_t rgb;
+ rgb.r = YUV2RO(y,u,v) & 0xff;
+ rgb.g = YUV2GO(y,u,v) & 0xff;
+ rgb.b = YUV2BO(y,u,v) & 0xff;
+ return rgb.color;
+}
+
+/* YUV pixel descriptor. */
+struct YUVPixel {
+ uint8_t Y;
+ uint8_t U;
+ uint8_t V;
+
+ inline YUVPixel()
+ : Y(0), U(0), V(0)
+ {
+ }
+
+ inline explicit YUVPixel(uint16_t rgb565)
+ {
+ RGB565ToYUV(rgb565, &Y, &U, &V);
+ }
+
+ inline explicit YUVPixel(uint32_t rgb32)
+ {
+ RGB32ToYUV(rgb32, &Y, &U, &V);
+ }
+
+ inline void get(uint8_t* pY, uint8_t* pU, uint8_t* pV) const
+ {
+ *pY = Y; *pU = U; *pV = V;
+ }
+};
+
+/* Converts an YV12 framebuffer to RGB565 framebuffer.
+ * Param:
+ * yv12 - YV12 framebuffer.
+ * rgb - RGB565 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void YV12ToRGB565(const void* yv12, void* rgb, int width, int height);
+
+/* Converts an YV12 framebuffer to RGB32 framebuffer.
+ * Param:
+ * yv12 - YV12 framebuffer.
+ * rgb - RGB32 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void YV12ToRGB32(const void* yv12, void* rgb, int width, int height);
+
+/* Converts an YU12 framebuffer to RGB32 framebuffer.
+ * Param:
+ * yu12 - YU12 framebuffer.
+ * rgb - RGB32 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB565 framebuffer.
+ * Param:
+ * nv12 - NV12 framebuffer.
+ * rgb - RGB565 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB32 framebuffer.
+ * Param:
+ * nv12 - NV12 framebuffer.
+ * rgb - RGB32 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB565 framebuffer.
+ * Param:
+ * nv21 - NV21 framebuffer.
+ * rgb - RGB565 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB32 framebuffer.
+ * Param:
+ * nv21 - NV21 framebuffer.
+ * rgb - RGB32 framebuffer.
+ * width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height);
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_CONVERTERS_H */
diff --git a/tools/emulator/system/camera/EmulatedCamera.cpp b/tools/emulator/system/camera/EmulatedCamera.cpp
new file mode 100755
index 0000000..9cabe75
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCamera.cpp
@@ -0,0 +1,1002 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCamera that encapsulates
+ * functionality common to all emulated cameras ("fake", "webcam", "video file",
+ * etc.). Instances of this class (for each emulated camera) are created during
+ * the construction of the EmulatedCameraFactory instance. This class serves as
+ * an entry point for all camera API calls that defined by camera_device_ops_t
+ * API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Camera"
+#include <cutils/log.h>
+#include <ui/Rect.h>
+#include "EmulatedCamera.h"
+#include "EmulatedFakeCameraDevice.h"
+#include "Converters.h"
+
+/* Defines whether we should trace parameter changes. */
+#define DEBUG_PARAM 1
+
+namespace android {
+
+#if DEBUG_PARAM
+/* Calculates and logs parameter changes.
+ * Param:
+ * current - Current set of camera parameters.
+ * new_par - String representation of new parameters.
+ */
+static void PrintParamDiff(const CameraParameters& current, const char* new_par);
+#else
+#define PrintParamDiff(current, new_par) (void(0))
+#endif /* DEBUG_PARAM */
+
+/* A helper routine that adds a value to the camera parameter.
+ * Param:
+ * param - Camera parameter to add a value to.
+ * val - Value to add.
+ * Return:
+ * A new string containing parameter with the added value on success, or NULL on
+ * a failure. If non-NULL string is returned, the caller is responsible for
+ * freeing it with 'free'.
+ */
+static char* AddValue(const char* param, const char* val);
+
+EmulatedCamera::EmulatedCamera(int cameraId, struct hw_module_t* module)
+ : mPreviewWindow(),
+ mCallbackNotifier(),
+ mCameraID(cameraId)
+{
+ /*
+ * Initialize camera_device descriptor for this object.
+ */
+
+ /* Common header */
+ common.tag = HARDWARE_DEVICE_TAG;
+ common.version = 0;
+ common.module = module;
+ common.close = EmulatedCamera::close;
+
+ /* camera_device fields. */
+ ops = &mDeviceOps;
+ priv = this;
+}
+
+EmulatedCamera::~EmulatedCamera()
+{
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedCamera::Initialize()
+{
+ /*
+ * Fake required parameters.
+ */
+
+ mParameters.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES, "320x240,0x0");
+ mParameters.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, "6");
+ mParameters.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, "-6");
+ mParameters.set(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, "0.5");
+ mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, "512");
+ mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, "384");
+ mParameters.set(CameraParameters::KEY_JPEG_QUALITY, "90");
+ mParameters.set(CameraParameters::KEY_FOCAL_LENGTH, "4.31");
+ mParameters.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "54.8");
+ mParameters.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "42.5");
+ mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90");
+
+ /* Preview format settings used here are related to panoramic view only. It's
+ * not related to the preview window that works only with RGB frames, which
+ * is explicitly stated when set_buffers_geometry is called on the preview
+ * window object. */
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS,
+ CameraParameters::PIXEL_FORMAT_YUV420SP);
+ mParameters.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
+
+ /* We don't relay on the actual frame rates supported by the camera device,
+ * since we will emulate them through timeouts in the emulated camera device
+ * worker thread. */
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES,
+ "30,24,20,15,10,5");
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(5,30)");
+ mParameters.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "5,30");
+ mParameters.setPreviewFrameRate(24);
+
+ /* Only PIXEL_FORMAT_YUV420P is accepted by camera framework in emulator! */
+ mParameters.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
+ CameraParameters::PIXEL_FORMAT_YUV420P);
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
+ CameraParameters::PIXEL_FORMAT_JPEG);
+ mParameters.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
+
+ /*
+ * Not supported features
+ */
+
+ mParameters.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES, CameraParameters::FOCUS_MODE_FIXED);
+ mParameters.set(CameraParameters::KEY_FOCUS_MODE, CameraParameters::FOCUS_MODE_FIXED);
+
+ return NO_ERROR;
+}
+
+void EmulatedCamera::onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev)
+{
+ /* Notify the preview window first. */
+ mPreviewWindow.onNextFrameAvailable(frame, timestamp, camera_dev);
+
+ /* Notify callback notifier next. */
+ mCallbackNotifier.onNextFrameAvailable(frame, timestamp, camera_dev);
+}
+
+void EmulatedCamera::onCameraDeviceError(int err)
+{
+ /* Errors are reported through the callback notifier */
+ mCallbackNotifier.onCameraDeviceError(err);
+}
+
+/****************************************************************************
+ * Camera API implementation.
+ ***************************************************************************/
+
+status_t EmulatedCamera::connectCamera(hw_device_t** device)
+{
+ LOGV("%s", __FUNCTION__);
+
+ status_t res = EINVAL;
+ EmulatedCameraDevice* const camera_dev = getCameraDevice();
+ LOGE_IF(camera_dev == NULL, "%s: No camera device instance.", __FUNCTION__);
+
+ if (camera_dev != NULL) {
+ /* Connect to the camera device. */
+ res = getCameraDevice()->connectDevice();
+ if (res == NO_ERROR) {
+ *device = &common;
+ }
+ }
+
+ return -res;
+}
+
+status_t EmulatedCamera::closeCamera()
+{
+ LOGV("%s", __FUNCTION__);
+
+ return cleanupCamera();
+}
+
+status_t EmulatedCamera::getCameraInfo(struct camera_info* info)
+{
+ LOGV("%s", __FUNCTION__);
+
+ const char* valstr = NULL;
+
+ valstr = mParameters.get(EmulatedCamera::FACING_KEY);
+ if (valstr != NULL) {
+ if (strcmp(valstr, EmulatedCamera::FACING_FRONT) == 0) {
+ info->facing = CAMERA_FACING_FRONT;
+ }
+ else if (strcmp(valstr, EmulatedCamera::FACING_BACK) == 0) {
+ info->facing = CAMERA_FACING_BACK;
+ }
+ } else {
+ info->facing = CAMERA_FACING_BACK;
+ }
+
+ valstr = mParameters.get(EmulatedCamera::ORIENTATION_KEY);
+ if (valstr != NULL) {
+ info->orientation = atoi(valstr);
+ } else {
+ info->orientation = 0;
+ }
+
+ return NO_ERROR;
+}
+
+status_t EmulatedCamera::setPreviewWindow(struct preview_stream_ops* window)
+{
+ /* Callback should return a negative errno. */
+ return -mPreviewWindow.setPreviewWindow(window,
+ mParameters.getPreviewFrameRate());
+}
+
+void EmulatedCamera::setCallbacks(camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user)
+{
+ mCallbackNotifier.setCallbacks(notify_cb, data_cb, data_cb_timestamp,
+ get_memory, user);
+}
+
+void EmulatedCamera::enableMsgType(int32_t msg_type)
+{
+ mCallbackNotifier.enableMessage(msg_type);
+}
+
+void EmulatedCamera::disableMsgType(int32_t msg_type)
+{
+ mCallbackNotifier.disableMessage(msg_type);
+}
+
+int EmulatedCamera::isMsgTypeEnabled(int32_t msg_type)
+{
+ return mCallbackNotifier.isMessageEnabled(msg_type);
+}
+
+status_t EmulatedCamera::startPreview()
+{
+ /* Callback should return a negative errno. */
+ return -doStartPreview();
+}
+
+void EmulatedCamera::stopPreview()
+{
+ doStopPreview();
+}
+
+int EmulatedCamera::isPreviewEnabled()
+{
+ return mPreviewWindow.isPreviewEnabled();
+}
+
+status_t EmulatedCamera::storeMetaDataInBuffers(int enable)
+{
+ /* Callback should return a negative errno. */
+ return -mCallbackNotifier.storeMetaDataInBuffers(enable);
+}
+
+status_t EmulatedCamera::startRecording()
+{
+ /* Callback should return a negative errno. */
+ return -mCallbackNotifier.enableVideoRecording(mParameters.getPreviewFrameRate());
+}
+
+void EmulatedCamera::stopRecording()
+{
+ mCallbackNotifier.disableVideoRecording();
+}
+
+int EmulatedCamera::isRecordingEnabled()
+{
+ return mCallbackNotifier.isVideoRecordingEnabled();
+}
+
+void EmulatedCamera::releaseRecordingFrame(const void* opaque)
+{
+ mCallbackNotifier.releaseRecordingFrame(opaque);
+}
+
+status_t EmulatedCamera::setAutoFocus()
+{
+ LOGV("%s", __FUNCTION__);
+
+ /* TODO: Future enhancements. */
+ return NO_ERROR;
+}
+
+status_t EmulatedCamera::cancelAutoFocus()
+{
+ LOGV("%s", __FUNCTION__);
+
+ /* TODO: Future enhancements. */
+ return NO_ERROR;
+}
+
+status_t EmulatedCamera::takePicture()
+{
+ LOGV("%s", __FUNCTION__);
+
+ status_t res;
+ int width, height;
+ uint32_t org_fmt;
+
+ /* Collect frame info for the picture. */
+ mParameters.getPictureSize(&width, &height);
+ const char* pix_fmt = mParameters.getPictureFormat();
+ if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+ org_fmt = V4L2_PIX_FMT_YUV420;
+ } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+ org_fmt = V4L2_PIX_FMT_RGB32;
+ } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+ org_fmt = V4L2_PIX_FMT_NV21;
+ } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_JPEG) == 0) {
+ /* We only have JPEG converted for NV21 format. */
+ org_fmt = V4L2_PIX_FMT_NV21;
+ } else {
+ LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+ return EINVAL;
+ }
+ /* Get JPEG quality. */
+ int jpeg_quality = mParameters.getInt(CameraParameters::KEY_JPEG_QUALITY);
+ if (jpeg_quality <= 0) {
+ jpeg_quality = 90; /* Fall back to default. */
+ }
+
+ /*
+ * Make sure preview is not running, and device is stopped before taking
+ * picture.
+ */
+
+ const bool preview_on = mPreviewWindow.isPreviewEnabled();
+ if (preview_on) {
+ doStopPreview();
+ }
+
+ /* Camera device should have been stopped when the shutter message has been
+ * enabled. */
+ EmulatedCameraDevice* const camera_dev = getCameraDevice();
+ if (camera_dev->isStarted()) {
+ LOGW("%s: Camera device is started", __FUNCTION__);
+ camera_dev->stopDeliveringFrames();
+ camera_dev->stopDevice();
+ }
+
+ /*
+ * Take the picture now.
+ */
+
+ /* Start camera device for the picture frame. */
+ LOGD("Starting camera for picture: %.4s(%s)[%dx%d]",
+ reinterpret_cast<const char*>(&org_fmt), pix_fmt, width, height);
+ res = camera_dev->startDevice(width, height, org_fmt);
+ if (res != NO_ERROR) {
+ if (preview_on) {
+ doStartPreview();
+ }
+ return res;
+ }
+
+ /* Deliver one frame only. */
+ mCallbackNotifier.setJpegQuality(jpeg_quality);
+ mCallbackNotifier.setTakingPicture(true);
+ res = camera_dev->startDeliveringFrames(true);
+ if (res != NO_ERROR) {
+ mCallbackNotifier.setTakingPicture(false);
+ if (preview_on) {
+ doStartPreview();
+ }
+ }
+ return res;
+}
+
+status_t EmulatedCamera::cancelPicture()
+{
+ LOGV("%s", __FUNCTION__);
+
+ return NO_ERROR;
+}
+
+status_t EmulatedCamera::setParameters(const char* parms)
+{
+ LOGV("%s", __FUNCTION__);
+ PrintParamDiff(mParameters, parms);
+
+ CameraParameters new_param;
+ String8 str8_param(parms);
+ new_param.unflatten(str8_param);
+ mParameters = new_param;
+
+ /*
+ * In emulation, there are certain parameters that are required by the
+ * framework to be exact, and supported by the camera. Since we can't predict
+ * the values of such parameters, we must dynamically update them as they
+ * are set by the framework.
+ */
+
+ /* Supported preview size. */
+ const char* check = mParameters.get(CameraParameters::KEY_PREVIEW_SIZE);
+ if (check != NULL) {
+ const char* current =
+ mParameters.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES);
+ if (strstr(current, check) == NULL) {
+ /* Required size doesn't exist in the list. Add it. */
+ char* to_add = AddValue(current, check);
+ if (to_add != NULL) {
+ LOGD("+++ %s: Added %s to supported preview sizes",
+ __FUNCTION__, check);
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, to_add);
+ free(to_add);
+ }
+ }
+ }
+
+ /* Supported preview frame rate. */
+ check = mParameters.get(CameraParameters::KEY_PREVIEW_FRAME_RATE);
+ if (check != NULL) {
+ const char* current =
+ mParameters.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES);
+ if (strstr(current, check) == NULL) {
+ char* to_add = AddValue(current, check);
+ if (to_add != NULL) {
+ LOGD("+++ %s: Added %s to supported preview frame rates",
+ __FUNCTION__, check);
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, to_add);
+ free(to_add);
+ }
+ }
+ }
+
+ /* Supported picture size. */
+ check = mParameters.get(CameraParameters::KEY_PICTURE_SIZE);
+ if (check != NULL) {
+ const char* current =
+ mParameters.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES);
+ if (strstr(current, check) == NULL) {
+ char* to_add = AddValue(current, check);
+ if (to_add != NULL) {
+ LOGD("+++ %s: Added %s to supported picture sizes",
+ __FUNCTION__, check);
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, to_add);
+ free(to_add);
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/* A dumb variable indicating "no params" / error on the exit from
+ * EmulatedCamera::getParameters(). */
+static char lNoParam = '\0';
+char* EmulatedCamera::getParameters()
+{
+ String8 params(mParameters.flatten());
+ char* ret_str =
+ reinterpret_cast<char*>(malloc(sizeof(char) * (params.length()+1)));
+ memset(ret_str, 0, params.length()+1);
+ if (ret_str != NULL) {
+ strncpy(ret_str, params.string(), params.length()+1);
+ return ret_str;
+ } else {
+ LOGE("%s: Unable to allocate string for %s", __FUNCTION__, params.string());
+ /* Apparently, we can't return NULL fron this routine. */
+ return &lNoParam;
+ }
+}
+
+void EmulatedCamera::putParameters(char* params)
+{
+ /* This method simply frees parameters allocated in getParameters(). */
+ if (params != NULL && params != &lNoParam) {
+ free(params);
+ }
+}
+
+status_t EmulatedCamera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
+{
+ LOGV("%s: cmd = %d, arg1 = %d, arg2 = %d", __FUNCTION__, cmd, arg1, arg2);
+
+ /* TODO: Future enhancements. */
+ return 0;
+}
+
+void EmulatedCamera::releaseCamera()
+{
+ LOGV("%s", __FUNCTION__);
+
+ cleanupCamera();
+}
+
+status_t EmulatedCamera::dumpCamera(int fd)
+{
+ LOGV("%s", __FUNCTION__);
+
+ /* TODO: Future enhancements. */
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Preview management.
+ ***************************************************************************/
+
+status_t EmulatedCamera::doStartPreview()
+{
+ LOGV("%s", __FUNCTION__);
+
+ EmulatedCameraDevice* camera_dev = getCameraDevice();
+ if (camera_dev->isStarted()) {
+ camera_dev->stopDeliveringFrames();
+ camera_dev->stopDevice();
+ }
+
+ status_t res = mPreviewWindow.startPreview();
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /* Make sure camera device is connected. */
+ if (!camera_dev->isConnected()) {
+ res = camera_dev->connectDevice();
+ if (res != NO_ERROR) {
+ mPreviewWindow.stopPreview();
+ return res;
+ }
+ }
+
+ int width, height;
+ /* Lets see what should we use for frame width, and height. */
+ if (mParameters.get(CameraParameters::KEY_VIDEO_SIZE) != NULL) {
+ mParameters.getVideoSize(&width, &height);
+ } else {
+ mParameters.getPreviewSize(&width, &height);
+ }
+ /* Lets see what should we use for the frame pixel format. */
+ const char* pix_fmt =
+ mParameters.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
+ if (pix_fmt == NULL) {
+ pix_fmt = mParameters.getPreviewFormat();
+ }
+ if (pix_fmt == NULL) {
+ LOGE("%s: Unable to obtain video format", __FUNCTION__);
+ mPreviewWindow.stopPreview();
+ return EINVAL;
+ }
+ uint32_t org_fmt;
+ if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+ org_fmt = V4L2_PIX_FMT_YUV420;
+ } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+ org_fmt = V4L2_PIX_FMT_RGB32;
+ } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+ org_fmt = V4L2_PIX_FMT_NV21;
+ } else {
+ LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+ mPreviewWindow.stopPreview();
+ return EINVAL;
+ }
+ LOGD("Starting camera: %dx%d -> %.4s(%s)",
+ width, height, reinterpret_cast<const char*>(&org_fmt), pix_fmt);
+ res = camera_dev->startDevice(width, height, org_fmt);
+ if (res != NO_ERROR) {
+ mPreviewWindow.stopPreview();
+ return res;
+ }
+
+ res = camera_dev->startDeliveringFrames(false);
+ if (res != NO_ERROR) {
+ camera_dev->stopDevice();
+ mPreviewWindow.stopPreview();
+ }
+
+ return res;
+}
+
+status_t EmulatedCamera::doStopPreview()
+{
+ LOGV("%s", __FUNCTION__);
+
+ status_t res = NO_ERROR;
+ if (mPreviewWindow.isPreviewEnabled()) {
+ /* Stop the camera. */
+ if (getCameraDevice()->isStarted()) {
+ getCameraDevice()->stopDeliveringFrames();
+ res = getCameraDevice()->stopDevice();
+ }
+
+ if (res == NO_ERROR) {
+ /* Disable preview as well. */
+ mPreviewWindow.stopPreview();
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/****************************************************************************
+ * Private API.
+ ***************************************************************************/
+
+status_t EmulatedCamera::cleanupCamera()
+{
+ status_t res = NO_ERROR;
+
+ /* If preview is running - stop it. */
+ res = doStopPreview();
+ if (res != NO_ERROR) {
+ return -res;
+ }
+
+ /* Stop and disconnect the camera device. */
+ EmulatedCameraDevice* const camera_dev = getCameraDevice();
+ if (camera_dev != NULL) {
+ if (camera_dev->isStarted()) {
+ camera_dev->stopDeliveringFrames();
+ res = camera_dev->stopDevice();
+ if (res != NO_ERROR) {
+ return -res;
+ }
+ }
+ if (camera_dev->isConnected()) {
+ res = camera_dev->disconnectDevice();
+ if (res != NO_ERROR) {
+ return -res;
+ }
+ }
+ }
+
+ mCallbackNotifier.cleanupCBNotifier();
+
+ return NO_ERROR;
+}
+
+/****************************************************************************
+ * Camera API callbacks as defined by camera_device_ops structure.
+ *
+ * Callbacks here simply dispatch the calls to an appropriate method inside
+ * EmulatedCamera instance, defined by the 'dev' parameter.
+ ***************************************************************************/
+
+int EmulatedCamera::set_preview_window(struct camera_device* dev,
+ struct preview_stream_ops* window)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->setPreviewWindow(window);
+}
+
+void EmulatedCamera::set_callbacks(
+ struct camera_device* dev,
+ camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->setCallbacks(notify_cb, data_cb, data_cb_timestamp, get_memory, user);
+}
+
+void EmulatedCamera::enable_msg_type(struct camera_device* dev, int32_t msg_type)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->enableMsgType(msg_type);
+}
+
+void EmulatedCamera::disable_msg_type(struct camera_device* dev, int32_t msg_type)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->disableMsgType(msg_type);
+}
+
+int EmulatedCamera::msg_type_enabled(struct camera_device* dev, int32_t msg_type)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->isMsgTypeEnabled(msg_type);
+}
+
+int EmulatedCamera::start_preview(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->startPreview();
+}
+
+void EmulatedCamera::stop_preview(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->stopPreview();
+}
+
+int EmulatedCamera::preview_enabled(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->isPreviewEnabled();
+}
+
+int EmulatedCamera::store_meta_data_in_buffers(struct camera_device* dev,
+ int enable)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->storeMetaDataInBuffers(enable);
+}
+
+int EmulatedCamera::start_recording(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->startRecording();
+}
+
+void EmulatedCamera::stop_recording(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->stopRecording();
+}
+
+int EmulatedCamera::recording_enabled(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->isRecordingEnabled();
+}
+
+void EmulatedCamera::release_recording_frame(struct camera_device* dev,
+ const void* opaque)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->releaseRecordingFrame(opaque);
+}
+
+int EmulatedCamera::auto_focus(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->setAutoFocus();
+}
+
+int EmulatedCamera::cancel_auto_focus(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->cancelAutoFocus();
+}
+
+int EmulatedCamera::take_picture(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->takePicture();
+}
+
+int EmulatedCamera::cancel_picture(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->cancelPicture();
+}
+
+int EmulatedCamera::set_parameters(struct camera_device* dev, const char* parms)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->setParameters(parms);
+}
+
+char* EmulatedCamera::get_parameters(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return NULL;
+ }
+ return ec->getParameters();
+}
+
+void EmulatedCamera::put_parameters(struct camera_device* dev, char* params)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->putParameters(params);
+}
+
+int EmulatedCamera::send_command(struct camera_device* dev,
+ int32_t cmd,
+ int32_t arg1,
+ int32_t arg2)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->sendCommand(cmd, arg1, arg2);
+}
+
+void EmulatedCamera::release(struct camera_device* dev)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return;
+ }
+ ec->releaseCamera();
+}
+
+int EmulatedCamera::dump(struct camera_device* dev, int fd)
+{
+ EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->dumpCamera(fd);
+}
+
+int EmulatedCamera::close(struct hw_device_t* device)
+{
+ EmulatedCamera* ec =
+ reinterpret_cast<EmulatedCamera*>(reinterpret_cast<struct camera_device*>(device)->priv);
+ if (ec == NULL) {
+ LOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+ return -EINVAL;
+ }
+ return ec->closeCamera();
+}
+
+/****************************************************************************
+ * Static initializer for the camera callback API
+ ****************************************************************************/
+
+camera_device_ops_t EmulatedCamera::mDeviceOps = {
+ EmulatedCamera::set_preview_window,
+ EmulatedCamera::set_callbacks,
+ EmulatedCamera::enable_msg_type,
+ EmulatedCamera::disable_msg_type,
+ EmulatedCamera::msg_type_enabled,
+ EmulatedCamera::start_preview,
+ EmulatedCamera::stop_preview,
+ EmulatedCamera::preview_enabled,
+ EmulatedCamera::store_meta_data_in_buffers,
+ EmulatedCamera::start_recording,
+ EmulatedCamera::stop_recording,
+ EmulatedCamera::recording_enabled,
+ EmulatedCamera::release_recording_frame,
+ EmulatedCamera::auto_focus,
+ EmulatedCamera::cancel_auto_focus,
+ EmulatedCamera::take_picture,
+ EmulatedCamera::cancel_picture,
+ EmulatedCamera::set_parameters,
+ EmulatedCamera::get_parameters,
+ EmulatedCamera::put_parameters,
+ EmulatedCamera::send_command,
+ EmulatedCamera::release,
+ EmulatedCamera::dump
+};
+
+/****************************************************************************
+ * Common keys
+ ***************************************************************************/
+
+const char EmulatedCamera::FACING_KEY[] = "prop-facing";
+const char EmulatedCamera::ORIENTATION_KEY[] = "prop-orientation";
+
+/****************************************************************************
+ * Common string values
+ ***************************************************************************/
+
+const char EmulatedCamera::FACING_BACK[] = "back";
+const char EmulatedCamera::FACING_FRONT[] = "front";
+
+/****************************************************************************
+ * Helper routines
+ ***************************************************************************/
+
+static char* AddValue(const char* param, const char* val)
+{
+ const size_t len1 = strlen(param);
+ const size_t len2 = strlen(val);
+ char* ret = reinterpret_cast<char*>(malloc(len1 + len2 + 2));
+ LOGE_IF(ret == NULL, "%s: Memory failure", __FUNCTION__);
+ if (ret != NULL) {
+ memcpy(ret, param, len1);
+ ret[len1] = ',';
+ memcpy(ret + len1 + 1, val, len2);
+ ret[len1 + len2 + 1] = '\0';
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Parameter debugging helpers
+ ***************************************************************************/
+
+#if DEBUG_PARAM
+static void PrintParamDiff(const CameraParameters& current,
+ const char* new_par)
+{
+ char tmp[2048];
+ const char* wrk = new_par;
+
+ /* Divided with ';' */
+ const char* next = strchr(wrk, ';');
+ while (next != NULL) {
+ snprintf(tmp, sizeof(tmp), "%.*s", next-wrk, wrk);
+ /* in the form key=value */
+ char* val = strchr(tmp, '=');
+ if (val != NULL) {
+ *val = '\0'; val++;
+ const char* in_current = current.get(tmp);
+ if (in_current != NULL) {
+ if (strcmp(in_current, val)) {
+ LOGD("=== Value changed: %s: %s -> %s", tmp, in_current, val);
+ }
+ } else {
+ LOGD("+++ New parameter: %s=%s", tmp, val);
+ }
+ } else {
+ LOGW("No value separator in %s", tmp);
+ }
+ wrk = next + 1;
+ next = strchr(wrk, ';');
+ }
+}
+#endif /* DEBUG_PARAM */
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedCamera.h b/tools/emulator/system/camera/EmulatedCamera.h
new file mode 100755
index 0000000..8afdd83
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCamera.h
@@ -0,0 +1,417 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedCamera that encapsulates functionality
+ * common to all emulated cameras ("fake", "webcam", "video file", etc.).
+ * Instances of this class (for each emulated camera) are created during the
+ * construction of the EmulatedCameraFactory instance.
+ * This class serves as an entry point for all camera API calls that defined
+ * by camera_device_ops_t API.
+ */
+
+#include <camera/CameraParameters.h>
+#include "EmulatedCameraDevice.h"
+#include "PreviewWindow.h"
+#include "CallbackNotifier.h"
+
+namespace android {
+
+/* Encapsulates functionality common to all emulated cameras ("fake", "webcam",
+ * "file stream", etc.).
+ *
+ * Note that EmulatedCameraFactory instantiates object of this class just once,
+ * when EmulatedCameraFactory instance gets constructed. Connection to /
+ * disconnection from the actual camera device is handled by calls to connectDevice(),
+ * and closeCamera() methods of this class that are ivoked in response to
+ * hw_module_methods_t::open, and camera_device::close callbacks.
+ */
+class EmulatedCamera : public camera_device {
+public:
+ /* Constructs EmulatedCamera instance.
+ * Param:
+ * cameraId - Zero based camera identifier, which is an index of the camera
+ * instance in camera factory's array.
+ * module - Emulated camera HAL module descriptor.
+ */
+ EmulatedCamera(int cameraId, struct hw_module_t* module);
+
+ /* Destructs EmulatedCamera instance. */
+ virtual ~EmulatedCamera();
+
+ /****************************************************************************
+ * Abstract API
+ ***************************************************************************/
+
+public:
+ /* Gets emulated camera device used by this instance of the emulated camera.
+ */
+ virtual EmulatedCameraDevice* getCameraDevice() = 0;
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Initializes EmulatedCamera instance.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ virtual status_t Initialize();
+
+ /* Next frame is available in the camera device.
+ * This is a notification callback that is invoked by the camera device when
+ * a new frame is available.
+ * Note that most likely this method is called in context of a worker thread
+ * that camera device has created for frame capturing.
+ * Param:
+ * frame - Captured frame, or NULL if camera device didn't pull the frame
+ * yet. If NULL is passed in this parameter use GetCurrentFrame method
+ * of the camera device class to obtain the next frame. Also note that
+ * the size of the frame that is passed here (as well as the frame
+ * returned from the GetCurrentFrame method) is defined by the current
+ * frame settings (width + height + pixel format) for the camera device.
+ * timestamp - Frame's timestamp.
+ * camera_dev - Camera device instance that delivered the frame.
+ */
+ virtual void onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev);
+
+ /* Entry point for notifications that occur in camera device.
+ * Param:
+ * err - CAMERA_ERROR_XXX error code.
+ */
+ virtual void onCameraDeviceError(int err);
+
+ /****************************************************************************
+ * Camera API implementation
+ ***************************************************************************/
+
+public:
+ /* Creates connection to the emulated camera device.
+ * This method is called in response to hw_module_methods_t::open callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t connectCamera(hw_device_t** device);
+
+ /* Closes connection to the emulated camera.
+ * This method is called in response to camera_device::close callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t closeCamera();
+
+ /* Gets camera information.
+ * This method is called in response to camera_module_t::get_camera_info
+ * callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t getCameraInfo(struct camera_info* info);
+
+ /****************************************************************************
+ * Camera API implementation.
+ * These methods are called from the camera API callback routines.
+ ***************************************************************************/
+
+protected:
+ /* Actual handler for camera_device_ops_t::set_preview_window callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t setPreviewWindow(struct preview_stream_ops *window);
+
+ /* Actual handler for camera_device_ops_t::set_callbacks callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void setCallbacks(camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user);
+
+ /* Actual handler for camera_device_ops_t::enable_msg_type callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void enableMsgType(int32_t msg_type);
+
+ /* Actual handler for camera_device_ops_t::disable_msg_type callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void disableMsgType(int32_t msg_type);
+
+ /* Actual handler for camera_device_ops_t::msg_type_enabled callback.
+ * NOTE: When this method is called the object is locked.
+ * Return:
+ * 0 if message(s) is (are) disabled, != 0 if enabled.
+ */
+ virtual int isMsgTypeEnabled(int32_t msg_type);
+
+ /* Actual handler for camera_device_ops_t::start_preview callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t startPreview();
+
+ /* Actual handler for camera_device_ops_t::stop_preview callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void stopPreview();
+
+ /* Actual handler for camera_device_ops_t::preview_enabled callback.
+ * NOTE: When this method is called the object is locked.
+ * Return:
+ * 0 if preview is disabled, != 0 if enabled.
+ */
+ virtual int isPreviewEnabled();
+
+ /* Actual handler for camera_device_ops_t::store_meta_data_in_buffers callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t storeMetaDataInBuffers(int enable);
+
+ /* Actual handler for camera_device_ops_t::start_recording callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t startRecording();
+
+ /* Actual handler for camera_device_ops_t::stop_recording callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void stopRecording();
+
+ /* Actual handler for camera_device_ops_t::recording_enabled callback.
+ * NOTE: When this method is called the object is locked.
+ * Return:
+ * 0 if recording is disabled, != 0 if enabled.
+ */
+ virtual int isRecordingEnabled();
+
+ /* Actual handler for camera_device_ops_t::release_recording_frame callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void releaseRecordingFrame(const void* opaque);
+
+ /* Actual handler for camera_device_ops_t::auto_focus callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t setAutoFocus();
+
+ /* Actual handler for camera_device_ops_t::cancel_auto_focus callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t cancelAutoFocus();
+
+ /* Actual handler for camera_device_ops_t::take_picture callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t takePicture();
+
+ /* Actual handler for camera_device_ops_t::cancel_picture callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t cancelPicture();
+
+ /* Actual handler for camera_device_ops_t::set_parameters callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t setParameters(const char* parms);
+
+ /* Actual handler for camera_device_ops_t::get_parameters callback.
+ * NOTE: When this method is called the object is locked.
+ * Return:
+ * Flattened parameters string. The caller will free the buffer allocated
+ * for the string by calling camera_device_ops_t::put_parameters callback.
+ */
+ virtual char* getParameters();
+
+ /* Actual handler for camera_device_ops_t::put_parameters callback.
+ * Called to free the string returned from camera_device_ops_t::get_parameters
+ * callback. There is nothing more to it: the name of the callback is just
+ * misleading.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void putParameters(char* params);
+
+ /* Actual handler for camera_device_ops_t::send_command callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+
+ /* Actual handler for camera_device_ops_t::release callback.
+ * NOTE: When this method is called the object is locked.
+ */
+ virtual void releaseCamera();
+
+ /* Actual handler for camera_device_ops_t::dump callback.
+ * NOTE: When this method is called the object is locked.
+ * Note that failures in this method are reported as negave EXXX statuses.
+ */
+ virtual status_t dumpCamera(int fd);
+
+ /****************************************************************************
+ * Preview management.
+ ***************************************************************************/
+
+protected:
+ /* Starts preview.
+ * Note that when this method is called mPreviewWindow may be NULL,
+ * indicating that framework has an intention to start displaying video
+ * frames, but didn't create the preview window yet.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ virtual status_t doStartPreview();
+
+ /* Stops preview.
+ * This method reverts DoStartPreview.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ virtual status_t doStopPreview();
+
+ /****************************************************************************
+ * Private API.
+ ***************************************************************************/
+
+protected:
+ /* Cleans up camera when released. */
+ virtual status_t cleanupCamera();
+
+ /****************************************************************************
+ * Camera API callbacks as defined by camera_device_ops structure.
+ * See hardware/libhardware/include/hardware/camera.h for information on
+ * each of these callbacks. Implemented in this class, these callbacks simply
+ * dispatch the call into an instance of EmulatedCamera class defined by the
+ * 'camera_device' parameter.
+ ***************************************************************************/
+
+private:
+ static int set_preview_window(struct camera_device* dev,
+ struct preview_stream_ops* window);
+
+ static void set_callbacks(struct camera_device* dev,
+ camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void* user);
+
+ static void enable_msg_type(struct camera_device* dev, int32_t msg_type);
+
+ static void disable_msg_type(struct camera_device* dev, int32_t msg_type);
+
+ static int msg_type_enabled(struct camera_device* dev, int32_t msg_type);
+
+ static int start_preview(struct camera_device* dev);
+
+ static void stop_preview(struct camera_device* dev);
+
+ static int preview_enabled(struct camera_device* dev);
+
+ static int store_meta_data_in_buffers(struct camera_device* dev, int enable);
+
+ static int start_recording(struct camera_device* dev);
+
+ static void stop_recording(struct camera_device* dev);
+
+ static int recording_enabled(struct camera_device* dev);
+
+ static void release_recording_frame(struct camera_device* dev,
+ const void* opaque);
+
+ static int auto_focus(struct camera_device* dev);
+
+ static int cancel_auto_focus(struct camera_device* dev);
+
+ static int take_picture(struct camera_device* dev);
+
+ static int cancel_picture(struct camera_device* dev);
+
+ static int set_parameters(struct camera_device* dev, const char* parms);
+
+ static char* get_parameters(struct camera_device* dev);
+
+ static void put_parameters(struct camera_device* dev, char* params);
+
+ static int send_command(struct camera_device* dev,
+ int32_t cmd,
+ int32_t arg1,
+ int32_t arg2);
+
+ static void release(struct camera_device* dev);
+
+ static int dump(struct camera_device* dev, int fd);
+
+ static int close(struct hw_device_t* device);
+
+ /****************************************************************************
+ * Data members
+ ***************************************************************************/
+
+protected:
+ /* Locks this instance for parameters, state, etc. change. */
+ Mutex mObjectLock;
+
+ /* Camera parameters. */
+ CameraParameters mParameters;
+
+ /* Preview window. */
+ PreviewWindow mPreviewWindow;
+
+ /* Callback notifier. */
+ CallbackNotifier mCallbackNotifier;
+
+ /* Zero-based ID assigned to this camera. */
+ int mCameraID;
+
+private:
+ /* Registered callbacks implementing camera API. */
+ static camera_device_ops_t mDeviceOps;
+
+ /****************************************************************************
+ * Common keys
+ ***************************************************************************/
+
+public:
+ static const char FACING_KEY[];
+ static const char ORIENTATION_KEY[];
+
+ /****************************************************************************
+ * Common string values
+ ***************************************************************************/
+
+ /* Possible values for FACING_KEY */
+ static const char FACING_BACK[];
+ static const char FACING_FRONT[];
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_H */
diff --git a/tools/emulator/system/camera/EmulatedCameraCommon.h b/tools/emulator/system/camera/EmulatedCameraCommon.h
new file mode 100755
index 0000000..907985a
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraCommon.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H
+
+/*
+ * Contains common declarations that are used across the camera emulation.
+ */
+
+#include <linux/videodev2.h>
+#include <hardware/camera.h>
+
+/* A helper class that tracks a routine execution.
+ * Basically, it dumps an enry message in its constructor, and an exit message
+ * in its destructor. Use LOGRE() macro (declared bellow) to create instances
+ * of this class at the beginning of the tracked routines / methods.
+ */
+class HWERoutineTracker {
+public:
+ /* Constructor that prints an "entry" trace message. */
+ explicit HWERoutineTracker(const char* name)
+ : mName(name) {
+ LOGV("Entering %s", mName);
+ }
+
+ /* Destructor that prints a "leave" trace message. */
+ ~HWERoutineTracker() {
+ LOGV("Leaving %s", mName);
+ }
+
+private:
+ /* Stores the routine name. */
+ const char* mName;
+};
+
+/* Logs an execution of a routine / method. */
+#define LOGRE() HWERoutineTracker hwertracker_##__LINE__(__FUNCTION__)
+
+/*
+ * min / max macros
+ */
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H */
diff --git a/tools/emulator/system/camera/EmulatedCameraDevice.cpp b/tools/emulator/system/camera/EmulatedCameraDevice.cpp
new file mode 100755
index 0000000..e09bead
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraDevice.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ * - Obtaining and setting camera parameters
+ * - Capturing frames
+ * - Streaming video
+ * - etc.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Device"
+#include <cutils/log.h>
+#include <sys/select.h>
+#include "EmulatedCameraDevice.h"
+#include "Converters.h"
+
+namespace android {
+
+EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal)
+ : mObjectLock(),
+ mCurFrameTimestamp(0),
+ mCameraHAL(camera_hal),
+ mCurrentFrame(NULL),
+ mState(ECDS_CONSTRUCTED)
+{
+}
+
+EmulatedCameraDevice::~EmulatedCameraDevice()
+{
+ if (mCurrentFrame != NULL) {
+ delete[] mCurrentFrame;
+ }
+}
+
+/****************************************************************************
+ * Emulated camera device public API
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::Initialize()
+{
+ if (isInitialized()) {
+ LOGW("%s: Emulated camera device is already initialized: mState = %d",
+ __FUNCTION__, mState);
+ return NO_ERROR;
+ }
+
+ /* Instantiate worker thread object. */
+ mWorkerThread = new WorkerThread(this);
+ if (getWorkerThread() == NULL) {
+ LOGE("%s: Unable to instantiate worker thread object", __FUNCTION__);
+ return ENOMEM;
+ }
+
+ mState = ECDS_INITIALIZED;
+
+ return NO_ERROR;
+}
+
+status_t EmulatedCameraDevice::startDeliveringFrames(bool one_burst)
+{
+ LOGV("%s", __FUNCTION__);
+
+ if (!isStarted()) {
+ LOGE("%s: Device is not started", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* Frames will be delivered from the thread routine. */
+ const status_t res = startWorkerThread(one_burst);
+ LOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+ return res;
+}
+
+status_t EmulatedCameraDevice::stopDeliveringFrames()
+{
+ LOGV("%s", __FUNCTION__);
+
+ if (!isStarted()) {
+ LOGW("%s: Device is not started", __FUNCTION__);
+ return NO_ERROR;
+ }
+
+ const status_t res = stopWorkerThread();
+ LOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+ return res;
+}
+
+status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer)
+{
+ if (!isStarted()) {
+ LOGE("%s: Device is not started", __FUNCTION__);
+ return EINVAL;
+ }
+ if (mCurrentFrame == NULL || buffer == NULL) {
+ LOGE("%s: No framebuffer", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* In emulation the framebuffer is never RGB. */
+ switch (mPixelFormat) {
+ case V4L2_PIX_FMT_YVU420:
+ YV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+ return NO_ERROR;
+ case V4L2_PIX_FMT_YUV420:
+ YU12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+ return NO_ERROR;
+ case V4L2_PIX_FMT_NV21:
+ NV21ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+ return NO_ERROR;
+ case V4L2_PIX_FMT_NV12:
+ NV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+ return NO_ERROR;
+
+ default:
+ LOGE("%s: Unknown pixel format %.4s",
+ __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat));
+ return EINVAL;
+ }
+}
+
+/****************************************************************************
+ * Emulated camera device private API
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::commonStartDevice(int width,
+ int height,
+ uint32_t pix_fmt)
+{
+ /* Validate pixel format, and calculate framebuffer size at the same time. */
+ switch (pix_fmt) {
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12:
+ mFrameBufferSize = (width * height * 12) / 8;
+ break;
+
+ default:
+ LOGE("%s: Unknown pixel format %.4s",
+ __FUNCTION__, reinterpret_cast<const char*>(&pix_fmt));
+ return EINVAL;
+ }
+
+ /* Cache framebuffer info. */
+ mFrameWidth = width;
+ mFrameHeight = height;
+ mPixelFormat = pix_fmt;
+ mTotalPixels = width * height;
+
+ /* Allocate framebuffer. */
+ mCurrentFrame = new uint8_t[mFrameBufferSize];
+ if (mCurrentFrame == NULL) {
+ LOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
+ return ENOMEM;
+ }
+ LOGV("%s: Allocated %p %d bytes for %d pixels in %.4s[%dx%d] frame",
+ __FUNCTION__, mCurrentFrame, mFrameBufferSize, mTotalPixels,
+ reinterpret_cast<const char*>(&mPixelFormat), mFrameWidth, mFrameHeight);
+ return NO_ERROR;
+}
+
+void EmulatedCameraDevice::commonStopDevice()
+{
+ mFrameWidth = mFrameHeight = mTotalPixels = 0;
+ mPixelFormat = 0;
+
+ if (mCurrentFrame != NULL) {
+ delete[] mCurrentFrame;
+ mCurrentFrame = NULL;
+ }
+}
+
+/****************************************************************************
+ * Worker thread management.
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::startWorkerThread(bool one_burst)
+{
+ LOGV("%s", __FUNCTION__);
+
+ if (!isInitialized()) {
+ LOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
+ return EINVAL;
+ }
+
+ const status_t res = getWorkerThread()->startThread(one_burst);
+ LOGE_IF(res != NO_ERROR, "%s: Unable to start worker thread", __FUNCTION__);
+ return res;
+}
+
+status_t EmulatedCameraDevice::stopWorkerThread()
+{
+ LOGV("%s", __FUNCTION__);
+
+ if (!isInitialized()) {
+ LOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
+ return EINVAL;
+ }
+
+ const status_t res = getWorkerThread()->stopThread();
+ LOGE_IF(res != NO_ERROR, "%s: Unable to stop worker thread", __FUNCTION__);
+ return res;
+}
+
+bool EmulatedCameraDevice::inWorkerThread()
+{
+ /* This will end the thread loop, and will terminate the thread. Derived
+ * classes must override this method. */
+ return false;
+}
+
+/****************************************************************************
+ * Worker thread implementation.
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::WorkerThread::readyToRun()
+{
+ LOGV("Starting emulated camera device worker thread...");
+
+ LOGW_IF(mThreadControl >= 0 || mControlFD >= 0,
+ "%s: Thread control FDs are opened", __FUNCTION__);
+ /* Create a pair of FDs that would be used to control the thread. */
+ int thread_fds[2];
+ if (pipe(thread_fds) == 0) {
+ mThreadControl = thread_fds[1];
+ mControlFD = thread_fds[0];
+ LOGV("Emulated device's worker thread has been started.");
+ return NO_ERROR;
+ } else {
+ LOGE("%s: Unable to create thread control FDs: %d -> %s",
+ __FUNCTION__, errno, strerror(errno));
+ return errno;
+ }
+}
+
+status_t EmulatedCameraDevice::WorkerThread::stopThread()
+{
+ LOGV("Stopping emulated camera device's worker thread...");
+
+ status_t res = EINVAL;
+ if (mThreadControl >= 0) {
+ /* Send "stop" message to the thread loop. */
+ const ControlMessage msg = THREAD_STOP;
+ const int wres =
+ TEMP_FAILURE_RETRY(write(mThreadControl, &msg, sizeof(msg)));
+ if (wres == sizeof(msg)) {
+ /* Stop the thread, and wait till it's terminated. */
+ res = requestExitAndWait();
+ if (res == NO_ERROR) {
+ /* Close control FDs. */
+ if (mThreadControl >= 0) {
+ close(mThreadControl);
+ mThreadControl = -1;
+ }
+ if (mControlFD >= 0) {
+ close(mControlFD);
+ mControlFD = -1;
+ }
+ LOGV("Emulated camera device's worker thread has been stopped.");
+ } else {
+ LOGE("%s: requestExitAndWait failed: %d -> %s",
+ __FUNCTION__, res, strerror(-res));
+ }
+ } else {
+ LOGE("%s: Unable to send THREAD_STOP message: %d -> %s",
+ __FUNCTION__, errno, strerror(errno));
+ res = errno ? errno : EINVAL;
+ }
+ } else {
+ LOGE("%s: Thread control FDs are not opened", __FUNCTION__);
+ }
+
+ return res;
+}
+
+EmulatedCameraDevice::WorkerThread::SelectRes
+EmulatedCameraDevice::WorkerThread::Select(int fd, int timeout)
+{
+ fd_set fds[1];
+ struct timeval tv, *tvp = NULL;
+
+ const int fd_num = (fd >= 0) ? max(fd, mControlFD) + 1 :
+ mControlFD + 1;
+ FD_ZERO(fds);
+ FD_SET(mControlFD, fds);
+ if (fd >= 0) {
+ FD_SET(fd, fds);
+ }
+ if (timeout) {
+ tv.tv_sec = timeout / 1000000;
+ tv.tv_usec = timeout % 1000000;
+ tvp = &tv;
+ }
+ int res = TEMP_FAILURE_RETRY(select(fd_num, fds, NULL, NULL, tvp));
+ if (res < 0) {
+ LOGE("%s: select returned %d and failed: %d -> %s",
+ __FUNCTION__, res, errno, strerror(errno));
+ return ERROR;
+ } else if (res == 0) {
+ /* Timeout. */
+ return TIMEOUT;
+ } else if (FD_ISSET(mControlFD, fds)) {
+ /* A control event. Lets read the message. */
+ ControlMessage msg;
+ res = TEMP_FAILURE_RETRY(read(mControlFD, &msg, sizeof(msg)));
+ if (res != sizeof(msg)) {
+ LOGE("%s: Unexpected message size %d, or an error %d -> %s",
+ __FUNCTION__, res, errno, strerror(errno));
+ return ERROR;
+ }
+ /* THREAD_STOP is the only message expected here. */
+ if (msg == THREAD_STOP) {
+ LOGV("%s: THREAD_STOP message is received", __FUNCTION__);
+ return EXIT_THREAD;
+ } else {
+ LOGE("Unknown worker thread message %d", msg);
+ return ERROR;
+ }
+ } else {
+ /* Must be an FD. */
+ LOGW_IF(fd < 0 || !FD_ISSET(fd, fds), "%s: Undefined 'select' result",
+ __FUNCTION__);
+ return READY;
+ }
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedCameraDevice.h b/tools/emulator/system/camera/EmulatedCameraDevice.h
new file mode 100755
index 0000000..88e2dd2
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraDevice.h
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ * - Obtaining and setting camera device parameters
+ * - Capturing frames
+ * - Streaming video
+ * - etc.
+ */
+
+#include <utils/threads.h>
+#include "EmulatedCameraCommon.h"
+
+namespace android {
+
+class EmulatedCamera;
+
+/* Encapsulates an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ * - Obtaining and setting camera device parameters
+ * - Capturing frames
+ * - Streaming video
+ * - etc.
+ */
+class EmulatedCameraDevice {
+public:
+ /* Constructs EmulatedCameraDevice instance.
+ * Param:
+ * camera_hal - Emulated camera that implements the camera HAL API, and
+ * manages (contains) this object.
+ */
+ explicit EmulatedCameraDevice(EmulatedCamera* camera_hal);
+
+ /* Destructs EmulatedCameraDevice instance. */
+ virtual ~EmulatedCameraDevice();
+
+ /***************************************************************************
+ * Emulated camera device abstract interface
+ **************************************************************************/
+
+public:
+ /* Connects to the camera device.
+ * This method must be called on an initialized instance of this class.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t connectDevice() = 0;
+
+ /* Disconnects from the camera device.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status. If this method is
+ * called for already disconnected, or uninitialized instance of this class,
+ * a successful status must be returned from this method. If this method is
+ * called for an instance that is in the "started" state, this method must
+ * return a failure.
+ */
+ virtual status_t disconnectDevice() = 0;
+
+ /* Starts the camera device.
+ * This method tells the camera device to start capturing frames of the given
+ * dimensions for the given pixel format. Note that this method doesn't start
+ * the delivery of the captured frames to the emulated camera. Call
+ * startDeliveringFrames method to start delivering frames. This method must
+ * be called on a connected instance of this class. If it is called on a
+ * disconnected instance, this method must return a failure.
+ * Param:
+ * width, height - Frame dimensions to use when capturing video frames.
+ * pix_fmt - Pixel format to use when capturing video frames.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t startDevice(int width, int height, uint32_t pix_fmt) = 0;
+
+ /* Stops the camera device.
+ * This method tells the camera device to stop capturing frames. Note that
+ * this method doesn't stop delivering frames to the emulated camera. Always
+ * call stopDeliveringFrames prior to calling this method.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status. If this method is
+ * called for an object that is not capturing frames, or is disconnected,
+ * or is uninitialized, a successful status must be returned from this
+ * method.
+ */
+ virtual status_t stopDevice() = 0;
+
+ /***************************************************************************
+ * Emulated camera device public API
+ **************************************************************************/
+
+public:
+ /* Initializes EmulatedCameraDevice instance.
+ * Derived classes should override this method in order to cache static
+ * properties of the physical device (list of supported pixel formats, frame
+ * sizes, etc.) If this method is called on an already initialized instance,
+ * it must return a successful status.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t Initialize();
+
+ /* Starts delivering frames captured from the camera device.
+ * This method will start the worker thread that would be pulling frames from
+ * the camera device, and will deliver the pulled frames back to the emulated
+ * camera via onNextFrameAvailable callback. This method must be called on a
+ * connected instance of this class with a started camera device. If it is
+ * called on a disconnected instance, or camera device has not been started,
+ * this method must return a failure.
+ * Param:
+ * one_burst - Controls how many frames should be delivered. If this
+ * parameter is 'true', only one captured frame will be delivered to the
+ * emulated camera. If this parameter is 'false', frames will keep
+ * coming until stopDeliveringFrames method is called. Typically, this
+ * parameter is set to 'true' only in order to obtain a single frame
+ * that will be used as a "picture" in takePicture method of the
+ * emulated camera.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t startDeliveringFrames(bool one_burst);
+
+ /* Stops delivering frames captured from the camera device.
+ * This method will stop the worker thread started by startDeliveringFrames.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t stopDeliveringFrames();
+
+ /* Gets current framebuffer, converted into preview frame format.
+ * This method must be called on a connected instance of this class with a
+ * started camera device. If it is called on a disconnected instance, or
+ * camera device has not been started, this method must return a failure.
+ * Note that this method should be called only after at least one frame has
+ * been captured and delivered. Otherwise it will return garbage in the
+ * preview frame buffer. Typically, this method shuld be called from
+ * onNextFrameAvailable callback.
+ * Param:
+ * buffer - Buffer, large enough to contain the entire preview frame.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t getCurrentPreviewFrame(void* buffer);
+
+ /* Gets width of the frame obtained from the physical device.
+ * Return:
+ * Width of the frame obtained from the physical device. Note that value
+ * returned from this method is valid only in case if camera device has been
+ * started.
+ */
+ inline int getFrameWidth() const
+ {
+ LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+ return mFrameWidth;
+ }
+
+ /* Gets height of the frame obtained from the physical device.
+ * Return:
+ * Height of the frame obtained from the physical device. Note that value
+ * returned from this method is valid only in case if camera device has been
+ * started.
+ */
+ inline int getFrameHeight() const
+ {
+ LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+ return mFrameHeight;
+ }
+
+ /* Gets byte size of the current frame buffer.
+ * Return:
+ * Byte size of the frame buffer. Note that value returned from this method
+ * is valid only in case if camera device has been started.
+ */
+ inline size_t getFrameBufferSize() const
+ {
+ LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+ return mFrameBufferSize;
+ }
+
+ /* Gets number of pixels in the current frame buffer.
+ * Return:
+ * Number of pixels in the frame buffer. Note that value returned from this
+ * method is valid only in case if camera device has been started.
+ */
+ inline int getPixelNum() const
+ {
+ LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+ return mTotalPixels;
+ }
+
+ /* Gets pixel format of the frame that camera device streams to this class.
+ * Throughout camera framework, there are three different forms of pixel
+ * format representation:
+ * - Original format, as reported by the actual camera device. Values for
+ * this format are declared in bionic/libc/kernel/common/linux/videodev2.h
+ * - String representation as defined in CameraParameters::PIXEL_FORMAT_XXX
+ * strings in frameworks/base/include/camera/CameraParameters.h
+ * - HAL_PIXEL_FORMAT_XXX format, as defined in system/core/include/system/graphics.h
+ * Since emulated camera device gets its data from the actual device, it gets
+ * pixel format in the original form. And that's the pixel format
+ * representation that will be returned from this method. HAL components will
+ * need to translate value returned from this method to the appropriate form.
+ * This method must be called only on started instance of this class, since
+ * it's applicable only when camera device is ready to stream frames.
+ * Param:
+ * pix_fmt - Upon success contains the original pixel format.
+ * Return:
+ * Current framebuffer's pixel format. Note that value returned from this
+ * method is valid only in case if camera device has been started.
+ */
+ inline uint32_t getOriginalPixelFormat() const
+ {
+ LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+ return mPixelFormat;
+ }
+
+ /*
+ * State checkers.
+ */
+
+ inline bool isInitialized() const {
+ /* Instance is initialized when the worker thread has been successfuly
+ * created (but not necessarily started). */
+ return mWorkerThread.get() != NULL && mState != ECDS_CONSTRUCTED;
+ }
+ inline bool isConnected() const {
+ /* Instance is connected when its status is either"connected", or
+ * "started". */
+ return mState == ECDS_CONNECTED || mState == ECDS_STARTED;
+ }
+ inline bool isStarted() const {
+ return mState == ECDS_STARTED;
+ }
+
+ /****************************************************************************
+ * Emulated camera device private API
+ ***************************************************************************/
+protected:
+ /* Performs common validation and calculation of startDevice parameters.
+ * Param:
+ * width, height, pix_fmt - Parameters passed to the startDevice method.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t commonStartDevice(int width, int height, uint32_t pix_fmt);
+
+ /* Performs common cleanup on stopDevice.
+ * This method will undo what commonStartDevice had done.
+ */
+ virtual void commonStopDevice();
+
+ /****************************************************************************
+ * Worker thread management.
+ * Typicaly when emulated camera device starts capturing frames from the
+ * actual device, it does that in a worker thread created in StartCapturing,
+ * and terminated in StopCapturing. Since this is such a typical scenario,
+ * it makes sence to encapsulate worker thread management in the base class
+ * for all emulated camera devices.
+ ***************************************************************************/
+
+protected:
+ /* Starts the worker thread.
+ * Typically, worker thread is started from startDeliveringFrames method of
+ * this class.
+ * Param:
+ * one_burst - Controls how many times thread loop should run. If this
+ * parameter is 'true', thread routine will run only once If this
+ * parameter is 'false', thread routine will run until stopWorkerThread
+ * method is called. See startDeliveringFrames for more info.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t startWorkerThread(bool one_burst);
+
+ /* Stops the worker thread.
+ * Note that this method will always wait for the worker thread to terminate.
+ * Typically, worker thread is started from stopDeliveringFrames method of
+ * this class.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t stopWorkerThread();
+
+ /* Implementation of the worker thread routine.
+ * In the default implementation of the worker thread routine we simply
+ * return 'false' forcing the thread loop to exit, and the thread to
+ * terminate. Derived class should override that method to provide there the
+ * actual frame delivery.
+ * Return:
+ * true To continue thread loop (this method will be called again), or false
+ * to exit the thread loop and to terminate the thread.
+ */
+ virtual bool inWorkerThread();
+
+ /* Encapsulates a worker thread used by the emulated camera device.
+ */
+ friend class WorkerThread;
+ class WorkerThread : public Thread {
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+ public:
+ inline explicit WorkerThread(EmulatedCameraDevice* camera_dev)
+ : Thread(true), // Callbacks may involve Java calls.
+ mCameraDevice(camera_dev),
+ mThreadControl(-1),
+ mControlFD(-1)
+ {
+ }
+
+ inline ~WorkerThread()
+ {
+ LOGW_IF(mThreadControl >= 0 || mControlFD >= 0,
+ "%s: Control FDs are opened in the destructor",
+ __FUNCTION__);
+ if (mThreadControl >= 0) {
+ close(mThreadControl);
+ }
+ if (mControlFD >= 0) {
+ close(mControlFD);
+ }
+ }
+
+ /* Starts the thread
+ * Param:
+ * one_burst - Controls how many times thread loop should run. If
+ * this parameter is 'true', thread routine will run only once
+ * If this parameter is 'false', thread routine will run until
+ * stopThread method is called. See startWorkerThread for more
+ * info.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ inline status_t startThread(bool one_burst)
+ {
+ mOneBurst = one_burst;
+ return run(NULL, ANDROID_PRIORITY_URGENT_DISPLAY, 0);
+ }
+
+ /* Overriden base class method.
+ * It is overriden in order to provide one-time initialization just
+ * prior to starting the thread routine.
+ */
+ status_t readyToRun();
+
+ /* Stops the thread. */
+ status_t stopThread();
+
+ /* Values returned from the Select method of this class. */
+ enum SelectRes {
+ /* A timeout has occurred. */
+ TIMEOUT,
+ /* Data are available for read on the provided FD. */
+ READY,
+ /* Thread exit request has been received. */
+ EXIT_THREAD,
+ /* An error has occurred. */
+ ERROR
+ };
+
+ /* Select on an FD event, keeping in mind thread exit message.
+ * Param:
+ * fd - File descriptor on which to wait for an event. This
+ * parameter may be negative. If it is negative this method will
+ * only wait on a control message to the thread.
+ * timeout - Timeout in microseconds. 0 indicates no timeout (wait
+ * forever).
+ * Return:
+ * See SelectRes enum comments.
+ */
+ SelectRes Select(int fd, int timeout);
+
+ /****************************************************************************
+ * Private API
+ ***************************************************************************/
+
+ private:
+ /* Implements abstract method of the base Thread class. */
+ bool threadLoop()
+ {
+ /* Simply dispatch the call to the containing camera device. */
+ if (mCameraDevice->inWorkerThread()) {
+ /* Respect "one burst" parameter (see startThread). */
+ return !mOneBurst;
+ } else {
+ return false;
+ }
+ }
+
+ /* Containing camera device object. */
+ EmulatedCameraDevice* mCameraDevice;
+
+ /* FD that is used to send control messages into the thread. */
+ int mThreadControl;
+
+ /* FD that thread uses to receive control messages. */
+ int mControlFD;
+
+ /* Controls number of times the thread loop runs.
+ * See startThread for more information. */
+ bool mOneBurst;
+
+ /* Enumerates control messages that can be sent into the thread. */
+ enum ControlMessage {
+ /* Stop the thread. */
+ THREAD_STOP
+ };
+ };
+
+ /* Worker thread accessor. */
+ inline WorkerThread* getWorkerThread() const
+ {
+ return mWorkerThread.get();
+ }
+
+ /****************************************************************************
+ * Data members
+ ***************************************************************************/
+
+protected:
+ /* Locks this instance for parameters, state, etc. change. */
+ Mutex mObjectLock;
+
+ /* Worker thread that is used in frame capturing. */
+ sp<WorkerThread> mWorkerThread;
+
+ /* Timestamp of the current frame. */
+ nsecs_t mCurFrameTimestamp;
+
+ /* Emulated camera object containing this instance. */
+ EmulatedCamera* mCameraHAL;
+
+ /* Framebuffer containing the current frame. */
+ uint8_t* mCurrentFrame;
+
+ /*
+ * Framebuffer properties.
+ */
+
+ /* Byte size of the framebuffer. */
+ size_t mFrameBufferSize;
+
+ /* Original pixel format (one of the V4L2_PIX_FMT_XXX values, as defined in
+ * bionic/libc/kernel/common/linux/videodev2.h */
+ uint32_t mPixelFormat;
+
+ /* Frame width */
+ int mFrameWidth;
+
+ /* Frame height */
+ int mFrameHeight;
+
+ /* Total number of pixels */
+ int mTotalPixels;
+
+ /* Defines possible states of the emulated camera device object.
+ */
+ enum EmulatedCameraDeviceState {
+ /* Object has been constructed. */
+ ECDS_CONSTRUCTED,
+ /* Object has been initialized. */
+ ECDS_INITIALIZED,
+ /* Object has been connected to the physical device. */
+ ECDS_CONNECTED,
+ /* Camera device has been started. */
+ ECDS_STARTED,
+ };
+
+ /* Object state. */
+ EmulatedCameraDeviceState mState;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H */
diff --git a/tools/emulator/system/camera/EmulatedCameraFactory.cpp b/tools/emulator/system/camera/EmulatedCameraFactory.cpp
new file mode 100755
index 0000000..5c5c5de
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraFactory.cpp
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCameraFactory that manages cameras
+ * available for emulation.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Factory"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedFakeCamera.h"
+#include "EmulatedCameraFactory.h"
+
+extern camera_module_t HAL_MODULE_INFO_SYM;
+
+/* A global instance of EmulatedCameraFactory is statically instantiated and
+ * initialized when camera emulation HAL is loaded.
+ */
+android::EmulatedCameraFactory gEmulatedCameraFactory;
+
+namespace android {
+
+EmulatedCameraFactory::EmulatedCameraFactory()
+ : mQemuClient(),
+ mEmulatedCameras(NULL),
+ mEmulatedCameraNum(0),
+ mFakeCameraID(-1),
+ mConstructedOK(false)
+
+{
+ /* Connect to the factory service in the emulator, and create Qemu cameras. */
+ if (mQemuClient.connectClient(NULL) == NO_ERROR) {
+ /* Connection has succeeded. Create emulated cameras for each camera
+ * device, reported by the service. */
+ createQemuCameras();
+ }
+
+ if (isFakeCameraEmulationOn()) {
+ /* ID fake camera with the number of created 'qemud' cameras. */
+ mFakeCameraID = mEmulatedCameraNum;
+ mEmulatedCameraNum++;
+
+ /* Make sure that array is allocated (in case there were no 'qemu'
+ * cameras created. */
+ if (mEmulatedCameras == NULL) {
+ mEmulatedCameras = new EmulatedCamera*[mEmulatedCameraNum];
+ if (mEmulatedCameras == NULL) {
+ LOGE("%s: Unable to allocate emulated camera array for %d entries",
+ __FUNCTION__, mEmulatedCameraNum);
+ return;
+ }
+ memset(mEmulatedCameras, 0, mEmulatedCameraNum * sizeof(EmulatedCamera*));
+ }
+
+ /* Create, and initialize the fake camera */
+ mEmulatedCameras[mFakeCameraID] =
+ new EmulatedFakeCamera(mFakeCameraID, &HAL_MODULE_INFO_SYM.common);
+ if (mEmulatedCameras[mFakeCameraID] != NULL) {
+ if (mEmulatedCameras[mFakeCameraID]->Initialize() != NO_ERROR) {
+ delete mEmulatedCameras[mFakeCameraID];
+ mEmulatedCameras--;
+ mFakeCameraID = -1;
+ }
+ } else {
+ mEmulatedCameras--;
+ mFakeCameraID = -1;
+ LOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
+ }
+ } else {
+ LOGD("Fake camera emulation is disabled.");
+ }
+
+ LOGV("%d cameras are being emulated. Fake camera ID is %d",
+ mEmulatedCameraNum, mFakeCameraID);
+
+ mConstructedOK = true;
+}
+
+EmulatedCameraFactory::~EmulatedCameraFactory()
+{
+ if (mEmulatedCameras != NULL) {
+ for (int n = 0; n < mEmulatedCameraNum; n++) {
+ if (mEmulatedCameras[n] != NULL) {
+ delete mEmulatedCameras[n];
+ }
+ }
+ delete[] mEmulatedCameras;
+ }
+}
+
+/****************************************************************************
+ * Camera HAL API handlers.
+ *
+ * Each handler simply verifies existence of an appropriate EmulatedCamera
+ * instance, and dispatches the call to that instance.
+ *
+ ***************************************************************************/
+
+int EmulatedCameraFactory::cameraDeviceOpen(int camera_id, hw_device_t** device)
+{
+ LOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+ *device = NULL;
+
+ if (!isConstructedOK()) {
+ LOGE("%s: EmulatedCameraFactory has failed to initialize", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
+ LOGE("%s: Camera id %d is out of bounds (%d)",
+ __FUNCTION__, camera_id, getEmulatedCameraNum());
+ return -EINVAL;
+ }
+
+ return mEmulatedCameras[camera_id]->connectCamera(device);
+}
+
+int EmulatedCameraFactory::getCameraInfo(int camera_id, struct camera_info* info)
+{
+ LOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+ if (!isConstructedOK()) {
+ LOGE("%s: EmulatedCameraFactory has failed to initialize", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
+ LOGE("%s: Camera id %d is out of bounds (%d)",
+ __FUNCTION__, camera_id, getEmulatedCameraNum());
+ return -EINVAL;
+ }
+
+ return mEmulatedCameras[camera_id]->getCameraInfo(info);
+}
+
+/****************************************************************************
+ * Camera HAL API callbacks.
+ ***************************************************************************/
+
+int EmulatedCameraFactory::device_open(const hw_module_t* module,
+ const char* name,
+ hw_device_t** device)
+{
+ /*
+ * Simply verify the parameters, and dispatch the call inside the
+ * EmulatedCameraFactory instance.
+ */
+
+ if (module != &HAL_MODULE_INFO_SYM.common) {
+ LOGE("%s: Invalid module %p expected %p",
+ __FUNCTION__, module, &HAL_MODULE_INFO_SYM.common);
+ return -EINVAL;
+ }
+ if (name == NULL) {
+ LOGE("%s: NULL name is not expected here", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ return gEmulatedCameraFactory.cameraDeviceOpen(atoi(name), device);
+}
+
+int EmulatedCameraFactory::get_number_of_cameras(void)
+{
+ return gEmulatedCameraFactory.getEmulatedCameraNum();
+}
+
+int EmulatedCameraFactory::get_camera_info(int camera_id,
+ struct camera_info* info)
+{
+ return gEmulatedCameraFactory.getCameraInfo(camera_id, info);
+}
+
+/********************************************************************************
+ * Internal API
+ *******************************************************************************/
+
+/*
+ * Camera information tokens passed in response to the "list" factory query.
+ */
+
+/* Device name token. */
+static const char lListNameToken[] = "name=";
+/* Frame dimensions token. */
+static const char lListDimsToken[] = "framedims=";
+/* Facing direction token. */
+static const char lListDirToken[] = "dir=";
+
+void EmulatedCameraFactory::createQemuCameras()
+{
+ /* Obtain camera list. */
+ char* camera_list = NULL;
+ status_t res = mQemuClient.listCameras(&camera_list);
+ /* Empty list, or list containing just an EOL means that there were no
+ * connected cameras found. */
+ if (res != NO_ERROR || camera_list == NULL || *camera_list == '\0' ||
+ *camera_list == '\n') {
+ if (camera_list != NULL) {
+ free(camera_list);
+ }
+ return;
+ }
+
+ /*
+ * Calculate number of connected cameras. Number of EOLs in the camera list
+ * is the number of the connected cameras.
+ */
+
+ int num = 0;
+ const char* eol = strchr(camera_list, '\n');
+ while (eol != NULL) {
+ num++;
+ eol = strchr(eol + 1, '\n');
+ }
+
+ /* Allocate the array for emulated camera instances. Note that we allocate
+ * one more entry for the fake camera emulation. */
+ mEmulatedCameras = new EmulatedCamera*[num + 1];
+ if (mEmulatedCameras == NULL) {
+ LOGE("%s: Unable to allocate emulated camera array for %d entries",
+ __FUNCTION__, num + 1);
+ free(camera_list);
+ return;
+ }
+ memset(mEmulatedCameras, 0, sizeof(EmulatedCamera*) * (num + 1));
+
+ /*
+ * Iterate the list, creating, and initializin emulated qemu cameras for each
+ * entry (line) in the list.
+ */
+
+ int index = 0;
+ char* cur_entry = camera_list;
+ while (cur_entry != NULL && *cur_entry != '\0' && index < num) {
+ /* Find the end of the current camera entry, and terminate it with zero
+ * for simpler string manipulation. */
+ char* next_entry = strchr(cur_entry, '\n');
+ if (next_entry != NULL) {
+ *next_entry = '\0';
+ next_entry++; // Start of the next entry.
+ }
+
+ /* Find 'name', 'framedims', and 'dir' tokens that are required here. */
+ char* name_start = strstr(cur_entry, lListNameToken);
+ char* dim_start = strstr(cur_entry, lListDimsToken);
+ char* dir_start = strstr(cur_entry, lListDirToken);
+ if (name_start != NULL && dim_start != NULL && dir_start != NULL) {
+ /* Advance to the token values. */
+ name_start += strlen(lListNameToken);
+ dim_start += strlen(lListDimsToken);
+ dir_start += strlen(lListDirToken);
+
+ /* Terminate token values with zero. */
+ char* s = strchr(name_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+ s = strchr(dim_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+ s = strchr(dir_start, ' ');
+ if (s != NULL) {
+ *s = '\0';
+ }
+
+ /* Create and initialize qemu camera. */
+ EmulatedQemuCamera* qemu_cam =
+ new EmulatedQemuCamera(index, &HAL_MODULE_INFO_SYM.common);
+ if (NULL != qemu_cam) {
+ res = qemu_cam->Initialize(name_start, dim_start, dir_start);
+ if (res == NO_ERROR) {
+ mEmulatedCameras[index] = qemu_cam;
+ index++;
+ } else {
+ delete qemu_cam;
+ }
+ } else {
+ LOGE("%s: Unable to instantiate EmulatedQemuCamera",
+ __FUNCTION__);
+ }
+ } else {
+ LOGW("%s: Bad camera information: %s", __FUNCTION__, cur_entry);
+ }
+
+ cur_entry = next_entry;
+ }
+
+ mEmulatedCameraNum = index;
+}
+
+bool EmulatedCameraFactory::isFakeCameraEmulationOn()
+{
+ /* Defined by 'qemu.sf.fake_camera' boot property: If property is there
+ * and contains 'off', fake camera emulation is disabled. */
+ char prop[PROPERTY_VALUE_MAX];
+ if (property_get("qemu.sf.fake_camera", prop, NULL) <= 0 ||
+ strcmp(prop, "off")) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/********************************************************************************
+ * Initializer for the static member structure.
+ *******************************************************************************/
+
+/* Entry point for camera HAL API. */
+struct hw_module_methods_t EmulatedCameraFactory::mCameraModuleMethods = {
+ open: EmulatedCameraFactory::device_open
+};
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedCameraFactory.h b/tools/emulator/system/camera/EmulatedCameraFactory.h
new file mode 100755
index 0000000..19745a3
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraFactory.h
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
+
+#include "EmulatedCamera.h"
+#include "QemuClient.h"
+
+namespace android {
+
+/*
+ * Contains declaration of a class EmulatedCameraFactory that manages cameras
+ * available for the emulation. A global instance of this class is statically
+ * instantiated and initialized when camera emulation HAL is loaded.
+ */
+
+/* Class EmulatedCameraFactoryManages cameras available for the emulation.
+ *
+ * When the global static instance of this class is created on the module load,
+ * it enumerates cameras available for the emulation by connecting to the
+ * emulator's 'camera' service. For every camera found out there it creates an
+ * instance of an appropriate class, and stores it an in array of emulated
+ * cameras. In addition to the cameras reported by the emulator, a fake camera
+ * emulator is always created, so there is always at least one camera that is
+ * available.
+ *
+ * Instance of this class is also used as the entry point for the camera HAL API,
+ * including:
+ * - hw_module_methods_t::open entry point
+ * - camera_module_t::get_number_of_cameras entry point
+ * - camera_module_t::get_camera_info entry point
+ *
+ */
+class EmulatedCameraFactory {
+public:
+ /* Constructs EmulatedCameraFactory instance.
+ * In this constructor the factory will create and initialize a list of
+ * emulated cameras. All errors that occur on this constructor are reported
+ * via mConstructedOK data member of this class.
+ */
+ EmulatedCameraFactory();
+
+ /* Destructs EmulatedCameraFactory instance. */
+ ~EmulatedCameraFactory();
+
+ /****************************************************************************
+ * Camera HAL API handlers.
+ ***************************************************************************/
+
+public:
+ /* Opens (connects to) a camera device.
+ * This method is called in response to hw_module_methods_t::open callback.
+ */
+ int cameraDeviceOpen(int camera_id, hw_device_t** device);
+
+ /* Gets emulated camera information.
+ * This method is called in response to camera_module_t::get_camera_info callback.
+ */
+ int getCameraInfo(int camera_id, struct camera_info *info);
+
+ /****************************************************************************
+ * Camera HAL API callbacks.
+ ***************************************************************************/
+
+public:
+ /* camera_module_t::get_number_of_cameras callback entry point. */
+ static int get_number_of_cameras(void);
+
+ /* camera_module_t::get_camera_info callback entry point. */
+ static int get_camera_info(int camera_id, struct camera_info *info);
+
+private:
+ /* hw_module_methods_t::open callback entry point. */
+ static int device_open(const hw_module_t* module,
+ const char* name,
+ hw_device_t** device);
+
+ /****************************************************************************
+ * Public API.
+ ***************************************************************************/
+
+public:
+
+ /* Gets fake camera orientation. */
+ int getFakeCameraOrientation() {
+ /* TODO: Have a boot property that controls that. */
+ return 90;
+ }
+
+ /* Gets qemu camera orientation. */
+ int getQemuCameraOrientation() {
+ /* TODO: Have a boot property that controls that. */
+ return 270;
+ }
+
+ /* Gets number of emulated cameras.
+ */
+ int getEmulatedCameraNum() const {
+ return mEmulatedCameraNum;
+ }
+
+ /* Checks whether or not the constructor has succeeded.
+ */
+ bool isConstructedOK() const {
+ return mConstructedOK;
+ }
+
+ /****************************************************************************
+ * Private API
+ ***************************************************************************/
+
+private:
+ /* Populates emulated cameras array with cameras that are available via
+ * 'camera' service in the emulator. For each such camera and instance of
+ * the EmulatedCameraQemud will be created and added to the mEmulatedCameras
+ * array.
+ */
+ void createQemuCameras();
+
+ /* Checks if fake camera emulation is on. */
+ bool isFakeCameraEmulationOn();
+
+ /****************************************************************************
+ * Data members.
+ ***************************************************************************/
+
+private:
+ /* Connection to the camera service in the emulator. */
+ FactoryQemuClient mQemuClient;
+
+ /* Array of cameras available for the emulation. */
+ EmulatedCamera** mEmulatedCameras;
+
+ /* Number of emulated cameras (including the fake one). */
+ int mEmulatedCameraNum;
+
+ /* Fake camera ID. */
+ int mFakeCameraID;
+
+ /* Flags whether or not constructor has succeeded. */
+ bool mConstructedOK;
+
+public:
+ /* Contains device open entry point, as required by HAL API. */
+ static struct hw_module_methods_t mCameraModuleMethods;
+};
+
+}; /* namespace android */
+
+/* References the global EmulatedCameraFactory instance. */
+extern android::EmulatedCameraFactory gEmulatedCameraFactory;
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H */
diff --git a/tools/emulator/system/camera/EmulatedCameraHal.cpp b/tools/emulator/system/camera/EmulatedCameraHal.cpp
new file mode 100755
index 0000000..7f3f7b8
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedCameraHal.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of the camera HAL layer in the system running
+ * under the emulator.
+ *
+ * This file contains only required HAL header, which directs all the API calls
+ * to the EmulatedCameraFactory class implementation, wich is responsible for
+ * managing emulated cameras.
+ */
+
+#include "EmulatedCameraFactory.h"
+
+/*
+ * Required HAL header.
+ */
+camera_module_t HAL_MODULE_INFO_SYM = {
+ common: {
+ tag: HARDWARE_MODULE_TAG,
+ version_major: 1,
+ version_minor: 0,
+ id: CAMERA_HARDWARE_MODULE_ID,
+ name: "Emulated Camera Module",
+ author: "The Android Open Source Project",
+ methods: &android::EmulatedCameraFactory::mCameraModuleMethods,
+ dso: NULL,
+ reserved: {0},
+ },
+ get_number_of_cameras: android::EmulatedCameraFactory::get_number_of_cameras,
+ get_camera_info: android::EmulatedCameraFactory::get_camera_info,
+};
diff --git a/tools/emulator/system/camera/EmulatedFakeCamera.cpp b/tools/emulator/system/camera/EmulatedFakeCamera.cpp
new file mode 100755
index 0000000..d82fd78
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedFakeCamera.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCamera that encapsulates
+ * functionality of a fake camera.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeCamera"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "EmulatedFakeCamera.h"
+#include "EmulatedCameraFactory.h"
+
+namespace android {
+
+EmulatedFakeCamera::EmulatedFakeCamera(int cameraId, struct hw_module_t* module)
+ : EmulatedCamera(cameraId, module),
+ mFakeCameraDevice(this)
+{
+}
+
+EmulatedFakeCamera::~EmulatedFakeCamera()
+{
+}
+
+/****************************************************************************
+ * Public API overrides
+ ***************************************************************************/
+
+status_t EmulatedFakeCamera::Initialize()
+{
+ status_t res = mFakeCameraDevice.Initialize();
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /* Fake camera facing is defined by the qemu.sf.fake_camera boot property. */
+ char prop[PROPERTY_VALUE_MAX];
+ property_get("qemu.sf.fake_camera", prop, EmulatedCamera::FACING_BACK);
+ const char* facing = prop;
+
+ mParameters.set(EmulatedCamera::FACING_KEY, facing);
+ LOGD("%s: Fake camera is facing %s", __FUNCTION__, facing);
+
+ mParameters.set(EmulatedCamera::ORIENTATION_KEY,
+ gEmulatedCameraFactory.getFakeCameraOrientation());
+
+ res = EmulatedCamera::Initialize();
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /*
+ * Parameters provided by the camera device.
+ */
+
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "640x480");
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "640x480");
+ mParameters.setPreviewSize(640, 480);
+ mParameters.setPictureSize(640, 480);
+
+ return NO_ERROR;
+}
+
+EmulatedCameraDevice* EmulatedFakeCamera::getCameraDevice()
+{
+ return &mFakeCameraDevice;
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedFakeCamera.h b/tools/emulator/system/camera/EmulatedFakeCamera.h
new file mode 100755
index 0000000..3debe9e
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedFakeCamera.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedFakeCamera that encapsulates
+ * functionality of a fake camera. This class is nothing more than a placeholder
+ * for EmulatedFakeCameraDevice instance.
+ */
+
+#include "EmulatedCamera.h"
+#include "EmulatedFakeCameraDevice.h"
+
+namespace android {
+
+/* Encapsulates functionality of a fake camera.
+ * This class is nothing more than a placeholder for EmulatedFakeCameraDevice
+ * instance that emulates a fake camera device.
+ */
+class EmulatedFakeCamera : public EmulatedCamera {
+public:
+ /* Constructs EmulatedFakeCamera instance. */
+ EmulatedFakeCamera(int cameraId, struct hw_module_t* module);
+
+ /* Destructs EmulatedFakeCamera instance. */
+ ~EmulatedFakeCamera();
+
+ /****************************************************************************
+ * EmulatedCamera virtual overrides.
+ ***************************************************************************/
+
+public:
+ /* Initializes EmulatedFakeCamera instance. */
+ status_t Initialize();
+
+ /****************************************************************************
+ * EmulatedCamera abstract API implementation.
+ ***************************************************************************/
+
+protected:
+ /* Gets emulated camera device ised by this instance of the emulated camera.
+ */
+ EmulatedCameraDevice* getCameraDevice();
+
+ /****************************************************************************
+ * Data memebers.
+ ***************************************************************************/
+
+protected:
+ /* Contained fake camera device object. */
+ EmulatedFakeCameraDevice mFakeCameraDevice;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H */
diff --git a/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp b/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp
new file mode 100755
index 0000000..53a5b1b
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCameraDevice that encapsulates
+ * fake camera device.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeDevice"
+#include <cutils/log.h>
+#include "EmulatedFakeCamera.h"
+#include "EmulatedFakeCameraDevice.h"
+
+namespace android {
+
+EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal)
+ : EmulatedCameraDevice(camera_hal),
+ mBlackYUV(kBlack32),
+ mWhiteYUV(kWhite32),
+ mRedYUV(kRed8),
+ mGreenYUV(kGreen8),
+ mBlueYUV(kBlue8),
+ mLastRedrawn(0),
+ mCheckX(0),
+ mCheckY(0),
+ mCcounter(0)
+#if EFCD_ROTATE_FRAME
+ , mLastRotatedAt(0),
+ mCurrentFrameType(0),
+ mCurrentColor(&mWhiteYUV)
+#endif // EFCD_ROTATE_FRAME
+{
+}
+
+EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice()
+{
+}
+
+/****************************************************************************
+ * Emulated camera device abstract interface implementation.
+ ***************************************************************************/
+
+status_t EmulatedFakeCameraDevice::connectDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isInitialized()) {
+ LOGE("%s: Fake camera device is not initialized.", __FUNCTION__);
+ return EINVAL;
+ }
+ if (isConnected()) {
+ LOGW("%s: Fake camera device is already connected.", __FUNCTION__);
+ return NO_ERROR;
+ }
+
+ /* There is no device to connect to. */
+ mState = ECDS_CONNECTED;
+
+ return NO_ERROR;
+}
+
+status_t EmulatedFakeCameraDevice::disconnectDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isConnected()) {
+ LOGW("%s: Fake camera device is already disconnected.", __FUNCTION__);
+ return NO_ERROR;
+ }
+ if (isStarted()) {
+ LOGE("%s: Cannot disconnect from the started device.", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* There is no device to disconnect from. */
+ mState = ECDS_INITIALIZED;
+
+ return NO_ERROR;
+}
+
+status_t EmulatedFakeCameraDevice::startDevice(int width,
+ int height,
+ uint32_t pix_fmt)
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isConnected()) {
+ LOGE("%s: Fake camera device is not connected.", __FUNCTION__);
+ return EINVAL;
+ }
+ if (isStarted()) {
+ LOGE("%s: Fake camera device is already started.", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* Initialize the base class. */
+ const status_t res =
+ EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
+ if (res == NO_ERROR) {
+ /* Calculate U/V panes inside the framebuffer. */
+ switch (mPixelFormat) {
+ case V4L2_PIX_FMT_YVU420:
+ mFrameV = mCurrentFrame + mTotalPixels;
+ mFrameU = mFrameU + mTotalPixels / 4;
+ mUVStep = 1;
+ mUVTotalNum = mTotalPixels / 4;
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ mFrameU = mCurrentFrame + mTotalPixels;
+ mFrameV = mFrameU + mTotalPixels / 4;
+ mUVStep = 1;
+ mUVTotalNum = mTotalPixels / 4;
+ break;
+
+ case V4L2_PIX_FMT_NV21:
+ /* Interleaved UV pane, V first. */
+ mFrameV = mCurrentFrame + mTotalPixels;
+ mFrameU = mFrameV + 1;
+ mUVStep = 2;
+ mUVTotalNum = mTotalPixels / 4;
+ break;
+
+ case V4L2_PIX_FMT_NV12:
+ /* Interleaved UV pane, U first. */
+ mFrameU = mCurrentFrame + mTotalPixels;
+ mFrameV = mFrameU + 1;
+ mUVStep = 2;
+ mUVTotalNum = mTotalPixels / 4;
+ break;
+
+ default:
+ LOGE("%s: Unknown pixel format %.4s", __FUNCTION__,
+ reinterpret_cast<const char*>(&mPixelFormat));
+ return EINVAL;
+ }
+ /* Number of items in a single row inside U/V panes. */
+ mUVInRow = (width / 2) * mUVStep;
+ mState = ECDS_STARTED;
+ } else {
+ LOGE("%s: commonStartDevice failed", __FUNCTION__);
+ }
+
+ return res;
+}
+
+status_t EmulatedFakeCameraDevice::stopDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isStarted()) {
+ LOGW("%s: Fake camera device is not started.", __FUNCTION__);
+ return NO_ERROR;
+ }
+
+ mFrameU = mFrameV = NULL;
+ EmulatedCameraDevice::commonStopDevice();
+ mState = ECDS_CONNECTED;
+
+ return NO_ERROR;
+}
+
+/****************************************************************************
+ * Worker thread management overrides.
+ ***************************************************************************/
+
+bool EmulatedFakeCameraDevice::inWorkerThread()
+{
+ /* Wait till FPS timeout expires, or thread exit message is received. */
+ WorkerThread::SelectRes res =
+ getWorkerThread()->Select(-1, 1000000 / mEmulatedFPS);
+ if (res == WorkerThread::EXIT_THREAD) {
+ LOGV("%s: Worker thread has been terminated.", __FUNCTION__);
+ return false;
+ }
+
+ /* Lets see if we need to generate a new frame. */
+ if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRedrawn) >= mRedrawAfter) {
+ /*
+ * Time to generate a new frame.
+ */
+
+#if EFCD_ROTATE_FRAME
+ const int frame_type = rotateFrame();
+ switch (frame_type) {
+ case 0:
+ drawCheckerboard();
+ break;
+ case 1:
+ drawStripes();
+ break;
+ case 2:
+ drawSolid(mCurrentColor);
+ break;
+ }
+#else
+ /* Draw the checker board. */
+ drawCheckerboard();
+
+#endif // EFCD_ROTATE_FRAME
+
+ mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ /* Timestamp the current frame, and notify the camera HAL about new frame. */
+ mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
+
+ return true;
+}
+
+/****************************************************************************
+ * Fake camera device private API
+ ***************************************************************************/
+
+void EmulatedFakeCameraDevice::drawCheckerboard()
+{
+ const int size = mFrameWidth / 10;
+ bool black = true;
+
+ if((mCheckX / size) & 1)
+ black = false;
+ if((mCheckY / size) & 1)
+ black = !black;
+
+ int county = mCheckY % size;
+ int checkxremainder = mCheckX % size;
+ uint8_t* Y = mCurrentFrame;
+ uint8_t* U_pos = mFrameU;
+ uint8_t* V_pos = mFrameV;
+ uint8_t* U = U_pos;
+ uint8_t* V = V_pos;
+
+ for(int y = 0; y < mFrameHeight; y++) {
+ int countx = checkxremainder;
+ bool current = black;
+ for(int x = 0; x < mFrameWidth; x += 2) {
+ if (current) {
+ mBlackYUV.get(Y, U, V);
+ } else {
+ mWhiteYUV.get(Y, U, V);
+ }
+ Y[1] = *Y;
+ Y += 2; U += mUVStep; V += mUVStep;
+ countx += 2;
+ if(countx >= size) {
+ countx = 0;
+ current = !current;
+ }
+ }
+ if (y & 0x1) {
+ U_pos = U;
+ V_pos = V;
+ } else {
+ U = U_pos;
+ V = V_pos;
+ }
+ if(county++ >= size) {
+ county = 0;
+ black = !black;
+ }
+ }
+ mCheckX += 3;
+ mCheckY++;
+
+ /* Run the square. */
+ int sqx = ((mCcounter * 3) & 255);
+ if(sqx > 128) sqx = 255 - sqx;
+ int sqy = ((mCcounter * 5) & 255);
+ if(sqy > 128) sqy = 255 - sqy;
+ const int sqsize = mFrameWidth / 10;
+ drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1,
+ (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
+ mCcounter++;
+}
+
+void EmulatedFakeCameraDevice::drawSquare(int x,
+ int y,
+ int size,
+ const YUVPixel* color)
+{
+ const int square_xstop = min(mFrameWidth, x + size);
+ const int square_ystop = min(mFrameHeight, y + size);
+ uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x;
+
+ // Draw the square.
+ for (; y < square_ystop; y++) {
+ const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep;
+ uint8_t* sqU = mFrameU + iUV;
+ uint8_t* sqV = mFrameV + iUV;
+ uint8_t* sqY = Y_pos;
+ for (int i = x; i < square_xstop; i += 2) {
+ color->get(sqY, sqU, sqV);
+ sqY[1] = *sqY;
+ sqY += 2; sqU += mUVStep; sqV += mUVStep;
+ }
+ Y_pos += mFrameWidth;
+ }
+}
+
+#if EFCD_ROTATE_FRAME
+
+void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color)
+{
+ /* All Ys are the same. */
+ memset(mCurrentFrame, color->Y, mTotalPixels);
+
+ /* Fill U, and V panes. */
+ uint8_t* U = mFrameU;
+ uint8_t* V = mFrameV;
+ for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) {
+ *U = color->U;
+ *V = color->V;
+ }
+}
+
+void EmulatedFakeCameraDevice::drawStripes()
+{
+ /* Divide frame into 4 stripes. */
+ const int change_color_at = mFrameHeight / 4;
+ const int each_in_row = mUVInRow / mUVStep;
+ uint8_t* pY = mCurrentFrame;
+ for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) {
+ /* Select the color. */
+ YUVPixel* color;
+ const int color_index = y / change_color_at;
+ if (color_index == 0) {
+ /* White stripe on top. */
+ color = &mWhiteYUV;
+ } else if (color_index == 1) {
+ /* Then the red stripe. */
+ color = &mRedYUV;
+ } else if (color_index == 2) {
+ /* Then the green stripe. */
+ color = &mGreenYUV;
+ } else {
+ /* And the blue stripe at the bottom. */
+ color = &mBlueYUV;
+ }
+
+ /* All Ys at the row are the same. */
+ memset(pY, color->Y, mFrameWidth);
+
+ /* Offset of the current row inside U/V panes. */
+ const int uv_off = (y / 2) * mUVInRow;
+ /* Fill U, and V panes. */
+ uint8_t* U = mFrameU + uv_off;
+ uint8_t* V = mFrameV + uv_off;
+ for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
+ *U = color->U;
+ *V = color->V;
+ }
+ }
+}
+
+int EmulatedFakeCameraDevice::rotateFrame()
+{
+ if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) {
+ mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCurrentFrameType++;
+ if (mCurrentFrameType > 2) {
+ mCurrentFrameType = 0;
+ }
+ if (mCurrentFrameType == 2) {
+ LOGD("********** Rotated to the SOLID COLOR frame **********");
+ /* Solid color: lets rotate color too. */
+ if (mCurrentColor == &mWhiteYUV) {
+ LOGD("----- Painting a solid RED frame -----");
+ mCurrentColor = &mRedYUV;
+ } else if (mCurrentColor == &mRedYUV) {
+ LOGD("----- Painting a solid GREEN frame -----");
+ mCurrentColor = &mGreenYUV;
+ } else if (mCurrentColor == &mGreenYUV) {
+ LOGD("----- Painting a solid BLUE frame -----");
+ mCurrentColor = &mBlueYUV;
+ } else {
+ /* Back to white. */
+ LOGD("----- Painting a solid WHITE frame -----");
+ mCurrentColor = &mWhiteYUV;
+ }
+ } else if (mCurrentFrameType == 0) {
+ LOGD("********** Rotated to the CHECKERBOARD frame **********");
+ } else {
+ LOGD("********** Rotated to the STRIPED frame **********");
+ }
+ }
+
+ return mCurrentFrameType;
+}
+
+#endif // EFCD_ROTATE_FRAME
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedFakeCameraDevice.h b/tools/emulator/system/camera/EmulatedFakeCameraDevice.h
new file mode 100755
index 0000000..f54127e
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedFakeCameraDevice.h
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of a class EmulatedFakeCameraDevice that encapsulates
+ * a fake camera device.
+ */
+
+#include "Converters.h"
+#include "EmulatedCameraDevice.h"
+
+/* This is used for debugging format / conversion issues. If EFCD_ROTATE_FRAME is
+ * set to 0, the frame content will be always the "checkerboard". Otherwise, if
+ * EFCD_ROTATE_FRAME is set to a non-zero value, the frame content will "rotate"
+ * from a "checkerboard" frame to a "white/red/green/blue stripes" frame, to a
+ * "white/red/green/blue" frame. Frame content rotation helps finding bugs in
+ * format conversions.
+ */
+#define EFCD_ROTATE_FRAME 1
+
+namespace android {
+
+class EmulatedFakeCamera;
+
+/* Encapsulates a fake camera device.
+ * Fake camera device emulates a camera device by providing frames containing
+ * a black and white checker board, moving diagonally towards the 0,0 corner.
+ * There is also a green, or red square that bounces inside the frame, changing
+ * its color when bouncing off the 0,0 corner.
+ */
+class EmulatedFakeCameraDevice : public EmulatedCameraDevice {
+public:
+ /* Constructs EmulatedFakeCameraDevice instance. */
+ explicit EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal);
+
+ /* Destructs EmulatedFakeCameraDevice instance. */
+ ~EmulatedFakeCameraDevice();
+
+ /***************************************************************************
+ * Emulated camera device abstract interface implementation.
+ * See declarations of these methods in EmulatedCameraDevice class for
+ * information on each of these methods.
+ **************************************************************************/
+
+public:
+ /* Connects to the camera device.
+ * Since there is no real device to connect to, this method does nothing,
+ * but changes the state.
+ */
+ status_t connectDevice();
+
+ /* Disconnects from the camera device.
+ * Since there is no real device to disconnect from, this method does
+ * nothing, but changes the state.
+ */
+ status_t disconnectDevice();
+
+ /* Starts the camera device. */
+ status_t startDevice(int width, int height, uint32_t pix_fmt);
+
+ /* Stops the camera device. */
+ status_t stopDevice();
+
+ /* Gets current preview fame into provided buffer. */
+ status_t getPreviewFrame(void* buffer);
+
+ /***************************************************************************
+ * Worker thread management overrides.
+ * See declarations of these methods in EmulatedCameraDevice class for
+ * information on each of these methods.
+ **************************************************************************/
+
+protected:
+ /* Implementation of the worker thread routine.
+ * This method simply sleeps for a period of time defined by the FPS property
+ * of the fake camera (simulating frame frequency), and then calls emulated
+ * camera's onNextFrameAvailable method.
+ */
+ bool inWorkerThread();
+
+ /****************************************************************************
+ * Fake camera device private API
+ ***************************************************************************/
+
+private:
+ /* Draws a black and white checker board in the current frame buffer. */
+ void drawCheckerboard();
+
+ /* Draws a square of the given color in the current frame buffer.
+ * Param:
+ * x, y - Coordinates of the top left corner of the square in the buffer.
+ * size - Size of the square's side.
+ * color - Square's color.
+ */
+ void drawSquare(int x, int y, int size, const YUVPixel* color);
+
+#if EFCD_ROTATE_FRAME
+ void drawSolid(YUVPixel* color);
+ void drawStripes();
+ int rotateFrame();
+#endif // EFCD_ROTATE_FRAME
+
+ /****************************************************************************
+ * Fake camera device data members
+ ***************************************************************************/
+
+private:
+ /*
+ * Pixel colors in YUV format used when drawing the checker board.
+ */
+
+ YUVPixel mBlackYUV;
+ YUVPixel mWhiteYUV;
+ YUVPixel mRedYUV;
+ YUVPixel mGreenYUV;
+ YUVPixel mBlueYUV;
+
+ /* Last time the frame has been redrawn. */
+ nsecs_t mLastRedrawn;
+
+ /*
+ * Precalculated values related to U/V panes.
+ */
+
+ /* U pane inside the framebuffer. */
+ uint8_t* mFrameU;
+
+ /* V pane inside the framebuffer. */
+ uint8_t* mFrameV;
+
+ /* Defines byte distance between adjacent U, and V values. */
+ int mUVStep;
+
+ /* Defines number of Us and Vs in a row inside the U/V panes.
+ * Note that if U/V panes are interleaved, this value reflects the total
+ * number of both, Us and Vs in a single row in the interleaved UV pane. */
+ int mUVInRow;
+
+ /* Total number of each, U, and V elements in the framebuffer. */
+ int mUVTotalNum;
+
+ /*
+ * Checkerboard drawing related stuff
+ */
+
+ int mCheckX;
+ int mCheckY;
+ int mCcounter;
+
+ /* Emulated FPS (frames per second).
+ * We will emulate 50 FPS. */
+ static const int mEmulatedFPS = 50;
+
+ /* Defines time (in nanoseconds) between redrawing the checker board.
+ * We will redraw the checker board every 15 milliseconds. */
+ static const nsecs_t mRedrawAfter = 15000000LL;
+
+#if EFCD_ROTATE_FRAME
+ /* Frame rotation frequency in nanosec (currently - 3 sec) */
+ static const nsecs_t mRotateFreq = 3000000000LL;
+
+ /* Last time the frame has rotated. */
+ nsecs_t mLastRotatedAt;
+
+ /* Type of the frame to display in the current rotation:
+ * 0 - Checkerboard.
+ * 1 - White/Red/Green/Blue horisontal stripes
+ * 2 - Solid color. */
+ int mCurrentFrameType;
+
+ /* Color to use to paint the solid color frame. Colors will rotate between
+ * white, red, gree, and blue each time rotation comes to the solid color
+ * frame. */
+ YUVPixel* mCurrentColor;
+#endif // EFCD_ROTATE_FRAME
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H */
diff --git a/tools/emulator/system/camera/EmulatedQemuCamera.cpp b/tools/emulator/system/camera/EmulatedQemuCamera.cpp
new file mode 100755
index 0000000..611b6b5
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedQemuCamera.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedQemuCamera that encapsulates
+ * functionality of an emulated camera connected to the host.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuCamera"
+#include <cutils/log.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedCameraFactory.h"
+
+namespace android {
+
+EmulatedQemuCamera::EmulatedQemuCamera(int cameraId, struct hw_module_t* module)
+ : EmulatedCamera(cameraId, module),
+ mQemuCameraDevice(this)
+{
+}
+
+EmulatedQemuCamera::~EmulatedQemuCamera()
+{
+}
+
+/****************************************************************************
+ * EmulatedCamera virtual overrides.
+ ***************************************************************************/
+
+status_t EmulatedQemuCamera::Initialize(const char* device_name,
+ const char* frame_dims,
+ const char* facing_dir)
+{
+ LOGV("%s:\n Name=%s\n Facing '%s'\n Dimensions=%s",
+ __FUNCTION__, device_name, facing_dir, frame_dims);
+ /* Save dimensions. */
+ mFrameDims = frame_dims;
+
+ /* Initialize camera device. */
+ status_t res = mQemuCameraDevice.Initialize(device_name);
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /* Initialize base class. */
+ res = EmulatedCamera::Initialize();
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /*
+ * Set customizable parameters.
+ */
+
+ mParameters.set(EmulatedCamera::FACING_KEY, facing_dir);
+ mParameters.set(EmulatedCamera::ORIENTATION_KEY,
+ gEmulatedCameraFactory.getQemuCameraOrientation());
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, frame_dims);
+ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, frame_dims);
+
+ /*
+ * Use first dimension reported by the device to set current preview and
+ * picture sizes.
+ */
+
+ char first_dim[128];
+ /* Dimensions are separated with ',' */
+ const char* c = strchr(frame_dims, ',');
+ if (c == NULL) {
+ strncpy(first_dim, frame_dims, sizeof(first_dim));
+ first_dim[sizeof(first_dim)-1] = '\0';
+ } else if (static_cast<size_t>(c - frame_dims) < sizeof(first_dim)) {
+ memcpy(first_dim, frame_dims, c - frame_dims);
+ first_dim[c - frame_dims] = '\0';
+ } else {
+ memcpy(first_dim, frame_dims, sizeof(first_dim));
+ first_dim[sizeof(first_dim)-1] = '\0';
+ }
+
+ /* Width and height are separated with 'x' */
+ char* sep = strchr(first_dim, 'x');
+ if (sep == NULL) {
+ LOGE("%s: Invalid first dimension format in %s",
+ __FUNCTION__, frame_dims);
+ return EINVAL;
+ }
+
+ *sep = '\0';
+ const int x = atoi(first_dim);
+ const int y = atoi(sep + 1);
+ mParameters.setPreviewSize(x, y);
+ mParameters.setPictureSize(x, y);
+
+ LOGV("%s: Qemu camera %s is initialized. Current frame is %dx%d",
+ __FUNCTION__, device_name, x, y);
+
+ return NO_ERROR;
+}
+
+EmulatedCameraDevice* EmulatedQemuCamera::getCameraDevice()
+{
+ return &mQemuCameraDevice;
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedQemuCamera.h b/tools/emulator/system/camera/EmulatedQemuCamera.h
new file mode 100755
index 0000000..1b826c7
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedQemuCamera.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedQemuCamera that encapsulates
+ * functionality of an emulated camera connected to the host.
+ */
+
+#include "EmulatedCamera.h"
+#include "EmulatedQemuCameraDevice.h"
+
+namespace android {
+
+/* Encapsulates functionality of an emulated camera connected to the host.
+ */
+class EmulatedQemuCamera : public EmulatedCamera {
+public:
+ /* Constructs EmulatedQemuCamera instance. */
+ EmulatedQemuCamera(int cameraId, struct hw_module_t* module);
+
+ /* Destructs EmulatedQemuCamera instance. */
+ ~EmulatedQemuCamera();
+
+ /***************************************************************************
+ * EmulatedCamera virtual overrides.
+ **************************************************************************/
+
+public:
+ /* Initializes EmulatedQemuCamera instance. */
+ status_t Initialize(const char* device_name,
+ const char* frame_dims,
+ const char* facing_dir);
+
+ /***************************************************************************
+ * EmulatedCamera abstract API implementation.
+ **************************************************************************/
+
+protected:
+ /* Gets emulated camera device ised by this instance of the emulated camera.
+ */
+ EmulatedCameraDevice* getCameraDevice();
+
+ /***************************************************************************
+ * Data memebers.
+ **************************************************************************/
+
+protected:
+ /* Contained qemu camera device object. */
+ EmulatedQemuCameraDevice mQemuCameraDevice;
+
+ /* Supported frame dimensions reported by the camera device. */
+ String8 mFrameDims;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H */
diff --git a/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp b/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp
new file mode 100755
index 0000000..57dbc98
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedQemuCameraDevice that encapsulates
+ * an emulated camera device connected to the host.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuDevice"
+#include <cutils/log.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedQemuCameraDevice.h"
+
+namespace android {
+
+EmulatedQemuCameraDevice::EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal)
+ : EmulatedCameraDevice(camera_hal),
+ mQemuClient(),
+ mPreviewFrame(NULL)
+{
+}
+
+EmulatedQemuCameraDevice::~EmulatedQemuCameraDevice()
+{
+ if (mPreviewFrame != NULL) {
+ delete[] mPreviewFrame;
+ }
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::Initialize(const char* device_name)
+{
+ /* Connect to the service. */
+ char connect_str[256];
+ snprintf(connect_str, sizeof(connect_str), "name=%s", device_name);
+ status_t res = mQemuClient.connectClient(connect_str);
+ if (res != NO_ERROR) {
+ return res;
+ }
+
+ /* Initialize base class. */
+ res = EmulatedCameraDevice::Initialize();
+ if (res == NO_ERROR) {
+ LOGV("%s: Connected to the emulated camera service '%s'",
+ __FUNCTION__, device_name);
+ mDeviceName = device_name;
+ } else {
+ mQemuClient.queryDisconnect();
+ }
+
+ return res;
+}
+
+/****************************************************************************
+ * Emulated camera device abstract interface implementation.
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::connectDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isInitialized()) {
+ LOGE("%s: Qemu camera device is not initialized.", __FUNCTION__);
+ return EINVAL;
+ }
+ if (isConnected()) {
+ LOGW("%s: Qemu camera device '%s' is already connected.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return NO_ERROR;
+ }
+
+ /* Connect to the camera device via emulator. */
+ const status_t res = mQemuClient.queryConnect();
+ if (res == NO_ERROR) {
+ LOGV("%s: Connected to device '%s'",
+ __FUNCTION__, (const char*)mDeviceName);
+ mState = ECDS_CONNECTED;
+ } else {
+ LOGE("%s: Connection to device '%s' failed",
+ __FUNCTION__, (const char*)mDeviceName);
+ }
+
+ return res;
+}
+
+status_t EmulatedQemuCameraDevice::disconnectDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isConnected()) {
+ LOGW("%s: Qemu camera device '%s' is already disconnected.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return NO_ERROR;
+ }
+ if (isStarted()) {
+ LOGE("%s: Cannot disconnect from the started device '%s.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return EINVAL;
+ }
+
+ /* Disconnect from the camera device via emulator. */
+ const status_t res = mQemuClient.queryDisconnect();
+ if (res == NO_ERROR) {
+ LOGV("%s: Disonnected from device '%s'",
+ __FUNCTION__, (const char*)mDeviceName);
+ mState = ECDS_INITIALIZED;
+ } else {
+ LOGE("%s: Disconnection from device '%s' failed",
+ __FUNCTION__, (const char*)mDeviceName);
+ }
+
+ return res;
+}
+
+status_t EmulatedQemuCameraDevice::startDevice(int width,
+ int height,
+ uint32_t pix_fmt)
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isConnected()) {
+ LOGE("%s: Qemu camera device '%s' is not connected.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return EINVAL;
+ }
+ if (isStarted()) {
+ LOGW("%s: Qemu camera device '%s' is already started.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return NO_ERROR;
+ }
+
+ status_t res = EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
+ if (res != NO_ERROR) {
+ LOGE("%s: commonStartDevice failed", __FUNCTION__);
+ return res;
+ }
+
+ /* Allocate preview frame buffer. */
+ /* TODO: Watch out for preview format changes! At this point we implement
+ * RGB32 only.*/
+ mPreviewFrame = new uint32_t[mTotalPixels];
+ if (mPreviewFrame == NULL) {
+ LOGE("%s: Unable to allocate %d bytes for preview frame",
+ __FUNCTION__, mTotalPixels);
+ return ENOMEM;
+ }
+
+ /* Start the actual camera device. */
+ res = mQemuClient.queryStart(mPixelFormat, mFrameWidth, mFrameHeight);
+ if (res == NO_ERROR) {
+ LOGV("%s: Qemu camera device '%s' is started for %.4s[%dx%d] frames",
+ __FUNCTION__, (const char*)mDeviceName,
+ reinterpret_cast<const char*>(&mPixelFormat),
+ mFrameWidth, mFrameHeight);
+ mState = ECDS_STARTED;
+ } else {
+ LOGE("%s: Unable to start device '%s' for %.4s[%dx%d] frames",
+ __FUNCTION__, (const char*)mDeviceName,
+ reinterpret_cast<const char*>(&pix_fmt), width, height);
+ }
+
+ return res;
+}
+
+status_t EmulatedQemuCameraDevice::stopDevice()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ if (!isStarted()) {
+ LOGW("%s: Qemu camera device '%s' is not started.",
+ __FUNCTION__, (const char*)mDeviceName);
+ return NO_ERROR;
+ }
+
+ /* Stop the actual camera device. */
+ status_t res = mQemuClient.queryStop();
+ if (res == NO_ERROR) {
+ if (mPreviewFrame == NULL) {
+ delete[] mPreviewFrame;
+ mPreviewFrame = NULL;
+ }
+ EmulatedCameraDevice::commonStopDevice();
+ mState = ECDS_CONNECTED;
+ LOGV("%s: Qemu camera device '%s' is stopped",
+ __FUNCTION__, (const char*)mDeviceName);
+ } else {
+ LOGE("%s: Unable to stop device '%s'",
+ __FUNCTION__, (const char*)mDeviceName);
+ }
+
+ return res;
+}
+
+/****************************************************************************
+ * EmulatedCameraDevice virtual overrides
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::getCurrentPreviewFrame(void* buffer)
+{
+ LOGW_IF(mPreviewFrame == NULL, "%s: No preview frame", __FUNCTION__);
+ if (mPreviewFrame != NULL) {
+ memcpy(buffer, mPreviewFrame, mTotalPixels * 4);
+ return 0;
+ } else {
+ return EmulatedCameraDevice::getCurrentPreviewFrame(buffer);
+ }
+}
+
+/****************************************************************************
+ * Worker thread management overrides.
+ ***************************************************************************/
+
+bool EmulatedQemuCameraDevice::inWorkerThread()
+{
+ /* Wait till FPS timeout expires, or thread exit message is received. */
+ WorkerThread::SelectRes res =
+ getWorkerThread()->Select(-1, 1000000 / mEmulatedFPS);
+ if (res == WorkerThread::EXIT_THREAD) {
+ LOGV("%s: Worker thread has been terminated.", __FUNCTION__);
+ return false;
+ }
+
+ /* Query frames from the service. */
+ status_t query_res = mQemuClient.queryFrame(mCurrentFrame, mPreviewFrame,
+ mFrameBufferSize,
+ mTotalPixels * 4);
+ if (query_res == NO_ERROR) {
+ /* Timestamp the current frame, and notify the camera HAL. */
+ mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
+ return true;
+ } else {
+ LOGE("%s: Unable to get current video frame: %s",
+ __FUNCTION__, strerror(query_res));
+ mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED);
+ return false;
+ }
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedQemuCameraDevice.h b/tools/emulator/system/camera/EmulatedQemuCameraDevice.h
new file mode 100755
index 0000000..8ef562b
--- /dev/null
+++ b/tools/emulator/system/camera/EmulatedQemuCameraDevice.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of a class EmulatedQemuCameraDevice that encapsulates
+ * an emulated camera device connected to the host.
+ */
+
+#include "EmulatedCameraDevice.h"
+#include "QemuClient.h"
+
+namespace android {
+
+class EmulatedQemuCamera;
+
+/* Encapsulates an emulated camera device connected to the host.
+ */
+class EmulatedQemuCameraDevice : public EmulatedCameraDevice {
+public:
+ /* Constructs EmulatedQemuCameraDevice instance. */
+ explicit EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal);
+
+ /* Destructs EmulatedQemuCameraDevice instance. */
+ ~EmulatedQemuCameraDevice();
+
+ /***************************************************************************
+ * Public API
+ **************************************************************************/
+
+public:
+ /* Initializes EmulatedQemuCameraDevice instance.
+ * Param:
+ * device_name - Name of the camera device connected to the host. The name
+ * that is used here must have been reported by the 'factory' camera
+ * service when it listed camera devices connected to the host.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ status_t Initialize(const char* device_name);
+
+ /***************************************************************************
+ * Emulated camera device abstract interface implementation.
+ * See declarations of these methods in EmulatedCameraDevice class for
+ * information on each of these methods.
+ **************************************************************************/
+
+public:
+ /* Connects to the camera device. */
+ status_t connectDevice();
+
+ /* Disconnects from the camera device. */
+ status_t disconnectDevice();
+
+ /* Starts capturing frames from the camera device. */
+ status_t startDevice(int width, int height, uint32_t pix_fmt);
+
+ /* Stops capturing frames from the camera device. */
+ status_t stopDevice();
+
+ /***************************************************************************
+ * EmulatedCameraDevice virtual overrides
+ * See declarations of these methods in EmulatedCameraDevice class for
+ * information on each of these methods.
+ **************************************************************************/
+
+public:
+ /* Gets current preview fame into provided buffer.
+ * We override this method in order to provide preview frames cached in this
+ * object.
+ */
+ status_t getCurrentPreviewFrame(void* buffer);
+
+ /***************************************************************************
+ * Worker thread management overrides.
+ * See declarations of these methods in EmulatedCameraDevice class for
+ * information on each of these methods.
+ **************************************************************************/
+
+protected:
+ /* Implementation of the worker thread routine. */
+ bool inWorkerThread();
+
+ /***************************************************************************
+ * Qemu camera device data members
+ **************************************************************************/
+
+private:
+ /* Qemu client that is used to communicate with the 'emulated camera'
+ * service, created for this instance in the emulator. */
+ CameraQemuClient mQemuClient;
+
+ /* Name of the camera device connected to the host. */
+ String8 mDeviceName;
+
+ /* Current preview framebuffer. */
+ uint32_t* mPreviewFrame;
+
+ /* Emulated FPS (frames per second).
+ * We will emulate 50 FPS. */
+ static const int mEmulatedFPS = 50;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H */
diff --git a/tools/emulator/system/camera/JpegCompressor.cpp b/tools/emulator/system/camera/JpegCompressor.cpp
new file mode 100644
index 0000000..0e538a1
--- /dev/null
+++ b/tools/emulator/system/camera/JpegCompressor.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class NV21JpegCompressor that encapsulates a
+ * converter between NV21, and JPEG formats.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEG"
+#include <cutils/log.h>
+#include "JpegCompressor.h"
+
+namespace android {
+
+NV21JpegCompressor::NV21JpegCompressor()
+ : Yuv420SpToJpegEncoder(mStrides)
+{
+}
+
+NV21JpegCompressor::~NV21JpegCompressor()
+{
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t NV21JpegCompressor::compressRawImage(const void* image,
+ int width,
+ int height,
+ int quality)
+{
+ LOGV("%s: %p[%dx%d]", __FUNCTION__, image, width, height);
+ void* pY = const_cast<void*>(image);
+ int offsets[2];
+ offsets[0] = 0;
+ offsets[1] = width * height;
+ mStrides[0] = width;
+ mStrides[1] = width;
+ if (encode(&mStream, pY, width, height, offsets, quality)) {
+ LOGV("%s: Compressed JPEG: %d[%dx%d] -> %d bytes",
+ __FUNCTION__, (width * height * 12) / 8, width, height, mStream.getOffset());
+ return NO_ERROR;
+ } else {
+ LOGE("%s: JPEG compression failed", __FUNCTION__);
+ return errno ? errno : EINVAL;
+ }
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/JpegCompressor.h b/tools/emulator/system/camera/JpegCompressor.h
new file mode 100644
index 0000000..1f97ae4
--- /dev/null
+++ b/tools/emulator/system/camera/JpegCompressor.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+#define HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+
+/*
+ * Contains declaration of a class NV21JpegCompressor that encapsulates a
+ * converter between YV21, and JPEG formats.
+ */
+
+#include <YuvToJpegEncoder.h>
+
+namespace android {
+
+/* Encapsulates a converter between YV12, and JPEG formats.
+ */
+class NV21JpegCompressor : protected Yuv420SpToJpegEncoder
+{
+public:
+ /* Constructs JpegCompressor instance. */
+ NV21JpegCompressor();
+ /* Destructs JpegCompressor instance. */
+ ~NV21JpegCompressor();
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Compresses raw NV21 image into a JPEG.
+ * The compressed image will be saved in mStream member of this class. Use
+ * getCompressedSize method to obtain buffer size of the compressed image,
+ * and getCompressedImage to copy out the compressed image.
+ * Param:
+ * image - Raw NV21 image.
+ * width, height - Image dimensions.
+ * quality - JPEG quality.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ *
+ */
+ status_t compressRawImage(const void* image,
+ int width,
+ int height,
+ int quality);
+
+ /* Get size of the compressed JPEG buffer.
+ * This method must be called only after a successful completion of
+ * compressRawImage call.
+ * Return:
+ * Size of the compressed JPEG buffer.
+ */
+ size_t getCompressedSize() const
+ {
+ return mStream.getOffset();
+ }
+
+ /* Copies out compressed JPEG buffer.
+ * This method must be called only after a successful completion of
+ * compressRawImage call.
+ * Param:
+ * buff - Buffer where to copy the JPEG. Must be large enough to contain the
+ * entire image.
+ */
+ void getCompressedImage(void* buff) const
+ {
+ mStream.copyTo(buff);
+ }
+
+ /****************************************************************************
+ * Class data
+ ***************************************************************************/
+
+protected:
+ /* Memory stream where converted JPEG is saved. */
+ SkDynamicMemoryWStream mStream;
+ /* Strides for Y (the first element), and UV (the second one) panes. */
+ int mStrides[2];
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H */
diff --git a/tools/emulator/system/camera/PreviewWindow.cpp b/tools/emulator/system/camera/PreviewWindow.cpp
new file mode 100755
index 0000000..fb708d5
--- /dev/null
+++ b/tools/emulator/system/camera/PreviewWindow.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class PreviewWindow that encapsulates
+ * functionality of a preview window set via set_preview_window camera HAL API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Preview"
+#include <cutils/log.h>
+#include <ui/Rect.h>
+#include <ui/GraphicBufferMapper.h>
+#include "EmulatedCameraDevice.h"
+#include "PreviewWindow.h"
+
+namespace android {
+
+PreviewWindow::PreviewWindow()
+ : mPreviewWindow(NULL),
+ mLastPreviewed(0),
+ mPreviewFrameWidth(0),
+ mPreviewFrameHeight(0),
+ mPreviewEnabled(false)
+{
+}
+
+PreviewWindow::~PreviewWindow()
+{
+}
+
+/****************************************************************************
+ * Camera API
+ ***************************************************************************/
+
+status_t PreviewWindow::setPreviewWindow(struct preview_stream_ops* window,
+ int preview_fps)
+{
+ LOGV("%s: current: %p -> new: %p", __FUNCTION__, mPreviewWindow, window);
+
+ status_t res = NO_ERROR;
+ Mutex::Autolock locker(&mObjectLock);
+
+ /* Reset preview info. */
+ mPreviewFrameWidth = mPreviewFrameHeight = 0;
+ mPreviewAfter = 0;
+ mLastPreviewed = 0;
+
+ if (window != NULL) {
+ /* The CPU will write each frame to the preview window buffer.
+ * Note that we delay setting preview window buffer geometry until
+ * frames start to come in. */
+ res = window->set_usage(window, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ if (res == NO_ERROR) {
+ /* Set preview frequency. */
+ mPreviewAfter = 1000000 / preview_fps;
+ } else {
+ window = NULL;
+ res = -res; // set_usage returns a negative errno.
+ LOGE("%s: Error setting preview window usage %d -> %s",
+ __FUNCTION__, res, strerror(res));
+ }
+ }
+ mPreviewWindow = window;
+
+ return res;
+}
+
+status_t PreviewWindow::startPreview()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mPreviewEnabled = true;
+
+ return NO_ERROR;
+}
+
+void PreviewWindow::stopPreview()
+{
+ LOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock locker(&mObjectLock);
+ mPreviewEnabled = false;
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+void PreviewWindow::onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev)
+{
+ int res;
+ Mutex::Autolock locker(&mObjectLock);
+
+ if (!isPreviewEnabled() || mPreviewWindow == NULL || !isPreviewTime()) {
+ return;
+ }
+
+ /* Make sure that preview window dimensions are OK with the camera device */
+ if (adjustPreviewDimensions(camera_dev)) {
+ /* Need to set / adjust buffer geometry for the preview window.
+ * Note that in the emulator preview window uses only RGB for pixel
+ * formats. */
+ LOGV("%s: Adjusting preview windows %p geometry to %dx%d",
+ __FUNCTION__, mPreviewWindow, mPreviewFrameWidth,
+ mPreviewFrameHeight);
+ res = mPreviewWindow->set_buffers_geometry(mPreviewWindow,
+ mPreviewFrameWidth,
+ mPreviewFrameHeight,
+ HAL_PIXEL_FORMAT_RGBA_8888);
+ if (res != NO_ERROR) {
+ LOGE("%s: Error in set_buffers_geometry %d -> %s",
+ __FUNCTION__, -res, strerror(-res));
+ return;
+ }
+ }
+
+ /*
+ * Push new frame to the preview window.
+ */
+
+ /* Dequeue preview window buffer for the frame. */
+ buffer_handle_t* buffer = NULL;
+ int stride = 0;
+ res = mPreviewWindow->dequeue_buffer(mPreviewWindow, &buffer, &stride);
+ if (res != NO_ERROR || buffer == NULL) {
+ LOGE("%s: Unable to dequeue preview window buffer: %d -> %s",
+ __FUNCTION__, -res, strerror(-res));
+ return;
+ }
+
+ /* Let the preview window to lock the buffer. */
+ res = mPreviewWindow->lock_buffer(mPreviewWindow, buffer);
+ if (res != NO_ERROR) {
+ LOGE("%s: Unable to lock preview window buffer: %d -> %s",
+ __FUNCTION__, -res, strerror(-res));
+ mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+ return;
+ }
+
+ /* Now let the graphics framework to lock the buffer, and provide
+ * us with the framebuffer data address. */
+ void* img = NULL;
+ const Rect rect(mPreviewFrameWidth, mPreviewFrameHeight);
+ GraphicBufferMapper& grbuffer_mapper(GraphicBufferMapper::get());
+ res = grbuffer_mapper.lock(*buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, rect, &img);
+ if (res != NO_ERROR) {
+ LOGE("%s: grbuffer_mapper.lock failure: %d -> %s",
+ __FUNCTION__, res, strerror(res));
+ mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+ return;
+ }
+
+ /* Frames come in in YV12/NV12/NV21 format. Since preview window doesn't
+ * supports those formats, we need to obtain the frame in RGB565. */
+ res = camera_dev->getCurrentPreviewFrame(img);
+ if (res == NO_ERROR) {
+ /* Show it. */
+ mPreviewWindow->enqueue_buffer(mPreviewWindow, buffer);
+ } else {
+ LOGE("%s: Unable to obtain preview frame: %d", __FUNCTION__, res);
+ mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+ }
+ grbuffer_mapper.unlock(*buffer);
+}
+
+/***************************************************************************
+ * Private API
+ **************************************************************************/
+
+bool PreviewWindow::adjustPreviewDimensions(EmulatedCameraDevice* camera_dev)
+{
+ /* Match the cached frame dimensions against the actual ones. */
+ if (mPreviewFrameWidth == camera_dev->getFrameWidth() &&
+ mPreviewFrameHeight == camera_dev->getFrameHeight()) {
+ /* They match. */
+ return false;
+ }
+
+ /* They don't match: adjust the cache. */
+ mPreviewFrameWidth = camera_dev->getFrameWidth();
+ mPreviewFrameHeight = camera_dev->getFrameHeight();
+
+ return true;
+}
+
+bool PreviewWindow::isPreviewTime()
+{
+ timeval cur_time;
+ gettimeofday(&cur_time, NULL);
+ const uint64_t cur_mks = cur_time.tv_sec * 1000000LL + cur_time.tv_usec;
+ if ((cur_mks - mLastPreviewed) >= mPreviewAfter) {
+ mLastPreviewed = cur_mks;
+ return true;
+ }
+ return false;
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/PreviewWindow.h b/tools/emulator/system/camera/PreviewWindow.h
new file mode 100755
index 0000000..d037c95
--- /dev/null
+++ b/tools/emulator/system/camera/PreviewWindow.h
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H
+#define HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H
+
+/*
+ * Contains declaration of a class PreviewWindow that encapsulates functionality
+ * of a preview window set via set_preview_window camera HAL API.
+ */
+
+namespace android {
+
+class EmulatedCameraDevice;
+
+/* Encapsulates functionality of a preview window set via set_preview_window
+ * camera HAL API.
+ *
+ * Objects of this class are contained in EmulatedCamera objects, and handle
+ * relevant camera API callbacks.
+ */
+class PreviewWindow {
+public:
+ /* Constructs PreviewWindow instance. */
+ PreviewWindow();
+
+ /* Destructs PreviewWindow instance. */
+ ~PreviewWindow();
+
+ /***************************************************************************
+ * Camera API
+ **************************************************************************/
+
+public:
+ /* Actual handler for camera_device_ops_t::set_preview_window callback.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::set_preview_window callback.
+ * Param:
+ * window - Preview window to set. This parameter might be NULL, which
+ * indicates preview window reset.
+ * preview_fps - Preview's frame frequency. This parameter determins when
+ * a frame received via onNextFrameAvailable call will be pushed to
+ * the preview window. If 'window' parameter passed to this method is
+ * NULL, this parameter is ignored.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ status_t setPreviewWindow(struct preview_stream_ops* window,
+ int preview_fps);
+
+ /* Starts the preview.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::start_preview callback.
+ */
+ status_t startPreview();
+
+ /* Stops the preview.
+ * This method is called by the containing emulated camera object when it is
+ * handing the camera_device_ops_t::start_preview callback.
+ */
+ void stopPreview();
+
+ /* Checks if preview is enabled. */
+ inline bool isPreviewEnabled()
+ {
+ return mPreviewEnabled;
+ }
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Next frame is available in the camera device.
+ * This is a notification callback that is invoked by the camera device when
+ * a new frame is available.
+ * Note that most likely this method is called in context of a worker thread
+ * that camera device has created for frame capturing.
+ * Param:
+ * frame - Captured frame, or NULL if camera device didn't pull the frame
+ * yet. If NULL is passed in this parameter use GetCurrentFrame method
+ * of the camera device class to obtain the next frame. Also note that
+ * the size of the frame that is passed here (as well as the frame
+ * returned from the GetCurrentFrame method) is defined by the current
+ * frame settings (width + height + pixel format) for the camera device.
+ * timestamp - Frame's timestamp.
+ * camera_dev - Camera device instance that delivered the frame.
+ */
+ void onNextFrameAvailable(const void* frame,
+ nsecs_t timestamp,
+ EmulatedCameraDevice* camera_dev);
+
+ /***************************************************************************
+ * Private API
+ **************************************************************************/
+
+protected:
+ /* Adjusts cached dimensions of the preview window frame according to the
+ * frame dimensions used by the camera device.
+ *
+ * When preview is started, it's not known (hard to define) what are going
+ * to be the dimensions of the frames that are going to be displayed. Plus,
+ * it might be possible, that such dimensions can be changed on the fly. So,
+ * in order to be always in sync with frame dimensions, this method is
+ * called for each frame passed to onNextFrameAvailable method, in order to
+ * properly adjust frame dimensions, used by the preview window.
+ * Note that this method must be called while object is locked.
+ * Param:
+ * camera_dev - Camera device, prpviding frames displayed in the preview
+ * window.
+ * Return:
+ * true if cached dimensions have been adjusted, or false if cached
+ * dimensions match device's frame dimensions.
+ */
+ bool adjustPreviewDimensions(EmulatedCameraDevice* camera_dev);
+
+ /* Checks if it's the time to push new frame to the preview window.
+ * Note that this method must be called while object is locked. */
+ bool isPreviewTime();
+
+ /***************************************************************************
+ * Data members
+ **************************************************************************/
+
+protected:
+ /* Locks this instance for data changes. */
+ Mutex mObjectLock;
+
+ /* Preview window instance. */
+ preview_stream_ops* mPreviewWindow;
+
+ /* Timestamp (abs. microseconds) when last frame has been pushed to the
+ * preview window. */
+ uint64_t mLastPreviewed;
+
+ /* Preview frequency in microseconds. */
+ uint32_t mPreviewAfter;
+
+ /*
+ * Cached preview window frame dimensions.
+ */
+
+ int mPreviewFrameWidth;
+ int mPreviewFrameHeight;
+
+ /* Preview status. */
+ bool mPreviewEnabled;
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H */
diff --git a/tools/emulator/system/camera/QemuClient.cpp b/tools/emulator/system/camera/QemuClient.cpp
new file mode 100755
index 0000000..fd49585
--- /dev/null
+++ b/tools/emulator/system/camera/QemuClient.cpp
@@ -0,0 +1,554 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of classes that encapsulate connection to camera
+ * services in the emulator via qemu pipe.
+ */
+
+#define LOG_NDEBUG 1
+#define LOG_TAG "EmulatedCamera_QemuClient"
+#include <cutils/log.h>
+#include "EmulatedCamera.h"
+#include "QemuClient.h"
+
+#define LOG_QUERIES 0
+#if LOG_QUERIES
+#define LOGQ(...) LOGD(__VA_ARGS__)
+#else
+#define LOGQ(...) (void(0))
+
+#endif // LOG_QUERIES
+namespace android {
+
+/****************************************************************************
+ * Qemu query
+ ***************************************************************************/
+
+QemuQuery::QemuQuery()
+ : mQuery(mQueryPrealloc),
+ mQueryDeliveryStatus(NO_ERROR),
+ mReplyBuffer(NULL),
+ mReplyData(NULL),
+ mReplySize(0),
+ mReplyDataSize(0),
+ mReplyStatus(0)
+{
+ *mQuery = '\0';
+}
+
+QemuQuery::QemuQuery(const char* query_string)
+ : mQuery(mQueryPrealloc),
+ mQueryDeliveryStatus(NO_ERROR),
+ mReplyBuffer(NULL),
+ mReplyData(NULL),
+ mReplySize(0),
+ mReplyDataSize(0),
+ mReplyStatus(0)
+{
+ mQueryDeliveryStatus = QemuQuery::createQuery(query_string, NULL);
+}
+
+QemuQuery::QemuQuery(const char* query_name, const char* query_param)
+ : mQuery(mQueryPrealloc),
+ mQueryDeliveryStatus(NO_ERROR),
+ mReplyBuffer(NULL),
+ mReplyData(NULL),
+ mReplySize(0),
+ mReplyDataSize(0),
+ mReplyStatus(0)
+{
+ mQueryDeliveryStatus = QemuQuery::createQuery(query_name, query_param);
+}
+
+QemuQuery::~QemuQuery()
+{
+ QemuQuery::resetQuery();
+}
+
+status_t QemuQuery::createQuery(const char* name, const char* param)
+{
+ /* Reset from the previous use. */
+ resetQuery();
+
+ /* Query name cannot be NULL or an empty string. */
+ if (name == NULL || *name == '\0') {
+ LOGE("%s: NULL or an empty string is passed as query name.",
+ __FUNCTION__);
+ mQueryDeliveryStatus = EINVAL;
+ return EINVAL;
+ }
+
+ const size_t name_len = strlen(name);
+ const size_t param_len = (param != NULL) ? strlen(param) : 0;
+ const size_t required = strlen(name) + (param_len ? (param_len + 2) : 1);
+
+ if (required > sizeof(mQueryPrealloc)) {
+ /* Preallocated buffer was too small. Allocate a bigger query buffer. */
+ mQuery = new char[required];
+ if (mQuery == NULL) {
+ LOGE("%s: Unable to allocate %d bytes for query buffer",
+ __FUNCTION__, required);
+ mQueryDeliveryStatus = ENOMEM;
+ return ENOMEM;
+ }
+ }
+
+ /* At this point mQuery buffer is big enough for the query. */
+ if (param_len) {
+ sprintf(mQuery, "%s %s", name, param);
+ } else {
+ memcpy(mQuery, name, name_len + 1);
+ }
+
+ return NO_ERROR;
+}
+
+status_t QemuQuery::completeQuery(status_t status)
+{
+ /* Save query completion status. */
+ mQueryDeliveryStatus = status;
+ if (mQueryDeliveryStatus != NO_ERROR) {
+ return mQueryDeliveryStatus;
+ }
+
+ /* Make sure reply buffer contains at least 'ok', or 'ko'.
+ * Note that 'ok', or 'ko' prefixes are always 3 characters long: in case
+ * there are more data in the reply, that data will be separated from 'ok'/'ko'
+ * with a ':'. If there is no more data in the reply, the prefix will be
+ * zero-terminated, and the terminator will be inculded in the reply. */
+ if (mReplyBuffer == NULL || mReplySize < 3) {
+ LOGE("%s: Invalid reply to the query", __FUNCTION__);
+ mQueryDeliveryStatus = EINVAL;
+ return EINVAL;
+ }
+
+ /* Lets see the reply status. */
+ if (!memcmp(mReplyBuffer, "ok", 2)) {
+ mReplyStatus = 1;
+ } else if (!memcmp(mReplyBuffer, "ko", 2)) {
+ mReplyStatus = 0;
+ } else {
+ LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+ mQueryDeliveryStatus = EINVAL;
+ return EINVAL;
+ }
+
+ /* Lets see if there are reply data that follow. */
+ if (mReplySize > 3) {
+ /* There are extra data. Make sure they are separated from the status
+ * with a ':' */
+ if (mReplyBuffer[2] != ':') {
+ LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+ mQueryDeliveryStatus = EINVAL;
+ return EINVAL;
+ }
+ mReplyData = mReplyBuffer + 3;
+ mReplyDataSize = mReplySize - 3;
+ } else {
+ /* Make sure reply buffer containing just 'ok'/'ko' ends with
+ * zero-terminator. */
+ if (mReplyBuffer[2] != '\0') {
+ LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+ mQueryDeliveryStatus = EINVAL;
+ return EINVAL;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void QemuQuery::resetQuery()
+{
+ if (mQuery != NULL && mQuery != mQueryPrealloc) {
+ delete[] mQuery;
+ }
+ mQuery = mQueryPrealloc;
+ mQueryDeliveryStatus = NO_ERROR;
+ if (mReplyBuffer != NULL) {
+ free(mReplyBuffer);
+ mReplyBuffer = NULL;
+ }
+ mReplyData = NULL;
+ mReplySize = mReplyDataSize = 0;
+ mReplyStatus = 0;
+}
+
+/****************************************************************************
+ * Qemu client base
+ ***************************************************************************/
+
+/* Camera service name. */
+const char QemuClient::mCameraServiceName[] = "camera";
+
+QemuClient::QemuClient()
+ : mPipeFD(-1)
+{
+}
+
+QemuClient::~QemuClient()
+{
+ if (mPipeFD >= 0) {
+ close(mPipeFD);
+ }
+}
+
+/****************************************************************************
+ * Qemu client API
+ ***************************************************************************/
+
+status_t QemuClient::connectClient(const char* param)
+{
+ LOGV("%s: '%s'", __FUNCTION__, param ? param : "");
+
+ /* Make sure that client is not connected already. */
+ if (mPipeFD >= 0) {
+ LOGE("%s: Qemu client is already connected", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* Select one of the two: 'factory', or 'emulated camera' service */
+ if (param == NULL || *param == '\0') {
+ /* No parameters: connect to the factory service. */
+ char pipe_name[512];
+ snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", mCameraServiceName);
+ mPipeFD = qemu_pipe_open(pipe_name);
+ } else {
+ /* One extra char ':' that separates service name and parameters + six
+ * characters for 'qemud:'. This is required by qemu pipe protocol. */
+ char* connection_str = new char[strlen(mCameraServiceName) +
+ strlen(param) + 8];
+ sprintf(connection_str, "qemud:%s:%s", mCameraServiceName, param);
+
+ mPipeFD = qemu_pipe_open(connection_str);
+ delete[] connection_str;
+ }
+ if (mPipeFD < 0) {
+ LOGE("%s: Unable to connect to the camera service '%s': %s",
+ __FUNCTION__, param ? param : "Factory", strerror(errno));
+ return errno ? errno : EINVAL;
+ }
+
+ return NO_ERROR;
+}
+
+void QemuClient::disconnectClient()
+{
+ LOGV("%s", __FUNCTION__);
+
+ if (mPipeFD >= 0) {
+ close(mPipeFD);
+ mPipeFD = -1;
+ }
+}
+
+status_t QemuClient::sendMessage(const void* data, size_t data_size)
+{
+ if (mPipeFD < 0) {
+ LOGE("%s: Qemu client is not connected", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* Note that we don't use here qemud_client_send, since with qemu pipes we
+ * don't need to provide payload size prior to payload when we're writing to
+ * the pipe. So, we can use simple write, and qemu pipe will take care of the
+ * rest, calling the receiving end with the number of bytes transferred. */
+ const size_t written = qemud_fd_write(mPipeFD, data, data_size);
+ if (written == data_size) {
+ return NO_ERROR;
+ } else {
+ LOGE("%s: Error sending data via qemu pipe: '%s'",
+ __FUNCTION__, strerror(errno));
+ return errno ? errno : EIO;
+ }
+}
+
+status_t QemuClient::receiveMessage(void** data, size_t* data_size)
+{
+ *data = NULL;
+ *data_size = 0;
+
+ if (mPipeFD < 0) {
+ LOGE("%s: Qemu client is not connected", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* The way the service replies to a query, it sends payload size first, and
+ * then it sends the payload itself. Note that payload size is sent as a
+ * string, containing 8 characters representing a hexadecimal payload size
+ * value. Note also, that the string doesn't contain zero-terminator. */
+ size_t payload_size;
+ char payload_size_str[9];
+ int rd_res = qemud_fd_read(mPipeFD, payload_size_str, 8);
+ if (rd_res != 8) {
+ LOGE("%s: Unable to obtain payload size: %s",
+ __FUNCTION__, strerror(errno));
+ return errno ? errno : EIO;
+ }
+
+ /* Convert payload size. */
+ errno = 0;
+ payload_size_str[8] = '\0';
+ payload_size = strtol(payload_size_str, NULL, 16);
+ if (errno) {
+ LOGE("%s: Invalid payload size '%s'", __FUNCTION__, payload_size_str);
+ return EIO;
+ }
+
+ /* Allocate payload data buffer, and read the payload there. */
+ *data = malloc(payload_size);
+ if (*data == NULL) {
+ LOGE("%s: Unable to allocate %d bytes payload buffer",
+ __FUNCTION__, payload_size);
+ return ENOMEM;
+ }
+ rd_res = qemud_fd_read(mPipeFD, *data, payload_size);
+ if (static_cast<size_t>(rd_res) == payload_size) {
+ *data_size = payload_size;
+ return NO_ERROR;
+ } else {
+ LOGE("%s: Read size %d doesnt match expected payload size %d: %s",
+ __FUNCTION__, rd_res, payload_size, strerror(errno));
+ free(*data);
+ *data = NULL;
+ return errno ? errno : EIO;
+ }
+}
+
+status_t QemuClient::doQuery(QemuQuery* query)
+{
+ /* Make sure that query has been successfuly constructed. */
+ if (query->mQueryDeliveryStatus != NO_ERROR) {
+ LOGE("%s: Query is invalid", __FUNCTION__);
+ return query->mQueryDeliveryStatus;
+ }
+
+ LOGQ("Send query '%s'", query->mQuery);
+
+ /* Send the query. */
+ status_t res = sendMessage(query->mQuery, strlen(query->mQuery) + 1);
+ if (res == NO_ERROR) {
+ /* Read the response. */
+ res = receiveMessage(reinterpret_cast<void**>(&query->mReplyBuffer),
+ &query->mReplySize);
+ if (res == NO_ERROR) {
+ LOGQ("Response to query '%s': Status = '%.2s', %d bytes in response",
+ query->mQuery, query->mReplyBuffer, query->mReplySize);
+ } else {
+ LOGE("%s Response to query '%s' has failed: %s",
+ __FUNCTION__, query->mQuery, strerror(res));
+ }
+ } else {
+ LOGE("%s: Send query '%s' failed: %s",
+ __FUNCTION__, query->mQuery, strerror(res));
+ }
+
+ /* Complete the query, and return its completion handling status. */
+ const status_t res1 = query->completeQuery(res);
+ LOGE_IF(res1 != NO_ERROR && res1 != res,
+ "%s: Error %d in query '%s' completion",
+ __FUNCTION__, res1, query->mQuery);
+ return res1;
+}
+
+/****************************************************************************
+ * Qemu client for the 'factory' service.
+ ***************************************************************************/
+
+/*
+ * Factory service queries.
+ */
+
+/* Queries list of cameras connected to the host. */
+const char FactoryQemuClient::mQueryList[] = "list";
+
+FactoryQemuClient::FactoryQemuClient()
+ : QemuClient()
+{
+}
+
+FactoryQemuClient::~FactoryQemuClient()
+{
+}
+
+status_t FactoryQemuClient::listCameras(char** list)
+{
+ LOGV("%s", __FUNCTION__);
+
+ QemuQuery query(mQueryList);
+ if (doQuery(&query) || !query.isQuerySucceeded()) {
+ LOGE("%s: List cameras query failed: %s", __FUNCTION__,
+ query.mReplyData ? query.mReplyData : "No error message");
+ return query.getCompletionStatus();
+ }
+
+ /* Make sure there is a list returned. */
+ if (query.mReplyDataSize == 0) {
+ LOGE("%s: No camera list is returned.", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* Copy the list over. */
+ *list = (char*)malloc(query.mReplyDataSize);
+ if (*list != NULL) {
+ memcpy(*list, query.mReplyData, query.mReplyDataSize);
+ LOGD("Emulated camera list: %s", *list);
+ return NO_ERROR;
+ } else {
+ LOGE("%s: Unable to allocate %d bytes",
+ __FUNCTION__, query.mReplyDataSize);
+ return ENOMEM;
+ }
+}
+
+/****************************************************************************
+ * Qemu client for an 'emulated camera' service.
+ ***************************************************************************/
+
+/*
+ * Emulated camera queries
+ */
+
+/* Connect to the camera device. */
+const char CameraQemuClient::mQueryConnect[] = "connect";
+/* Disconect from the camera device. */
+const char CameraQemuClient::mQueryDisconnect[] = "disconnect";
+/* Start capturing video from the camera device. */
+const char CameraQemuClient::mQueryStart[] = "start";
+/* Stop capturing video from the camera device. */
+const char CameraQemuClient::mQueryStop[] = "stop";
+/* Get next video frame from the camera device. */
+const char CameraQemuClient::mQueryFrame[] = "frame";
+
+CameraQemuClient::CameraQemuClient()
+ : QemuClient()
+{
+}
+
+CameraQemuClient::~CameraQemuClient()
+{
+
+}
+
+status_t CameraQemuClient::queryConnect()
+{
+ LOGV("%s", __FUNCTION__);
+
+ QemuQuery query(mQueryConnect);
+ doQuery(&query);
+ const status_t res = query.getCompletionStatus();
+ LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+ __FUNCTION__, query.mReplyData ? query.mReplyData :
+ "No error message");
+ return res;
+}
+
+status_t CameraQemuClient::queryDisconnect()
+{
+ LOGV("%s", __FUNCTION__);
+
+ QemuQuery query(mQueryDisconnect);
+ doQuery(&query);
+ const status_t res = query.getCompletionStatus();
+ LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+ __FUNCTION__, query.mReplyData ? query.mReplyData :
+ "No error message");
+ return res;
+}
+
+status_t CameraQemuClient::queryStart(uint32_t pixel_format,
+ int width,
+ int height)
+{
+ LOGV("%s", __FUNCTION__);
+
+ char query_str[256];
+ snprintf(query_str, sizeof(query_str), "%s dim=%dx%d pix=%d",
+ mQueryStart, width, height, pixel_format);
+ QemuQuery query(query_str);
+ doQuery(&query);
+ const status_t res = query.getCompletionStatus();
+ LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+ __FUNCTION__, query.mReplyData ? query.mReplyData :
+ "No error message");
+ return res;
+}
+
+status_t CameraQemuClient::queryStop()
+{
+ LOGV("%s", __FUNCTION__);
+
+ QemuQuery query(mQueryStop);
+ doQuery(&query);
+ const status_t res = query.getCompletionStatus();
+ LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+ __FUNCTION__, query.mReplyData ? query.mReplyData :
+ "No error message");
+ return res;
+}
+
+status_t CameraQemuClient::queryFrame(void* vframe,
+ void* pframe,
+ size_t vframe_size,
+ size_t pframe_size)
+{
+ LOGV("%s", __FUNCTION__);
+
+ char query_str[256];
+ snprintf(query_str, sizeof(query_str), "%s video=%d preview=%d",
+ mQueryFrame, (vframe && vframe_size) ? vframe_size : 0,
+ (pframe && pframe_size) ? pframe_size : 0);
+ QemuQuery query(query_str);
+ doQuery(&query);
+ const status_t res = query.getCompletionStatus();
+ if( res != NO_ERROR) {
+ LOGE("%s: Query failed: %s",
+ __FUNCTION__, query.mReplyData ? query.mReplyData :
+ "No error message");
+ return res;
+ }
+
+ /* Copy requested frames. */
+ size_t cur_offset = 0;
+ const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
+ /* Video frame is always first. */
+ if (vframe != NULL && vframe_size != 0) {
+ /* Make sure that video frame is in. */
+ if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
+ memcpy(vframe, frame, vframe_size);
+ cur_offset += vframe_size;
+ } else {
+ LOGE("%s: Reply %d bytes is to small to contain %d bytes video frame",
+ __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
+ return EINVAL;
+ }
+ }
+ if (pframe != NULL && pframe_size != 0) {
+ /* Make sure that preview frame is in. */
+ if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
+ memcpy(pframe, frame + cur_offset, pframe_size);
+ cur_offset += pframe_size;
+ } else {
+ LOGE("%s: Reply %d bytes is to small to contain %d bytes preview frame",
+ __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
+ return EINVAL;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/QemuClient.h b/tools/emulator/system/camera/QemuClient.h
new file mode 100755
index 0000000..c0b8e61
--- /dev/null
+++ b/tools/emulator/system/camera/QemuClient.h
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_QEMU_CLIENT_H
+#define HW_EMULATOR_CAMERA_QEMU_CLIENT_H
+
+/*
+ * Contains declaration of classes that encapsulate connection to camera services
+ * in the emulator via qemu pipe.
+ */
+
+#include <hardware/qemud.h>
+
+namespace android {
+
+/****************************************************************************
+ * Qemu query
+ ***************************************************************************/
+
+/* Encapsulates a query to the emulator.
+ * Guest exchanges data with the emulator via queries sent over the qemu pipe.
+ * The queries as well as replies to the queries are all strings (except for the
+ * 'frame' query where reply is a framebuffer).
+ * Each query is formatted as such:
+ *
+ * "<query name>[ <parameters>]",
+ *
+ * where <query name> is a string representing query name, and <parameters> are
+ * optional parameters for the query. If parameters are present, they must be
+ * separated from the query name with a single space, and they must be formatted
+ * as such:
+ *
+ * "<name1>=<value1> <name2>=<value2> ... <nameN>=<valueN>"
+ *
+ * I.e.:
+ * - Every parameter must have a name, and a value.
+ * - Name and value must be separated with '='.
+ * - No spaces are allowed around '=' separating name and value.
+ * - Parameters must be separated with a single space character.
+ * - No '=' character is allowed in name and in value.
+ *
+ * There are certain restrictions on strings used in the query:
+ * - Spaces are allowed only as separators.
+ * - '=' are allowed only to divide parameter names from parameter values.
+ *
+ * Emulator replies to each query in two chunks:
+ * - 8 bytes encoding the payload size as a string containing hexadecimal
+ * representation of the payload size value. This is done in order to simplify
+ * dealing with different endianness on the host, and on the guest.
+ * - Payload, whose size is defined by the first chunk.
+ *
+ * Every payload always begins with two characters, encoding the result of the
+ * query:
+ * - 'ok' Encoding the success
+ * - 'ko' Encoding a failure.
+ * After that payload may have optional data. If payload has more data following
+ * the query result, there is a ':' character separating them. If payload carries
+ * only the result, it always ends with a zero-terminator. So, payload 'ok'/'ko'
+ * prefix is always 3 bytes long: it either includes a zero-terminator, if there
+ * is no data, or a ':' separator.
+ */
+class QemuQuery {
+public:
+ /* Constructs an uninitialized QemuQuery instance. */
+ QemuQuery();
+
+ /* Constructs and initializes QemuQuery instance for a query.
+ * Param:
+ * query_string - Query string. This constructor can also be used to
+ * construct a query that doesn't have parameters. In this case query
+ * name can be passed as a parameter here.
+ */
+ explicit QemuQuery(const char* query_string);
+
+ /* Constructs and initializes QemuQuery instance for a query with parameters.
+ * Param:
+ * query_name - Query name.
+ * query_param - Query parameters. Can be NULL.
+ */
+ QemuQuery(const char* query_name, const char* query_param);
+
+ /* Destructs QemuQuery instance. */
+ ~QemuQuery();
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+ /* Creates new query.
+ * Note: this method will reset this instance prior to creating a new query
+ * in order to discard possible "leftovers" from the previous query.
+ * Param:
+ * query_name - Query name.
+ * query_param - Query parameters. Can be NULL.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ status_t createQuery(const char* name, const char* param);
+
+ /* Completes the query after a reply from the emulator.
+ * This method will parse the reply buffer, and calculate the final query
+ * status, which depends not only on the transport success / failure, but
+ * also on 'ok' / 'ko' in the reply buffer.
+ * Param:
+ * status - Query delivery status. This status doesn't necessarily reflects
+ * the final query status (which is defined by 'ok'/'ko' prefix in the
+ * reply buffer). This status simply states whether or not the query has
+ * been sent, and a reply has been received successfuly. However, if
+ * this status indicates a failure, it means that the entire query has
+ * failed.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure. Note that
+ * status returned here just signals whether or not the method has succeeded.
+ * Use isQuerySucceeded() / getCompletionStatus() methods of this class to
+ * check the final query status.
+ */
+ status_t completeQuery(status_t status);
+
+ /* Resets the query from a previous use. */
+ void resetQuery();
+
+ /* Checks if query has succeeded.
+ * Note that this method must be called after completeQuery() method of this
+ * class has been executed.
+ */
+ inline bool isQuerySucceeded() const {
+ return mQueryDeliveryStatus == NO_ERROR && mReplyStatus != 0;
+ }
+
+ /* Gets final completion status of the query.
+ * Note that this method must be called after completeQuery() method of this
+ * class has been executed.
+ * Return:
+ * NO_ERROR if query has succeeded, or an appropriate error status on query
+ * failure.
+ */
+ inline status_t getCompletionStatus() const {
+ if (mQueryDeliveryStatus == NO_ERROR) {
+ if (mReplyStatus) {
+ return NO_ERROR;
+ } else {
+ return EINVAL;
+ }
+ } else {
+ return mQueryDeliveryStatus;
+ }
+ }
+
+ /****************************************************************************
+ * Public data memebers
+ ***************************************************************************/
+
+public:
+ /* Query string. */
+ char* mQuery;
+ /* Query delivery status. */
+ status_t mQueryDeliveryStatus;
+ /* Reply buffer */
+ char* mReplyBuffer;
+ /* Reply data (past 'ok'/'ko'). If NULL, there were no data in reply. */
+ char* mReplyData;
+ /* Reply buffer size. */
+ size_t mReplySize;
+ /* Reply data size. */
+ size_t mReplyDataSize;
+ /* Reply status: 1 - ok, 0 - ko. */
+ int mReplyStatus;
+
+ /****************************************************************************
+ * Private data memebers
+ ***************************************************************************/
+
+protected:
+ /* Preallocated buffer for small queries. */
+ char mQueryPrealloc[256];
+};
+
+/****************************************************************************
+ * Qemu client base
+ ***************************************************************************/
+
+/* Encapsulates a connection to the 'camera' service in the emulator via qemu
+ * pipe.
+ */
+class QemuClient {
+public:
+ /* Constructs QemuClient instance. */
+ QemuClient();
+
+ /* Destructs QemuClient instance. */
+ virtual ~QemuClient();
+
+ /****************************************************************************
+ * Qemu client API
+ ***************************************************************************/
+
+public:
+ /* Connects to the 'camera' service in the emulator via qemu pipe.
+ * Param:
+ * param - Parameters to pass to the camera service. There are two types of
+ * camera services implemented by the emulator. The first one is a
+ * 'camera factory' type of service that provides list of cameras
+ * connected to the host. Another one is an 'emulated camera' type of
+ * service that provides interface to a camera connected to the host. At
+ * the connection time emulator makes distinction between the two by
+ * looking at connection parameters: no parameters means connection to
+ * the 'factory' service, while connection with parameters means
+ * connection to an 'emulated camera' service, where camera is identified
+ * by one of the connection parameters. So, passing NULL, or an empty
+ * string to this method will establish a connection with the 'factory'
+ * service, while not empty string passed here will establish connection
+ * with an 'emulated camera' service. Parameters defining the emulated
+ * camera must be formatted as such:
+ *
+ * "name=<device name> [inp_channel=<input channel #>]",
+ *
+ * where 'device name' is a required parameter defining name of the
+ * camera device, and 'input channel' is an optional parameter (positive
+ * integer), defining the input channel to use on the camera device.
+ * Note that device name passed here must have been previously obtained
+ * from the factory service using 'list' query.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status.
+ */
+ virtual status_t connectClient(const char* param);
+
+ /* Disconnects from the service. */
+ virtual void disconnectClient();
+
+ /* Sends data to the service.
+ * Param:
+ * data, data_size - Data to send.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ virtual status_t sendMessage(const void* data, size_t data_size);
+
+ /* Receives data from the service.
+ * This method assumes that data to receive will come in two chunks: 8
+ * characters encoding the payload size in hexadecimal string, followed by
+ * the paylod (if any).
+ * This method will allocate data buffer where to receive the response.
+ * Param:
+ * data - Upon success contains address of the allocated data buffer with
+ * the data received from the service. The caller is responsible for
+ * freeing allocated data buffer.
+ * data_size - Upon success contains size of the data received from the
+ * service.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ virtual status_t receiveMessage(void** data, size_t* data_size);
+
+ /* Sends a query, and receives a response from the service.
+ * Param:
+ * query - Query to send to the service. When this method returns, the query
+ * is completed, and all its relevant data members are properly initialized.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure. Note that
+ * status returned here is not the final query status. Use isQuerySucceeded(),
+ * or getCompletionStatus() method on the query object to see if it has
+ * succeeded. However, if this method returns a failure, it means that the
+ * query has failed, and there is no guarantee that its data members are
+ * properly initialized (except for the 'mQueryDeliveryStatus', which is
+ * always in the proper state).
+ */
+ virtual status_t doQuery(QemuQuery* query);
+
+ /****************************************************************************
+ * Data members
+ ***************************************************************************/
+
+protected:
+ /* Qemu pipe handle. */
+ int mPipeFD;
+
+private:
+ /* Camera service name. */
+ static const char mCameraServiceName[];
+};
+
+/****************************************************************************
+ * Qemu client for the 'factory' service.
+ ***************************************************************************/
+
+/* Encapsulates QemuClient for the 'factory' service. */
+class FactoryQemuClient : public QemuClient {
+public:
+ /* Constructs FactoryQemuClient instance. */
+ FactoryQemuClient();
+
+ /* Destructs FactoryQemuClient instance. */
+ ~FactoryQemuClient();
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Lists camera devices connected to the host.
+ * Param:
+ * list - Upon success contains a list of cameras connected to the host. The
+ * list returned here is represented as a string, containing multiple
+ * lines separated with '\n', where each line represents a camera. Each
+ * camera line is formatted as such:
+ *
+ * "name=<device name> channel=<num> pix=<num> framedims=<dimensions>\n"
+ *
+ * Where:
+ * - 'name' is the name of the camera device attached to the host. This
+ * name must be used for subsequent connection to the 'emulated camera'
+ * service for that camera.
+ * - 'channel' - input channel number (positive int) to use to communicate
+ * with the camera.
+ * - 'pix' - pixel format (a "fourcc" uint), chosen for the video frames
+ * by the camera service.
+ * - 'framedims' contains a list of frame dimensions supported by the
+ * camera for the chosen pixel format. Each etry in the list is in form
+ * '<width>x<height>', where 'width' and 'height' are numeric values
+ * for width and height of a supported frame dimension. Entries in
+ * this list are separated with ',' with no spaces between the entries.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t listCameras(char** list);
+
+ /****************************************************************************
+ * Names of the queries available for the emulated camera factory.
+ ***************************************************************************/
+
+private:
+ /* List cameras connected to the host. */
+ static const char mQueryList[];
+};
+
+/****************************************************************************
+ * Qemu client for an 'emulated camera' service.
+ ***************************************************************************/
+
+/* Encapsulates QemuClient for an 'emulated camera' service.
+ */
+class CameraQemuClient : public QemuClient {
+public:
+ /* Constructs CameraQemuClient instance. */
+ CameraQemuClient();
+
+ /* Destructs CameraQemuClient instance. */
+ ~CameraQemuClient();
+
+ /****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+public:
+ /* Queries camera connection.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t queryConnect();
+
+ /* Queries camera disconnection.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t queryDisconnect();
+
+ /* Queries camera to start capturing video.
+ * Param:
+ * pixel_format - Pixel format that is used by the client to push video
+ * frames to the camera framework.
+ * width, height - Frame dimensions, requested by the framework.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t queryStart(uint32_t pixel_format, int width, int height);
+
+ /* Queries camera to stop capturing video.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t queryStop();
+
+ /* Queries camera for the next video frame.
+ * Param:
+ * vframe, vframe_size - Define buffer, allocated to receive a video frame.
+ * Any of these parameters can be 0, indicating that the caller is
+ * interested only in preview frame.
+ * pframe, pframe_size - Define buffer, allocated to receive a preview frame.
+ * Any of these parameters can be 0, indicating that the caller is
+ * interested only in video frame.
+ * Return:
+ * NO_ERROR on success, or an appropriate error status on failure.
+ */
+ status_t queryFrame(void* vframe,
+ void* pframe,
+ size_t vframe_size,
+ size_t pframe_size);
+
+ /****************************************************************************
+ * Names of the queries available for the emulated camera.
+ ***************************************************************************/
+
+private:
+ /* Connect to the camera. */
+ static const char mQueryConnect[];
+ /* Disconnect from the camera. */
+ static const char mQueryDisconnect[];
+ /* Start video capturing. */
+ static const char mQueryStart[];
+ /* Stop video capturing. */
+ static const char mQueryStop[];
+ /* Query frame(s). */
+ static const char mQueryFrame[];
+};
+
+}; /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_QEMU_CLIENT_H */
diff --git a/tools/emulator/system/camera/media_profiles.xml b/tools/emulator/system/camera/media_profiles.xml
new file mode 100644
index 0000000..ae1ce88
--- /dev/null
+++ b/tools/emulator/system/camera/media_profiles.xml
@@ -0,0 +1,400 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!DOCTYPE MediaSettings [
+<!ELEMENT MediaSettings (CamcorderProfiles,
+ EncoderOutputFileFormat+,
+ VideoEncoderCap+,
+ AudioEncoderCap+,
+ VideoDecoderCap,
+ AudioDecoderCap)>
+<!ELEMENT CamcorderProfiles (EncoderProfile+, ImageEncoding+, ImageDecoding, Camera)>
+<!ELEMENT EncoderProfile (Video, Audio)>
+<!ATTLIST EncoderProfile quality (high|low) #REQUIRED>
+<!ATTLIST EncoderProfile fileFormat (mp4|3gp) #REQUIRED>
+<!ATTLIST EncoderProfile duration (30|60) #REQUIRED>
+<!ATTLIST EncoderProfile cameraId (0|1) #REQUIRED>
+<!ELEMENT Video EMPTY>
+<!ATTLIST Video codec (h264|h263|m4v) #REQUIRED>
+<!ATTLIST Video bitRate CDATA #REQUIRED>
+<!ATTLIST Video width CDATA #REQUIRED>
+<!ATTLIST Video height CDATA #REQUIRED>
+<!ATTLIST Video frameRate CDATA #REQUIRED>
+<!ELEMENT Audio EMPTY>
+<!ATTLIST Audio codec (amrnb|amrwb|aac) #REQUIRED>
+<!ATTLIST Audio bitRate CDATA #REQUIRED>
+<!ATTLIST Audio sampleRate CDATA #REQUIRED>
+<!ATTLIST Audio channels (1|2) #REQUIRED>
+<!ELEMENT ImageEncoding EMPTY>
+<!ATTLIST ImageEncoding quality (90|80|70|60|50|40) #REQUIRED>
+<!ELEMENT ImageDecoding EMPTY>
+<!ATTLIST ImageDecoding memCap CDATA #REQUIRED>
+<!ELEMENT Camera EMPTY>
+<!ELEMENT EncoderOutputFileFormat EMPTY>
+<!ATTLIST EncoderOutputFileFormat name (mp4|3gp) #REQUIRED>
+<!ELEMENT VideoEncoderCap EMPTY>
+<!ATTLIST VideoEncoderCap name (h264|h263|m4v|wmv) #REQUIRED>
+<!ATTLIST VideoEncoderCap enabled (true|false) #REQUIRED>
+<!ATTLIST VideoEncoderCap minBitRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxBitRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameRate CDATA #REQUIRED>
+<!ELEMENT AudioEncoderCap EMPTY>
+<!ATTLIST AudioEncoderCap name (amrnb|amrwb|aac|wma) #REQUIRED>
+<!ATTLIST AudioEncoderCap enabled (true|false) #REQUIRED>
+<!ATTLIST AudioEncoderCap minBitRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap maxBitRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap minSampleRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap maxSampleRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap minChannels (1|2) #REQUIRED>
+<!ATTLIST AudioEncoderCap maxChannels (1|2) #REQUIRED>
+<!ELEMENT VideoDecoderCap EMPTY>
+<!ATTLIST VideoDecoderCap name (wmv) #REQUIRED>
+<!ATTLIST VideoDecoderCap enabled (true|false) #REQUIRED>
+<!ELEMENT AudioDecoderCap EMPTY>
+<!ATTLIST AudioDecoderCap name (wma) #REQUIRED>
+<!ATTLIST AudioDecoderCap enabled (true|false) #REQUIRED>
+<!ELEMENT VideoEditorCap EMPTY>
+<!ATTLIST VideoEditorCap maxInputFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxInputFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxOutputFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxOutputFrameHeight CDATA #REQUIRED>
+<!ELEMENT ExportVideoProfile EMPTY>
+<!ATTLIST ExportVideoProfile name (h264|h263|m4v) #REQUIRED>
+<!ATTLIST ExportVideoProfile profile CDATA #REQUIRED>
+<!ATTLIST ExportVideoProfile level CDATA #REQUIRED>
+]>
+<!--
+ This file is used to declare the multimedia profiles and capabilities
+ on an android-powered device.
+-->
+<MediaSettings>
+ <!-- Each camcorder profile defines a set of predefined configuration parameters -->
+ <CamcorderProfiles cameraId="0">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="1">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="2">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="3">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="4">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="5">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <CamcorderProfiles cameraId="6">
+
+ <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+ <Video codec="m4v"
+ bitRate="128000"
+ width="320"
+ height="240"
+ frameRate="15" />
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+ <Video codec="h264"
+ bitRate="192000"
+ width="176"
+ height="144"
+ frameRate="30" />
+ <!-- audio setting is ignored -->
+ <Audio codec="amrnb"
+ bitRate="12200"
+ sampleRate="8000"
+ channels="1" />
+ </EncoderProfile>
+
+ <ImageEncoding quality="95" />
+ <ImageEncoding quality="80" />
+ <ImageEncoding quality="70" />
+ <ImageDecoding memCap="20000000" />
+
+ </CamcorderProfiles>
+
+ <EncoderOutputFileFormat name="3gp" />
+ <EncoderOutputFileFormat name="mp4" />
+
+ <!--
+ If a codec is not enabled, it is invisible to the applications
+ In other words, the applications won't be able to use the codec
+ or query the capabilities of the codec at all if it is disabled
+ -->
+ <VideoEncoderCap name="h264" enabled="true"
+ minBitRate="64000" maxBitRate="192000"
+ minFrameWidth="176" maxFrameWidth="320"
+ minFrameHeight="144" maxFrameHeight="240"
+ minFrameRate="15" maxFrameRate="30" />
+
+ <VideoEncoderCap name="h263" enabled="true"
+ minBitRate="64000" maxBitRate="192000"
+ minFrameWidth="176" maxFrameWidth="320"
+ minFrameHeight="144" maxFrameHeight="240"
+ minFrameRate="15" maxFrameRate="30" />
+
+ <VideoEncoderCap name="m4v" enabled="true"
+ minBitRate="64000" maxBitRate="192000"
+ minFrameWidth="176" maxFrameWidth="320"
+ minFrameHeight="144" maxFrameHeight="240"
+ minFrameRate="15" maxFrameRate="30" />
+
+ <AudioEncoderCap name="aac" enabled="true"
+ minBitRate="8000" maxBitRate="96000"
+ minSampleRate="8000" maxSampleRate="48000"
+ minChannels="1" maxChannels="1" />
+
+ <AudioEncoderCap name="amrwb" enabled="true"
+ minBitRate="6600" maxBitRate="23050"
+ minSampleRate="16000" maxSampleRate="16000"
+ minChannels="1" maxChannels="1" />
+
+ <AudioEncoderCap name="amrnb" enabled="true"
+ minBitRate="5525" maxBitRate="12200"
+ minSampleRate="8000" maxSampleRate="8000"
+ minChannels="1" maxChannels="1" />
+
+ <!--
+ FIXME:
+ We do not check decoder capabilities at present
+ At present, we only check whether windows media is visible
+ for TEST applications. For other applications, we do
+ not perform any checks at all.
+ -->
+ <VideoDecoderCap name="wmv" enabled="false"/>
+ <AudioDecoderCap name="wma" enabled="false"/>
+ <VideoEditorCap maxInputFrameWidth="320"
+ maxInputFrameHeight="240" maxOutputFrameWidth="320"
+ maxOutputFrameHeight="240"/>
+ <!--
+ The VideoEditor Export codec profile and level values
+ correspond to the values in OMX_Video.h.
+ E.g. for h264, profile value 1 means OMX_VIDEO_AVCProfileBaseline
+ and level 4096 means OMX_VIDEO_AVCLevel41.
+ Please note that the values are in decimal.
+ These values are for video encoder.
+ -->
+ <!--
+ Codec = h.264, Baseline profile, level 4.1
+ -->
+ <ExportVideoProfile name="h264" profile= "1" level="512"/>
+ <!--
+ Codec = h.263, Baseline profile, level 0
+ -->
+ <ExportVideoProfile name="h263" profile= "1" level="1"/>
+ <!--
+ Codec = mpeg4, Simple profile, level 3
+ -->
+ <ExportVideoProfile name="m4v" profile= "1" level="16"/>
+</MediaSettings>
diff --git a/tools/idegen/src/IntelliJ.java b/tools/idegen/src/IntelliJ.java
index 00d731d..aa051ae 100644
--- a/tools/idegen/src/IntelliJ.java
+++ b/tools/idegen/src/IntelliJ.java
@@ -60,6 +60,19 @@
sourceRootsXml.append("<excludeFolder "
+ "url=\"file://$MODULE_DIR$/out/eclipse\"/>\n");
+ // Exclude some other directories that take a long time to scan.
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/.repo\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/external/bluetooth\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/external/chromium\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/external/icu4c\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/external/webkit\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/frameworks/base/docs\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/out/host\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/out/target/common/docs\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/out/target/product\"/>\n");
+ sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/prebuilt\"/>\n");
+
StringBuilder jarsXml = new StringBuilder();
for (File jar : c.jarFiles) {
jarsXml.append("<orderEntry type=\"module-library\">"
@@ -86,4 +99,4 @@
return path.toUpperCase().contains("TEST");
}
-}
\ No newline at end of file
+}
diff --git a/tools/templates/AndroidManifest.template b/tools/templates/AndroidManifest.template
new file mode 100644
index 0000000..4d1e053
--- /dev/null
+++ b/tools/templates/AndroidManifest.template
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="PACKAGE"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:label="@string/app_name" ICON>
+ <activity android:name="ACTIVITY_ENTRY_NAME"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tools/templates/AndroidManifest.tests.template b/tools/templates/AndroidManifest.tests.template
new file mode 100644
index 0000000..c74ff6d
--- /dev/null
+++ b/tools/templates/AndroidManifest.tests.template
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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="PACKAGE.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <!-- 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>
+ <!--
+ This declares that this application uses the instrumentation test runner targeting
+ the package of PACKAGE. To run the tests use the command:
+ "adb shell am instrument -w PACKAGE.tests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="PACKAGE"
+ android:label="Tests for PACKAGE"/>
+</manifest>
diff --git a/tools/templates/ic_launcher_hdpi.png b/tools/templates/ic_launcher_hdpi.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/tools/templates/ic_launcher_hdpi.png
Binary files differ
diff --git a/tools/templates/ic_launcher_ldpi.png b/tools/templates/ic_launcher_ldpi.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/tools/templates/ic_launcher_ldpi.png
Binary files differ
diff --git a/tools/templates/ic_launcher_mdpi.png b/tools/templates/ic_launcher_mdpi.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tools/templates/ic_launcher_mdpi.png
Binary files differ
diff --git a/tools/templates/java_file.template b/tools/templates/java_file.template
new file mode 100644
index 0000000..19714a8
--- /dev/null
+++ b/tools/templates/java_file.template
@@ -0,0 +1,15 @@
+package PACKAGE;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class ACTIVITY_CLASS_NAME extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/tools/templates/java_tests_file.template b/tools/templates/java_tests_file.template
new file mode 100644
index 0000000..08d6f9b
--- /dev/null
+++ b/tools/templates/java_tests_file.template
@@ -0,0 +1,21 @@
+package PACKAGE;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * This is a simple framework for a test of an Application. See
+ * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
+ * how to write and extend Application tests.
+ * <p/>
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class ACTIVITY_FQ_NAME \
+ * PACKAGE.tests/android.test.InstrumentationTestRunner
+ */
+public class ACTIVITY_CLASS_NAME extends ActivityInstrumentationTestCase2<ACTIVITY_TESTED_CLASS_NAME> {
+
+ public ACTIVITY_CLASS_NAME() {
+ super("PACKAGE", ACTIVITY_TESTED_CLASS_NAME.class);
+ }
+
+}
\ No newline at end of file
diff --git a/tools/templates/layout.template b/tools/templates/layout.template
new file mode 100644
index 0000000..7d62fbb
--- /dev/null
+++ b/tools/templates/layout.template
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Hello World, ACTIVITY_ENTRY_NAME"
+ />
+</LinearLayout>
+
diff --git a/tools/templates/strings.template b/tools/templates/strings.template
new file mode 100644
index 0000000..ee5af40
--- /dev/null
+++ b/tools/templates/strings.template
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">ACTIVITY_ENTRY_NAME</string>
+</resources>