Merge "Waveform member variables must be private"
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 7629c31..33f37f8 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -151,7 +151,7 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(com.android.internal.R.layout.preference_list_content,
+ return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
container, false);
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 3010178..dcac243 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -273,6 +273,11 @@
* @param accept TRUE if accept cookie
*/
public synchronized void setAcceptCookie(boolean accept) {
+ if (useChromiumHttpStack()) {
+ nativeSetAcceptCookie(accept);
+ return;
+ }
+
mAcceptCookie = accept;
}
@@ -281,6 +286,10 @@
* @return TRUE if accept cookie
*/
public synchronized boolean acceptCookie() {
+ if (useChromiumHttpStack()) {
+ return nativeAcceptCookie();
+ }
+
return mAcceptCookie;
}
@@ -418,6 +427,10 @@
* @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
*/
public String getCookie(String url) {
+ if (useChromiumHttpStack()) {
+ return nativeGetCookie(url);
+ }
+
WebAddress uri;
try {
uri = new WebAddress(url);
@@ -1035,5 +1048,8 @@
// Native functions
private static native boolean nativeUseChromiumHttpStack();
+ private static native boolean nativeAcceptCookie();
+ private static native String nativeGetCookie(String url);
private static native void nativeRemoveAllCookie();
+ private static native void nativeSetAcceptCookie(boolean accept);
}
diff --git a/core/res/res/layout-port/preference_header_item.xml b/core/res/res/layout-port/preference_header_item.xml
deleted file mode 100644
index cc76c8e..0000000
--- a/core/res/res/layout-port/preference_header_item.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layout of a header item in PreferenceActivity. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="96dp"
- android:background="?android:attr/activatedBackgroundIndicator"
- android:paddingRight="?android:attr/scrollbarSize">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center"
- android:paddingLeft="4dip"
- android:paddingRight="4dip"
- android:paddingTop="4dip"
- android:paddingBottom="4dip">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginBottom="2dip" />
-
- <TextView android:id="@+android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginBottom="2dip"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
-
- <TextView android:id="@+android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:ellipsize="end"
- android:maxLines="2" />
-
- </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/preference_header_item.xml b/core/res/res/layout/preference_header_item.xml
index aba7b2ad..f00e7aa 100644
--- a/core/res/res/layout/preference_header_item.xml
+++ b/core/res/res/layout/preference_header_item.xml
@@ -18,7 +18,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
+ android:minHeight="48dp"
android:background="?android:attr/activatedBackgroundIndicator"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 7c0b791..5b42407 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -21,7 +21,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
- android:layout_width="match_parent">
+ android:layout_width="match_parent"
+ android:background="@android:color/transparent">
<LinearLayout
android:orientation="horizontal"
@@ -34,6 +35,10 @@
android:orientation="vertical"
android:layout_width="0px"
android:layout_height="match_parent"
+ android:layout_marginLeft="32dp"
+ android:layout_marginTop="32dp"
+ android:layout_marginRight="16dp"
+ android:layout_marginBottom="32dp"
android:layout_weight="10">
<ListView android:id="@android:id/list"
@@ -41,6 +46,9 @@
android:layout_height="0px"
android:layout_weight="1"
android:drawSelectorOnTop="false"
+ android:background="#40404040"
+ android:cacheColorHint="@android:color/transparent"
+ android:listPreferredItemHeight="48dp"
android:scrollbarAlwaysDrawVerticalTrack="true" />
<FrameLayout android:id="@+id/list_footer"
@@ -53,7 +61,12 @@
<FrameLayout android:id="@+id/prefs"
android:layout_width="0px"
android:layout_height="match_parent"
- android:layout_weight="33"
+ android:layout_weight="25"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="#60202040"
android:visibility="gone" />
</LinearLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
new file mode 100644
index 0000000..c499b8a
--- /dev/null
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@android:color/transparent">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+ <RelativeLayout android:id="@+id/button_bar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="0"
+ android:background="@android:drawable/bottom_bar"
+ android:visibility="gone">
+
+ <Button android:id="@+id/back_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:layout_alignParentLeft="true"
+ android:drawableLeft="@drawable/ic_btn_back"
+ android:drawablePadding="3dip"
+ android:text="@string/back_button_label"
+ />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
+ </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 76a3c34..61167b5 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -408,7 +408,7 @@
<style name="Widget.TextView.ListSeparator">
<item name="android:background">@android:drawable/dark_header_dither</item>
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">25dip</item>
+ <item name="android:layout_height">wrap_content</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?textColorSecondary</item>
<item name="android:textSize">14sp</item>
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index d8d9eba..f019599 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -42,6 +42,10 @@
public void testEnable() {
int iterations = BluetoothTestRunner.sEnableIterations;
+ if (iterations == 0) {
+ return;
+ }
+
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
for (int i = 0; i < iterations; i++) {
@@ -53,6 +57,10 @@
public void testDiscoverable() {
int iterations = BluetoothTestRunner.sDiscoverableIterations;
+ if (iterations == 0) {
+ return;
+ }
+
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
mTestUtils.enable(adapter);
@@ -67,6 +75,10 @@
public void testScan() {
int iterations = BluetoothTestRunner.sScanIterations;
+ if (iterations == 0) {
+ return;
+ }
+
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
mTestUtils.enable(adapter);
@@ -78,4 +90,67 @@
mTestUtils.disable(adapter);
}
+
+ public void testPair() {
+ int iterations = BluetoothTestRunner.sPairIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sPairAddress);
+ mTestUtils.enable(adapter);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+ BluetoothTestRunner.sPairPin);
+ mTestUtils.unpair(adapter, device);
+ }
+ mTestUtils.disable(adapter);
+ }
+
+ public void testConnectA2dp() {
+ int iterations = BluetoothTestRunner.sConnectA2dpIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sA2dpAddress);
+ mTestUtils.enable(adapter);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+ BluetoothTestRunner.sPairPin);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP);
+ }
+
+ // TODO: Unpair from device if device can accept pairing after unpairing
+ mTestUtils.disable(adapter);
+ }
+
+ public void testConnectHeadset() {
+ int iterations = BluetoothTestRunner.sConnectHeadsetIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sHeadsetAddress);
+ mTestUtils.enable(adapter);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+ BluetoothTestRunner.sPairPin);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET);
+ }
+
+ // TODO: Unpair from device if device can accept pairing after unpairing
+ mTestUtils.disable(adapter);
+ }
}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
index cf0ff99..3e589fc 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
@@ -21,11 +21,24 @@
import android.os.Bundle;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
+import android.util.Log;
public class BluetoothTestRunner extends InstrumentationTestRunner {
+ private static final String TAG = "BluetoothTestRunner";
+
public static int sEnableIterations = 100;
public static int sDiscoverableIterations = 1000;
public static int sScanIterations = 1000;
+ public static int sPairIterations = 100;
+ public static int sConnectHeadsetIterations = 100;
+ public static int sConnectA2dpIterations = 100;
+
+ public static String sPairAddress = "";
+ public static String sHeadsetAddress = "";
+ public static String sA2dpAddress = "";
+
+ public static byte[] sPairPin = {'1', '2', '3', '4'};
+ public static int sPairPasskey = 123456;
@Override
public TestSuite getAllTests() {
@@ -41,8 +54,6 @@
@Override
public void onCreate(Bundle arguments) {
- super.onCreate(arguments);
-
String val = arguments.getString("enable_iterations");
if (val != null) {
try {
@@ -69,5 +80,76 @@
// Invalid argument, fall back to default value
}
}
+
+ val = arguments.getString("pair_iterations");
+ if (val != null) {
+ try {
+ sPairIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ val = arguments.getString("connect_a2dp_iterations");
+ if (val != null) {
+ try {
+ sConnectA2dpIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ val = arguments.getString("connect_headset_iterations");
+ if (val != null) {
+ try {
+ sConnectHeadsetIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ val = arguments.getString("pair_address");
+ if (val != null) {
+ sPairAddress = val;
+ }
+
+ val = arguments.getString("headset_address");
+ if (val != null) {
+ sHeadsetAddress = val;
+ }
+
+ val = arguments.getString("a2dp_address");
+ if (val != null) {
+ sA2dpAddress = val;
+ }
+
+ val = arguments.getString("pair_pin");
+ if (val != null) {
+ sPairPin = BluetoothDevice.convertPinToBytes(val);
+ }
+
+ val = arguments.getString("pair_passkey");
+ if (val != null) {
+ try {
+ sPairPasskey = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations));
+ Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations));
+ Log.i(TAG, String.format("scan_iterations=%d", sScanIterations));
+ Log.i(TAG, String.format("pair_iterations=%d", sPairIterations));
+ Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations));
+ Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations));
+ Log.i(TAG, String.format("pair_address=%s", sPairAddress));
+ Log.i(TAG, String.format("a2dp_address=%s", sA2dpAddress));
+ Log.i(TAG, String.format("headset_address=%s", sHeadsetAddress));
+ Log.i(TAG, String.format("pair_pin=%s", new String(sPairPin)));
+ Log.i(TAG, String.format("pair_passkey=%d", sPairPasskey));
+
+ // Call onCreate last since we want to set the static variables first.
+ super.onCreate(arguments);
}
}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
index cddf63d..328891c 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -29,6 +29,8 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
public class BluetoothTestUtils extends Assert {
@@ -57,73 +59,43 @@
*/
private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
- private static final int DISCOVERY_STARTED_FLAG = 1;
- private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
- private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
- private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
- private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
- private static final int STATE_OFF_FLAG = 1 << 5;
- private static final int STATE_TURNING_ON_FLAG = 1 << 6;
- private static final int STATE_ON_FLAG = 1 << 7;
- private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+ /**
+ * Timeout for {@link BluetoothDevice#createBond()} in ms.
+ */
+ private static final int PAIR_TIMEOUT = 20000;
+
+ /**
+ * Timeout for {@link BluetoothDevice#removeBond()} in ms.
+ */
+ private static final int UNPAIR_TIMEOUT = 20000;
+
+ /**
+ * Timeout for {@link BluetoothProfile#connect(BluetoothDevice)} in ms.
+ */
+ private static final int CONNECT_PROFILE_TIMEOUT = 20000;
+
+ /**
+ * Timeout for {@link BluetoothProfile#disconnect(BluetoothDevice)} in ms.
+ */
+ private static final int DISCONNECT_PROFILE_TIMEOUT = 20000;
+
+ /**
+ * Timeout to connect a profile proxy in ms.
+ */
+ private static final int CONNECT_PROXY_TIMEOUT = 5000;
/**
* Time between polls in ms.
*/
private static final int POLL_TIME = 100;
- private Context mContext;
-
- private BufferedWriter mOutputWriter;
-
- private String mOutputFile;
- private String mTag;
-
- private class BluetoothReceiver extends BroadcastReceiver {
+ private abstract class FlagReceiver extends BroadcastReceiver {
+ private int mExpectedFlags = 0;
private int mFiredFlags = 0;
+ private long mCompletedTime = -1;
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (this) {
- if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
- mFiredFlags |= DISCOVERY_STARTED_FLAG;
- } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
- mFiredFlags |= DISCOVERY_FINISHED_FLAG;
- } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
- int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
- BluetoothAdapter.ERROR);
- assertNotSame(mode, BluetoothAdapter.ERROR);
- switch (mode) {
- case BluetoothAdapter.SCAN_MODE_NONE:
- mFiredFlags |= SCAN_MODE_NONE_FLAG;
- break;
- case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
- mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
- break;
- case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
- mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
- break;
- }
- } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
- BluetoothAdapter.ERROR);
- assertNotSame(state, BluetoothAdapter.ERROR);
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- mFiredFlags |= STATE_OFF_FLAG;
- break;
- case BluetoothAdapter.STATE_TURNING_ON:
- mFiredFlags |= STATE_TURNING_ON_FLAG;
- break;
- case BluetoothAdapter.STATE_ON:
- mFiredFlags |= STATE_ON_FLAG;
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- mFiredFlags |= STATE_TURNING_OFF_FLAG;
- break;
- }
- }
- }
+ public FlagReceiver(int expectedFlags) {
+ mExpectedFlags = expectedFlags;
}
public int getFiredFlags() {
@@ -132,14 +104,232 @@
}
}
- public void resetFiredFlags() {
+ public long getCompletedTime() {
synchronized (this) {
- mFiredFlags = 0;
+ return mCompletedTime;
+ }
+ }
+
+ protected void setFiredFlag(int flag) {
+ synchronized (this) {
+ mFiredFlags |= flag;
+ if (mFiredFlags == mExpectedFlags) {
+ mCompletedTime = System.currentTimeMillis();
+ }
}
}
}
- private BluetoothReceiver mReceiver = new BluetoothReceiver();
+ private class BluetoothReceiver extends FlagReceiver {
+ private static final int DISCOVERY_STARTED_FLAG = 1;
+ private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
+ private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
+ private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
+ private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
+ private static final int STATE_OFF_FLAG = 1 << 5;
+ private static final int STATE_TURNING_ON_FLAG = 1 << 6;
+ private static final int STATE_ON_FLAG = 1 << 7;
+ private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+
+ public BluetoothReceiver(int expectedFlags) {
+ super(expectedFlags);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i("BT", intent.toString());
+ if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+ setFiredFlag(DISCOVERY_STARTED_FLAG);
+ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+ setFiredFlag(DISCOVERY_FINISHED_FLAG);
+ } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+ int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1);
+ assertNotSame(-1, mode);
+ switch (mode) {
+ case BluetoothAdapter.SCAN_MODE_NONE:
+ setFiredFlag(SCAN_MODE_NONE_FLAG);
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+ setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG);
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+ setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG);
+ break;
+ }
+ } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ assertNotSame(-1, state);
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ setFiredFlag(STATE_OFF_FLAG);
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ setFiredFlag(STATE_TURNING_ON_FLAG);
+ break;
+ case BluetoothAdapter.STATE_ON:
+ setFiredFlag(STATE_ON_FLAG);
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ setFiredFlag(STATE_TURNING_OFF_FLAG);
+ break;
+ }
+ }
+ }
+ }
+
+ private class PairReceiver extends FlagReceiver {
+ private static final int STATE_BONDED_FLAG = 1;
+ private static final int STATE_BONDING_FLAG = 1 << 1;
+ private static final int STATE_NONE_FLAG = 1 << 2;
+
+ private BluetoothDevice mDevice;
+ private int mPasskey;
+ private byte[] mPin;
+
+ public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) {
+ super(expectedFlags);
+
+ mDevice = device;
+ mPasskey = passkey;
+ mPin = pin;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+ return;
+ }
+
+ if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
+ int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1);
+ assertNotSame(-1, varient);
+ switch(varient) {
+ case BluetoothDevice.PAIRING_VARIANT_PIN:
+ mDevice.setPin(mPin);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+ mDevice.setPasskey(mPasskey);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+ case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+ mDevice.setPairingConfirmation(true);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+ mDevice.setRemoteOutOfBandData();
+ break;
+ }
+ } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+ int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+ assertNotSame(-1, state);
+ switch (state) {
+ case BluetoothDevice.BOND_NONE:
+ setFiredFlag(STATE_NONE_FLAG);
+ break;
+ case BluetoothDevice.BOND_BONDING:
+ setFiredFlag(STATE_BONDING_FLAG);
+ break;
+ case BluetoothDevice.BOND_BONDED:
+ setFiredFlag(STATE_BONDED_FLAG);
+ break;
+ }
+ }
+ }
+ }
+
+ private class ConnectProfileReceiver extends FlagReceiver {
+ private static final int STATE_DISCONNECTED_FLAG = 1;
+ private static final int STATE_CONNECTING_FLAG = 1 << 1;
+ private static final int STATE_CONNECTED_FLAG = 1 << 2;
+ private static final int STATE_DISCONNECTING_FLAG = 1 << 3;
+
+ private BluetoothDevice mDevice;
+ private int mProfile;
+ private String mConnectionAction;
+
+ public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) {
+ super(expectedFlags);
+
+ mDevice = device;
+ mProfile = profile;
+
+ switch(mProfile) {
+ case BluetoothProfile.A2DP:
+ mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED;
+ break;
+ case BluetoothProfile.HEADSET:
+ mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
+ break;
+ default:
+ mConnectionAction = null;
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) {
+ if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+ return;
+ }
+
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ assertNotSame(-1, state);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ setFiredFlag(STATE_DISCONNECTED_FLAG);
+ break;
+ case BluetoothProfile.STATE_CONNECTING:
+ setFiredFlag(STATE_CONNECTING_FLAG);
+ break;
+ case BluetoothProfile.STATE_CONNECTED:
+ setFiredFlag(STATE_CONNECTED_FLAG);
+ break;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ setFiredFlag(STATE_DISCONNECTING_FLAG);
+ break;
+ }
+ }
+ }
+ }
+
+ private BluetoothProfile.ServiceListener mServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ synchronized (this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = (BluetoothA2dp) proxy;
+ break;
+ case BluetoothProfile.HEADSET:
+ mHeadset = (BluetoothHeadset) proxy;
+ break;
+ }
+ }
+ }
+
+ public void onServiceDisconnected(int profile) {
+ synchronized (this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = null;
+ break;
+ case BluetoothProfile.HEADSET:
+ mHeadset = null;
+ break;
+ }
+ }
+ }
+ };
+
+ private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>();
+
+ private BufferedWriter mOutputWriter;
+ private String mTag;
+ private String mOutputFile;
+
+ private Context mContext;
+ private BluetoothA2dp mA2dp;
+ private BluetoothHeadset mHeadset;
public BluetoothTestUtils(Context context, String tag) {
this(context, tag, null);
@@ -161,17 +351,12 @@
mOutputWriter = null;
}
}
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
- filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- mContext.registerReceiver(mReceiver, filter);
}
public void close() {
- mContext.unregisterReceiver(mReceiver);
+ while (!mReceivers.isEmpty()) {
+ mContext.unregisterReceiver(mReceivers.remove(0));
+ }
if (mOutputWriter != null) {
try {
@@ -183,25 +368,30 @@
}
public void enable(BluetoothAdapter adapter) {
- int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG
+ | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG);
+ long start = -1;
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
int state = adapter.getState();
switch (state) {
case BluetoothAdapter.STATE_ON:
assertTrue(adapter.isEnabled());
+ removeReceiver(receiver);
return;
- case BluetoothAdapter.STATE_OFF:
- case BluetoothAdapter.STATE_TURNING_OFF:
- assertFalse(adapter.isEnabled());
- assertTrue(adapter.enable());
- break;
case BluetoothAdapter.STATE_TURNING_ON:
assertFalse(adapter.isEnabled());
mask = 0; // Don't check for received intents since we might have missed them.
break;
+ case BluetoothAdapter.STATE_OFF:
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ start = System.currentTimeMillis();
+ assertTrue(adapter.enable());
+ break;
default:
- fail("enable() invalid state: state=" + state);
+ removeReceiver(receiver);
+ fail(String.format("enable() invalid state: state=%d", state));
}
long s = System.currentTimeMillis();
@@ -209,10 +399,14 @@
state = adapter.getState();
if (state == BluetoothAdapter.STATE_ON) {
assertTrue(adapter.isEnabled());
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- writeOutput(String.format("enable() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("enable() completed in %d ms", (finish - start)));
+ } else {
+ writeOutput("enable() completed");
+ }
+ removeReceiver(receiver);
return;
}
} else {
@@ -222,27 +416,32 @@
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
state, BluetoothAdapter.STATE_ON, firedFlags, mask));
}
public void disable(BluetoothAdapter adapter) {
- int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG
+ | BluetoothReceiver.SCAN_MODE_NONE_FLAG);
+ long start = -1;
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
int state = adapter.getState();
switch (state) {
case BluetoothAdapter.STATE_OFF:
assertFalse(adapter.isEnabled());
+ removeReceiver(receiver);
return;
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- assertTrue(adapter.disable());
- break;
case BluetoothAdapter.STATE_TURNING_ON:
assertFalse(adapter.isEnabled());
+ start = System.currentTimeMillis();
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ start = System.currentTimeMillis();
assertTrue(adapter.disable());
break;
case BluetoothAdapter.STATE_TURNING_OFF:
@@ -250,7 +449,8 @@
mask = 0; // Don't check for received intents since we might have missed them.
break;
default:
- fail("disable() invalid state: state=" + state);
+ removeReceiver(receiver);
+ fail(String.format("disable() invalid state: state=%d", state));
}
long s = System.currentTimeMillis();
@@ -258,10 +458,15 @@
state = adapter.getState();
if (state == BluetoothAdapter.STATE_OFF) {
assertFalse(adapter.isEnabled());
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- writeOutput(String.format("disable() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("disable() completed in %d ms",
+ (finish - start)));
+ } else {
+ writeOutput("disable() completed");
+ }
+ removeReceiver(receiver);
return;
}
} else {
@@ -271,15 +476,14 @@
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
}
public void discoverable(BluetoothAdapter adapter) {
- int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
if (!adapter.isEnabled()) {
fail("discoverable() bluetooth not enabled");
@@ -290,35 +494,36 @@
return;
}
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
+ long start = System.currentTimeMillis();
assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
scanMode = adapter.getScanMode();
if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
+ if ((receiver.getFiredFlags() & mask) == mask) {
writeOutput(String.format("discoverable() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
return;
}
} else {
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
}
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
firedFlags, mask));
}
public void undiscoverable(BluetoothAdapter adapter) {
- int mask = SCAN_MODE_CONNECTABLE_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
if (!adapter.isEnabled()) {
fail("undiscoverable() bluetooth not enabled");
@@ -329,35 +534,36 @@
return;
}
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
+ long start = System.currentTimeMillis();
assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
scanMode = adapter.getScanMode();
if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
+ if ((receiver.getFiredFlags() & mask) == mask) {
writeOutput(String.format("undiscoverable() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
return;
}
} else {
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
}
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
mask));
}
public void startScan(BluetoothAdapter adapter) {
- int mask = DISCOVERY_STARTED_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG;
if (!adapter.isEnabled()) {
fail("startScan() bluetooth not enabled");
@@ -367,28 +573,29 @@
return;
}
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ long start = System.currentTimeMillis();
assertTrue(adapter.startDiscovery());
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
- if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
- mReceiver.resetFiredFlags();
+ while (System.currentTimeMillis() - start < START_DISCOVERY_TIMEOUT) {
+ if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
writeOutput(String.format("startScan() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
return;
}
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
adapter.isDiscovering(), firedFlags, mask));
}
public void stopScan(BluetoothAdapter adapter) {
- int mask = DISCOVERY_FINISHED_FLAG;
- mReceiver.resetFiredFlags();
+ int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG;
if (!adapter.isEnabled()) {
fail("stopScan() bluetooth not enabled");
@@ -398,27 +605,277 @@
return;
}
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ long start = System.currentTimeMillis();
// TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
adapter.cancelDiscovery();
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
- if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
- mReceiver.resetFiredFlags();
+ while (System.currentTimeMillis() - start < CANCEL_DISCOVERY_TIMEOUT) {
+ if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
writeOutput(String.format("stopScan() completed in %d ms",
- (System.currentTimeMillis() - s)));
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
return;
}
sleep(POLL_TIME);
}
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
adapter.isDiscovering(), firedFlags, mask));
}
+ public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) {
+ int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG;
+ long start = -1;
+
+ if (!adapter.isEnabled()) {
+ fail("pair() bluetooth not enabled");
+ }
+
+ PairReceiver receiver = getPairReceiver(device, passkey, pin, mask);
+
+ int state = device.getBondState();
+ switch (state) {
+ case BluetoothDevice.BOND_NONE:
+ assertFalse(adapter.getBondedDevices().contains(device));
+ start = System.currentTimeMillis();
+ assertTrue(device.createBond());
+ break;
+ case BluetoothDevice.BOND_BONDING:
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ case BluetoothDevice.BOND_BONDED:
+ assertTrue(adapter.getBondedDevices().contains(device));
+ return;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("pair() invalid state: device=%s, state=%d", device, state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < PAIR_TIMEOUT) {
+ state = device.getBondState();
+ if (state == BluetoothDevice.BOND_BONDED) {
+ assertTrue(adapter.getBondedDevices().contains(device));
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("pair() completed in %d ms: device=%s",
+ (finish - start), device));
+ } else {
+ writeOutput(String.format("pair() completed: device=%s", device));
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("pair() timeout: device=%s, state=%d (expected %d), "
+ + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+ firedFlags, mask));
+ }
+
+ public void unpair(BluetoothAdapter adapter, BluetoothDevice device) {
+ int mask = PairReceiver.STATE_NONE_FLAG;
+ long start = -1;
+
+ if (!adapter.isEnabled()) {
+ fail("unpair() bluetooth not enabled");
+ }
+
+ PairReceiver receiver = getPairReceiver(device, 0, null, mask);
+
+ int state = device.getBondState();
+ switch (state) {
+ case BluetoothDevice.BOND_NONE:
+ assertFalse(adapter.getBondedDevices().contains(device));
+ removeReceiver(receiver);
+ return;
+ case BluetoothDevice.BOND_BONDING:
+ start = System.currentTimeMillis();
+ assertTrue(device.removeBond());
+ break;
+ case BluetoothDevice.BOND_BONDED:
+ assertTrue(adapter.getBondedDevices().contains(device));
+ start = System.currentTimeMillis();
+ assertTrue(device.removeBond());
+ break;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("unpair() invalid state: device=%s, state=%d", device, state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < UNPAIR_TIMEOUT) {
+ if (device.getBondState() == BluetoothDevice.BOND_NONE) {
+ assertFalse(adapter.getBondedDevices().contains(device));
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("unpair() completed in %d ms: device=%s",
+ (finish - start), device));
+ } else {
+ writeOutput(String.format("unpair() completed: device=%s", device));
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ }
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("unpair() timeout: device=%s, state=%d (expected %d), "
+ + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+ firedFlags, mask));
+ }
+
+ public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+ int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG
+ | ConnectProfileReceiver.STATE_CONNECTED_FLAG);
+ long start = -1;
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("connectProfile() bluetooth not enabled: device=%s, profile=%d",
+ device, profile));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("connectProfile() device not paired: device=%s, profile=%d",
+ device, profile));
+ }
+
+ BluetoothProfile proxy = connectProxy(adapter, profile);
+ if (proxy == null) {
+ fail(String.format("connectProfile() unknown profile: device=%s, profile=%d",
+ device, profile));
+ }
+
+ ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+ int state = proxy.getConnectionState(device);
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTED:
+ removeReceiver(receiver);
+ return;
+ case BluetoothProfile.STATE_CONNECTING:
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ start = System.currentTimeMillis();
+ assertTrue(proxy.connect(device));
+ break;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("connectProfile() invalid state: device=%s, profile=%d, "
+ + "state=%d", device, profile, state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < CONNECT_PROFILE_TIMEOUT) {
+ state = proxy.getConnectionState(device);
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("connectProfile() completed in %d ms: "
+ +"device=%s, profile=%d", (finish - start), device, profile));
+ } else {
+ writeOutput(String.format("connectProfile() completed: device=%s, "
+ + "profile=%d", device, profile));
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("connectProfile() timeout: device=%s, profile=%s, "
+ + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+ BluetoothProfile.STATE_CONNECTED, firedFlags, mask));
+ }
+
+ public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+ int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG
+ | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG);
+ long start = -1;
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("disconnectProfile() bluetooth not enabled: device=%s, profile=%d",
+ device, profile));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("disconnectProfile() device not paired: device=%s, profile=%d",
+ device, profile));
+ }
+
+ BluetoothProfile proxy = connectProxy(adapter, profile);
+ if (proxy == null) {
+ fail(String.format("disconnectProfile() unknown profile: device=%s, profile=%d",
+ device, profile));
+ }
+
+ ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+ int state = proxy.getConnectionState(device);
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTED:
+ case BluetoothProfile.STATE_CONNECTING:
+ start = System.currentTimeMillis();
+ assertTrue(proxy.disconnect(device));
+ break;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ removeReceiver(receiver);
+ return;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("disconnectProfile() invalid state: device=%s, profile=%d, "
+ + "state=%d", device, profile, state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < DISCONNECT_PROFILE_TIMEOUT) {
+ state = proxy.getConnectionState(device);
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("disconnectProfile() completed in %d ms: "
+ +"device=%s, profile=%d", (finish - start), device, profile));
+ } else {
+ writeOutput(String.format("disconnectProfile() completed: device=%s, "
+ + "profile=%d", device, profile));
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("disconnectProfile() timeout: device=%s, profile=%s, "
+ + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+ BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask));
+ }
+
public void writeOutput(String s) {
Log.i(mTag, s);
if (mOutputWriter == null) {
@@ -432,6 +889,67 @@
}
}
+ private BluetoothReceiver getBluetoothReceiver(int expectedFlags) {
+ BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+ filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ mReceivers.add(receiver);
+ return receiver;
+ }
+
+ private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin,
+ int expectedFlags) {
+ PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ mReceivers.add(receiver);
+ return receiver;
+ }
+
+ private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile,
+ int expectedFlags) {
+ ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
+ expectedFlags);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ mContext.registerReceiver(receiver, filter);
+ mReceivers.add(receiver);
+ return receiver;
+ }
+
+ private void removeReceiver(BroadcastReceiver receiver) {
+ mContext.unregisterReceiver(receiver);
+ mReceivers.remove(receiver);
+ }
+
+ private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) {
+ adapter.getProfileProxy(mContext, mServiceListener, profile);
+ long s = System.currentTimeMillis();
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ while (mA2dp != null
+ && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+ sleep(POLL_TIME);
+ }
+ return mA2dp;
+ case BluetoothProfile.HEADSET:
+ while (mHeadset != null
+ && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+ sleep(POLL_TIME);
+ }
+ return mHeadset;
+ default:
+ return null;
+ }
+ }
+
private void sleep(long time) {
try {
Thread.sleep(time);
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 5505f14..c957dba 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -243,19 +243,22 @@
// derive capture scaling factor from peak value in current buffer
// this gives more interesting captures for display.
int32_t shift = 32;
- for (size_t i = 0; i < inBuffer->frameCount; i++) {
+ int len = inBuffer->frameCount * 2;
+ for (size_t i = 0; i < len; i++) {
int32_t smp = inBuffer->s16[i];
- if (smp < 0) smp = -smp;
+ if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
int32_t clz = __builtin_clz(smp);
if (shift > clz) shift = clz;
}
- // never scale by less than 8 to avoid returning unaltered PCM signal.
- // add one to combine the division by 2 needed after summing left and right channels below
- if (20 > shift) {
- shift = (31 - 8 + 1) - shift;
- } else {
- shift = (3 + 1);
+ // A maximum amplitude signal will have 17 leading zeros, which we want to
+ // translate to a shift of 8 (for converting 16 bit to 8 bit)
+ shift = 25 - shift;
+ // Never scale by less than 8 to avoid returning unaltered PCM signal.
+ if (shift < 3) {
+ shift = 3;
}
+ // add one to combine the division by 2 needed after summing left and right channels below
+ shift++;
uint32_t captIdx;
uint32_t inIdx;
@@ -264,7 +267,7 @@
inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
inIdx++, captIdx++) {
int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
- smp = (smp + (1 << (shift - 1))) >> shift;
+ smp = smp >> shift;
buf[captIdx] = ((uint8_t)smp)^0x80;
}
pContext->mCaptureIdx = captIdx;
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 27faf4f..092c33e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -281,7 +281,7 @@
mNumSyncSamples = U32_AT(&header[4]);
if (mNumSyncSamples < 2) {
- LOGW("Table of sync samples is empty or has only a single entry!");
+ LOGV("Table of sync samples is empty or has only a single entry!");
}
mSyncSamples = new uint32_t[mNumSyncSamples];
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 3908d71..b4ae593 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -300,6 +300,7 @@
if (duration <= 0){
assertTrue("stressRecordAndPlayback", false);
}
+ mp.release();
} catch (Exception e) {
assertTrue("stressRecordAndPlayback", false);
}
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 0d85404..8f2f974 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -536,7 +536,14 @@
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
if (mDetached && (mRenderer != null)) {
+ int renderMode = RENDERMODE_CONTINUOUSLY;
+ if (mGLThread != null) {
+ renderMode = mGLThread.getRenderMode();
+ }
mGLThread = new GLThread(mRenderer);
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
+ mGLThread.setRenderMode(renderMode);
+ }
mGLThread.start();
}
mDetached = false;
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
new file mode 100644
index 0000000..e74c22f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index dda86d2..65990ad 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -36,6 +36,7 @@
*/
final Object[] SERVICES = new Object[] {
R.string.config_statusBarComponent,
+ com.android.systemui.power.PowerUI.class,
};
/**
@@ -96,9 +97,19 @@
return;
}
- for (SystemUI ui: mServices) {
- pw.println("dumping service: " + ui.getClass().getName());
- ui.dump(fd, pw, args);
+ if (args == null || args.length == 0) {
+ for (SystemUI ui: mServices) {
+ pw.println("dumping service: " + ui.getClass().getName());
+ ui.dump(fd, pw, args);
+ }
+ } else {
+ String svc = args[0];
+ for (SystemUI ui: mServices) {
+ String name = ui.getClass().getName();
+ if (name.endsWith(svc)) {
+ ui.dump(fd, pw, args);
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
new file mode 100644
index 0000000..1a55830
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+public class PowerUI extends SystemUI {
+ static final String TAG = "PowerUI";
+
+ Handler mHandler = new Handler();
+
+ AlertDialog mLowBatteryDialog;
+ int mBatteryLevel;
+ TextView mBatteryLevelTextView;
+
+ public void start() {
+ // Register for Intent broadcasts for...
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+ }
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ mBatteryLevel = intent.getIntExtra("level", -1);
+ } else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+ showLowBatteryWarning();
+ } else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+ || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ }
+ } else {
+ Slog.w(TAG, "unknown intent: " + intent);
+ }
+ }
+ };
+
+ void showLowBatteryWarning() {
+ CharSequence levelText = mContext.getString(
+ R.string.battery_low_percent_format, mBatteryLevel);
+
+ if (mBatteryLevelTextView != null) {
+ mBatteryLevelTextView.setText(levelText);
+ } else {
+ View v = View.inflate(mContext, R.layout.battery_low, null);
+ mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
+
+ mBatteryLevelTextView.setText(levelText);
+
+ AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+ b.setCancelable(true);
+ b.setTitle(R.string.battery_low_title);
+ b.setView(v);
+ b.setIcon(android.R.drawable.ic_dialog_alert);
+ b.setPositiveButton(android.R.string.ok, null);
+
+ final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_HISTORY);
+ if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+ b.setNegativeButton(R.string.battery_low_why,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mContext.startActivity(intent);
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ }
+ }
+ });
+ }
+
+ AlertDialog d = b.create();
+ d.setOnDismissListener(mLowBatteryListener);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
+ mLowBatteryDialog = d;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.System.getInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) {
+ final String soundPath = Settings.System.getString(cr,
+ Settings.System.LOW_BATTERY_SOUND);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ if (sfx != null) {
+ sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.play();
+ }
+ }
+ }
+ }
+ }
+
+ private DialogInterface.OnDismissListener mLowBatteryListener
+ = new DialogInterface.OnDismissListener() {
+ public void onDismiss(DialogInterface dialog) {
+ mLowBatteryDialog = null;
+ mBatteryLevelTextView = null;
+ }
+ };
+
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (false) {
+ pw.println("args=" + Arrays.toString(args));
+ }
+ if (args == null || args.length == 0) {
+ pw.print("mLowBatteryDialog=");
+ pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
+ pw.print("mBatteryLevel=");
+ pw.println(Integer.toString(mBatteryLevel));
+ }
+
+ // DO NOT SUBMIT with this turned on.
+ if (false) {
+ if (args.length == 3 && "level".equals(args[1])) {
+ try {
+ final int level = Integer.parseInt(args[2]);
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra("level", level);
+ mIntentReceiver.onReceive(mContext, intent);
+ } catch (NumberFormatException ex) {
+ pw.println(ex);
+ }
+ } else if (args.length == 2 && "low".equals(args[1])) {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_LOW);
+ mIntentReceiver.onReceive(mContext, intent);
+ } else if (args.length == 2 && "ok".equals(args[1])) {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_OKAY);
+ mIntentReceiver.onReceive(mContext, intent);
+ }
+ }
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index fb74e70..a03a0ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -17,14 +17,11 @@
package com.android.systemui.statusbar.policy;
import android.app.StatusBarManager;
-import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothPbap;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
@@ -33,8 +30,6 @@
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -59,11 +54,8 @@
import android.util.Slog;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.TextView;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.IccCard;
@@ -95,24 +87,12 @@
private final Context mContext;
private final StatusBarManager mService;
- private final Handler mHandler = new StatusBarHandler();
+ private final Handler mHandler = new Handler();
private final IBatteryStats mBatteryStats;
// storage
private StorageManager mStorageManager;
- // battery
- private boolean mBatteryFirst = true;
- private boolean mBatteryPlugged;
- private int mBatteryLevel;
- private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
- private View mBatteryView;
- private int mBatteryViewSequence;
- private boolean mBatteryShowLowOnEndCall = false;
- private static final boolean SHOW_LOW_BATTERY_WARNING = true;
- private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
// phone
private TelephonyManager mPhone;
private int mPhoneSignalIconId;
@@ -357,13 +337,6 @@
else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
updateSyncState(intent);
}
- else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
- onBatteryLow(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_OKAY)
- || action.equals(Intent.ACTION_POWER_CONNECTED)) {
- onBatteryOkay(intent);
- }
else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
updateBluetooth(intent);
@@ -473,9 +446,6 @@
// Register for Intent broadcasts for...
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_ALARM_CHANGED);
filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -520,179 +490,6 @@
final int id = intent.getIntExtra("icon-small", 0);
int level = intent.getIntExtra("level", 0);
mService.setIcon("battery", id, level);
-
- boolean plugged = intent.getIntExtra("plugged", 0) != 0;
- level = intent.getIntExtra("level", -1);
- if (false) {
- Slog.d(TAG, "updateBattery level=" + level
- + " plugged=" + plugged
- + " mBatteryPlugged=" + mBatteryPlugged
- + " mBatteryLevel=" + mBatteryLevel
- + " mBatteryFirst=" + mBatteryFirst);
- }
-
- boolean oldPlugged = mBatteryPlugged;
-
- mBatteryPlugged = plugged;
- mBatteryLevel = level;
-
- if (mBatteryFirst) {
- mBatteryFirst = false;
- }
- /*
- * No longer showing the battery view because it draws attention away
- * from the USB storage notification. We could still show it when
- * connected to a brick, but that could lead to the user into thinking
- * the device does not charge when plugged into USB (since he/she would
- * not see the same battery screen on USB as he sees on brick).
- */
- if (false) {
- Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
- }
- }
-
- private void onBatteryLow(Intent intent) {
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
-
- if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private void onBatteryOkay(Intent intent) {
- if (mLowBatteryDialog != null
- && SHOW_LOW_BATTERY_WARNING) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
- }
-
- private void setBatteryLevel(View parent, int id, int height, int background, int level) {
- ImageView v = (ImageView)parent.findViewById(id);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
- lp.weight = height;
- if (background != 0) {
- v.setBackgroundResource(background);
- Drawable bkg = v.getBackground();
- bkg.setLevel(level);
- }
- }
-
- private void showLowBatteryWarning() {
- closeLastBatteryView();
-
- // Show exact battery level.
- CharSequence levelText = mContext.getString(
- R.string.battery_low_percent_format, mBatteryLevel);
-
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
- } else {
- View v = View.inflate(mContext, R.layout.battery_low, null);
- mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
- AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(R.string.battery_low_title);
- b.setView(v);
- b.setIcon(android.R.drawable.ic_dialog_alert);
- b.setPositiveButton(android.R.string.ok, null);
-
- final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_HISTORY);
- if (intent.resolveActivity(mContext.getPackageManager()) != null) {
- b.setNegativeButton(R.string.battery_low_why,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mContext.startActivity(intent);
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
- }
- });
- }
-
- AlertDialog d = b.create();
- d.setOnDismissListener(mLowBatteryListener);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- d.show();
- mLowBatteryDialog = d;
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.System.getInt(cr,
- Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
- {
- final String soundPath = Settings.System.getString(cr,
- Settings.System.LOW_BATTERY_SOUND);
- if (soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
- }
- }
-
- private final void updateCallState(int state) {
- mPhoneState = state;
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- if (mBatteryShowLowOnEndCall) {
- if (!mBatteryPlugged) {
- showLowBatteryWarning();
- }
- mBatteryShowLowOnEndCall = false;
- }
- } else {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private DialogInterface.OnDismissListener mLowBatteryListener
- = new DialogInterface.OnDismissListener() {
- public void onDismiss(DialogInterface dialog) {
- mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
- }
- };
-
- private void scheduleCloseBatteryView() {
- Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
- m.arg1 = (++mBatteryViewSequence);
- mHandler.sendMessageDelayed(m, 3000);
- }
-
- private void closeLastBatteryView() {
- if (mBatteryView != null) {
- //mBatteryView.debug();
- WindowManagerImpl.getDefault().removeView(mBatteryView);
- mBatteryView = null;
- }
}
private void updateConnectivity(Intent intent) {
@@ -753,7 +550,6 @@
@Override
public void onCallStateChanged(int state, String incomingNumber) {
- updateCallState(state);
// In cdma, if a voice call is made, RSSI should switch to 1x.
if (isCdma()) {
updateSignalStrength();
@@ -1181,18 +977,4 @@
}
mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
}
-
-
- private class StatusBarHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BATTERY_CLOSE:
- if (msg.arg1 == mBatteryViewSequence) {
- closeLastBatteryView();
- }
- break;
- }
- }
- }
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 32dd0d5..23bd5c8 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -120,10 +120,13 @@
private long mDischargeStartTime;
private int mDischargeStartLevel;
+ private Led mLed;
+
private boolean mSentLowBatteryBroadcast = false;
- public BatteryService(Context context) {
+ public BatteryService(Context context, LightsService lights) {
mContext = context;
+ mLed = new Led(context, lights);
mBatteryStats = BatteryStatsService.getService();
mLowBatteryWarningLevel = mContext.getResources().getInteger(
@@ -311,9 +314,9 @@
* (becomes <= mLowBatteryWarningLevel).
*/
final boolean sendBatteryLow = !plugged
- && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mBatteryLevel <= mLowBatteryWarningLevel
- && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryLevel <= mLowBatteryWarningLevel
+ && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
sendIntent();
@@ -341,6 +344,9 @@
mContext.sendBroadcast(statusIntent);
}
+ // Update the battery LED
+ mLed.updateLightsLocked();
+
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlier(dischargeDuration);
@@ -495,4 +501,66 @@
pw.println(" technology: " + mBatteryTechnology);
}
}
+
+ class Led {
+ private LightsService mLightsService;
+ private LightsService.Light mBatteryLight;
+
+ private int mBatteryLowARGB;
+ private int mBatteryMediumARGB;
+ private int mBatteryFullARGB;
+ private int mBatteryLedOn;
+ private int mBatteryLedOff;
+
+ private boolean mBatteryCharging;
+ private boolean mBatteryLow;
+ private boolean mBatteryFull;
+
+ Led(Context context, LightsService lights) {
+ mLightsService = lights;
+ mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
+
+ mBatteryLowARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+ mBatteryMediumARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+ mBatteryFullARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ mBatteryLedOn = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOn);
+ mBatteryLedOff = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOff);
+ }
+
+ /**
+ * Synchronize on BatteryService.
+ */
+ void updateLightsLocked() {
+ final int level = mBatteryLevel;
+ final int status = mBatteryStatus;
+ if (level < mLowBatteryWarningLevel) {
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+ // Solid red when battery is charging
+ mBatteryLight.setColor(mBatteryLowARGB);
+ } else {
+ // Flash red when battery is low and not charging
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
+ }
+ } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL) {
+ if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
+ // Solid green when full or charging and nearly full
+ mBatteryLight.setColor(mBatteryFullARGB);
+ } else {
+ // Solid orange when charging and halfway full
+ mBatteryLight.setColor(mBatteryMediumARGB);
+ }
+ } else {
+ // No lights if not charging and not low
+ mBatteryLight.turnOff();
+ }
+ }
+ }
}
+
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 8066fa7..1081a20 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -93,7 +93,6 @@
private WorkerHandler mHandler;
private StatusBarManagerService mStatusBar;
private LightsService mLightsService;
- private LightsService.Light mBatteryLight;
private LightsService.Light mNotificationLight;
private LightsService.Light mAttentionLight;
@@ -128,18 +127,8 @@
private ArrayList<ToastRecord> mToastQueue;
private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
-
- private boolean mBatteryCharging;
- private boolean mBatteryLow;
- private boolean mBatteryFull;
private NotificationRecord mLedNotification;
- private static int mBatteryLowARGB;
- private static int mBatteryMediumARGB;
- private static int mBatteryFullARGB;
- private static int mBatteryLedOn;
- private static int mBatteryLedOff;
-
private static String idDebugString(Context baseContext, String packageName, int id) {
Context c = null;
@@ -342,22 +331,7 @@
boolean queryRestart = false;
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
- int level = intent.getIntExtra("level", -1);
- boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
- int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
- boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
-
- if (batteryCharging != mBatteryCharging ||
- batteryLow != mBatteryLow ||
- batteryFull != mBatteryFull) {
- mBatteryCharging = batteryCharging;
- mBatteryLow = batteryLow;
- mBatteryFull = batteryFull;
- updateLights();
- }
- } else if (action.equals(Usb.ACTION_USB_STATE)) {
+ if (action.equals(Usb.ACTION_USB_STATE)) {
Bundle extras = intent.getExtras();
boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
@@ -435,7 +409,6 @@
{
super();
mContext = context;
- mLightsService = lights;
mAm = ActivityManagerNative.getDefault();
mSound = new NotificationPlayer(TAG);
mSound.setUsesWakeLock(context);
@@ -445,7 +418,6 @@
mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
- mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
@@ -457,17 +429,6 @@
mDefaultNotificationLedOff = resources.getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
- mBatteryLowARGB = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryLowARGB);
- mBatteryMediumARGB = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
- mBatteryFullARGB = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryFullARGB);
- mBatteryLedOn = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryLedOn);
- mBatteryLedOff = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryLedOff);
-
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -479,7 +440,6 @@
// register for battery changed notifications
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -1076,25 +1036,6 @@
// lock on mNotificationList
private void updateLightsLocked()
{
- // Battery low always shows, other states only show if charging.
- if (mBatteryLow) {
- if (mBatteryCharging) {
- mBatteryLight.setColor(mBatteryLowARGB);
- } else {
- // Flash when battery is low and not charging
- mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
- mBatteryLedOn, mBatteryLedOff);
- }
- } else if (mBatteryCharging) {
- if (mBatteryFull) {
- mBatteryLight.setColor(mBatteryFullARGB);
- } else {
- mBatteryLight.setColor(mBatteryMediumARGB);
- }
- } else {
- mBatteryLight.turnOff();
- }
-
// clear pending pulse notification if screen is on
if (mScreenOn || mLedNotification == null) {
mPendingPulseNotification = false;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 46797c5..237ab80 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -170,13 +170,13 @@
Slog.i(TAG, "System Content Providers");
ActivityManagerService.installSystemProviders();
- Slog.i(TAG, "Battery Service");
- battery = new BatteryService(context);
- ServiceManager.addService("battery", battery);
-
Slog.i(TAG, "Lights Service");
lights = new LightsService(context);
+ Slog.i(TAG, "Battery Service");
+ battery = new BatteryService(context, lights);
+ ServiceManager.addService("battery", battery);
+
Slog.i(TAG, "Vibrator Service");
ServiceManager.addService("vibrator", new VibratorService(context));
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 70140d8..d3bcb6c 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -6,7 +6,7 @@
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
- <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 1e1aba9..0dccc0d6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -18,13 +18,6 @@
import com.android.layoutlib.api.ILayoutLog;
-import android.graphics.DrawFilter;
-import android.graphics.Picture;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Xfermode;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontInfo;
import android.graphics.Paint.Style;
@@ -42,8 +35,6 @@
import java.util.List;
import java.util.Stack;
-import javax.microedition.khronos.opengles.GL;
-
/**
* Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
*/
@@ -509,7 +500,7 @@
// get the Graphics2D current matrix
AffineTransform currentTx = g.getTransform();
// get the AffineTransform from the matrix
- AffineTransform matrixTx = matrix.getTransform();
+ AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
// combine them so that the matrix is applied after.
currentTx.preConcatenate(matrixTx);
@@ -969,9 +960,9 @@
Graphics2D g = getGraphics2d();
// and apply the matrix
- g.setTransform(matrix.getTransform());
+ g.setTransform(Matrix_Delegate.getAffineTransform(matrix));
- if (mLogger != null && matrix.hasPerspective()) {
+ if (mLogger != null && Matrix_Delegate.hasPerspective(matrix)) {
mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
}
}
@@ -987,7 +978,7 @@
// get its current matrix
AffineTransform currentTx = g.getTransform();
// get the AffineTransform of the given matrix
- AffineTransform matrixTx = matrix.getTransform();
+ AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
// combine them so that the given matrix is applied after.
currentTx.preConcatenate(matrixTx);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
deleted file mode 100644
index 9e30671..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-
-
-/**
- * A matrix implementation overridden by the LayoutLib bridge.
- */
-public class Matrix extends _Original_Matrix {
-
- float mValues[] = new float[9];
-
- /**
- * Create an identity matrix
- */
- public Matrix() {
- reset();
- }
-
- /**
- * Create a matrix that is a (deep) copy of src
- * @param src The matrix to copy into this matrix
- */
- public Matrix(Matrix src) {
- set(src);
- }
-
- /**
- * Creates a Matrix object from the float array. The array becomes the internal storage
- * of the object.
- * @param data
- */
- private Matrix(float[] data) {
- assert data.length != 9;
- mValues = data;
- }
-
- //---------- Custom Methods
-
- /**
- * Adds the given transformation to the current Matrix
- * <p/>This in effect does this = this*matrix
- * @param matrix
- */
- private void addTransform(float[] matrix) {
- float[] tmp = new float[9];
-
- // first row
- tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
- tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
- tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
-
- // 2nd row
- tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
- tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
- tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
-
- // 3rd row
- tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
- tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
- tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
-
- // copy the result over to mValues
- mValues = tmp;
- }
-
- public AffineTransform getTransform() {
- // the AffineTransform constructor takes the value in a different order
- // for a matrix [ 0 1 2 ]
- // [ 3 4 5 ]
- // the order is 0, 3, 1, 4, 2, 5...
- return new AffineTransform(mValues[0], mValues[3], mValues[1],
- mValues[4], mValues[2], mValues[5]);
- }
-
- public boolean hasPerspective() {
- return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
- }
-
- //----------
-
- /**
- * Returns true if the matrix is identity.
- * This maybe faster than testing if (getType() == 0)
- */
- @Override
- public boolean isIdentity() {
- for (int i = 0, k = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++, k++) {
- if (mValues[k] != ((i==j) ? 1 : 0)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if will map a rectangle to another rectangle. This can be
- * true if the matrix is identity, scale-only, or rotates a multiple of 90
- * degrees.
- */
- @Override
- public boolean rectStaysRect() {
- return (computeTypeMask() & kRectStaysRect_Mask) != 0;
- }
-
- /**
- * (deep) copy the src matrix into this matrix. If src is null, reset this
- * matrix to the identity matrix.
- */
- public void set(Matrix src) {
- if (src == null) {
- reset();
- } else {
- System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
- }
- }
-
- @Override
- public void set(_Original_Matrix src) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /** Returns true if obj is a Matrix and its values equal our values.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj != null && obj instanceof Matrix) {
- Matrix matrix = (Matrix)obj;
- for (int i = 0 ; i < 9 ; i++) {
- if (mValues[i] != matrix.mValues[i]) {
- return false;
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- /** Set the matrix to identity */
- @Override
- public void reset() {
- for (int i = 0, k = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++, k++) {
- mValues[k] = ((i==j) ? 1 : 0);
- }
- }
- }
-
- /** Set the matrix to translate by (dx, dy). */
- @Override
- public void setTranslate(float dx, float dy) {
- mValues[0] = 1;
- mValues[1] = 0;
- mValues[2] = dx;
- mValues[3] = 0;
- mValues[4] = 1;
- mValues[5] = dy;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
- }
-
- /**
- * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
- */
- @Override
- public void setScale(float sx, float sy, float px, float py) {
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- mValues[0] = 1;
- mValues[1] = 0;
- mValues[2] = -px;
- mValues[3] = 0;
- mValues[4] = 1;
- mValues[5] = -py;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
-
- // scale
- addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
- }
-
- /** Set the matrix to scale by sx and sy. */
- @Override
- public void setScale(float sx, float sy) {
- mValues[0] = sx;
- mValues[1] = 0;
- mValues[2] = 0;
- mValues[3] = 0;
- mValues[4] = sy;
- mValues[5] = 0;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
- }
-
- /**
- * Set the matrix to rotate by the specified number of degrees, with a pivot
- * point at (px, py). The pivot point is the coordinate that should remain
- * unchanged by the specified transformation.
- */
- @Override
- public void setRotate(float degrees, float px, float py) {
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- mValues[0] = 1;
- mValues[1] = 0;
- mValues[2] = -px;
- mValues[3] = 0;
- mValues[4] = 1;
- mValues[5] = -py;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
-
- // scale
- double rad = Math.toRadians(degrees);
- float cos = (float)Math.cos(rad);
- float sin = (float)Math.sin(rad);
- addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
- }
-
- /**
- * Set the matrix to rotate about (0,0) by the specified number of degrees.
- */
- @Override
- public void setRotate(float degrees) {
- double rad = Math.toRadians(degrees);
- float cos = (float)Math.cos(rad);
- float sin = (float)Math.sin(rad);
-
- mValues[0] = cos;
- mValues[1] = -sin;
- mValues[2] = 0;
- mValues[3] = sin;
- mValues[4] = cos;
- mValues[5] = 0;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
- }
-
- /**
- * Set the matrix to rotate by the specified sine and cosine values, with a
- * pivot point at (px, py). The pivot point is the coordinate that should
- * remain unchanged by the specified transformation.
- */
- @Override
- public void setSinCos(float sinValue, float cosValue, float px, float py) {
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- mValues[0] = 1;
- mValues[1] = 0;
- mValues[2] = -px;
- mValues[3] = 0;
- mValues[4] = 1;
- mValues[5] = -py;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
-
- // scale
- addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
- }
-
- /** Set the matrix to rotate by the specified sine and cosine values. */
- @Override
- public void setSinCos(float sinValue, float cosValue) {
- mValues[0] = cosValue;
- mValues[1] = -sinValue;
- mValues[2] = 0;
- mValues[3] = sinValue;
- mValues[4] = cosValue;
- mValues[5] = 0;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
- }
-
- /**
- * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
- */
- @Override
- public void setSkew(float kx, float ky, float px, float py) {
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- mValues[0] = 1;
- mValues[1] = 0;
- mValues[2] = -px;
- mValues[3] = 0;
- mValues[4] = 1;
- mValues[5] = -py;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
-
- // scale
- addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
- }
-
- /** Set the matrix to skew by sx and sy. */
- @Override
- public void setSkew(float kx, float ky) {
- mValues[0] = 1;
- mValues[1] = kx;
- mValues[2] = -0;
- mValues[3] = ky;
- mValues[4] = 1;
- mValues[5] = 0;
- mValues[6] = 0;
- mValues[7] = 0;
- mValues[8] = 1;
- }
-
- /**
- * Set the matrix to the concatenation of the two specified matrices,
- * returning true if the the result can be represented. Either of the two
- * matrices may also be the target matrix. this = a * b
- */
- public boolean setConcat(Matrix a, Matrix b) {
- if (a == this) {
- preConcat(b);
- } else if (b == this) {
- postConcat(b);
- } else {
- Matrix tmp = new Matrix(b);
- tmp.addTransform(a.mValues);
- set(tmp);
- }
-
- return true;
- }
-
- @Override
- public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /**
- * Preconcats the matrix with the specified translation.
- * M' = M * T(dx, dy)
- */
- @Override
- public boolean preTranslate(float dx, float dy) {
- // create a matrix that will be multiply by this
- Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
- m.addTransform(this.mValues);
-
- System.arraycopy(m.mValues, 0, mValues, 0, 9);
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy, px, py)
- */
- @Override
- public boolean preScale(float sx, float sy, float px, float py) {
- Matrix m = new Matrix();
- m.setScale(sx, sy, px, py);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy)
- */
- @Override
- public boolean preScale(float sx, float sy) {
- Matrix m = new Matrix();
- m.setScale(sx, sy);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees, px, py)
- */
- @Override
- public boolean preRotate(float degrees, float px, float py) {
- Matrix m = new Matrix();
- m.setRotate(degrees, px, py);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees)
- */
- @Override
- public boolean preRotate(float degrees) {
- Matrix m = new Matrix();
- m.setRotate(degrees);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky, px, py)
- */
- @Override
- public boolean preSkew(float kx, float ky, float px, float py) {
- Matrix m = new Matrix();
- m.setSkew(kx, ky, px, py);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky)
- */
- @Override
- public boolean preSkew(float kx, float ky) {
- Matrix m = new Matrix();
- m.setSkew(kx, ky);
- m.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- /**
- * Preconcats the matrix with the specified matrix.
- * M' = M * other
- */
- public boolean preConcat(Matrix other) {
- Matrix m = new Matrix(other);
- other.addTransform(mValues);
- set(m);
-
- return true;
- }
-
- @Override
- public boolean preConcat(_Original_Matrix other) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /**
- * Postconcats the matrix with the specified translation.
- * M' = T(dx, dy) * M
- */
- @Override
- public boolean postTranslate(float dx, float dy) {
- addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy, px, py) * M
- */
- @Override
- public boolean postScale(float sx, float sy, float px, float py) {
- // TODO: do it in one pass
- // translate so that the pivot is in 0,0
- addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
- // scale
- addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy) * M
- */
- @Override
- public boolean postScale(float sx, float sy) {
- addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees, px, py) * M
- */
- @Override
- public boolean postRotate(float degrees, float px, float py) {
- // TODO: do it in one pass
- // translate so that the pivot is in 0,0
- addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
- // scale
- double rad = Math.toRadians(degrees);
- float cos = (float)Math.cos(rad);
- float sin = (float)Math.sin(rad);
- addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees) * M
- */
- @Override
- public boolean postRotate(float degrees) {
- double rad = Math.toRadians(degrees);
- float cos = (float)Math.cos(rad);
- float sin = (float)Math.sin(rad);
- addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky, px, py) * M
- */
- @Override
- public boolean postSkew(float kx, float ky, float px, float py) {
- // TODO: do it in one pass
- // translate so that the pivot is in 0,0
- addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
- // scale
- addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
- // translate back the pivot
- addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky) * M
- */
- @Override
- public boolean postSkew(float kx, float ky) {
- addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-
- return true;
- }
-
- /**
- * Postconcats the matrix with the specified matrix.
- * M' = other * M
- */
- public boolean postConcat(Matrix other) {
- addTransform(other.mValues);
-
- return true;
- }
-
- @Override
- public boolean postConcat(_Original_Matrix other) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /** Controlls how the src rect should align into the dst rect for
- setRectToRect().
- */
- public enum ScaleToFit {
- /**
- * Scale in X and Y independently, so that src matches dst exactly.
- * This may change the aspect ratio of the src.
- */
- FILL (0),
- /**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. START aligns the result to the
- * left and top edges of dst.
- */
- START (1),
- /**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. The result is centered inside dst.
- */
- CENTER (2),
- /**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. END aligns the result to the
- * right and bottom edges of dst.
- */
- END (3);
-
- // the native values must match those in SkMatrix.h
- ScaleToFit(int nativeInt) {
- this.nativeInt = nativeInt;
- }
- final int nativeInt;
- }
-
- /**
- * Set the matrix to the scale and translate values that map the source
- * rectangle to the destination rectangle, returning true if the result
- * can be represented.
- *
- * @param src the source rectangle to map from.
- * @param dst the destination rectangle to map to.
- * @param stf the ScaleToFit option
- * @return true if the matrix can be represented by the rectangle mapping.
- */
- public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
- if (dst == null || src == null) {
- throw new NullPointerException();
- }
-
- if (src.isEmpty()) {
- reset();
- return false;
- }
-
- if (dst.isEmpty()) {
- mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
- = mValues[6] = mValues[7] = 0;
- mValues[8] = 1;
- } else {
- float tx, sx = dst.width() / src.width();
- float ty, sy = dst.height() / src.height();
- boolean xLarger = false;
-
- if (stf != ScaleToFit.FILL) {
- if (sx > sy) {
- xLarger = true;
- sx = sy;
- } else {
- sy = sx;
- }
- }
-
- tx = dst.left - src.left * sx;
- ty = dst.top - src.top * sy;
- if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
- float diff;
-
- if (xLarger) {
- diff = dst.width() - src.width() * sy;
- } else {
- diff = dst.height() - src.height() * sy;
- }
-
- if (stf == ScaleToFit.CENTER) {
- diff = diff / 2;
- }
-
- if (xLarger) {
- tx += diff;
- } else {
- ty += diff;
- }
- }
-
- mValues[0] = sx;
- mValues[4] = sy;
- mValues[2] = tx;
- mValues[5] = ty;
- mValues[1] = mValues[3] = mValues[6] = mValues[7] = 0;
-
- }
- // shared cleanup
- mValues[8] = 1;
- return true;
- }
-
- @Override
- public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /**
- * Set the matrix such that the specified src points would map to the
- * specified dst points. The "points" are represented as an array of floats,
- * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
- *
- * @param src The array of src [x,y] pairs (points)
- * @param srcIndex Index of the first pair of src values
- * @param dst The array of dst [x,y] pairs (points)
- * @param dstIndex Index of the first pair of dst values
- * @param pointCount The number of pairs/points to be used. Must be [0..4]
- * @return true if the matrix was set to the specified transformation
- */
- @Override
- public boolean setPolyToPoly(float[] src, int srcIndex,
- float[] dst, int dstIndex,
- int pointCount) {
- if (pointCount > 4) {
- throw new IllegalArgumentException();
- }
- checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
- throw new UnsupportedOperationException("STUB NEEDED");
- }
-
- /**
- * If this matrix can be inverted, return true and if inverse is not null,
- * set inverse to be the inverse of this matrix. If this matrix cannot be
- * inverted, ignore inverse and return false.
- */
- public boolean invert(Matrix inverse) {
- if (inverse == null) {
- return false;
- }
-
- try {
- AffineTransform affineTransform = getTransform();
- AffineTransform inverseTransform = affineTransform.createInverse();
- inverse.mValues[0] = (float)inverseTransform.getScaleX();
- inverse.mValues[1] = (float)inverseTransform.getShearX();
- inverse.mValues[2] = (float)inverseTransform.getTranslateX();
- inverse.mValues[3] = (float)inverseTransform.getScaleX();
- inverse.mValues[4] = (float)inverseTransform.getShearY();
- inverse.mValues[5] = (float)inverseTransform.getTranslateY();
-
- return true;
- } catch (NoninvertibleTransformException e) {
- return false;
- }
- }
-
- @Override
- public boolean invert(_Original_Matrix inverse) {
- throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
- }
-
- /**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
- *
- * @param dst The array of dst points (x,y pairs)
- * @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src points (x,y pairs)
- * @param srcIndex The index of the first [x,y] pair of src floats
- * @param pointCount The number of points (x,y pairs) to transform
- */
- @Override
- public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
- int pointCount) {
- checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-
- for (int i = 0 ; i < pointCount ; i++) {
- // just in case we are doing in place, we better put this in temp vars
- float x = mValues[0] * src[i + srcIndex] +
- mValues[1] * src[i + srcIndex + 1] +
- mValues[2];
- float y = mValues[3] * src[i + srcIndex] +
- mValues[4] * src[i + srcIndex + 1] +
- mValues[5];
-
- dst[i + dstIndex] = x;
- dst[i + dstIndex + 1] = y;
- }
- }
-
- /**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
- *
- * @param dst The array of dst vectors (x,y pairs)
- * @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src vectors (x,y pairs)
- * @param srcIndex The index of the first [x,y] pair of src floats
- * @param vectorCount The number of vectors (x,y pairs) to transform
- */
- @Override
- public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
- int vectorCount) {
- checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
- throw new UnsupportedOperationException("STUB NEEDED");
- }
-
- /**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
- *
- * @param dst The array of dst points (x,y pairs)
- * @param src The array of src points (x,y pairs)
- */
- @Override
- public void mapPoints(float[] dst, float[] src) {
- if (dst.length != src.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- mapPoints(dst, 0, src, 0, dst.length >> 1);
- }
-
- /**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
- *
- * @param dst The array of dst vectors (x,y pairs)
- * @param src The array of src vectors (x,y pairs)
- */
- @Override
- public void mapVectors(float[] dst, float[] src) {
- if (dst.length != src.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- mapVectors(dst, 0, src, 0, dst.length >> 1);
- }
-
- /**
- * Apply this matrix to the array of 2D points, and write the transformed
- * points back into the array
- *
- * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
- */
- @Override
- public void mapPoints(float[] pts) {
- mapPoints(pts, 0, pts, 0, pts.length >> 1);
- }
-
- /**
- * Apply this matrix to the array of 2D vectors, and write the transformed
- * vectors back into the array.
- * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
- */
- @Override
- public void mapVectors(float[] vecs) {
- mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
- }
-
- /**
- * Apply this matrix to the src rectangle, and write the transformed
- * rectangle into dst. This is accomplished by transforming the 4 corners of
- * src, and then setting dst to the bounds of those points.
- *
- * @param dst Where the transformed rectangle is written.
- * @param src The original rectangle to be transformed.
- * @return the result of calling rectStaysRect()
- */
- @Override
- public boolean mapRect(RectF dst, RectF src) {
- if (dst == null || src == null) {
- throw new NullPointerException();
- }
-
- // array with 4 corners
- float[] corners = new float[] {
- src.left, src.top,
- src.right, src.top,
- src.right, src.bottom,
- src.left, src.bottom,
- };
-
- // apply the transform to them.
- mapPoints(corners);
-
- // now put the result in the rect. We take the min/max of Xs and min/max of Ys
- dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
- dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
- dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
- dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
- return rectStaysRect();
- }
-
- /**
- * Apply this matrix to the rectangle, and write the transformed rectangle
- * back into it. This is accomplished by transforming the 4 corners of rect,
- * and then setting it to the bounds of those points
- *
- * @param rect The rectangle to transform.
- * @return the result of calling rectStaysRect()
- */
- @Override
- public boolean mapRect(RectF rect) {
- return mapRect(rect, rect);
- }
-
- /**
- * Return the mean radius of a circle after it has been mapped by
- * this matrix. NOTE: in perspective this value assumes the circle
- * has its center at the origin.
- */
- @Override
- public float mapRadius(float radius) {
- throw new UnsupportedOperationException("STUB NEEDED");
- }
-
- /** Copy 9 values from the matrix into the array.
- */
- @Override
- public void getValues(float[] values) {
- if (values.length < 9) {
- throw new ArrayIndexOutOfBoundsException();
- }
- System.arraycopy(mValues, 0, values, 0, mValues.length);
- }
-
- /** Copy 9 values from the array into the matrix.
- Depending on the implementation of Matrix, these may be
- transformed into 16.16 integers in the Matrix, such that
- a subsequent call to getValues() will not yield exactly
- the same values.
- */
- @Override
- public void setValues(float[] values) {
- if (values.length < 9) {
- throw new ArrayIndexOutOfBoundsException();
- }
- System.arraycopy(values, 0, mValues, 0, mValues.length);
- }
-
- @SuppressWarnings("unused")
- private final static int kIdentity_Mask = 0;
- private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation
- private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale
- private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates
- private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective
- private final static int kRectStaysRect_Mask = 0x10;
- @SuppressWarnings("unused")
- private final static int kUnknown_Mask = 0x80;
-
- @SuppressWarnings("unused")
- private final static int kAllMasks = kTranslate_Mask |
- kScale_Mask |
- kAffine_Mask |
- kPerspective_Mask |
- kRectStaysRect_Mask;
-
- // these guys align with the masks, so we can compute a mask from a variable 0/1
- @SuppressWarnings("unused")
- private final static int kTranslate_Shift = 0;
- @SuppressWarnings("unused")
- private final static int kScale_Shift = 1;
- @SuppressWarnings("unused")
- private final static int kAffine_Shift = 2;
- @SuppressWarnings("unused")
- private final static int kPerspective_Shift = 3;
- private final static int kRectStaysRect_Shift = 4;
-
- private int computeTypeMask() {
- int mask = 0;
-
- if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
- mask |= kPerspective_Mask;
- }
-
- if (mValues[2] != 0. || mValues[5] != 0.) {
- mask |= kTranslate_Mask;
- }
-
- float m00 = mValues[0];
- float m01 = mValues[1];
- float m10 = mValues[3];
- float m11 = mValues[4];
-
- if (m01 != 0. || m10 != 0.) {
- mask |= kAffine_Mask;
- }
-
- if (m00 != 1. || m11 != 1.) {
- mask |= kScale_Mask;
- }
-
- if ((mask & kPerspective_Mask) == 0) {
- // map non-zero to 1
- int im00 = m00 != 0 ? 1 : 0;
- int im01 = m01 != 0 ? 1 : 0;
- int im10 = m10 != 0 ? 1 : 0;
- int im11 = m11 != 0 ? 1 : 0;
-
- // record if the (p)rimary and (s)econdary diagonals are all 0 or
- // all non-zero (answer is 0 or 1)
- int dp0 = (im00 | im11) ^ 1; // true if both are 0
- int dp1 = im00 & im11; // true if both are 1
- int ds0 = (im01 | im10) ^ 1; // true if both are 0
- int ds1 = im01 & im10; // true if both are 1
-
- // return 1 if primary is 1 and secondary is 0 or
- // primary is 0 and secondary is 1
- mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
- }
-
- return mask;
- }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
new file mode 100644
index 0000000..ed2eff2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Matrix.ScaleToFit;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Matrix
+ *
+ * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Matrix class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Matrix_Delegate {
+
+ private final static int MATRIX_SIZE = 9;
+
+ // ---- delegate manager ----
+ private static final DelegateManager<Matrix_Delegate> sManager =
+ new DelegateManager<Matrix_Delegate>();
+
+ // ---- delegate data ----
+ private float mValues[] = new float[MATRIX_SIZE];
+
+ // ---- Public Helper methods ----
+
+ /**
+ * Returns an {@link AffineTransform} matching the given Matrix.
+ */
+ public static AffineTransform getAffineTransform(Matrix m) {
+ Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+ if (delegate == null) {
+ assert false;
+ return null;
+ }
+
+ return getAffineTransform(delegate);
+ }
+
+ public static boolean hasPerspective(Matrix m) {
+ Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+ if (delegate == null) {
+ assert false;
+ return false;
+ }
+
+ return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1);
+ }
+
+
+ // ---- native methods ----
+
+ public static int native_create(int native_src_or_zero) {
+ // create the delegate
+ Matrix_Delegate newDelegate = new Matrix_Delegate();
+
+ // copy from values if needed.
+ if (native_src_or_zero > 0) {
+ Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
+ if (oldDelegate != null) {
+ System.arraycopy(
+ oldDelegate.mValues, 0,
+ newDelegate.mValues, 0,
+ MATRIX_SIZE);
+ }
+ }
+
+ return sManager.addDelegate(newDelegate);
+ }
+
+ public static boolean native_isIdentity(int native_object) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ for (int i = 0, k = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++, k++) {
+ if (d.mValues[k] != ((i==j) ? 1 : 0)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean native_rectStaysRect(int native_object) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return true;
+ }
+
+ return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+ }
+
+ public static void native_reset(int native_object) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ reset(d.mValues);
+ }
+
+ public static void native_set(int native_object, int other) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ Matrix_Delegate src = sManager.getDelegate(other);
+ if (src == null) {
+ assert false;
+ return;
+ }
+
+ System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+ }
+
+ public static void native_setTranslate(int native_object, float dx, float dy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ setTranslate(d.mValues, dx, dy);
+ }
+
+ public static void native_setScale(int native_object, float sx, float sy, float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ d.mValues = getScale(sx, sy, px, py);
+ }
+
+ public static void native_setScale(int native_object, float sx, float sy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ d.mValues[0] = sx;
+ d.mValues[1] = 0;
+ d.mValues[2] = 0;
+ d.mValues[3] = 0;
+ d.mValues[4] = sy;
+ d.mValues[5] = 0;
+ d.mValues[6] = 0;
+ d.mValues[7] = 0;
+ d.mValues[8] = 1;
+ }
+
+ public static void native_setRotate(int native_object, float degrees, float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ d.mValues = getRotate(degrees, px, py);
+ }
+
+ public static void native_setRotate(int native_object, float degrees) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ setRotate(d.mValues, degrees);
+ }
+
+ public static void native_setSinCos(int native_object, float sinValue, float cosValue,
+ float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ // TODO: do it in one pass
+
+ // translate so that the pivot is in 0,0
+ setTranslate(d.mValues, -px, -py);
+
+ // scale
+ d.postTransform(getRotate(sinValue, cosValue));
+ // translate back the pivot
+ d.postTransform(getTranslate(px, py));
+ }
+
+ public static void native_setSinCos(int native_object, float sinValue, float cosValue) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ setRotate(d.mValues, sinValue, cosValue);
+ }
+
+ public static void native_setSkew(int native_object, float kx, float ky, float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ d.mValues = getSkew(kx, ky, px, py);
+ }
+
+ public static void native_setSkew(int native_object, float kx, float ky) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ d.mValues[0] = 1;
+ d.mValues[1] = kx;
+ d.mValues[2] = -0;
+ d.mValues[3] = ky;
+ d.mValues[4] = 1;
+ d.mValues[5] = 0;
+ d.mValues[6] = 0;
+ d.mValues[7] = 0;
+ d.mValues[8] = 1;
+ }
+
+ public static boolean native_setConcat(int native_object, int a, int b) {
+ if (a == native_object) {
+ return native_preConcat(native_object, b);
+ } else if (b == native_object) {
+ return native_postConcat(native_object, a);
+ }
+
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate a_mtx = sManager.getDelegate(a);
+ if (a_mtx == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate b_mtx = sManager.getDelegate(b);
+ if (b_mtx == null) {
+ assert false;
+ return false;
+ }
+
+ multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
+
+ return true;
+ }
+
+ public static boolean native_preTranslate(int native_object, float dx, float dy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getTranslate(dx, dy));
+ return true;
+ }
+
+ public static boolean native_preScale(int native_object, float sx, float sy,
+ float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getScale(sx, sy, px, py));
+ return true;
+ }
+
+ public static boolean native_preScale(int native_object, float sx, float sy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getScale(sx, sy));
+ return true;
+ }
+
+ public static boolean native_preRotate(int native_object, float degrees, float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getRotate(degrees, px, py));
+ return true;
+ }
+
+ public static boolean native_preRotate(int native_object, float degrees) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ double rad = Math.toRadians(degrees);
+ float sin = (float)Math.sin(rad);
+ float cos = (float)Math.cos(rad);
+
+ d.preTransform(getRotate(sin, cos));
+ return true;
+ }
+
+ public static boolean native_preSkew(int native_object, float kx, float ky,
+ float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getSkew(kx, ky, px, py));
+ return true;
+ }
+
+ public static boolean native_preSkew(int native_object, float kx, float ky) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getSkew(kx, ky));
+ return true;
+ }
+
+ public static boolean native_preConcat(int native_object, int other_matrix) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate other = sManager.getDelegate(other_matrix);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(other.mValues);
+ return true;
+ }
+
+ public static boolean native_postTranslate(int native_object, float dx, float dy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getTranslate(dx, dy));
+ return true;
+ }
+
+ public static boolean native_postScale(int native_object, float sx, float sy,
+ float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getScale(sx, sy, px, py));
+ return true;
+ }
+
+ public static boolean native_postScale(int native_object, float sx, float sy) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getScale(sx, sy));
+ return true;
+ }
+
+ public static boolean native_postRotate(int native_object, float degrees, float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.preTransform(getRotate(degrees, px, py));
+ return true;
+ }
+
+ public static boolean native_postRotate(int native_object, float degrees) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getRotate(degrees));
+ return true;
+ }
+
+ public static boolean native_postSkew(int native_object, float kx, float ky,
+ float px, float py) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getSkew(kx, ky, px, py));
+ return true;
+ }
+
+ public static boolean native_postSkew(int native_object, float kx, float ky) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(getSkew(kx, ky));
+ return true;
+ }
+
+ public static boolean native_postConcat(int native_object, int other_matrix) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate other = sManager.getDelegate(other_matrix);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ d.postTransform(other.mValues);
+ return true;
+ }
+
+ public static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ if (src.isEmpty()) {
+ reset(d.mValues);
+ return false;
+ }
+
+ if (dst.isEmpty()) {
+ d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
+ = d.mValues[6] = d.mValues[7] = 0;
+ d.mValues[8] = 1;
+ } else {
+ float tx, sx = dst.width() / src.width();
+ float ty, sy = dst.height() / src.height();
+ boolean xLarger = false;
+
+ if (stf != ScaleToFit.FILL.nativeInt) {
+ if (sx > sy) {
+ xLarger = true;
+ sx = sy;
+ } else {
+ sy = sx;
+ }
+ }
+
+ tx = dst.left - src.left * sx;
+ ty = dst.top - src.top * sy;
+ if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
+ float diff;
+
+ if (xLarger) {
+ diff = dst.width() - src.width() * sy;
+ } else {
+ diff = dst.height() - src.height() * sy;
+ }
+
+ if (stf == ScaleToFit.CENTER.nativeInt) {
+ diff = diff / 2;
+ }
+
+ if (xLarger) {
+ tx += diff;
+ } else {
+ ty += diff;
+ }
+ }
+
+ d.mValues[0] = sx;
+ d.mValues[4] = sy;
+ d.mValues[2] = tx;
+ d.mValues[5] = ty;
+ d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
+
+ }
+ // shared cleanup
+ d.mValues[8] = 1;
+ return true;
+ }
+
+ public static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
+ float[] dst, int dstIndex, int pointCount) {
+ // FIXME
+ throw new UnsupportedOperationException("NATIVE DELEGATE NEEDED");
+ }
+
+ public static boolean native_invert(int native_object, int inverse) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
+ if (inv_mtx == null) {
+ assert false;
+ return false;
+ }
+
+
+ try {
+ AffineTransform affineTransform = getAffineTransform(d);
+ AffineTransform inverseTransform = affineTransform.createInverse();
+ inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
+ inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
+ inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
+ inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
+ inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
+ inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+
+ return true;
+ } catch (NoninvertibleTransformException e) {
+ return false;
+ }
+ }
+
+ public static void native_mapPoints(int native_object, float[] dst, int dstIndex,
+ float[] src, int srcIndex, int ptCount, boolean isPts) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ if (isPts) {
+ d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+ } else {
+ // src is vectors
+ // FIXME
+ throw new UnsupportedOperationException("NATIVE DELEGATE NEEDED");
+ }
+ }
+
+ public static boolean native_mapRect(int native_object, RectF dst, RectF src) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return false;
+ }
+
+ // array with 4 corners
+ float[] corners = new float[] {
+ src.left, src.top,
+ src.right, src.top,
+ src.right, src.bottom,
+ src.left, src.bottom,
+ };
+
+ // apply the transform to them.
+ d.mapPoints(corners);
+
+ // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+ dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+ dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+ dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+ dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+
+
+ return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+ }
+
+ public static float native_mapRadius(int native_object, float radius) {
+ // FIXME
+ throw new UnsupportedOperationException();
+ }
+
+ public static void native_getValues(int native_object, float[] values) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+ }
+
+ public static void native_setValues(int native_object, float[] values) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ assert false;
+ return;
+ }
+
+ System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
+ }
+
+ public static boolean native_equals(int native_a, int native_b) {
+ Matrix_Delegate a = sManager.getDelegate(native_a);
+ if (a == null) {
+ assert false;
+ return false;
+ }
+
+ Matrix_Delegate b = sManager.getDelegate(native_b);
+ if (b == null) {
+ assert false;
+ return false;
+ }
+
+ for (int i = 0 ; i < MATRIX_SIZE ; i++) {
+ if (a.mValues[i] != b.mValues[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static void finalizer(int native_instance) {
+ sManager.removeDelegate(native_instance);
+ }
+
+ // ---- Private helper methods ----
+
+ private static AffineTransform getAffineTransform(Matrix_Delegate d) {
+ // the AffineTransform constructor takes the value in a different order
+ // for a matrix [ 0 1 2 ]
+ // [ 3 4 5 ]
+ // the order is 0, 3, 1, 4, 2, 5...
+ return new AffineTransform(
+ d.mValues[0], d.mValues[3], d.mValues[1],
+ d.mValues[4], d.mValues[2], d.mValues[5]);
+ }
+
+
+ /**
+ * Reset a matrix to the identity
+ */
+ private static void reset(float[] mtx) {
+ for (int i = 0, k = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++, k++) {
+ mtx[k] = ((i==j) ? 1 : 0);
+ }
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private final static int kIdentity_Mask = 0;
+ private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation
+ private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale
+ private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates
+ private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective
+ private final static int kRectStaysRect_Mask = 0x10;
+ @SuppressWarnings("unused")
+ private final static int kUnknown_Mask = 0x80;
+
+ @SuppressWarnings("unused")
+ private final static int kAllMasks = kTranslate_Mask |
+ kScale_Mask |
+ kAffine_Mask |
+ kPerspective_Mask |
+ kRectStaysRect_Mask;
+
+ // these guys align with the masks, so we can compute a mask from a variable 0/1
+ @SuppressWarnings("unused")
+ private final static int kTranslate_Shift = 0;
+ @SuppressWarnings("unused")
+ private final static int kScale_Shift = 1;
+ @SuppressWarnings("unused")
+ private final static int kAffine_Shift = 2;
+ @SuppressWarnings("unused")
+ private final static int kPerspective_Shift = 3;
+ private final static int kRectStaysRect_Shift = 4;
+
+ private int computeTypeMask() {
+ int mask = 0;
+
+ if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
+ mask |= kPerspective_Mask;
+ }
+
+ if (mValues[2] != 0. || mValues[5] != 0.) {
+ mask |= kTranslate_Mask;
+ }
+
+ float m00 = mValues[0];
+ float m01 = mValues[1];
+ float m10 = mValues[3];
+ float m11 = mValues[4];
+
+ if (m01 != 0. || m10 != 0.) {
+ mask |= kAffine_Mask;
+ }
+
+ if (m00 != 1. || m11 != 1.) {
+ mask |= kScale_Mask;
+ }
+
+ if ((mask & kPerspective_Mask) == 0) {
+ // map non-zero to 1
+ int im00 = m00 != 0 ? 1 : 0;
+ int im01 = m01 != 0 ? 1 : 0;
+ int im10 = m10 != 0 ? 1 : 0;
+ int im11 = m11 != 0 ? 1 : 0;
+
+ // record if the (p)rimary and (s)econdary diagonals are all 0 or
+ // all non-zero (answer is 0 or 1)
+ int dp0 = (im00 | im11) ^ 1; // true if both are 0
+ int dp1 = im00 & im11; // true if both are 1
+ int ds0 = (im01 | im10) ^ 1; // true if both are 0
+ int ds1 = im01 & im10; // true if both are 1
+
+ // return 1 if primary is 1 and secondary is 0 or
+ // primary is 0 and secondary is 1
+ mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+ }
+
+ return mask;
+ }
+
+ /**
+ * Adds the given transformation to the current Matrix
+ * <p/>This in effect does this = this*matrix
+ * @param matrix
+ */
+ private void postTransform(float[] matrix) {
+ float[] tmp = new float[9];
+ multiply(tmp, mValues, matrix);
+ mValues = tmp;
+ }
+
+ /**
+ * Adds the given transformation to the current Matrix
+ * <p/>This in effect does this = matrix*this
+ * @param matrix
+ */
+ private void preTransform(float[] matrix) {
+ float[] tmp = new float[9];
+ multiply(tmp, matrix, mValues);
+ mValues = tmp;
+ }
+
+ /**
+ * Apply this matrix to the array of 2D points specified by src, and write
+ * the transformed points into the array of points specified by dst. The
+ * two arrays represent their "points" as pairs of floats [x, y].
+ *
+ * @param dst The array of dst points (x,y pairs)
+ * @param dstIndex The index of the first [x,y] pair of dst floats
+ * @param src The array of src points (x,y pairs)
+ * @param srcIndex The index of the first [x,y] pair of src floats
+ * @param pointCount The number of points (x,y pairs) to transform
+ */
+
+ private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
+ int pointCount) {
+ //checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
+
+ float[] tmpDest = dst;
+ boolean inPlace = dst == src;
+ if (inPlace) {
+ tmpDest = new float[dstIndex + pointCount * 2];
+ }
+
+ for (int i = 0 ; i < pointCount ; i++) {
+ // just in case we are doing in place, we better put this in temp vars
+ float x = mValues[0] * src[i + srcIndex] +
+ mValues[1] * src[i + srcIndex + 1] +
+ mValues[2];
+ float y = mValues[3] * src[i + srcIndex] +
+ mValues[4] * src[i + srcIndex + 1] +
+ mValues[5];
+
+ tmpDest[i + dstIndex] = x;
+ tmpDest[i + dstIndex + 1] = y;
+ }
+
+ if (inPlace) {
+ System.arraycopy(tmpDest, dstIndex, dst, dstIndex, pointCount * 2);
+ }
+ }
+
+ /**
+ * Apply this matrix to the array of 2D points, and write the transformed
+ * points back into the array
+ *
+ * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
+ */
+
+ private void mapPoints(float[] pts) {
+ mapPoints(pts, 0, pts, 0, pts.length >> 1);
+ }
+
+ /**
+ * multiply two matrices and store them in a 3rd.
+ * <p/>This in effect does dest = a*b
+ * dest cannot be the same as a or b.
+ */
+ private static void multiply(float dest[], float[] a, float[] b) {
+ // first row
+ dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
+ dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
+ dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
+
+ // 2nd row
+ dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
+ dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
+ dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
+
+ // 3rd row
+ dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
+ dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
+ dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
+ }
+
+ /**
+ * Returns a matrix that represents a given translate
+ * @param dx
+ * @param dy
+ * @return
+ */
+ private static float[] getTranslate(float dx, float dy) {
+ return setTranslate(new float[9], dx, dy);
+ }
+
+ private static float[] setTranslate(float[] dest, float dx, float dy) {
+ dest[0] = 1;
+ dest[1] = 0;
+ dest[2] = dx;
+ dest[3] = 0;
+ dest[4] = 1;
+ dest[5] = dy;
+ dest[6] = 0;
+ dest[7] = 0;
+ dest[8] = 1;
+ return dest;
+ }
+
+ private static float[] getScale(float sx, float sy) {
+ return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+ }
+
+ /**
+ * Returns a matrix that represents the given scale info.
+ * @param sx
+ * @param sy
+ * @param px
+ * @param py
+ */
+ private static float[] getScale(float sx, float sy, float px, float py) {
+ float[] tmp = new float[9];
+ float[] tmp2 = new float[9];
+
+ // TODO: do it in one pass
+
+ // translate tmp so that the pivot is in 0,0
+ setTranslate(tmp, -px, -py);
+
+ // scale into tmp2
+ multiply(tmp2, tmp, getScale(sx, sy));
+
+ // translate back the pivot back into tmp
+ multiply(tmp, tmp2, getTranslate(px, py));
+
+ return tmp;
+ }
+
+
+ private static float[] getRotate(float degrees) {
+ double rad = Math.toRadians(degrees);
+ float sin = (float)Math.sin(rad);
+ float cos = (float)Math.cos(rad);
+
+ return getRotate(sin, cos);
+ }
+
+ private static float[] getRotate(float sin, float cos) {
+ return setRotate(new float[9], sin, cos);
+ }
+
+ private static float[] setRotate(float[] dest, float degrees) {
+ double rad = Math.toRadians(degrees);
+ float sin = (float)Math.sin(rad);
+ float cos = (float)Math.cos(rad);
+
+ return setRotate(dest, sin, cos);
+ }
+
+ private static float[] setRotate(float[] dest, float sin, float cos) {
+ dest[0] = cos;
+ dest[1] = -sin;
+ dest[2] = 0;
+ dest[3] = sin;
+ dest[4] = cos;
+ dest[5] = 0;
+ dest[6] = 0;
+ dest[7] = 0;
+ dest[8] = 1;
+ return dest;
+ }
+
+ private static float[] getRotate(float degrees, float px, float py) {
+ float[] tmp = new float[9];
+ float[] tmp2 = new float[9];
+
+ // TODO: do it in one pass
+
+ // translate so that the pivot is in 0,0
+ setTranslate(tmp, -px, -py);
+
+ // rotate into tmp2
+ double rad = Math.toRadians(degrees);
+ float cos = (float)Math.cos(rad);
+ float sin = (float)Math.sin(rad);
+ multiply(tmp2, tmp, getRotate(sin, cos));
+
+ // translate back the pivot back into tmp
+ multiply(tmp, tmp2, getTranslate(px, py));
+
+ return tmp;
+ }
+
+ private static float[] getSkew(float kx, float ky) {
+ return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
+ }
+
+ private static float[] getSkew(float kx, float ky, float px, float py) {
+ float[] tmp = new float[9];
+ float[] tmp2 = new float[9];
+
+ // TODO: do it in one pass
+
+ // translate so that the pivot is in 0,0
+ setTranslate(tmp, -px, -py);
+
+ // skew into tmp2
+ multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+ // translate back the pivot back into tmp
+ multiply(tmp, tmp2, getTranslate(px, py));
+
+ return tmp;
+ }
+
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
index 12d2cde..c0bc005 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path.java
@@ -31,13 +31,13 @@
* text on a path.
*/
public class Path {
-
+
private FillType mFillType = FillType.WINDING;
private GeneralPath mPath = new GeneralPath();
-
+
private float mLastX = 0;
private float mLastY = 0;
-
+
//---------- Custom methods ----------
public Shape getAwtShape() {
@@ -60,7 +60,7 @@
public Path(Path src) {
mPath.append(src.mPath, false /* connect */);
}
-
+
/**
* Clear any lines and curves from the path, making it empty.
* This does NOT change the fill-type setting.
@@ -92,7 +92,7 @@
EVEN_ODD (GeneralPath.WIND_EVEN_ODD, false),
INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
-
+
FillType(int rule, boolean inverse) {
this.rule = rule;
this.inverse = inverse;
@@ -101,7 +101,7 @@
final int rule;
final boolean inverse;
}
-
+
/**
* Return the path's fill type. This defines how "inside" is
* computed. The default value is WINDING.
@@ -121,7 +121,7 @@
mFillType = ft;
mPath.setWindingRule(ft.rule);
}
-
+
/**
* Returns true if the filltype is one of the INVERSE variants
*
@@ -130,7 +130,7 @@
public boolean isInverseFillType() {
return mFillType.inverse;
}
-
+
/**
* Toggles the INVERSE state of the filltype
*/
@@ -150,7 +150,7 @@
break;
}
}
-
+
/**
* Returns true if the path is empty (contains no lines or curves)
*
@@ -350,7 +350,7 @@
boolean forceMoveTo) {
throw new UnsupportedOperationException();
}
-
+
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
@@ -365,7 +365,7 @@
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
throw new UnsupportedOperationException();
}
-
+
/**
* Close the current contour. If the current point is not equal to the
* first point of the contour, a line segment is automatically added.
@@ -383,13 +383,13 @@
CW (0), // must match enum in SkPath.h
/** counter-clockwise */
CCW (1); // must match enum in SkPath.h
-
+
Direction(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
-
+
/**
* Add a closed rectangle contour to the path
*
@@ -400,7 +400,7 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
-
+
addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}
@@ -446,7 +446,7 @@
// FIXME Need to support direction
Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
-
+
mPath.append(ovalShape, false /* connect */);
}
@@ -493,7 +493,7 @@
// FIXME
throw new UnsupportedOperationException();
}
-
+
/**
* Add a closed round-rectangle contour to the path. Each corner receives
* two radius values [X, Y]. The corners are ordered top-left, top-right,
@@ -513,7 +513,7 @@
// FIXME
throw new UnsupportedOperationException();
}
-
+
/**
* Add a copy of src to the path, offset by (dx,dy)
*
@@ -554,11 +554,11 @@
*/
public void offset(float dx, float dy, Path dst) {
GeneralPath newPath = new GeneralPath();
-
+
PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-
+
newPath.append(iterator, false /* connect */);
-
+
if (dst != null) {
dst.mPath = newPath;
} else {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index 106d7ed..8592731 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -267,7 +267,7 @@
customStyle = parser.getAttributeValue(null /* namespace*/, "style");
}
if (customStyle != null) {
- IResourceValue item = findResValue(customStyle);
+ IResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/);
if (item instanceof IStyleResourceValue) {
defStyleValues = (IStyleResourceValue)item;
@@ -284,7 +284,7 @@
if (item != null) {
// item is a reference to a style entry. Search for it.
- item = findResValue(item.getValue());
+ item = findResValue(item.getValue(), false /*forceFrameworkOnly*/);
if (item instanceof IStyleResourceValue) {
defStyleValues = (IStyleResourceValue)item;
@@ -414,7 +414,7 @@
}
// get the IResourceValue referenced by this value
- IResourceValue resValue = findResValue(value);
+ IResourceValue resValue = findResValue(value, false /*forceFrameworkOnly*/);
// if resValue is null, but value is not null, this means it was not a reference.
// we return the name/value wrapper in a IResourceValue
@@ -450,7 +450,7 @@
}
// else attempt to find another IResourceValue referenced by this one.
- IResourceValue resolvedValue = findResValue(value.getValue());
+ IResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
// if the value did not reference anything, then we simply return the input value
if (resolvedValue == null) {
@@ -477,9 +477,11 @@
* only support the android namespace.
*
* @param reference the resource reference to search for.
+ * @param forceFrameworkOnly if true all references are considered to be toward framework
+ * resource even if the reference does not include the android: prefix.
* @return a {@link IResourceValue} or <code>null</code>.
*/
- IResourceValue findResValue(String reference) {
+ IResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
if (reference == null) {
return null;
}
@@ -561,7 +563,8 @@
segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
}
- return findResValue(segments[0], segments[1], frameworkOnly);
+ return findResValue(segments[0], segments[1],
+ forceFrameworkOnly ? true :frameworkOnly);
}
// Looks like the value didn't reference anything. Return null.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
index 4be6eabc..d145ff6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
@@ -41,7 +41,7 @@
/*
* (non-Javadoc)
* @see android.util.XmlPullAttributes#getAttributeNameResource(int)
- *
+ *
* This methods must return com.android.internal.R.attr.<name> matching
* the name of the attribute.
* It returns 0 if it doesn't find anything.
@@ -50,19 +50,19 @@
public int getAttributeNameResource(int index) {
// get the attribute name.
String name = getAttributeName(index);
-
+
// get the attribute namespace
String ns = mParser.getAttributeNamespace(index);
-
+
if (BridgeConstants.NS_RESOURCES.equals(ns)) {
Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
if (v != null) {
return v.intValue();
}
-
+
return 0;
}
-
+
// this is not an attribute in the android namespace, we query the customviewloader, if
// the namespaces match.
if (mContext.getProjectCallback().getNamespace().equals(ns)) {
@@ -75,7 +75,7 @@
return 0;
}
-
+
/*
* (non-Javadoc)
* @see android.util.XmlPullAttributes#getAttributeResourceValue(int, int)
@@ -83,7 +83,7 @@
@Override
public int getAttributeResourceValue(int index, int defaultValue) {
String value = getAttributeValue(index);
-
+
return resolveResourceValue(value, defaultValue);
}
@@ -94,14 +94,15 @@
@Override
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
String value = getAttributeValue(namespace, attribute);
-
+
return resolveResourceValue(value, defaultValue);
}
private int resolveResourceValue(String value, int defaultValue) {
// now look for this particular value
- IResourceValue resource = mContext.resolveResValue(mContext.findResValue(value));
-
+ IResourceValue resource = mContext.resolveResValue(
+ mContext.findResValue(value, mPlatformFile));
+
if (resource != null) {
Integer id = null;
if (mPlatformFile || resource.isFramework()) {
@@ -115,7 +116,7 @@
return id;
}
}
-
+
return defaultValue;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
new file mode 100644
index 0000000..3d9f960
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.util.SparseArray;
+
+/**
+ * Manages native delegates.
+ *
+ * This is used in conjunction with layoublib_create: certain Android java classes are mere
+ * wrappers around a heavily native based implementation, and we need a way to run these classes
+ * in our Eclipse rendering framework without bringing all the native code from the Android
+ * platform.
+ *
+ * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
+ * native methods by "delegate calls".
+ *
+ * For example, a native method android.graphics.Matrix.init(...) will actually become
+ * a call to android.graphics.Matrix_Delegate.init(...).
+ *
+ * The Android java classes that use native code uses an int (Java side) to reference native
+ * objects. This int is generally directly the pointer to the C structure counterpart.
+ * Typically a creation method will return such an int, and then this int will be passed later
+ * to a Java method to identify the C object to manipulate.
+ *
+ * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
+ * int -> Delegate class link.
+ *
+ * Native methods usually always have the int as parameters. The first thing the delegate method
+ * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
+ *
+ * Typical native init methods are returning a new int back to the Java class, so
+ * {@link #addDelegate(Object)} does the same.
+ *
+ * @param <T> the delegate class to manage
+ */
+public final class DelegateManager<T> {
+
+ private final SparseArray<T> mDelegates = new SparseArray<T>();
+ private int mDelegateCounter = 0;
+
+ /**
+ * Returns the delegate from the given native int.
+ * @param native_object the native int.
+ * @return the delegate or null if not found.
+ */
+ public T getDelegate(int native_object) {
+ synchronized (mDelegates) {
+ return mDelegates.get(native_object);
+ }
+ }
+
+ /**
+ * Adds a delegate to the manager and returns the native int used to identify it.
+ * @param newDelegate the delegate to add
+ * @return a unique native int to identify the delegate
+ */
+ public int addDelegate(T newDelegate) {
+ synchronized (mDelegates) {
+ int native_object = ++mDelegateCounter;
+ mDelegates.put(native_object, newDelegate);
+ return native_object;
+ }
+ }
+
+ /**
+ * Removes the delegate matching the given native int.
+ * @param native_object the native int.
+ */
+ public void removeDelegate(int native_object) {
+ synchronized (mDelegates) {
+ mDelegates.remove(native_object);
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
index 3d0dd73..f624753 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
@@ -148,8 +148,7 @@
parser.setInput(new FileReader(f));
d = Drawable.createFromXml(context.getResources(),
- // FIXME: we need to know if this resource is platform or not
- new BridgeXmlBlockParser(parser, context, false));
+ new BridgeXmlBlockParser(parser, context, isFramework));
return d;
} catch (XmlPullParserException e) {
context.getLogger().error(e);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
new file mode 100644
index 0000000..6eed8ba
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.create.CreateInfo;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that native delegate classes implement all the required methods.
+ *
+ * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * have their native methods reimplemented through a delegate.
+ *
+ * Since the reimplemented methods are not native anymore, we look for the annotation
+ * {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
+ * as the modified class with _Delegate added as a suffix).
+ * If the original native method is not static, then we make sure the delegate method also
+ * include the original class as first parameter (to access "this").
+ *
+ */
+public class TestNativeDelegate extends TestCase {
+
+ public void testNativeDelegates() {
+
+ final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+ final int count = classes.length;
+ for (int i = 0 ; i < count ; i++) {
+ loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+ }
+ }
+
+ private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
+ // load the classes
+ try {
+ ClassLoader classLoader = TestNativeDelegate.class.getClassLoader();
+ Class<?> originalClass = classLoader.loadClass(originalClassName);
+ Class<?> delegateClass = classLoader.loadClass(delegateClassName);
+
+ compare(originalClass, delegateClass);
+ } catch (ClassNotFoundException e) {
+ fail("Failed to load class: " + e.getMessage());
+ } catch (SecurityException e) {
+ fail("Failed to load class: " + e.getMessage());
+ }
+ }
+
+ private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
+ Method[] originalMethods = originalClass.getDeclaredMethods();
+
+ for (Method originalMethod : originalMethods) {
+ // look for methods that were native: they have the LayoutlibDelegate annotation
+ if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+ continue;
+ }
+
+ // get the signature.
+ Class<?>[] parameters = originalMethod.getParameterTypes();
+
+ // if the method is not static, then the class is added as the first parameter
+ // (for "this")
+ if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
+
+ Class<?>[] newParameters = new Class<?>[parameters.length + 1];
+ newParameters[0] = originalClass;
+ System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+ parameters = newParameters;
+ }
+
+ try {
+ // try to load the method with the given parameter types.
+ delegateClass.getMethod(originalMethod.getName(), parameters);
+ } catch (NoSuchMethodException e) {
+ // compute a full class name that's long but not too long.
+ StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
+ for (int j = 0; j < parameters.length; j++) {
+ Class<?> theClass = parameters[j];
+ sb.append(theClass.getName());
+ int dimensions = 0;
+ while (theClass.isArray()) {
+ dimensions++;
+ theClass = theClass.getComponentType();
+ }
+ for (int i = 0; i < dimensions; i++) {
+ sb.append("[]");
+ }
+ if (j < (parameters.length - 1)) {
+ sb.append(",");
+ }
+ }
+ sb.append(")");
+
+ fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString()));
+ }
+ }
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 92892784..2d0ee6d 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -16,6 +16,8 @@
package com.android.tools.layoutlib.create;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
/**
* Describes the work to be done by {@link AsmGenerator}.
*/
@@ -83,7 +85,9 @@
OverrideMethod.class,
MethodListener.class,
MethodAdapter.class,
- CreateInfo.class
+ ICreateInfo.class,
+ CreateInfo.class,
+ LayoutlibDelegate.class
};
/**
@@ -99,8 +103,7 @@
* The list of classes on which to delegate all native methods.
*/
private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
- // TODO: comment out once DelegateClass is working
- // "android.graphics.Paint"
+ "android.graphics.Matrix",
};
/**
@@ -126,7 +129,6 @@
"android.graphics.ComposeShader", "android.graphics._Original_ComposeShader",
"android.graphics.DashPathEffect", "android.graphics._Original_DashPathEffect",
"android.graphics.LinearGradient", "android.graphics._Original_LinearGradient",
- "android.graphics.Matrix", "android.graphics._Original_Matrix",
"android.graphics.Paint", "android.graphics._Original_Paint",
"android.graphics.Path", "android.graphics._Original_Path",
"android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode",