Merge "Keep overview mode when screen is rotated."
diff --git a/Android.mk b/Android.mk
index ebc5213..1407631 100644
--- a/Android.mk
+++ b/Android.mk
@@ -429,13 +429,13 @@
-samplecode $(sample_dir)/SpinnerTest \
resources/samples/SpinnerTest "SpinnerTest" \
-samplecode $(sample_dir)/StackWidget \
- resources/samples/StackWidget "StackWidget" \
+ resources/samples/StackWidget "StackView Widget" \
-samplecode $(sample_dir)/TicTacToeLib \
resources/samples/TicTacToeLib "TicTacToeLib" \
-samplecode $(sample_dir)/TicTacToeMain \
resources/samples/TicTacToeMain "TicTacToeMain" \
-samplecode $(sample_dir)/WeatherListWidget \
- resources/samples/WeatherListWidget "Weather List Widget Sample" \
+ resources/samples/WeatherListWidget "Weather List Widget" \
-samplecode $(sample_dir)/Wiktionary \
resources/samples/Wiktionary "Wiktionary" \
-samplecode $(sample_dir)/WiktionarySimple \
diff --git a/api/current.xml b/api/current.xml
index 5f732e1..7806c24 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -223867,17 +223867,6 @@
visibility="public"
>
</method>
-<field name="DRAG_FLAG_GLOBAL"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="DRAWING_CACHE_QUALITY_AUTO"
type="int"
transient="false"
@@ -237425,6 +237414,19 @@
>
<implements name="android.os.Parcelable">
</implements>
+<method name="containsExtraValueKey"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="s" type="java.lang.String">
+</parameter>
+</method>
<method name="describeContents"
return="int"
abstract="false"
@@ -237447,6 +237449,19 @@
visibility="public"
>
</method>
+<method name="getExtraValueOf"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="s" type="java.lang.String">
+</parameter>
+</method>
<method name="getIconResId"
return="int"
abstract="false"
@@ -257571,6 +257586,21 @@
<parameter name="started" type="boolean">
</parameter>
</method>
+<method name="setDisplayedChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="childIndex" type="int">
+</parameter>
+</method>
<method name="setDouble"
return="void"
abstract="false"
diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java
index 4009f13..377b5a05 100644
--- a/core/java/android/animation/FloatKeyframeSet.java
+++ b/core/java/android/animation/FloatKeyframeSet.java
@@ -87,7 +87,7 @@
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
- prevValue + fraction * (nextValue - prevValue) :
+ prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
} else if (fraction >= 1f) {
@@ -103,7 +103,7 @@
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
- prevValue + fraction * (nextValue - prevValue) :
+ prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
@@ -120,7 +120,7 @@
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
return mEvaluator == null ?
- prevValue + fraction * (nextValue - prevValue) :
+ prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java
index 5629c5e..7b7c876 100644
--- a/core/java/android/animation/IntKeyframeSet.java
+++ b/core/java/android/animation/IntKeyframeSet.java
@@ -87,7 +87,7 @@
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
- prevValue + (int)(fraction * (nextValue - prevValue)) :
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
} else if (fraction >= 1f) {
@@ -103,7 +103,7 @@
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
- prevValue + (int)(fraction * (nextValue - prevValue)) :
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
}
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
@@ -119,7 +119,7 @@
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
return mEvaluator == null ?
- prevValue + (int)(fraction * (nextValue - prevValue)) :
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f079e42..1bd8ef5 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1970,7 +1970,8 @@
if (rotationVector.length == 4) {
q0 = rotationVector[3];
} else {
- q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3);
+ q0 = 1 - q1*q1 - q2*q2 - q3*q3;
+ q0 = (q0 > 0) ? (float)Math.sqrt(q0) : 0;
}
float sq_q1 = 2 * q1 * q1;
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 3f03a2a..9c36b12 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
@@ -38,12 +39,13 @@
*/
private final int prefixLength;
- public LinkAddress(InetAddress address, InetAddress mask) {
- this.address = address;
- this.prefixLength = computeprefixLength(mask);
- }
-
public LinkAddress(InetAddress address, int prefixLength) {
+ if (address == null || prefixLength < 0 ||
+ ((address instanceof Inet4Address) && prefixLength > 32) ||
+ (prefixLength > 128)) {
+ throw new IllegalArgumentException("Bad LinkAddress params " + address +
+ prefixLength);
+ }
this.address = address;
this.prefixLength = prefixLength;
}
@@ -53,18 +55,6 @@
this.prefixLength = interfaceAddress.getNetworkPrefixLength();
}
- private static int computeprefixLength(InetAddress mask) {
- int count = 0;
- for (byte b : mask.getAddress()) {
- for (int i = 0; i < 8; ++i) {
- if ((b & (1 << i)) != 0) {
- ++count;
- }
- }
- }
- return count;
- }
-
@Override
public String toString() {
return (address == null ? "" : (address.getHostAddress() + "/" + prefixLength));
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index c007605..de71763 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -59,6 +59,10 @@
public static final String EVENT_BEGIN_TIME = "beginTime";
public static final String EVENT_END_TIME = "endTime";
+ /**
+ * This must not be changed or horrible, unspeakable things could happen.
+ * For instance, the Calendar app might break. Also, the db might not work.
+ */
public static final String AUTHORITY = "com.android.calendar";
/**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8f922e2..b05b078 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3843,60 +3843,6 @@
* @hide
*/
public static final String SNIPPET_ARGS_PARAM_KEY = "snippet_args";
-
- /**
- * The ID of the data row that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_DATA_ID = "snippet_data_id";
-
- /**
- * The type of data that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_MIMETYPE = "snippet_mimetype";
-
- /**
- * The {@link Data#DATA1} field of the data row that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_DATA1 = "snippet_data1";
-
- /**
- * The {@link Data#DATA2} field of the data row that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_DATA2 = "snippet_data2";
-
- /**
- * The {@link Data#DATA3} field of the data row that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_DATA3 = "snippet_data3";
-
- /**
- * The {@link Data#DATA4} field of the data row that was matched by the filter.
- *
- * @hide
- * @deprecated
- */
- @Deprecated
- public static final String SNIPPET_DATA4 = "snippet_data4";
}
/**
diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java
new file mode 100644
index 0000000..7ffa5ae
--- /dev/null
+++ b/core/java/android/server/BluetoothInputProfileHandler.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothProfileState;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This handles all the operations on the HID profile.
+ * All functions are called by BluetoothService, as Bluetooth Service
+ * is the Service handler for the HID profile.
+ */
+final class BluetoothInputProfileHandler {
+ private static final String TAG = "BluetoothInputProfileHandler";
+ private static final boolean DBG = true;
+
+ public static BluetoothInputProfileHandler sInstance;
+ private Context mContext;
+ private BluetoothService mBluetoothService;
+ private final HashMap<BluetoothDevice, Integer> mInputDevices;
+ private final BluetoothProfileState mHidProfileState;
+
+ private BluetoothInputProfileHandler(Context context, BluetoothService service) {
+ mContext = context;
+ mBluetoothService = service;
+ mInputDevices = new HashMap<BluetoothDevice, Integer>();
+ mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
+ mHidProfileState.start();
+ }
+
+ static synchronized BluetoothInputProfileHandler getInstance(Context context,
+ BluetoothService service) {
+ if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service);
+ return sInstance;
+ }
+
+ synchronized boolean connectInputDevice(BluetoothDevice device,
+ BluetoothDeviceProfileState state) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ if (objectPath == null ||
+ getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
+ getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
+ return false;
+ }
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
+ msg.obj = state;
+ mHidProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
+ if (!mBluetoothService.connectInputDeviceNative(objectPath)) {
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
+ return false;
+ }
+ return true;
+ }
+
+ synchronized boolean disconnectInputDevice(BluetoothDevice device,
+ BluetoothDeviceProfileState state) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ if (objectPath == null ||
+ getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) {
+ return false;
+ }
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
+ msg.obj = state;
+ mHidProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
+ if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) {
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
+ return false;
+ }
+ return true;
+ }
+
+ synchronized int getInputDeviceState(BluetoothDevice device) {
+ if (mInputDevices.get(device) == null) {
+ return BluetoothInputDevice.STATE_DISCONNECTED;
+ }
+ return mInputDevices.get(device);
+ }
+
+ synchronized List<BluetoothDevice> getConnectedInputDevices() {
+ List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
+ new int[] {BluetoothInputDevice.STATE_CONNECTED});
+ return devices;
+ }
+
+ synchronized int getInputDevicePriority(BluetoothDevice device) {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+ BluetoothInputDevice.PRIORITY_UNDEFINED);
+ }
+
+ synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+ if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+ return false;
+ }
+ return Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+ priority);
+ }
+
+ synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+ List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
+
+ for (BluetoothDevice device: mInputDevices.keySet()) {
+ int inputDeviceState = getInputDeviceState(device);
+ for (int state : states) {
+ if (state == inputDeviceState) {
+ inputDevices.add(device);
+ break;
+ }
+ }
+ }
+ return inputDevices;
+ }
+
+ private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
+ int prevState;
+ if (mInputDevices.get(device) == null) {
+ prevState = BluetoothInputDevice.STATE_DISCONNECTED;
+ } else {
+ prevState = mInputDevices.get(device);
+ }
+ if (prevState == state) return;
+
+ mInputDevices.put(device, state);
+
+ if (getInputDevicePriority(device) >
+ BluetoothInputDevice.PRIORITY_OFF &&
+ state == BluetoothInputDevice.STATE_CONNECTING ||
+ state == BluetoothInputDevice.STATE_CONNECTED) {
+ // We have connected or attempting to connect.
+ // Bump priority
+ setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
+ }
+
+ Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
+ intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
+ mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
+
+ debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
+ mBluetoothService.sendConnectionStateChange(device, state, prevState);
+ }
+
+ synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
+ int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
+ BluetoothInputDevice.STATE_DISCONNECTED;
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(address);
+ handleInputDeviceStateChange(device, state);
+ }
+
+ synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) {
+ switch (state) {
+ case BluetoothDevice.BOND_BONDED:
+ if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
+ setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
+ }
+ break;
+ case BluetoothDevice.BOND_NONE:
+ setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
+ break;
+ }
+ }
+
+ private static void debugLog(String msg) {
+ if (DBG) Log.d(TAG, msg);
+ }
+
+ private static void errorLog(String msg) {
+ Log.e(TAG, msg);
+ }
+}
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java
new file mode 100644
index 0000000..fb96439
--- /dev/null
+++ b/core/java/android/server/BluetoothPanProfileHandler.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources.NotFoundException;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This handles the PAN profile. All calls into this are made
+ * from Bluetooth Service.
+ */
+final class BluetoothPanProfileHandler {
+ private static final String TAG = "BluetoothPanProfileHandler";
+ private static final boolean DBG = true;
+
+ private ArrayList<String> mBluetoothIfaceAddresses;
+ private int mMaxPanDevices;
+
+ private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
+ private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
+ private static final int BLUETOOTH_PREFIX_LENGTH = 24;
+ public static BluetoothPanProfileHandler sInstance;
+ private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
+ private boolean mTetheringOn;
+ private Context mContext;
+ private BluetoothService mBluetoothService;
+
+ private BluetoothPanProfileHandler(Context context, BluetoothService service) {
+ mContext = context;
+ mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
+ mBluetoothService = service;
+ mTetheringOn = false;
+ mBluetoothIfaceAddresses = new ArrayList<String>();
+ try {
+ mMaxPanDevices = context.getResources().getInteger(
+ com.android.internal.R.integer.config_max_pan_devices);
+ } catch (NotFoundException e) {
+ mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
+ }
+ }
+
+ static synchronized BluetoothPanProfileHandler getInstance(Context context,
+ BluetoothService service) {
+ if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service);
+ return sInstance;
+ }
+
+ synchronized boolean isTetheringOn() {
+ return mTetheringOn;
+ }
+
+ synchronized boolean allowIncomingTethering() {
+ if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
+ return true;
+ return false;
+ }
+
+ private BroadcastReceiver mTetheringReceiver = null;
+
+ synchronized void setBluetoothTethering(boolean value) {
+ if (!value) {
+ disconnectPanServerDevices();
+ }
+
+ if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mTetheringReceiver = new BroadcastReceiver() {
+ @Override
+ public synchronized void onReceive(Context context, Intent intent) {
+ if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
+ == BluetoothAdapter.STATE_ON) {
+ mTetheringOn = true;
+ mContext.unregisterReceiver(mTetheringReceiver);
+ }
+ }
+ };
+ mContext.registerReceiver(mTetheringReceiver, filter);
+ } else {
+ mTetheringOn = value;
+ }
+ }
+
+ synchronized int getPanDeviceState(BluetoothDevice device) {
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ if (panDevice == null) {
+ return BluetoothPan.STATE_DISCONNECTED;
+ }
+ return panDevice.mState;
+ }
+
+ synchronized boolean connectPanDevice(BluetoothDevice device) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")");
+ if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
+ errorLog(device + " already connected to PAN");
+ }
+
+ int connectedCount = 0;
+ for (BluetoothDevice panDevice: mPanDevices.keySet()) {
+ if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
+ connectedCount ++;
+ }
+ }
+ if (connectedCount > 8) {
+ debugLog(device + " could not connect to PAN because 8 other devices are"
+ + "already connected");
+ return false;
+ }
+
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING,
+ BluetoothPan.LOCAL_PANU_ROLE);
+ if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) {
+ debugLog("connecting to PAN");
+ return true;
+ } else {
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED,
+ BluetoothPan.LOCAL_PANU_ROLE);
+ errorLog("could not connect to PAN");
+ return false;
+ }
+ }
+
+ private synchronized boolean disconnectPanServerDevices() {
+ debugLog("disconnect all PAN devices");
+
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ int state = panDevice.mState;
+ if (state == BluetoothPan.STATE_CONNECTED &&
+ panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
+ panDevice.mLocalRole);
+
+ if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath,
+ device.getAddress(),
+ panDevice.mIfaceAddr)) {
+ errorLog("could not disconnect Pan Server Device "+device.getAddress());
+
+ // Restore prev state
+ handlePanDeviceStateChange(device, state,
+ panDevice.mLocalRole);
+
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ synchronized List<BluetoothDevice> getConnectedPanDevices() {
+ List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+ devices.add(device);
+ }
+ }
+ return devices;
+ }
+
+ synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+ String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ debugLog("disconnect PAN(" + objectPath + ")");
+
+ int state = getPanDeviceState(device);
+ if (state != BluetoothPan.STATE_CONNECTED) {
+ debugLog(device + " already disconnected from PAN");
+ return false;
+ }
+
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+
+ if (panDevice == null) {
+ errorLog("No record for this Pan device:" + device);
+ return false;
+ }
+
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
+ panDevice.mLocalRole);
+ if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
+ if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(),
+ panDevice.mIface)) {
+ // Restore prev state, this shouldn't happen
+ handlePanDeviceStateChange(device, state, panDevice.mLocalRole);
+ return false;
+ }
+ } else {
+ if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) {
+ // Restore prev state, this shouldn't happen
+ handlePanDeviceStateChange(device, state, panDevice.mLocalRole);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ String iface, int state, int role) {
+ int prevState;
+ String ifaceAddr = null;
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+
+ if (panDevice == null) {
+ prevState = BluetoothPan.STATE_DISCONNECTED;
+ } else {
+ prevState = panDevice.mState;
+ ifaceAddr = panDevice.mIfaceAddr;
+ }
+ if (prevState == state) return;
+
+ if (role == BluetoothPan.LOCAL_NAP_ROLE) {
+ if (state == BluetoothPan.STATE_CONNECTED) {
+ ifaceAddr = enableTethering(iface);
+ if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
+ } else if (state == BluetoothPan.STATE_DISCONNECTED) {
+ if (ifaceAddr != null) {
+ mBluetoothIfaceAddresses.remove(ifaceAddr);
+ ifaceAddr = null;
+ }
+ }
+ } else {
+ // PANU Role = reverse Tether
+ if (state == BluetoothPan.STATE_CONNECTED) {
+ BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
+ } else if (state == BluetoothPan.STATE_DISCONNECTED &&
+ (prevState == BluetoothPan.STATE_CONNECTED ||
+ prevState == BluetoothPan.STATE_DISCONNECTING)) {
+ BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface);
+ }
+ }
+
+ if (panDevice == null) {
+ panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role);
+ mPanDevices.put(device, panDevice);
+ } else {
+ panDevice.mState = state;
+ panDevice.mIfaceAddr = ifaceAddr;
+ panDevice.mLocalRole = role;
+ }
+
+ if (state == BluetoothPan.STATE_DISCONNECTED) {
+ mPanDevices.remove(device);
+ }
+
+ Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
+ intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
+ intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
+ mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
+
+ debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
+ mBluetoothService.sendConnectionStateChange(device, state, prevState);
+ }
+
+ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ int state, int role) {
+ handlePanDeviceStateChange(device, null, state, role);
+ }
+
+ private class BluetoothPanDevice {
+ private int mState;
+ private String mIfaceAddr;
+ private String mIface;
+ private int mLocalRole; // Which local role is this PAN device bound to
+
+ BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
+ mState = state;
+ mIfaceAddr = ifaceAddr;
+ mIface = iface;
+ mLocalRole = localRole;
+ }
+ }
+
+ private String createNewTetheringAddressLocked() {
+ if (getConnectedPanDevices().size() == mMaxPanDevices) {
+ debugLog ("Max PAN device connections reached");
+ return null;
+ }
+ String address = BLUETOOTH_IFACE_ADDR_START;
+ while (true) {
+ if (mBluetoothIfaceAddresses.contains(address)) {
+ String[] addr = address.split("\\.");
+ Integer newIp = Integer.parseInt(addr[2]) + 1;
+ address = address.replace(addr[2], newIp.toString());
+ } else {
+ break;
+ }
+ }
+ mBluetoothIfaceAddresses.add(address);
+ return address;
+ }
+
+ // configured when we start tethering
+ private synchronized String enableTethering(String iface) {
+ debugLog("updateTetherState:" + iface);
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+ ConnectivityManager cm =
+ (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+ // bring toggle the interfaces
+ String[] currentIfaces = new String[0];
+ try {
+ currentIfaces = service.listInterfaces();
+ } catch (Exception e) {
+ Log.e(TAG, "Error listing Interfaces :" + e);
+ return null;
+ }
+
+ boolean found = false;
+ for (String currIface: currentIfaces) {
+ if (currIface.equals(iface)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) return null;
+
+ String address = createNewTetheringAddressLocked();
+ if (address == null) return null;
+
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = service.getInterfaceConfig(iface);
+ if (ifcg != null) {
+ InetAddress addr = null;
+ if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
+ addr.equals(InetAddress.getByName("0.0.0.0")) ||
+ addr.equals(InetAddress.getByName("::0"))) {
+ addr = InetAddress.getByName(address);
+ }
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+ ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
+ service.setInterfaceConfig(iface, ifcg);
+ if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Error tethering "+iface);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+ return null;
+ }
+ return address;
+ }
+
+ private static void debugLog(String msg) {
+ if (DBG) Log.d(TAG, msg);
+ }
+
+ private static void errorLog(String msg) {
+ Log.e(TAG, msg);
+ }
+}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index df5097e..a295de5 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -24,17 +24,17 @@
package android.server;
+import com.android.internal.app.IBatteryStats;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDeviceProfileState;
import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothSocket;
-import android.bluetooth.BluetoothTetheringDataTracker;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
@@ -44,14 +44,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.content.res.Resources.NotFoundException;
-import android.net.ConnectivityManager;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -60,8 +55,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.internal.app.IBatteryStats;
-
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -76,7 +69,6 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -96,7 +88,6 @@
private int mBluetoothState;
private boolean mRestart = false; // need to call enable() after disable()
private boolean mIsDiscovering;
- private boolean mTetheringOn;
private int[] mAdapterSdpHandles;
private ParcelUuid[] mAdapterUuids;
@@ -106,7 +97,7 @@
private final Context mContext;
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
- private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
@@ -125,13 +116,6 @@
private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
- private ArrayList<String> mBluetoothIfaceAddresses;
- private int mMaxPanDevices;
-
- private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
- private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
- private static final String BLUETOOTH_NETMASK = "255.255.255.0";
-
// The timeout used to sent the UUIDs Intent
// This timeout should be greater than the page timeout
private static final int UUID_INTENT_DELAY = 6000;
@@ -155,11 +139,8 @@
private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
private final BluetoothProfileState mA2dpProfileState;
private final BluetoothProfileState mHfpProfileState;
- private final BluetoothProfileState mHidProfileState;
private BluetoothA2dpService mA2dpService;
- private final HashMap<BluetoothDevice, Integer> mInputDevices;
- private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
@@ -167,9 +148,9 @@
private static String mDockAddress;
private String mDockPin;
- private String mIface;
-
private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+ private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
+ private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
private static class RemoteService {
public String address;
@@ -218,7 +199,7 @@
mBluetoothState = BluetoothAdapter.STATE_OFF;
mIsDiscovering = false;
- mTetheringOn = false;
+
mAdapterProperties = new HashMap<String, String>();
mDeviceProperties = new HashMap<String, Map<String,String>>();
@@ -230,27 +211,17 @@
mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
- mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
-
- mBluetoothIfaceAddresses = new ArrayList<String>();
- try {
- mMaxPanDevices = context.getResources().getInteger(
- com.android.internal.R.integer.config_max_pan_devices);
- } catch (NotFoundException e) {
- mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
- }
mHfpProfileState.start();
mA2dpProfileState.start();
- mHidProfileState.start();
IntentFilter filter = new IntentFilter();
registerForAirplaneMode(filter);
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(mReceiver, filter);
- mInputDevices = new HashMap<BluetoothDevice, Integer>();
- mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>();
+ mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
+ mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
}
public static synchronized String readDockBluetoothAddress() {
@@ -590,21 +561,16 @@
persistBluetoothOnSetting(true);
}
- updateSdpRecords();
-
mIsDiscovering = false;
mBondState.readAutoPairingData();
mBondState.loadBondState();
initProfileState();
- // Log bluetooth on to battery stats.
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteBluetoothOn();
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ // This should be the last step of the the enable thread.
+ // Because this adds SDP records which asynchronously
+ // broadcasts the Bluetooth On State in updateBluetoothState.
+ // So we want all internal state setup before this.
+ updateSdpRecords();
} else {
setBluetoothState(BluetoothAdapter.STATE_OFF);
}
@@ -652,6 +618,11 @@
}
}
+ /**
+ * This function is called from Bluetooth Event Loop when onPropertyChanged
+ * for adapter comes in with UUID property.
+ * @param uuidsThe uuids of adapter as reported by Bluez.
+ */
synchronized void updateBluetoothState(String uuids) {
if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) {
ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
@@ -662,6 +633,15 @@
String[] propVal = {"Pairable", getProperty("Pairable")};
mEventLoop.onPropertyChanged(propVal);
+ // Log bluetooth on to battery stats.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteBluetoothOn();
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
disable(false);
}
@@ -825,7 +805,8 @@
// HID is handled by BluetoothService, other profiles
// will be handled by their respective services.
- setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
+ mBluetoothInputProfileHandler.setInitialInputDevicePriority(
+ mAdapter.getRemoteDevice(address), state);
if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
reason + ")");
@@ -1472,425 +1453,6 @@
return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
}
- public synchronized boolean isTetheringOn() {
- return mTetheringOn;
- }
-
- /*package*/ synchronized boolean allowIncomingTethering() {
- if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
- return true;
- return false;
- }
-
- private BroadcastReceiver mTetheringReceiver = null;
-
- public synchronized void setBluetoothTethering(boolean value) {
- if (!value) {
- disconnectPan();
- }
-
- if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- mTetheringReceiver = new BroadcastReceiver() {
- @Override
- public synchronized void onReceive(Context context, Intent intent) {
- if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
- == BluetoothAdapter.STATE_ON) {
- mTetheringOn = true;
- mContext.unregisterReceiver(mTetheringReceiver);
- }
- }
- };
- mContext.registerReceiver(mTetheringReceiver, filter);
- } else {
- mTetheringOn = value;
- }
- }
-
- public synchronized int getPanDeviceState(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- Pair<Integer, String> panDevice = mPanDevices.get(device);
- if (panDevice == null) {
- return BluetoothPan.STATE_DISCONNECTED;
- }
- return panDevice.first;
- }
-
- public synchronized boolean connectPanDevice(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
-
- String objectPath = getObjectPathFromAddress(device.getAddress());
- if (DBG) log("connect PAN(" + objectPath + ")");
- if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
- log (device + " already connected to PAN");
- }
-
- int connectedCount = 0;
- for (BluetoothDevice panDevice: mPanDevices.keySet()) {
- if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
- connectedCount ++;
- }
- }
- if (connectedCount > 8) {
- log (device + " could not connect to PAN because 8 other devices are already connected");
- return false;
- }
-
- handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING,
- BluetoothPan.LOCAL_PANU_ROLE);
- if (connectPanDeviceNative(objectPath, "nap")) {
- log ("connecting to PAN");
- return true;
- } else {
- handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED,
- BluetoothPan.LOCAL_PANU_ROLE);
- log ("could not connect to PAN");
- return false;
- }
- }
-
- private synchronized boolean disconnectPan() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (DBG) log("disconnect all PAN devices");
-
- for (BluetoothDevice device: mPanDevices.keySet()) {
- if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
- if (!disconnectPanDevice(device)) {
- log ("could not disconnect Pan Device "+device.getAddress());
- return false;
- }
- }
- }
- return true;
- }
-
- public synchronized List<BluetoothDevice> getConnectedPanDevices() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-
- for (BluetoothDevice device: mPanDevices.keySet()) {
- if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
- devices.add(device);
- }
- }
- return devices;
- }
-
- public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- String objectPath = getObjectPathFromAddress(device.getAddress());
- if (DBG) log("disconnect PAN(" + objectPath + ")");
- if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
- log (device + " already disconnected from PAN");
- }
- handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING,
- BluetoothPan.LOCAL_PANU_ROLE);
- return disconnectPanDeviceNative(objectPath);
- }
-
- /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
- String iface,
- int state,
- int role) {
- int prevState;
- String ifaceAddr = null;
-
- if (mPanDevices.get(device) == null) {
- prevState = BluetoothPan.STATE_DISCONNECTED;
- } else {
- prevState = mPanDevices.get(device).first;
- ifaceAddr = mPanDevices.get(device).second;
- }
- if (prevState == state) return;
-
- if (role == BluetoothPan.LOCAL_NAP_ROLE) {
- if (state == BluetoothPan.STATE_CONNECTED) {
- ifaceAddr = enableTethering(iface);
- if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
- } else if (state == BluetoothPan.STATE_DISCONNECTED) {
- if (ifaceAddr != null) {
- mBluetoothIfaceAddresses.remove(ifaceAddr);
- ifaceAddr = null;
- }
- }
- } else {
- // PANU Role = reverse Tether
- if (state == BluetoothPan.STATE_CONNECTED) {
- mIface = iface;
- BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
- } else if (state == BluetoothPan.STATE_DISCONNECTED &&
- (prevState == BluetoothPan.STATE_CONNECTED ||
- prevState == BluetoothPan.STATE_DISCONNECTING)) {
- BluetoothTetheringDataTracker.getInstance().stopReverseTether(mIface);
- }
- }
-
- Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr);
- mPanDevices.put(device, value);
-
- Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
- intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
- intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
- if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
- sendConnectionStateChange(device, state, prevState);
- }
-
- /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
- int state, int role) {
- handlePanDeviceStateChange(device, null, state, role);
- }
-
- private String createNewTetheringAddressLocked() {
- if (getConnectedPanDevices().size() == mMaxPanDevices) {
- log("Max PAN device connections reached");
- return null;
- }
- String address = BLUETOOTH_IFACE_ADDR_START;
- while (true) {
- if (mBluetoothIfaceAddresses.contains(address)) {
- String[] addr = address.split("\\.");
- Integer newIp = Integer.parseInt(addr[2]) + 1;
- address = address.replace(addr[2], newIp.toString());
- } else {
- break;
- }
- }
- mBluetoothIfaceAddresses.add(address);
- return address;
- }
-
- // configured when we start tethering
- private synchronized String enableTethering(String iface) {
- log("updateTetherState:" + iface);
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- ConnectivityManager cm =
- (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
-
- // bring toggle the interfaces
- String[] currentIfaces = new String[0];
- try {
- currentIfaces = service.listInterfaces();
- } catch (Exception e) {
- Log.e(TAG, "Error listing Interfaces :" + e);
- return null;
- }
-
- boolean found = false;
- for (String currIface: currentIfaces) {
- if (currIface.equals(iface)) {
- found = true;
- break;
- }
- }
-
- if (!found) return null;
-
- String address = createNewTetheringAddressLocked();
- if (address == null) return null;
-
- InterfaceConfiguration ifcg = null;
- try {
- ifcg = service.getInterfaceConfig(iface);
- if (ifcg != null) {
- InetAddress mask = InetAddress.getByName(BLUETOOTH_NETMASK);
- InetAddress addr = null;
- if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
- addr.equals(InetAddress.getByName("0.0.0.0")) ||
- addr.equals(InetAddress.getByName("::0"))) {
- addr = InetAddress.getByName(address);
- }
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
- ifcg.addr = new LinkAddress(addr, mask);
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
- service.setInterfaceConfig(iface, ifcg);
- if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- Log.e(TAG, "Error tethering "+iface);
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
- return null;
- }
- return address;
- }
-
- public synchronized boolean connectInputDevice(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
-
- String objectPath = getObjectPathFromAddress(device.getAddress());
- if (objectPath == null ||
- getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
- getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
- return false;
- }
- BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
- if (state != null) {
- Message msg = new Message();
- msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
- msg.obj = state;
- mHidProfileState.sendMessage(msg);
- return true;
- }
- return false;
- }
-
- public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
- String objectPath = getObjectPathFromAddress(device.getAddress());
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
- if (!connectInputDeviceNative(objectPath)) {
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
- return false;
- }
- return true;
- }
-
- public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
-
- String objectPath = getObjectPathFromAddress(device.getAddress());
- if (objectPath == null ||
- getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) {
- return false;
- }
- BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
- if (state != null) {
- Message msg = new Message();
- msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
- msg.obj = state;
- mHidProfileState.sendMessage(msg);
- return true;
- }
- return false;
- }
-
- public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
- String objectPath = getObjectPathFromAddress(device.getAddress());
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
- if (!disconnectInputDeviceNative(objectPath)) {
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
- return false;
- }
- return true;
- }
-
- public synchronized int getInputDeviceState(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- if (mInputDevices.get(device) == null) {
- return BluetoothInputDevice.STATE_DISCONNECTED;
- }
- return mInputDevices.get(device);
- }
-
- public synchronized List<BluetoothDevice> getConnectedInputDevices() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
- new int[] {BluetoothInputDevice.STATE_CONNECTED});
- return devices;
- }
-
- public synchronized int getInputDevicePriority(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
- BluetoothInputDevice.PRIORITY_UNDEFINED);
- }
-
- public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- return false;
- }
- return Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
- priority);
- }
-
- /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
- List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
-
- for (BluetoothDevice device: mInputDevices.keySet()) {
- int inputDeviceState = getInputDeviceState(device);
- for (int state : states) {
- if (state == inputDeviceState) {
- inputDevices.add(device);
- break;
- }
- }
- }
- return inputDevices;
- }
-
- private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
- int prevState;
- if (mInputDevices.get(device) == null) {
- prevState = BluetoothInputDevice.STATE_DISCONNECTED;
- } else {
- prevState = mInputDevices.get(device);
- }
- if (prevState == state) return;
-
- mInputDevices.put(device, state);
-
- if (getInputDevicePriority(device) >
- BluetoothInputDevice.PRIORITY_OFF &&
- state == BluetoothInputDevice.STATE_CONNECTING ||
- state == BluetoothInputDevice.STATE_CONNECTED) {
- // We have connected or attempting to connect.
- // Bump priority
- setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
- }
-
- Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
- intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
- if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
- sendConnectionStateChange(device, state, prevState);
- }
-
- /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
- int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
- BluetoothInputDevice.STATE_DISCONNECTED;
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- handleInputDeviceStateChange(device, state);
- }
-
- private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
- switch (state) {
- case BluetoothDevice.BOND_BONDED:
- if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
- setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
- }
- break;
- case BluetoothDevice.BOND_NONE:
- setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
- break;
- }
- }
-
- /*package*/ boolean isRemoteDeviceInCache(String address) {
- return (mDeviceProperties.get(address) != null);
- }
-
/*package*/ String[] getRemoteDeviceProperties(String address) {
if (!isEnabledInternal()) return null;
@@ -2675,6 +2237,114 @@
if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
}
+ /**** Handlers for PAN Profile ****/
+
+ public synchronized boolean isTetheringOn() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothPanProfileHandler.isTetheringOn();
+ }
+
+ /*package*/ synchronized boolean allowIncomingTethering() {
+ return mBluetoothPanProfileHandler.allowIncomingTethering();
+ }
+
+ public synchronized void setBluetoothTethering(boolean value) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ mBluetoothPanProfileHandler.setBluetoothTethering(value);
+ }
+
+ public synchronized int getPanDeviceState(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothPanProfileHandler.getPanDeviceState(device);
+ }
+
+ public synchronized boolean connectPanDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ return mBluetoothPanProfileHandler.connectPanDevice(device);
+ }
+
+ public synchronized List<BluetoothDevice> getConnectedPanDevices() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothPanProfileHandler.getConnectedPanDevices();
+ }
+
+ public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ return mBluetoothPanProfileHandler.disconnectPanDevice(device);
+ }
+
+ /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ String iface,
+ int state,
+ int role) {
+ mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
+ }
+
+ /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ int state, int role) {
+ mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
+ }
+
+ /**** Handlers for Input Device Profile ****/
+
+ public synchronized boolean connectInputDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+ return mBluetoothInputProfileHandler.connectInputDevice(device, state);
+ }
+
+ public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+ return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
+ }
+
+ public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+ return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
+ }
+
+ public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+ return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
+ }
+
+ public synchronized int getInputDeviceState(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothInputProfileHandler.getInputDeviceState(device);
+
+ }
+
+ public synchronized List<BluetoothDevice> getConnectedInputDevices() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothInputProfileHandler.getConnectedInputDevices();
+ }
+
+ public synchronized int getInputDevicePriority(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothInputProfileHandler.getInputDevicePriority(device);
+ }
+
+ public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
+ }
+
+ /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+ return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
+ }
+
+ /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
+ mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
+ }
+
+ /*package*/ boolean isRemoteDeviceInCache(String address) {
+ return (mDeviceProperties.get(address) != null);
+ }
+
public boolean connectHeadset(String address) {
if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
@@ -2928,12 +2598,14 @@
short channel);
private native boolean removeServiceRecordNative(int handle);
private native boolean setLinkTimeoutNative(String path, int num_slots);
- private native boolean connectInputDeviceNative(String path);
- private native boolean disconnectInputDeviceNative(String path);
+ native boolean connectInputDeviceNative(String path);
+ native boolean disconnectInputDeviceNative(String path);
- private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
- private native boolean connectPanDeviceNative(String path, String dstRole);
- private native boolean disconnectPanDeviceNative(String path);
+ native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
+ native boolean connectPanDeviceNative(String path, String dstRole);
+ native boolean disconnectPanDeviceNative(String path);
+ native boolean disconnectPanServerDeviceNative(String path,
+ String address, String iface);
private native int[] addReservedServiceRecordsNative(int[] uuuids);
private native boolean removeReservedServiceRecordsNative(int[] handles);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 8700af8..97a216a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -600,8 +600,9 @@
* are at different run levels (and thus there's a split caret).
* @param offset the offset
* @return true if at a level boundary
+ * @hide
*/
- private boolean isLevelBoundary(int offset) {
+ public boolean isLevelBoundary(int offset) {
int line = getLineForOffset(offset);
Directions dirs = getLineDirections(line);
if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
@@ -1148,8 +1149,7 @@
int bottom = getLineTop(line+1);
float h1 = getPrimaryHorizontal(point) - 0.5f;
- float h2 = isLevelBoundary(point) ?
- getSecondaryHorizontal(point) - 0.5f : h1;
+ float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1;
int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 03a6aa5..b982c7b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1421,55 +1421,6 @@
}
/**
- * Used by views that contain lists of items. This state indicates that
- * the view is showing the last item.
- * @hide
- */
- protected static final int[] LAST_STATE_SET = {R.attr.state_last};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is showing the first item.
- * @hide
- */
- protected static final int[] FIRST_STATE_SET = {R.attr.state_first};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is showing the middle item.
- * @hide
- */
- protected static final int[] MIDDLE_STATE_SET = {R.attr.state_middle};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is showing only one item.
- * @hide
- */
- protected static final int[] SINGLE_STATE_SET = {R.attr.state_single};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is pressed and showing the last item.
- * @hide
- */
- protected static final int[] PRESSED_LAST_STATE_SET = {R.attr.state_last, R.attr.state_pressed};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is pressed and showing the first item.
- * @hide
- */
- protected static final int[] PRESSED_FIRST_STATE_SET = {R.attr.state_first, R.attr.state_pressed};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is pressed and showing the middle item.
- * @hide
- */
- protected static final int[] PRESSED_MIDDLE_STATE_SET = {R.attr.state_middle, R.attr.state_pressed};
- /**
- * Used by views that contain lists of items. This state indicates that
- * the view is pressed and showing only one item.
- * @hide
- */
- protected static final int[] PRESSED_SINGLE_STATE_SET = {R.attr.state_single, R.attr.state_pressed};
-
- /**
* Temporary Rect currently for use in setBackground(). This will probably
* be extended in the future to hold our own class with more than just
* a Rect. :)
@@ -1497,14 +1448,14 @@
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "measurement")
- /*package*/ int mMeasuredWidth;
+ int mMeasuredWidth;
/**
* Height as measured during measure pass.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "measurement")
- /*package*/ int mMeasuredHeight;
+ int mMeasuredHeight;
/**
* Flag to indicate that this view was marked INVALIDATED, or had its display list
@@ -2187,6 +2138,13 @@
private int[] mDrawableState = null;
+ /**
+ * Set to true when drawing cache is enabled and cannot be created.
+ *
+ * @hide
+ */
+ public boolean mCachingFailed;
+
private Bitmap mDrawingCache;
private Bitmap mUnscaledDrawingCache;
private DisplayList mDisplayList;
@@ -2283,6 +2241,8 @@
* {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, all visible applications will be able to participate
* in the drag operation and receive the dragged content.
+ *
+ * @hide
*/
public static final int DRAG_FLAG_GLOBAL = 1;
@@ -3749,16 +3709,6 @@
}
/**
- * Determine if this view has the FITS_SYSTEM_WINDOWS flag set.
- * @return True if window has FITS_SYSTEM_WINDOWS set
- *
- * @hide
- */
- public boolean isFitsSystemWindowsFlagSet() {
- return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
- }
-
- /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -8414,6 +8364,7 @@
* @see #setLayerType(int, android.graphics.Paint)
*/
public void setDrawingCacheEnabled(boolean enabled) {
+ mCachingFailed = false;
setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
}
@@ -8436,6 +8387,7 @@
*
* @hide
*/
+ @SuppressWarnings({"UnusedDeclaration"})
public void outputDirtyFlags(String indent, boolean clear, int clearMask) {
Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.DIRTY_MASK) +
") DRAWN(" + (mPrivateFlags & DRAWN) + ")" + " CACHE_VALID(" +
@@ -8473,10 +8425,7 @@
* @hide
*/
public boolean canHaveDisplayList() {
- if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
- return false;
- }
- return true;
+ return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
}
/**
@@ -8658,7 +8607,7 @@
public void buildDrawingCache() {
buildDrawingCache(false);
}
-
+
/**
* <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
*
@@ -8685,6 +8634,7 @@
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
+ mCachingFailed = false;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -8710,6 +8660,7 @@
(width * height * (opaque && !use32BitCache ? 2 : 4) >
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
destroyDrawingCache();
+ mCachingFailed = true;
return;
}
@@ -8719,12 +8670,14 @@
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
Bitmap.Config quality;
if (!opaque) {
+ // Never pick ARGB_4444 because it looks awful
+ // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
case DRAWING_CACHE_QUALITY_AUTO:
quality = Bitmap.Config.ARGB_8888;
break;
case DRAWING_CACHE_QUALITY_LOW:
- quality = Bitmap.Config.ARGB_4444;
+ quality = Bitmap.Config.ARGB_8888;
break;
case DRAWING_CACHE_QUALITY_HIGH:
quality = Bitmap.Config.ARGB_8888;
@@ -8760,6 +8713,7 @@
} else {
mUnscaledDrawingCache = null;
}
+ mCachingFailed = true;
return;
}
@@ -11324,6 +11278,7 @@
* </p>
*/
public boolean dispatchDragEvent(DragEvent event) {
+ //noinspection SimplifiableIfStatement
if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnDragListener.onDrag(this, event)) {
return true;
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0444496..cc4e89c 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -152,12 +152,12 @@
* should be at least equal to the size of the screen in ARGB888 format.
*/
@Deprecated
- private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
+ private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888
/**
* The coefficient of friction applied to flings/scrolls.
*/
- private static float SCROLL_FRICTION = 0.015f;
+ private static final float SCROLL_FRICTION = 0.015f;
/**
* Max distance to overscroll for edge effects
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ad6b0f6..b5a2558 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -17,10 +17,6 @@
package android.view;
import android.animation.LayoutTransition;
-import android.view.animation.AlphaAnimation;
-import com.android.internal.R;
-import com.android.internal.util.Predicate;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
@@ -39,10 +35,13 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
+import com.android.internal.R;
+import com.android.internal.util.Predicate;
import java.util.ArrayList;
import java.util.HashSet;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 0a9386d..25f2229 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -19,9 +19,11 @@
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -32,12 +34,17 @@
* specified subtype of the designated input method directly.
*/
public final class InputMethodSubtype implements Parcelable {
+ private static final String TAG = InputMethodSubtype.class.getSimpleName();
+ private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
+ private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
+
private final int mSubtypeNameResId;
private final int mSubtypeIconResId;
private final String mSubtypeLocale;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
private final int mSubtypeHashCode;
+ private HashMap<String, String> mExtraValueHashMapCache;
/**
* Constructor
@@ -106,6 +113,46 @@
return mSubtypeExtraValue;
}
+ private HashMap<String, String> getExtraValueHashMap() {
+ if (mExtraValueHashMapCache == null) {
+ mExtraValueHashMapCache = new HashMap<String, String>();
+ final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
+ final int N = pairs.length;
+ for (int i = 0; i < N; ++i) {
+ final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
+ if (pair.length == 1) {
+ mExtraValueHashMapCache.put(pair[0], null);
+ } else if (pair.length > 1) {
+ if (pair.length > 2) {
+ Slog.w(TAG, "ExtraValue has two or more '='s");
+ }
+ mExtraValueHashMapCache.put(pair[0], pair[1]);
+ }
+ }
+ }
+ return mExtraValueHashMapCache;
+ }
+
+ /**
+ * The string of ExtraValue in subtype should be defined as follows:
+ * example: key0,key1=value1,key2,key3,key4=value4
+ * @param key the key of extra value
+ * @return the subtype contains specified the extra value
+ */
+ public boolean containsExtraValueKey(String key) {
+ return getExtraValueHashMap().containsKey(key);
+ }
+
+ /**
+ * The string of ExtraValue in subtype should be defined as follows:
+ * example: key0,key1=value1,key2,key3,key4=value4
+ * @param key the key of extra value
+ * @return the value of the specified key
+ */
+ public String getExtraValueOf(String key) {
+ return getExtraValueHashMap().get(key);
+ }
+
@Override
public int hashCode() {
return mSubtypeHashCode;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 874eac8..7d8289a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1175,6 +1175,8 @@
mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
+
+ setScrollBarStyle(super.getScrollBarStyle());
}
/**
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index e416da2..08c8144 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -849,22 +849,29 @@
public void onNewPicture(WebViewCore.DrawData drawData) {
final int viewWidth = mWebView.getViewWidth();
final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
+ final float newZoomOverviewScale = getZoomOverviewScale();
WebSettings settings = mWebView.getSettings();
if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() &&
settings.getUseFixedViewport() &&
(mInitialZoomOverview || mInZoomOverview)) {
- mTextWrapScale = getReadingLevelScale();
+ // Keep mobile site's text wrap scale unchanged. For mobile sites,
+ // the text wrap scale is the same as zoom overview scale, which is 1.0f.
+ if (exceedsMinScaleIncrement(mTextWrapScale, 1.0f) ||
+ exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f)) {
+ mTextWrapScale = getReadingLevelScale();
+ } else {
+ mTextWrapScale = newZoomOverviewScale;
+ }
}
- final float zoomOverviewScale = getZoomOverviewScale();
if (!mMinZoomScaleFixed) {
- mMinZoomScale = zoomOverviewScale;
+ mMinZoomScale = newZoomOverviewScale;
}
// fit the content width to the current view. Ignore the rounding error case.
if (!mWebView.drawHistory() && (mInitialZoomOverview || (mInZoomOverview
&& Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) {
mInitialZoomOverview = false;
- setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
+ setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
!mWebView.getSettings().getUseFixedViewport());
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3f38f2e..27020c5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -334,6 +334,7 @@
* the drawing cache was enabled on the children
*/
boolean mCachingStarted;
+ boolean mCachingActive;
/**
* The position of the view that received the down motion event
@@ -4169,7 +4170,7 @@
if (mScrollingCacheEnabled && !mCachingStarted) {
setChildrenDrawnWithCacheEnabled(true);
setChildrenDrawingCacheEnabled(true);
- mCachingStarted = true;
+ mCachingStarted = mCachingActive = true;
}
}
@@ -4178,7 +4179,7 @@
mClearScrollingCache = new Runnable() {
public void run() {
if (mCachingStarted) {
- mCachingStarted = false;
+ mCachingStarted = mCachingActive = false;
setChildrenDrawnWithCacheEnabled(false);
if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
setChildrenDrawingCacheEnabled(false);
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 190c0fc..072992e 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -279,6 +279,7 @@
*
* @param whichChild the index of the child view to display
*/
+ @android.view.RemotableViewMethod
public void setDisplayedChild(int whichChild) {
setDisplayedChild(whichChild, true);
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 8811492..560fc68 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -604,6 +604,7 @@
removePromptView();
mPopup.setContentView(null);
mDropDownList = null;
+ mHandler.removeCallbacks(mResizePopupRunnable);
}
/**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 12a0ebf..2802144 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3013,12 +3013,9 @@
return mItemsCanFocus;
}
- /**
- * @hide Pending API council approval.
- */
@Override
public boolean isOpaque() {
- return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque &&
+ return (mCachingActive && mIsCacheColorOpaque && mDividerIsOpaque &&
hasOpaqueScrollbars()) || super.isOpaque();
}
@@ -3071,6 +3068,10 @@
@Override
protected void dispatchDraw(Canvas canvas) {
+ if (mCachingStarted) {
+ mCachingActive = true;
+ }
+
// Draw the dividers
final int dividerHeight = mDividerHeight;
final Drawable overscrollHeader = mOverScrollHeader;
@@ -3164,7 +3165,6 @@
}
} else {
int top;
- int listTop = effectivePaddingTop;
final int scrollY = mScrollY;
@@ -3181,7 +3181,7 @@
View child = getChildAt(i);
top = child.getTop();
// Don't draw dividers next to items that are not enabled
- if (top > listTop) {
+ if (top > effectivePaddingTop) {
if ((areAllItemsSelectable ||
(adapter.isEnabled(first + i) && (i == count - 1 ||
adapter.isEnabled(first + i + 1))))) {
@@ -3220,6 +3220,15 @@
super.dispatchDraw(canvas);
}
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ boolean more = super.drawChild(canvas, child, drawingTime);
+ if (mCachingActive && child.mCachingFailed) {
+ mCachingActive = false;
+ }
+ return more;
+ }
+
/**
* Draws a divider for the given child in the given bounds.
*
@@ -3558,6 +3567,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ //noinspection SimplifiableIfStatement
if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 482ce56..c854fac 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1056,24 +1056,34 @@
}
/**
- * Equivalent to calling {@link AdapterViewFlipper#showNext()}
+ * Equivalent to calling {@link AdapterViewAnimator#showNext()}
*
- * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()}
+ * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
*/
public void showNext(int viewId) {
addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
}
/**
- * Equivalent to calling {@link AdapterViewFlipper#showPrevious()}
+ * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
*
- * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()}
+ * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
*/
public void showPrevious(int viewId) {
addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
}
/**
+ * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
+ *
+ * @param viewId The id of the view on which to call
+ * {@link AdapterViewAnimator#setDisplayedChild(int)}
+ */
+ public void setDisplayedChild(int viewId, int childIndex) {
+ setInt(viewId, "setDisplayedChild", childIndex);
+ }
+
+ /**
* Equivalent to calling View.setVisibility
*
* @param viewId The id of the view whose visibility should change
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 28b106b..993af31 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -304,15 +304,19 @@
}
InputMethodState mInputMethodState;
- int mTextSelectHandleLeftRes;
- int mTextSelectHandleRightRes;
- int mTextSelectHandleRes;
- int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
- int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
+ private int mTextSelectHandleLeftRes;
+ private int mTextSelectHandleRightRes;
+ private int mTextSelectHandleRes;
+ private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
+ private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
- Drawable mSelectHandleLeft;
- Drawable mSelectHandleRight;
- Drawable mSelectHandleCenter;
+ private int mCursorDrawableRes;
+ private final Drawable[] mCursorDrawable = new Drawable[2];
+ private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
+
+ private Drawable mSelectHandleLeft;
+ private Drawable mSelectHandleRight;
+ private Drawable mSelectHandleCenter;
private int mLastDownPositionX, mLastDownPositionY;
private Callback mCustomSelectionActionModeCallback;
@@ -742,6 +746,10 @@
}
break;
+ case com.android.internal.R.styleable.TextView_textCursorDrawable:
+ mCursorDrawableRes = a.getResourceId(attr, 0);
+ break;
+
case com.android.internal.R.styleable.TextView_textSelectHandleLeft:
mTextSelectHandleLeftRes = a.getResourceId(attr, 0);
break;
@@ -3770,33 +3778,40 @@
if (mHighlightPathBogus) {
invalidateCursor();
} else {
- synchronized (sTempRect) {
- /*
- * The reason for this concern about the thickness of the
- * cursor and doing the floor/ceil on the coordinates is that
- * some EditTexts (notably textfields in the Browser) have
- * anti-aliased text where not all the characters are
- * necessarily at integer-multiple locations. This should
- * make sure the entire cursor gets invalidated instead of
- * sometimes missing half a pixel.
- */
+ final int horizontalPadding = getCompoundPaddingLeft();
+ final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
- float thick = FloatMath.ceil(mTextPaint.getStrokeWidth());
- if (thick < 1.0f) {
- thick = 1.0f;
+ if (mCursorCount == 0) {
+ synchronized (sTempRect) {
+ /*
+ * The reason for this concern about the thickness of the
+ * cursor and doing the floor/ceil on the coordinates is that
+ * some EditTexts (notably textfields in the Browser) have
+ * anti-aliased text where not all the characters are
+ * necessarily at integer-multiple locations. This should
+ * make sure the entire cursor gets invalidated instead of
+ * sometimes missing half a pixel.
+ */
+ float thick = FloatMath.ceil(mTextPaint.getStrokeWidth());
+ if (thick < 1.0f) {
+ thick = 1.0f;
+ }
+
+ thick /= 2.0f;
+
+ mHighlightPath.computeBounds(sTempRect, false);
+
+ invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick),
+ (int) FloatMath.floor(verticalPadding + sTempRect.top - thick),
+ (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick),
+ (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick));
}
-
- thick /= 2;
-
- mHighlightPath.computeBounds(sTempRect, false);
-
- int left = getCompoundPaddingLeft();
- int top = getExtendedPaddingTop() + getVerticalOffset(true);
-
- invalidate((int) FloatMath.floor(left + sTempRect.left - thick),
- (int) FloatMath.floor(top + sTempRect.top - thick),
- (int) FloatMath.ceil(left + sTempRect.right + thick),
- (int) FloatMath.ceil(top + sTempRect.bottom + thick));
+ } else {
+ for (int i = 0; i < mCursorCount; i++) {
+ Rect bounds = mCursorDrawable[i].getBounds();
+ invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
+ bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
+ }
}
}
}
@@ -3836,13 +3851,23 @@
line2 = mLayout.getLineForOffset(last);
int bottom = mLayout.getLineTop(line2 + 1);
- int voffset = getVerticalOffset(true);
- int left = getCompoundPaddingLeft() + mScrollX;
- invalidate(left, top + voffset + getExtendedPaddingTop(),
- left + getWidth() - getCompoundPaddingLeft() -
- getCompoundPaddingRight(),
- bottom + voffset + getExtendedPaddingTop());
+ final int horizontalPadding = getCompoundPaddingLeft();
+ final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
+
+ // If used, the cursor drawables can have an arbitrary dimension that can go beyond
+ // the invalidated lines specified above.
+ for (int i = 0; i < mCursorCount; i++) {
+ Rect bounds = mCursorDrawable[i].getBounds();
+ top = Math.min(top, bounds.top);
+ bottom = Math.max(bottom, bounds.bottom);
+ // Horizontal bounds are already full width, no need to update
+ }
+
+ invalidate(horizontalPadding + mScrollX, top + verticalPadding,
+ horizontalPadding + mScrollX + getWidth() -
+ getCompoundPaddingLeft() - getCompoundPaddingRight(),
+ bottom + verticalPadding);
}
}
}
@@ -4346,6 +4371,7 @@
Path highlight = null;
int selStart = -1, selEnd = -1;
+ boolean drawCursor = false;
// If there is no movement method, then there can be no selection.
// Check that first and attempt to skip everything having to do with
@@ -4366,6 +4392,7 @@
if (mHighlightPathBogus) {
mHighlightPath.reset();
mLayout.getCursorPath(selStart, mHighlightPath, mText);
+ updateCursorsPositions();
mHighlightPathBogus = false;
}
@@ -4377,7 +4404,11 @@
}
mHighlightPaint.setStyle(Paint.Style.STROKE);
- highlight = mHighlightPath;
+ if (mCursorCount > 0) {
+ drawCursor = true;
+ } else {
+ highlight = mHighlightPath;
+ }
}
} else {
if (mHighlightPathBogus) {
@@ -4460,6 +4491,8 @@
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
+ if (drawCursor) drawCursor(canvas, cursorOffsetVertical);
+
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
if (mMarquee != null && mMarquee.shouldDrawGhost()) {
@@ -4478,6 +4511,52 @@
updateCursorControllerPositions();
}
+ private void updateCursorsPositions() {
+ if (mCursorDrawableRes == 0) return;
+
+ final int offset = getSelectionStart();
+ final int line = mLayout.getLineForOffset(offset);
+ final int top = mLayout.getLineTop(line);
+ final int bottom = mLayout.getLineTop(line + 1);
+
+ mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
+
+ int middle = bottom;
+ if (mCursorCount == 2) {
+ // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
+ middle = (top + bottom) >> 1;
+ }
+
+ updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
+
+ if (mCursorCount == 2) {
+ updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
+ }
+ }
+
+ private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
+ if (mCursorDrawable[cursorIndex] == null)
+ mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
+
+ if (mTempRect == null) mTempRect = new Rect();
+
+ mCursorDrawable[cursorIndex].getPadding(mTempRect);
+ final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
+ horizontal = Math.max(0.5f, horizontal - 0.5f);
+ final int left = (int) (horizontal) - mTempRect.left;
+ mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+ bottom + mTempRect.bottom);
+ }
+
+ private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
+ final boolean translate = cursorOffsetVertical != 0;
+ if (translate) canvas.translate(0, cursorOffsetVertical);
+ for (int i = 0; i < mCursorCount; i++) {
+ mCursorDrawable[i].draw(canvas);
+ }
+ if (translate) canvas.translate(0, -cursorOffsetVertical);
+ }
+
/**
* Update the positions of the CursorControllers. Needed by WebTextView,
* which does not draw.
@@ -8699,7 +8778,7 @@
}
mDrawable = mSelectHandleLeft;
handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = (handleWidth * 3) / 4;
+ mHotspotX = handleWidth * 3.0f / 4.0f;
break;
}
@@ -8710,7 +8789,7 @@
}
mDrawable = mSelectHandleRight;
handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth / 4;
+ mHotspotX = handleWidth / 4.0f;
break;
}
@@ -8722,7 +8801,7 @@
}
mDrawable = mSelectHandleCenter;
handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth / 2;
+ mHotspotX = handleWidth / 2.0f;
mIsInsertionHandle = true;
break;
}
@@ -8937,8 +9016,8 @@
final int lineBottom = mLayout.getLineBottom(line);
final Rect bounds = sCursorControllerTempRect;
- bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - mHotspotX)
- + TextView.this.mScrollX;
+ bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) +
+ TextView.this.mScrollX;
bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY;
bounds.right = bounds.left + width;
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 7b66893..3c683d6 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -96,6 +96,7 @@
*
* @param whichChild the index of the child view to display
*/
+ @android.view.RemotableViewMethod
public void setDisplayedChild(int whichChild) {
mWhichChild = whichChild;
if (whichChild >= getChildCount()) {
@@ -122,6 +123,7 @@
/**
* Manually shows the next child.
*/
+ @android.view.RemotableViewMethod
public void showNext() {
setDisplayedChild(mWhichChild + 1);
}
@@ -129,6 +131,7 @@
/**
* Manually shows the previous child.
*/
+ @android.view.RemotableViewMethod
public void showPrevious() {
setDisplayedChild(mWhichChild - 1);
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 1f93eac..6c9e7bb 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -101,8 +101,10 @@
}
if (anchor != null) {
- mTreeObserver = anchor.getViewTreeObserver();
- mTreeObserver.addOnGlobalLayoutListener(this);
+ if (mTreeObserver == null) {
+ mTreeObserver = anchor.getViewTreeObserver();
+ mTreeObserver.addOnGlobalLayoutListener(this);
+ }
mPopup.setAnchorView(anchor);
} else {
return false;
@@ -123,10 +125,10 @@
public void onDismiss() {
mPopup = null;
- if (mTreeObserver != null) {
- mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this);
- mTreeObserver = null;
+ if (mTreeObserver != null && mTreeObserver.isAlive()) {
+ mTreeObserver.removeGlobalOnLayoutListener(this);
}
+ mTreeObserver = null;
}
public boolean isShowing() {
@@ -134,6 +136,8 @@
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (!isShowing()) return;
+
MenuItem item = null;
if (mOverflowOnly) {
item = mMenu.getOverflowItem(position);
@@ -184,13 +188,15 @@
@Override
public void onGlobalLayout() {
if (!isShowing()) {
- mTreeObserver.removeGlobalOnLayoutListener(this);
+ if (mTreeObserver.isAlive()) {
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ }
mTreeObserver = null;
} else {
final View anchor = mAnchorView != null ? mAnchorView.get() : null;
- if (anchor != null && !anchor.isShown()) {
+ if (anchor == null || !anchor.isShown()) {
dismiss();
- } else {
+ } else if (isShowing()) {
// Recompute window size and position
mPopup.show();
}
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 2c39871..bf0504f 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -1214,6 +1214,45 @@
return JNI_FALSE;
}
+static jboolean disconnectPanServerDeviceNative(JNIEnv *env, jobject object,
+ jstring path, jstring address,
+ jstring iface) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ LOGE("disconnectPanServerDeviceNative");
+ native_data_t *nat = get_native_data(env, object);
+ jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+ struct event_loop_native_data_t *eventLoopNat =
+ get_EventLoop_native_data(env, eventLoop);
+
+ if (nat && eventLoopNat) {
+ const char *c_address = env->GetStringUTFChars(address, NULL);
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+ const char *c_iface = env->GetStringUTFChars(iface, NULL);
+
+ int len = env->GetStringLength(path) + 1;
+ char *context_path = (char *)calloc(len, sizeof(char));
+ strlcpy(context_path, c_path, len); // for callback
+
+ bool ret = dbus_func_args_async(env, nat->conn, -1,
+ onPanDeviceConnectionResult,
+ context_path, eventLoopNat,
+ get_adapter_path(env, object),
+ DBUS_NETWORKSERVER_IFACE,
+ "DisconnectDevice",
+ DBUS_TYPE_STRING, &c_address,
+ DBUS_TYPE_STRING, &c_iface,
+ DBUS_TYPE_INVALID);
+
+ env->ReleaseStringUTFChars(address, c_address);
+ env->ReleaseStringUTFChars(iface, c_iface);
+ env->ReleaseStringUTFChars(path, c_path);
+ return ret ? JNI_TRUE : JNI_FALSE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
@@ -1274,6 +1313,8 @@
{"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void *)connectPanDeviceNative},
{"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
+ {"disconnectPanServerDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ (void *)disconnectPanServerDeviceNative},
};
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
index e05345d..a499a09 100644
--- a/core/res/res/anim/wallpaper_intra_close_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -19,16 +19,16 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale=".9" android:toYScale="1.0"
+ <scale android:fromXScale=".95" android:toXScale="1.0"
+ android:fromYScale=".95" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="200"
+ android:startOffset="160"
android:duration="300" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="200"
+ android:interpolator="@interpolator/decelerate_cubic"
+ android:startOffset="160"
android:duration="300"/>
-</set>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
index df7acc9..12a8df5 100644
--- a/core/res/res/anim/wallpaper_intra_close_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -19,14 +19,14 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="0.9"
- android:fromYScale="1.0" android:toYScale="0.9"
+ <scale android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%p" android:pivotY="50%p"
android:fillEnabled="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
+ android:interpolator="@interpolator/linear"
android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillAfter="true"
+ android:fillEnabled="true" android:fillAfter="true"
android:interpolator="@interpolator/decelerate_cubic"
- android:duration="150"/>
-</set>
+ android:duration="120"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
index ff310a1..a499a09 100644
--- a/core/res/res/anim/wallpaper_intra_open_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -19,14 +19,16 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false">
- <scale android:fromXScale="0.95" android:toXScale="1.0"
- android:fromYScale="0.95" android:toYScale="1.0"
+ <scale android:fromXScale=".95" android:toXScale="1.0"
+ android:fromYScale=".95" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
+ android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="200"
+ android:startOffset="160"
android:duration="300" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_cubic"
- android:startOffset="200"
+ android:startOffset="160"
android:duration="300"/>
-</set>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
index 47ea0b4..12a8df5 100644
--- a/core/res/res/anim/wallpaper_intra_open_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -22,9 +22,11 @@
<scale android:fromXScale="1.0" android:toXScale="1.0"
android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%p" android:pivotY="50%p"
+ android:fillEnabled="true" android:fillAfter="true"
android:interpolator="@interpolator/linear"
android:duration="300" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:fillEnabled="true" android:fillAfter="true"
android:interpolator="@interpolator/decelerate_cubic"
- android:duration="160"/>
-</set>
+ android:duration="120"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
new file mode 100644
index 0000000..b9435b6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
new file mode 100644
index 0000000..477d820
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8802003..6f37dc0 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -778,6 +778,9 @@
<!-- Color of link text (URLs). -->
<attr name="textColorLink" format="reference|color" />
+ <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
+ <attr name="textCursorDrawable" format="reference" />
+
<!-- Indicates that the content of a non-editable TextView can be selected.
Default value is false. EditText content is always selectable. -->
<attr name="textIsSelectable" format="boolean" />
@@ -2783,6 +2786,9 @@
<!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
<attr name="textEditSideNoPasteWindowLayout" />
+ <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
+ <attr name="textCursorDrawable" />
+
<!-- Indicates that the content of a non-editable text can be selected. -->
<attr name="textIsSelectable" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index aaf071b..4542575 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1642,4 +1642,7 @@
<!-- Default icon for applications that don't specify an icon. -->
<public type="mipmap" name="sym_def_app_icon" id="0x010d0000" />
+ <!-- Theme attribute used to customize the text insertion cursor -->
+ <!-- Commented out for HC MR1 to prevent an API change -->
+ <!-- <public type="attr" name="textCursorDrawable" id="0x01010362" /> -->
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5700641..8cc5944 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -426,6 +426,7 @@
<item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
<item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item>
<item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item>
+ <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item>
</style>
<style name="Widget.TextView.ListSeparator">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 6d5b482..927a668 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -180,6 +180,7 @@
<item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
<item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item>
<item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item>
+ <item name="textCursorDrawable">@null</item>
<!-- Widget styles -->
<item name="absListViewStyle">@android:style/Widget.AbsListView</item>
@@ -906,6 +907,7 @@
<item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
<item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+ <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item>
<!-- Widget styles -->
<item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
@@ -1181,6 +1183,7 @@
<item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
<item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+ <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item>
<!-- Widget styles -->
<item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 1ecf103..3667c7b 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -46,8 +46,8 @@
* <accesspoint></accesspoint>. The supported configuration includes: ssid,
* security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
* in which each is included in the corresponding tags. Static IP setting is also supported.
- * Tags that can be used include: ip, gateway, netmask, dns1, dns2. All access points have to be
- * enclosed in tags of <resources></resources>.
+ * Tags that can be used include: ip, gateway, networkprefixlength, dns1, dns2. All access points
+ * have to be enclosed in tags of <resources></resources>.
*
* The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
* <resources>
@@ -62,7 +62,8 @@
* </resources>
*
* Note:ssid and security have to be the first two tags
- * for static ip setting, tag "ip" should be listed before other fields: dns, gateway, netmask.
+ * for static ip setting, tag "ip" should be listed before other fields: dns, gateway,
+ * networkprefixlength.
*/
public class AccessPointParserHelper {
private static final String KEYSTORE_SPACE = "keystore://";
@@ -106,7 +107,6 @@
boolean ip = false;
boolean gateway = false;
boolean networkprefix = false;
- boolean netmask = false;
boolean dns1 = false;
boolean dns2 = false;
boolean eap = false;
@@ -163,9 +163,6 @@
if (tagName.equalsIgnoreCase("networkprefixlength")) {
networkprefix = true;
}
- if (tagName.equalsIgnoreCase("netmask")) {
- netmask = true;
- }
if (tagName.equalsIgnoreCase("dns1")) {
dns1 = true;
}
@@ -321,19 +318,6 @@
}
networkprefix = false;
}
- if (netmask) {
- try {
- String netMaskStr = new String(ch, start, length);
- if (!InetAddress.isNumeric(netMaskStr)) {
- throw new SAXException();
- }
- InetAddress netMaskAddr = InetAddress.getByName(netMaskStr);
- mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, netMaskAddr));
- } catch (UnknownHostException e) {
- throw new SAXException();
- }
- netmask = false;
- }
if (dns1) {
try {
String dnsAddr = new String(ch, start, length);
diff --git a/docs/html/guide/appendix/market-filters.jd b/docs/html/guide/appendix/market-filters.jd
index 6ca8acc..f826f43 100644
--- a/docs/html/guide/appendix/market-filters.jd
+++ b/docs/html/guide/appendix/market-filters.jd
@@ -5,23 +5,25 @@
<div id="qv">
<h2>Quickview</h2>
-<ul> <li>Android Market applies filters to that let you control whether your app is shown to a
-user who is browing or searching for apps.</li>
-<li>Filtering is determined by elements in an app's manifest file,
-aspects of the device being used, and other factors.</li> </ul>
+<ul>
+<li>Android Market applies filters that control which Android-powered devices can access your
+application on Market.</li>
+<li>Filtering is determined by comparing device configurations that you declare in you app's
+manifest file to the configurations defined by the device, as well as other factors.</li> </ul>
<h2>In this document</h2>
<ol> <li><a href="#how-filters-work">How Filters Work in Android Market</a></li>
<li><a href="#manifest-filters">Filtering based on Manifest File Elements</a></li>
<li><a href="#other-filters">Other Filters</a></li>
+<li><a href="#advanced-filters">Advanced Manifest Filters</a></li>
</ol>
<h2>See also</h2>
<ol>
<li><a
-href="{@docRoot}guide/practices/compatibility.html">Compatibility</a></li>
-<li style="margin-top:2px;"><code><a
+href="{@docRoot}guide/practices/compatibility.html">Android Compatibility</a></li>
+<li><code><a
href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><supports-screens></a></code></li>
<li><code><a
href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><uses-configuration></a></code></li>
@@ -42,36 +44,40 @@
href="http://market.android.com/publish">Go to Android Market »</a> </div>
</div>
-</div> </div>
+</div>
+</div>
-<p>When a user searches or browses in Android Market, the results are filtered, and
-some applications might not be visible. For example, if an application requires a
+
+<p>When a user searches or browses in Android Market, the results are filtered based on which
+applications are compatible with the user's device. For example, if an application requires a
trackball (as specified in the manifest file), then Android Market will not show
-the app on any device that does not have a trackball.</p> <p>The manifest file and
-the device's hardware and features are only part of how applications are filtered
-— filtering also depends on the country and carrier, the presence or absence
-of a SIM card, and other factors. </p>
+the app on any device that does not have a trackball.</p>
+
+<p>The manifest file and the device's hardware and features are only part of how applications are
+filtered—filtering might also depend on the country and carrier, the presence or absence of a
+SIM card, and other factors. </p>
<p>Changes to the Android Market filters are independent of changes
to the Android platform itself. This document will be updated periodically to reflect
-any changes that occur. </p>
+any changes that affect the way Android Market filters applications.</p>
+
<h2 id="how-filters-work">How Filters Work in Android Market</h2>
<p>Android Market uses the filter restrictions described below to determine
whether to show your application to a user who is browsing or searching for
-applications on a given device. When determining whether to display your app,
+applications on an Android-powered device. When determining whether to display your app,
Market checks the device's hardware and software capabilities, as well as it's
carrier, location, and other characteristics. It then compares those against the
restrictions and dependencies expressed by the application itself, in its
-manifest, <code>.apk</code>, and publishing details. If the application is
+manifest file and publishing details. If the application is
compatible with the device according to the filter rules, Market displays the
application to the user. Otherwise, Market hides your application from search
results and category browsing. </p>
-<p> You can use the filters described below to control whether Market shows or
-hides your application to users. You can request any combination of the
-available filters for your app — for example, you could set a
+<p>You can use the filters described below to control whether Market shows or
+hides your application to users. You can use any combination of the
+available filters for your app—for example, you can set a
<code>minSdkVersion</code> requirement of <code>"4"</code> and set
<code>smallScreens="false"</code> in the app, then when uploading the app to
Market you could target European countries (carriers) only. Android Market's
@@ -92,16 +98,18 @@
available. </li>
</ul>
+
+
<h2 id="manifest-filters">Filtering based on Manifest Elements</h2>
<p>Most Market filters are triggered by elements within an application's
manifest file, <a
href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>,
-although not everything in the manifest file can trigger filtering. The
-table below lists the manifest elements that you can use to trigger Android
-Market filtering, and explains how the filtering works.</p>
+although not everything in the manifest file can trigger filtering.
+Table 1 lists the manifest elements that you should use to trigger Android
+Market filtering, and explains how the filtering for each element works.</p>
-<p class="table-caption"><strong>Table 1.</strong> Manifest elements that
+<p id="table1" class="table-caption"><strong>Table 1.</strong> Manifest elements that
trigger filtering on Market.</p>
<table>
<tr>
@@ -313,10 +321,13 @@
</tr>
</table>
+
<h2 id="other-filters">Other Filters</h2>
+
<p>Android Market uses other application characteristics to determine whether to show or hide an application for a particular user on a given device, as described in the table below. </p>
-<p class="table-caption"><strong>Table 2.</strong> Application and publishing characteristics that affect filtering on Market.</p>
+<p id="table2" class="table-caption"><strong>Table 2.</strong> Application and publishing
+characteristics that affect filtering on Market.</p>
<table> <tr>
<th>Filter Name</th> <th>How It Works</th> </tr>
@@ -351,3 +362,38 @@
developer devices or unreleased devices.</p></td> </tr> </table>
+
+
+<h2 id="advanced-filters">Advanced Manifest Filters</h2>
+
+<p>In addition to the manifest elements in <a href="#table1">table 1</a>, Android Market can also
+filter applications based on the advanced manifest elements in table 3.</p>
+
+<p>These manifest elements and the filtering they trigger are for exceptional use-cases
+only. They are designed for some types of high-performance games and similar applications that
+require strict controls on application distribution. <strong>Most applications should never use
+these filters</strong>.</p>
+
+<p id="table3" class="table-caption"><strong>Table 3.</strong> Advanced manifest elements for
+Android Market filtering.</p>
+<table>
+ <tr><th>Manifest Element</th><th>Summary</th></tr>
+ <tr>
+ <td><nobr><a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+<compatible-screens>}</a></nobr></td>
+ <td>
+ <p>Android Market filters the application if the device screen size and density does not match
+any of the screen configurations (declared by a {@code <screen>} element) in the {@code
+<compatible-screens>} element.</p>
+ <p class="caution"><strong>Caution:</strong> Normally, <strong>you should not use
+this manifest element</strong>. Using this element can dramatically
+reduce the potential user base for your application, by excluding all combinations of screen size
+and density that you have not listed. You should instead use the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> manifest element (described above in <a href="#table1">table
+1</a>) to enable screen compatibility mode for screen configurations you have not accounted for
+with alternative resources.</p>
+ </td>
+ </tr>
+</table>
+
diff --git a/docs/html/guide/developing/testing/index.jd b/docs/html/guide/developing/testing/index.jd
index 8ffaf58..8a08959 100644
--- a/docs/html/guide/developing/testing/index.jd
+++ b/docs/html/guide/developing/testing/index.jd
@@ -18,14 +18,14 @@
which guides you through a more complex testing scenario.
</p>
<dl>
- <dt><a href="testing_eclipse.html">Testing in Eclipse, with ADT</a></dt>
+ <dt><a href="testing_eclipse.html">Testing from Eclipse, with ADT</a></dt>
<dd>
The ADT plugin lets you quickly set up and manage test projects directly in
the Eclipse UI. Once you have written your tests, you can build and run them and
then see the results in the Eclipse JUnit view. You can also use the SDK command-line
tools to execute your tests if needed.
</dd>
- <dt><a href="testing_otheride.html">Testing in Other IDEs</a></dt>
+ <dt><a href="testing_otheride.html">Testing from Other IDEs</a></dt>
<dd>
The SDK command-line tools provide the same capabilities as the ADT plugin. You can
use them to set up and manage test projects, build your test application,
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 0ea4b83..24ccfdb 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -65,6 +65,9 @@
<li><a href="<?cs var:toroot ?>guide/topics/fundamentals/fragments.html">
<span class="en">Fragments</span>
</a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/providers/loaders.html">
+ <span class="en">Loaders</span>
+ </a><span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>guide/topics/fundamentals/tasks-and-back-stack.html">
<span class="en">Tasks and Back Stack</span>
</a></li>
@@ -95,8 +98,9 @@
<ul>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/ui/index.html">
- <span class="en">User Interface</span>
- </a></div>
+ <span class="en">User Interface</span>
+ </a>
+ <span class="new">more!</span></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/ui/declaring-layout.html">
<span class="en">Declaring Layout</span>
@@ -207,6 +211,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/manifest/activity-alias-element.html"><activity-alias></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/application-element.html"><application></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/category-element.html"><category></a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/manifest/compatible-screens-element.html"><compatible-screens></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/data-element.html"><data></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/instrumentation-element.html"><instrumentation></a></li>
@@ -233,8 +238,9 @@
<ul>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/graphics/index.html">
- <span class="en">Graphics</span>
- </a></div>
+ <span class="en">Graphics</span>
+ </a>
+ <span class="new">more!</span></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/2d-graphics.html">
<span class="en">2D Graphics</span>
@@ -253,15 +259,15 @@
</a></li>
</ul>
</li>
- <li>
- <a href="<?cs var:toroot ?>guide/topics/clipboard/copy-paste.html">
- <span class="en">Copying and Pasting</span>
- </a>
- <span class="new">new!</span>
- </li>
<li><a href="<?cs var:toroot ?>guide/topics/media/index.html">
<span class="en">Audio and Video</span>
</a></li>
+ <li>
+ <a href="<?cs var:toroot ?>guide/topics/clipboard/copy-paste.html">
+ <span class="en">Copy and Paste</span>
+ </a>
+ <span class="new">new!</span>
+ </li>
<!--<li class="toggle-list">
<div><a style="color:gray;">Sensors</a></div>
<ul>
diff --git a/docs/html/guide/topics/clipboard/copy-paste.jd b/docs/html/guide/topics/clipboard/copy-paste.jd
index 9a50a35..6c86f47 100644
--- a/docs/html/guide/topics/clipboard/copy-paste.jd
+++ b/docs/html/guide/topics/clipboard/copy-paste.jd
@@ -1,4 +1,4 @@
-page.title=Copying and Pasting
+page.title=Copy and Paste
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/manifest/compatible-screens-element.jd b/docs/html/guide/topics/manifest/compatible-screens-element.jd
new file mode 100644
index 0000000..9fb0fd2
--- /dev/null
+++ b/docs/html/guide/topics/manifest/compatible-screens-element.jd
@@ -0,0 +1,108 @@
+page.title=<compatible-screens>
+@jd:body
+
+<dl class="xml">
+<dt>syntax:</dt>
+<dd>
+<pre>
+<<a href="#compatible-screens">compatible-screens</a>>
+ <<a href="#screen">screen</a> android:<a href="#screenSize">screenSize</a>=["small" | "normal" | "large" | "xlarge"]
+ android:<a href="#screenDensity">screenDensity</a>=["ldpi" | "mdpi" | "hdpi" | "xhdpi"] />
+ ...
+</compatible-screens>
+</pre>
+</dd>
+
+<dt>contained in:</dt>
+<dd><code><a
+href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code></dd>
+
+<dt>description:</dt>
+<dd>Specifies each screen configuration with which the application is compatible. Only one instance
+of the {@code <compatible-screens>} element is allowed in the manifest, but it can
+contain multiple <code><screen></code> elements. Each <code><screen></code> element
+specifies a specific screen size-density combination with which the application is compatible.
+
+ <p>The Android system <em>does not</em> read the {@code <compatible-screens>} manifest
+element (neither at install-time nor at runtime). This element is informational only and may be used
+by external services (such as Android Market) to better understand the application's compatibility
+with specific screen configurations and enable filtering for users. Any screen configuration that is
+<em>not</em> declared in this element is a screen with which the application is <em>not</em>
+compatible. Thus, external services (such as Android Market) should not provide the application to
+devices with such screens.</p>
+
+ <p class="caution"><strong>Caution:</strong> Normally, <strong>you should not use this manifest
+element</strong>. Using this element can dramatically reduce the potential user base for your
+application, by not allowing users to install your application if they have a device with a screen
+configuration that you have not listed. You should use it only as a last resort, when the
+application absolutely does not work with all screen configurations. Instead of using this element,
+you should follow the guide to <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+Multiple Screens</a>, in order to provide complete support for multiple screens, by adding
+alternative resources for different screen sizes and densities.</p>
+
+ <p>If you want to set only a minimum screen <em>size</em> for your your application, then you
+should use the <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> element. For example, if you want your application to be available
+only for <em>large</em> and <em>xlarge</em> screen devices, the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> element allows you to declare that your application does not
+support <em>small</em> and <em>normal</em> screen sizes. External services (such as Android
+Market) will filter your application accordingly. You can also use the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+<supports-screens>}</a> element to declare whether the system should resize your
+application for different screen sizes.</p>
+
+ <p>Also see the <a href="{@docRoot}guide/appendix/market-filters.html">Market Filters</a>
+document for more information about how Android Market filters applications using this and
+other manifest elements.</p>
+
+</dd>
+
+<dt>child elements:</dt>
+<dd>
+ <dl class="tag-list">
+
+ <dt id="screen">{@code <screen>}</dt>
+ <dd>Specifies a single screen configuration with which the application is compatible.
+ <p>At least one instance of this element must be placed inside the {@code
+<compatible-screens>} element. This element <em>must include both</em> the {@code
+android:screenSize} and {@code android:screenDensity} attributes (if you do not declare both
+attributes, then the element is ignored).</p>
+
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt id="screenSize"><code>android:screenSize</code></dt>
+ <dd><b>Required.</b> Specifies the screen size for this screen configuration.
+ <p>Accepted values:</p>
+ <ul>
+ <li>{@code small}</li>
+ <li>{@code normal}</li>
+ <li>{@code large}</li>
+ <li>{@code xlarge}</li>
+ </ul>
+ <p>For information about the different screen sizes, see <a
+href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple Screens</a>.</p>
+ </dd>
+ <dt id="screenDensity"><code>android:screenDensity</code></dt>
+ <dd><b>Required.</b> Specifies the screen density for this screen configuration.
+ <p>Accepted values:</p>
+ <ul>
+ <li>{@code ldpi}</li>
+ <li>{@code mdpi}</li>
+ <li>{@code hdpi}</li>
+ <li>{@code xhdpi}</li>
+ </ul>
+ <p>For information about the different screen densities, see <a
+href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple Screens</a>.</p>
+ </dd>
+ </dl>
+ </dd>
+ </dl>
+</dd>
+<dt>introduced in:</dt>
+<dd>API Level 9</dd>
+<dt>see also:</dt>
+<dd><a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></dd>
+<dd><a href="{@docRoot}guide/appendix/market-filters.html">Market Filters</a></dd>
+</dl>
diff --git a/docs/html/guide/topics/manifest/supports-screens-element.jd b/docs/html/guide/topics/manifest/supports-screens-element.jd
index 64a7a58..92c769e 100644
--- a/docs/html/guide/topics/manifest/supports-screens-element.jd
+++ b/docs/html/guide/topics/manifest/supports-screens-element.jd
@@ -6,7 +6,8 @@
<dt>syntax:</dt>
<dd>
<pre class="stx">
-<supports-screens android:<a href="#small">smallScreens</a>=["true" | "false"]
+<supports-screens android:<a href="#resizeable">resizeable</a>=["true" | "false"]
+ android:<a href="#small">smallScreens</a>=["true" | "false"]
android:<a href="#normal">normalScreens</a>=["true" | "false"]
android:<a href="#large">largeScreens</a>=["true" | "false"]
android:<a href="#xlarge">xlargeScreens</a>=["true" | "false"]
@@ -19,17 +20,33 @@
<dt>description:</dt>
<dd>Lets you specify the screen dimensions the
-application supports. By default a modern application (using API Level 4 or higher) supports all
-screen sizes and must explicitly disable certain screen sizes here;
-older applications are assumed to support only the "normal"
-screen size. Note that screen size is a separate axis from
-density. Screen size is determined as the available pixels to an application
-after density scaling has been applied.
+application supports. By default, a modern application (using API Level 4 or higher) supports all
+screen sizes; older applications are assumed to support only the "normal" screen size. Screen
+size is determined as the available pixels to an application after density scaling has been
+applied. (Note that screen size is a separate axis from screen density.)
-<p>Based on the target device screen density, the Android
-framework will scale down assets by a factor of 0.75 (low dpi screens)
-or scale them up by a factor of 1.5 (high dpi screens).
-The screen density is expressed as dots-per-inch (dpi).</p>
+<p>An application "supports" a given screen size if it fills the entire screen and works as
+expected. By default, the system will resize your application to fill the screen, if you have set
+either <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+minSdkVersion}</a> or <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+targetSdkVersion}</a> to {@code "4"} or higher. Resizing works well for most applications and
+you don't have to do any extra work to make your application work on larger screens.</p>
+
+<p>In addition to allowing the system to resize your application, you can add additional support
+for different screen sizes by providing <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">alternative
+layout resources</a> for different sizes. For instance, you might want to modify the layout
+of an activity when it is on a tablet or similar device that has an <em>xlarge</em> screen.</p>
+
+<p>If your application does not support <em>large</em> or <em>xlarge</em> screens, then you should
+declare that it is not resizeable by setting <a href="#resizeable">{@code android:resizeable}</a> to
+{@code "false"}, so that the system will not resize your application on larger screens.</p>
+
+<p>If your application does not support <em>small</em> screens, then
+there isn't much the system can do to make the application work well on a smaller screen, so
+external services (such as Android Market) should not allow users to install the application on such
+screens.</p>
+
<p>For more information, see
<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.</p>
@@ -38,16 +55,40 @@
<dt>attributes:</dt>
<dd>
-<dl class="attr"><dt><a name="small"></a>{@code android:smallScreens}</dt>
+<dl class="attr">
+
+ <dt><a name="resizeable"></a>{@code android:resizeable}</dt>
+ <dd>Indicates whether the application is resizeable for different screen sizes. This attribute is
+true, by default, if you have set either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> to
+{@code "4"} or higher. Otherwise, it is false by default. If set false, the system will not resize
+your application when run on <em>large</em> or <em>xlarge</em> screens. Instead, the
+application appears in a "postage stamp" that equals the <em>normal</em> screen size that your
+application does support. This is less than an ideal experience for users, because the
+application appears smaller than the available screen, but it might help your application run
+normally if it were designed only for the <em>normal</em> screen size and some behaviors do not work
+when resized.</p>
+ <p>To provide the best experience on all screen sizes, you should allow resizing and, if your
+application does not work well on larger screens, follow the guide to <a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a> to enable
+additional screen support.</p>
+ </dd>
+
+
+ <dt><a name="small"></a>{@code android:smallScreens}</dt>
<dd>Indicates whether the application supports smaller screen form-factors.
A small screen is defined as one with a smaller aspect ratio than
the "normal" (traditional HVGA) screen. An application that does
not support small screens <em>will not be available</em> for
- small screen devices, because there is little the platform can do
- to make such an application work on a smaller screen. If the application has set the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element's
-{@code android:minSdkVersion} or {@code android:targetSdkVersion} attribute to "4" or higher,
-the default value for this is "true", any value less than "4" results in this set to "false".
+ small screen devices from external services (such as Android Market), because there is little
+the platform can do
+ to make such an application work on a smaller screen. If the application has set either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> to
+{@code "4"} or higher,
+the default value for this is {@code "true"}, any value less than {@code "4"} results in this set to
+{@code "false"}.
</dd>
<dt><a name="normal"></a>{@code android:normalScreens}</dt>
@@ -61,38 +102,44 @@
<dt><a name="large"></a>{@code android:largeScreens}</dt>
<dd>Indicates whether the application supports larger screen form-factors.
A large screen is defined as a screen that is significantly larger
- than a "normal" phone screen, and thus may require some special care
- on the application's part to make good use of it. An application that
- does not support large screens (declares this "false")—but does support "normal" or
-"small" screens—will be placed as a "postage stamp" on
- a large screen, so that it retains the dimensions it was originally
- designed for. If the application has set the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element's
-{@code android:minSdkVersion} or {@code android:targetSdkVersion} attribute to "4" or higher,
-the default value for this is "true", any value less than "4" results in this set to "false".
+ than a "normal" phone screen, and thus might require some special care
+ on the application's part to make good use of it, though it may rely on resizing by the
+system to fill the screen. If the application has set either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> to
+{@code "4"} or higher,
+the default value for this is {@code "true"}, any value less than {@code "4"} results in this set to
+{@code "false"}.
</dd>
-
+
<dt><a name="xlarge"></a>{@code android:xlargeScreens}</dt>
<dd>Indicates whether the application supports extra large screen form-factors.
An xlarge screen is defined as a screen that is significantly larger
than a "large" screen, such as a tablet (or something larger) and may require special care
- on the application's part to make good use of it. An application that
- does not support xlarge screens (declares this "false")—but does support "large",
-"normal", or "small" screens—will be placed as a "postage stamp" on
- an xlarge screen, so that it retains the dimensions it was originally
- designed for. If the application has set the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element's
-{@code android:minSdkVersion} or {@code android:targetSdkVersion} attribute to "4" or higher,
-the default value for this is "true", any value less than "4" results in this set to "false".
+ on the application's part to make good use of it, though it may rely on resizing by the
+system to fill the screen. If the application has set either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> to
+{@code "4"} or higher,
+the default value for this is {@code "true"}, any value less than {@code "4"} results in this set to
+{@code "false"}.
<p>This attribute was introduced in API Level 9.</p>
</dd>
<dt><a name="any"></a>{@code android:anyDensity}</dt>
<dd>Indicates whether the application includes resources to accommodate any screen
density. Older applications (before API Level 4) are assumed unable to
- accomodate all densities and this is "false" by default. Applications using
- API Level 4 or higher are assumed able to and this is "true" by default.
+ accomodate all densities and this is {@code "false"} by default. If the application has set
+either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> to
+{@code "4"} or higher,
+the default value for this is {@code "true"}. Otherwise, it is {@code "false"}.
You can explicitly supply your abilities here.
+ <p>Based on the "standard" device screen density (medium dpi), the Android framework will scale
+down application assets by a factor of 0.75 (low dpi screens) or scale them up by a factor of 1.5
+(high dpi screens), when you don't provide alternative resources for a specifc screen density. The
+screen density is expressed as dots-per-inch (dpi).</p>
</dd>
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 5242126..0828e8b 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -644,33 +644,46 @@
</tr>
<tr>
- <td rowspan="4">Touchscreen</td>
+ <td rowspan="5">Touchscreen</td>
+ <td><code>android.hardware.faketouch</code></td>
+ <td>The application uses basic touch interaction events, such as "click down", "click
+up", and drag.</td>
+ <td>When declared, this indicates that the application is compatible with a device that offers an
+emulated touchscreen (or better). A device that offers an emulated touchscreen provides a user input
+system that can emulate a subset of touchscreen capabilities. An example of such an input system is
+a mouse or remote control that drives an on-screen cursor. If your application does not require
+complicated gestures and you want your application available to devices with an emulated
+touchscreen, you should declare this feature.</td>
+</tr>
+<tr>
<td><code>android.hardware.touchscreen</code></td>
- <td>The application uses touchscreen capabilities on the device.</td>
+ <td>The application uses touchscreen capabilities, for gestures more interactive
+than basic touches, such as a fling. This is a superset of the faketouch features.</td>
<td></td>
</tr>
<tr>
<td><code>android.hardware.touchscreen.multitouch</code></td>
- <td>Subfeature. The application uses basic two-point multitouch capabilities on the device
-screen.</td>
+ <td>The application uses basic two-point multitouch capabilities on the device
+screen, such as for pinch gestures, but does not need to track touches independently. This
+is a superset of touchscreen features.</td>
<td>If declared with the <code>"android:required="true"</code> attribute, this
-subfeature implicitly declares the <code>android.hardware.touchscreen</code>
+implicitly declares the <code>android.hardware.touchscreen</code>
parent feature. </td>
</tr>
<tr>
<td><code>android.hardware.touchscreen.multitouch.distinct</code></td>
<td>Subfeature. The application uses advanced multipoint multitouch
capabilities on the device screen, such as for tracking two or more points fully
-independently.</td>
+independently. This is a superset of multitouch features.</td>
<td rowspan="2">If declared with the <code>"android:required="true"</code> attribute, this
-subfeature implicitly declares the
+implicitly declares the
<code>android.hardware.touchscreen.multitouch</code> parent feature. </td>
</tr>
<tr>
<td><code>android.hardware.touchscreen.multitouch.jazzhand</code></td>
- <td>Subfeature. The application uses advanced multipoint multitouch
+ <td>The application uses advanced multipoint multitouch
capabilities on the device screen, for tracking up to five points fully
-independently.</td>
+independently. This is a superset of distinct multitouch features.</td>
</tr>
<tr>
diff --git a/docs/html/guide/topics/providers/loaders.jd b/docs/html/guide/topics/providers/loaders.jd
new file mode 100644
index 0000000..c54656c
--- /dev/null
+++ b/docs/html/guide/topics/providers/loaders.jd
@@ -0,0 +1,492 @@
+page.title=Using Loaders
+@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#summary">Loader API Summary</a></li>
+ <li><a href="#app">Using Loaders in an Application</a>
+ <ol>
+ <li><a href="#requirements"></a></li>
+ <li><a href="#starting">Starting a Loader</a></li>
+ <li><a href="#restarting">Restarting a Loader</a></li>
+ <li><a href="#callback">Using the LoaderManager Callbacks</a></li>
+ </ol>
+ </li>
+ <li><a href="#example">Example</a>
+ <ol>
+ <li><a href="#more_examples">More Examples</a></li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.app.LoaderManager}</li>
+ <li>{@link android.content.Loader}</li>
+
+ </ol>
+
+ <h2>Related samples</h2>
+ <ol>
+ <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentCursorLoader.html"> FragmentCursorLoader</a></li>
+ <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a></li>
+ </ol>
+ </div>
+</div>
+
+<p>Introduced in Android 3.0, loaders make it easy to asynchronously load data
+in an activity or fragment. Loaders have these characteristics:</p>
+ <ul>
+ <li>They are available to every {@link android.app.Activity} and {@link
+android.app.Fragment}.</li>
+ <li>They provide asynchronous loading of data.</li>
+ <li>They monitor the source of their data and deliver new results when the
+content changes.</li>
+ <li>They automatically reconnect to the last loader's cursor when being
+recreated after a configuration change. Thus, they don't need to re-query their
+data.</li>
+ </ul>
+
+<h2 id="summary">Loader API Summary</h2>
+
+<p>There are multiple classes and interfaces that may be involved in using
+loaders in an application. They are summarized in this table:</p>
+
+<table>
+ <tr>
+ <th>Class/Interface</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>{@link android.app.LoaderManager}</td>
+ <td>An abstract class associated with an {@link android.app.Activity} or
+{@link android.app.Fragment} for managing one or more {@link
+android.content.Loader} instances. This helps an application manage
+longer-running operations in conjunction with the {@link android.app.Activity}
+or {@link android.app.Fragment} lifecycle; the most common use of this is with a
+{@link android.content.CursorLoader}, however applications are free to write
+their own loaders for loading other types of data.
+ <br />
+ <br />
+ There is only one {@link android.app.LoaderManager} per activity or fragment. But a {@link android.app.LoaderManager} can have
+multiple loaders.</td>
+ </tr>
+ <tr>
+ <td>{@link android.app.LoaderManager.LoaderCallbacks}</td>
+ <td>A callback interface for a client to interact with the {@link
+android.app.LoaderManager}. For example, you use the {@link
+android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
+callback method to create a new loader.</td>
+ </tr>
+ <tr>
+ <td>{@link android.content.Loader}</td>
+ <td>An abstract class that performs asynchronous loading of data. This is
+the base class for a loader. You would typically use {@link
+android.content.CursorLoader}, but you can implement your own subclass. While
+loaders are active they should monitor the source of their data and deliver new
+results when the contents change. </td>
+ </tr>
+ <tr>
+ <td>{@link android.content.AsyncTaskLoader}</td>
+ <td>Abstract loader that provides an {@link android.os.AsyncTask} to do the work.</td>
+ </tr>
+ <tr>
+ <td>{@link android.content.CursorLoader}</td>
+ <td>A subclass of {@link android.content.AsyncTaskLoader} that queries the
+{@link android.content.ContentResolver} and returns a {@link
+android.database.Cursor}. This class implements the {@link
+android.content.Loader} protocol in a standard way for querying cursors,
+building on {@link android.content.AsyncTaskLoader} to perform the cursor query
+on a background thread so that it does not block the application's UI. Using
+this loader is the best way to asynchronously load data from a {@link
+android.content.ContentProvider}, instead of performing a managed query through
+the fragment or activity's APIs.</td>
+ </tr>
+</table>
+
+<p>The classes and interfaces in the above table are the essential components
+you'll use to implement a loader in your application. You won't need all of them
+for each loader you create, but you'll always need a reference to the {@link
+android.app.LoaderManager} in order to initialize a loader and an implementation
+of a {@link android.content.Loader} class such as {@link
+android.content.CursorLoader}. The following sections show you how to use these
+classes and interfaces in an application.</p>
+
+<h2 id ="app">Using Loaders in an Application</h2>
+<p>This section describes how to use loaders in an Android application. An
+application that uses loaders typically includes the following:</p>
+<ul>
+ <li>An {@link android.app.Activity} or {@link android.app.Fragment}.</li>
+ <li>An instance of the {@link android.app.LoaderManager}.</li>
+ <li>A {@link android.content.CursorLoader} to load data backed by a {@link
+android.content.ContentProvider}. Alternatively, you can implement your own subclass
+of {@link android.content.Loader} or {@link android.content.AsyncTaskLoader} to
+load data from some other source.</li>
+ <li>An implementation for {@link android.app.LoaderManager.LoaderCallbacks}.
+This is where you create new loaders and manage your references to existing
+loaders.</li>
+<li>A way of displaying the loader's data, such as a {@link
+android.widget.SimpleCursorAdapter}.</li>
+ <li>A data source, such as a {@link android.content.ContentProvider}, when using a
+{@link android.content.CursorLoader}.</li>
+</ul>
+<h3 id="starting">Starting a Loader</h3>
+
+<p>The {@link android.app.LoaderManager} manages one or more {@link
+android.content.Loader} instances within an {@link android.app.Activity} or
+{@link android.app.Fragment}. There is only one {@link
+android.app.LoaderManager} per activity or fragment.</p>
+
+<p>You typically
+initialize a {@link android.content.Loader} within the activity's {@link
+android.app.Activity#onCreate onCreate()} method, or within the fragment's
+{@link android.app.Fragment#onActivityCreated onActivityCreated()} method. You
+do this as follows:</p>
+
+<pre>// Prepare the loader. Either re-connect with an existing one,
+// or start a new one.
+getLoaderManager().initLoader(0, null, this);</pre>
+
+<p>The {@link android.app.LoaderManager#initLoader initLoader()} method takes
+the following parameters:</p>
+<ul>
+ <li>A unique ID that identifies the loader. In this example, the ID is 0.</li>
+<li>Optional arguments to supply to the loader at
+construction (<code>null</code> in this example).</li>
+
+<li>A {@link android.app.LoaderManager.LoaderCallbacks} implementation, which
+the {@link android.app.LoaderManager} calls to report loader events. In this
+example, the local class implements the {@link
+android.app.LoaderManager.LoaderCallbacks} interface, so it passes a reference
+to itself, {@code this}.</li>
+</ul>
+<p>The {@link android.app.LoaderManager#initLoader initLoader()} call ensures that a loader
+is initialized and active. It has two possible outcomes:</p>
+<ul>
+ <li>If the loader specified by the ID already exists, the last created loader
+is reused.</li>
+ <li>If the loader specified by the ID does <em>not</em> exist,
+{@link android.app.LoaderManager#initLoader initLoader()} triggers the
+{@link android.app.LoaderManager.LoaderCallbacks} method {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}.
+This is where you implement the code to instantiate and return a new loader.
+For more discussion, see the section <a
+href="#onCreateLoader">onCreateLoader</a>.</li>
+</ul>
+<p>In either case, the given {@link android.app.LoaderManager.LoaderCallbacks}
+implementation is associated with the loader, and will be called when the
+loader state changes. If at the point of this call the caller is in its
+started state, and the requested loader already exists and has generated its
+data, then the system calls {@link
+android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+immediately (during {@link android.app.LoaderManager#initLoader initLoader()}),
+so you must be prepared for this to happen. See <a href="#onLoadFinished">
+onLoadFinished</a> for more discussion of this callback</p>
+
+<p>Note that the {@link android.app.LoaderManager#initLoader initLoader()}
+method returns the {@link android.content.Loader} that is created, but you don't
+need to capture a reference to it. The {@link android.app.LoaderManager} manages
+the life of the loader automatically. The {@link android.app.LoaderManager}
+starts and stops loading when necessary, and maintains the state of the loader
+and its associated content. As this implies, you rarely interact with loaders
+directly (though for an example of using loader methods to fine-tune a loader's
+behavior, see the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> sample).
+You most commonly use the {@link
+android.app.LoaderManager.LoaderCallbacks} methods to intervene in the loading
+process when particular events occur. For more discussion of this topic, see <a
+href="#callback">Using the LoaderManager Callbacks</a>.</p>
+
+<h3 id="restarting">Restarting a Loader</h3>
+
+<p>When you use {@link android.app.LoaderManager#initLoader initLoader()}, as
+shown above, it uses an existing loader with the specified ID if there is one.
+If there isn't, it creates one. But sometimes you want to discard your old data
+and start over.</p>
+
+<p>To discard your old data, you use {@link
+android.app.LoaderManager#restartLoader restartLoader()}. For example, this
+implementation of {@link android.widget.SearchView.OnQueryTextListener} restarts
+the loader when the user's query changes. The loader needs to be restarted so
+that it can use the revised search filter to do a new query:</p>
+
+<pre>
+public boolean onQueryTextChanged(String newText) {
+ // Called when the action bar search text has changed. Update
+ // the search filter, and restart the loader to do a new query
+ // with this filter.
+ mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+ getLoaderManager().restartLoader(0, null, this);
+ return true;
+}</pre>
+
+<h3 id="callback">Using the LoaderManager Callbacks</h3>
+
+<p>{@link android.app.LoaderManager.LoaderCallbacks} is a callback interface
+that lets a client interact with the {@link android.app.LoaderManager}. </p>
+<p>Loaders, in particular {@link android.content.CursorLoader}, are expected to
+retain their data after being stopped. This allows applications to keep their
+data across the activity or fragment's {@link android.app.Activity#onStop
+onStop()} and {@link android.app.Activity#onStart onStart()} methods, so that
+when users return to an application, they don't have to wait for the data to
+reload. You use the {@link android.app.LoaderManager.LoaderCallbacks} methods
+when to know when to create a new loader, and to tell the application when it is
+ time to stop using a loader's data.</p>
+
+<p>{@link android.app.LoaderManager.LoaderCallbacks} includes these
+methods:</p>
+<ul>
+ <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} —
+Instantiate and return a new {@link android.content.Loader} for the given ID.
+</li></ul>
+<ul>
+ <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+— Called when a previously created loader has finished its load.
+</li></ul>
+<ul>
+ <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}
+ — Called when a previously created loader is being reset, thus making its
+data unavailable.
+</li>
+</ul>
+<p>These methods are described in more detail in the following sections.</p>
+
+<h4 id ="onCreateLoader">onCreateLoader</h4>
+
+<p>When you attempt to access a loader (for example, through {@link
+android.app.LoaderManager#initLoader initLoader()}), it checks to see whether
+the loader specified by the ID exists. If it doesn't, it triggers the {@link
+android.app.LoaderManager.LoaderCallbacks} method {@link
+android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. This
+is where you create a new loader. Typically this will be a {@link
+android.content.CursorLoader}, but you can implement your own {@link
+android.content.Loader} subclass. </p>
+
+<p>In this example, the {@link
+android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
+callback method creates a {@link android.content.CursorLoader}. You must build
+the {@link android.content.CursorLoader} using its constructor method, which
+requires the complete set of information needed to perform a query to the {@link
+android.content.ContentProvider}. Specifically, it needs:</p>
+<ul>
+ <li><em>uri</em> — The URI for the content to retrieve. </li>
+ <li><em>projection</em> — A list of which columns to return. Passing
+<code>null</code> will return all columns, which is inefficient. </li>
+ <li><em>selection</em> — A filter declaring which rows to return,
+formatted as an SQL WHERE clause (excluding the WHERE itself). Passing
+<code>null</code> will return all rows for the given URI. </li>
+ <li><em>selectionArgs</em> — You may include ?s in the selection, which will
+be replaced by the values from <em>selectionArgs</em>, in the order that they appear in
+the selection. The values will be bound as Strings. </li>
+ <li><em>sortOrder</em> — How to order the rows, formatted as an SQL
+ORDER BY clause (excluding the ORDER BY itself). Passing <code>null</code> will
+use the default sort order, which may be unordered.</li>
+</ul>
+<p>For example:</p>
+<pre>
+ // If non-null, this is the current filter the user has provided.
+String mCurFilter;
+...
+public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ // First, pick the base URI to use depending on whether we are
+ // currently filtering.
+ Uri baseUri;
+ if (mCurFilter != null) {
+ baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+ Uri.encode(mCurFilter));
+ } else {
+ baseUri = Contacts.CONTENT_URI;
+ }
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ + Contacts.DISPLAY_NAME + " != '' ))";
+ return new CursorLoader(getActivity(), baseUri,
+ CONTACTS_SUMMARY_PROJECTION, select, null,
+ Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+}</pre>
+<h4 id="onLoadFinished">onLoadFinished</h4>
+
+<p>This method is called when a previously created loader has finished its load.
+This method is guaranteed to be called prior to the release of the last data
+that was supplied for this loader. At this point you should remove all use of
+the old data (since it will be released soon), but should not do your own
+release of the data since its loader owns it and will take care of that.</p>
+
+
+<p>The loader will release the data once it knows the application is no longer
+using it. For example, if the data is a cursor from a {@link
+android.content.CursorLoader}, you should not call {@link
+android.database.Cursor#close close()} on it yourself. If the cursor is being
+placed in a {@link android.widget.CursorAdapter}, you should use the {@link
+android.widget.SimpleCursorAdapter#swapCursor swapCursor()} method so that the
+old {@link android.database.Cursor} is not closed. For example:</p>
+
+<pre>
+// This is the Adapter being used to display the list's data.<br
+/>SimpleCursorAdapter mAdapter;
+...
+
+public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mAdapter.swapCursor(data);
+}</pre>
+
+<h4 id="onLoaderReset">onLoaderReset</h4>
+
+<p>This method is called when a previously created loader is being reset, thus
+making its data unavailable. This callback lets you find out when the data is
+about to be released so you can remove your reference to it. </p>
+<p>This implementation calls
+{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}
+with a value of <code>null</code>:</p>
+
+<pre>
+// This is the Adapter being used to display the list's data.
+SimpleCursorAdapter mAdapter;
+...
+
+public void onLoaderReset(Loader<Cursor> loader) {
+ // This is called when the last Cursor provided to onLoadFinished()
+ // above is about to be closed. We need to make sure we are no
+ // longer using it.
+ mAdapter.swapCursor(null);
+}</pre>
+
+
+<h2 id="example">Example</h2>
+
+<p>As an example, here is the full implementation of a {@link
+android.app.Fragment} that displays a {@link android.widget.ListView} containing
+the results of a query against the contacts content provider. It uses a {@link
+android.content.CursorLoader} to manage the query on the provider.</p>
+
+<p>For an application to access a user's contacts, as shown in this example, its
+manifest must include the permission
+{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p>
+
+<pre>
+public static class CursorLoaderListFragment extends ListFragment
+ implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
+
+ // This is the Adapter being used to display the list's data.
+ SimpleCursorAdapter mAdapter;
+
+ // If non-null, this is the current filter the user has provided.
+ String mCurFilter;
+
+ @Override public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText("No phone numbers");
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new SimpleCursorAdapter(getActivity(),
+ android.R.layout.simple_list_item_2, null,
+ new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
+ new int[] { android.R.id.text1, android.R.id.text2 }, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ // Place an action bar item for searching.
+ MenuItem item = menu.add("Search");
+ item.setIcon(android.R.drawable.ic_menu_search);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ SearchView sv = new SearchView(getActivity());
+ sv.setOnQueryTextListener(this);
+ item.setActionView(sv);
+ }
+
+ public boolean onQueryTextChange(String newText) {
+ // Called when the action bar search text has changed. Update
+ // the search filter, and restart the loader to do a new query
+ // with this filter.
+ mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+ getLoaderManager().restartLoader(0, null, this);
+ return true;
+ }
+
+ @Override public boolean onQueryTextSubmit(String query) {
+ // Don't care about this.
+ return true;
+ }
+
+ @Override public void onListItemClick(ListView l, View v, int position, long id) {
+ // Insert desired behavior here.
+ Log.i("FragmentComplexList", "Item clicked: " + id);
+ }
+
+ // These are the Contacts rows that we will retrieve.
+ static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
+ Contacts._ID,
+ Contacts.DISPLAY_NAME,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.PHOTO_ID,
+ Contacts.LOOKUP_KEY,
+ };
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ // First, pick the base URI to use depending on whether we are
+ // currently filtering.
+ Uri baseUri;
+ if (mCurFilter != null) {
+ baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+ Uri.encode(mCurFilter));
+ } else {
+ baseUri = Contacts.CONTENT_URI;
+ }
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ + Contacts.DISPLAY_NAME + " != '' ))";
+ return new CursorLoader(getActivity(), baseUri,
+ CONTACTS_SUMMARY_PROJECTION, select, null,
+ Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mAdapter.swapCursor(data);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ // This is called when the last Cursor provided to onLoadFinished()
+ // above is about to be closed. We need to make sure we are no
+ // longer using it.
+ mAdapter.swapCursor(null);
+ }
+}</pre>
+<h3 id="more_examples">More Examples</h3>
+
+<p>There are a few different samples in <strong>ApiDemos</strong> that
+illustrate how to use loaders:</p>
+<ul>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentCursorLoader.html"> FragmentCursorLoader</a> — A complete version of the
+snippet shown above.</li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — An example of how to use throttling to
+reduce the number of queries a content provider does then its data changes.</li>
+</ul>
+
+<p>For information on downloading and installing the SDK samples, see <a
+href="http://developer.android.com/resources/samples/get.html"> Getting the
+Samples</a>. </p>
+
diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd
index 588b05b..46ccdf8 100644
--- a/docs/html/guide/topics/ui/drag-drop.jd
+++ b/docs/html/guide/topics/ui/drag-drop.jd
@@ -88,8 +88,8 @@
<h2>Related Samples</h2>
<ol>
<li>
- <a href="{@docRoot}resources/samples/Honeycomb-Gallery/index.html">
- Honeycomb-Gallery</a> sample application.
+ <a href="{@docRoot}resources/samples/HoneycombGallery/index.html">
+ Honeycomb Gallery</a>.
</li>
<li>
<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html">
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index febdb9a..5839064 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -569,7 +569,7 @@
tags: ['sample', 'new', 'newfeature', 'widgets'],
path: 'samples/StackWidget/index.html',
title: {
- en: 'StackView App Widget'
+ en: 'StackView Widget'
},
description: {
en: 'Demonstrates how to create a simple collection widget containing a StackView.'
@@ -619,7 +619,7 @@
tags: ['sample', 'widgets', 'newfeature', 'new'],
path: 'samples/WeatherListWidget/index.html',
title: {
- en: 'Weather List Widget Sample'
+ en: 'Weather List Widget'
},
description: {
en: 'A more complex collection-widget example which uses a ContentProvider as its data source.'
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index 4be98e6..136bcd9 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -131,7 +131,10 @@
the activity.</p>
<p>For more information about using fragments, read the <a
-href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> documentation.</p>
+href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> documentation. Several
+samples are also available in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">
+API Demos</a> application.</p>
@@ -181,7 +184,10 @@
android:targetSdkVersion}</a> to {@code "11"}.</p>
<p>For more information about the Action Bar, read the <a
-href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> documentation.</p>
+href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> documentation. Several
+samples are also available in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#ActionBar">
+API Demos</a> application.</p>
@@ -221,9 +227,11 @@
object) at a time, but one {@link android.content.ClipData} can contain multiple {@link
android.content.ClipData.Item}s.</p>
-<p>For more information, see the {@link android.content.ClipData} class reference. You can also see
-an example implementation of copy and paste in the <a
-href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sample application.</p>
+<p>For more information, read the <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy
+and Paste</a> documentation. You can also see a simple implementation of copy and paste in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/content/ClipboardSample.
+html">API Demos</a> and a more complete implementation in the <a
+href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> application.</p>
@@ -265,6 +273,13 @@
android.content.ClipData} object, this is not related to the system clipboard. A drag and drop
operation should never put the dragged data in the system clipboard.</p>
+<p>For more information, read the <a href="{@docRoot}guide/topics/ui/drag-drop.html">Dragging and
+Dropping</a> documentation. You can also see an implementation of drag and drop in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html">
+API Demos</a> application and the <a
+href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a>
+application.</p>
+
<h3>App widgets</h3>
@@ -279,8 +294,8 @@
android.widget.ListView}, and {@link android.widget.StackView} that are backed by remote data,
such as from a content provider.</p>
-<p>The {@link android.appwidget.AppWidgetProviderInfo} class (defined with an {@code
-<appwidget-provider> XML file) also supports two new fields: {@link
+<p>The {@link android.appwidget.AppWidgetProviderInfo} class (defined in XML with an {@code
+<appwidget-provider>} element) also supports two new fields: {@link
android.appwidget.AppWidgetProviderInfo#autoAdvanceViewId} and {@link
android.appwidget.AppWidgetProviderInfo#previewImage}. The {@link
android.appwidget.AppWidgetProviderInfo#autoAdvanceViewId} field lets you specify the view ID of the
@@ -295,7 +310,10 @@
app widget for your application and set it up how you'd like your preview image to appear, then save
it and place it in your application's drawable resources.</p>
-
+<p>You can see an implementation of the new app widget features in the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView App Widget</a> and <a
+href="{@docRoot}resources/samples/WeatherListWidget/index.html">Weather List Widget</a>
+applications.</p>
@@ -319,7 +337,6 @@
-
<h3>Content loaders</h3>
<p>New framework APIs facilitate asynchronous loading of data using the {@link
@@ -333,6 +350,13 @@
changed, then call {@link android.app.LoaderManager#initLoader initLoader()} to initialize the
loader for your activity or fragment.</p>
+<p>For more information, read the <a
+href="{@docRoot}guide/topics/providers/loaders.html">Loaders</a> documentation. You can also see
+example code using loaders in the <a
+href="{@docRoot}samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.html">
+FragmentListCursorLoader</a> and <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
+LoaderThrottle</a> samples.</p>
@@ -395,7 +419,10 @@
discussed above.</p>
<p>For more information, see the <a
-href="{@docRoot}guide/topics/graphics/animation.html">Animation</a> developer guide.</p>
+href="{@docRoot}guide/topics/graphics/animation.html">Property Animation</a> documentation. You can
+also see several samples using the animation APIs in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API
+Demos</a> application.</p>
@@ -582,7 +609,10 @@
<p>Renderscript is a runtime 3D framework that provides both an API for building 3D scenes as well
as a special, platform-independent shader language for maximum performance. Using Renderscript, you
can accelerate graphics operations and data processing. Renderscript is an ideal way to create
-high-performance 3D effects for applications, wallpapers, carousels, and more.</p></li>
+high-performance 3D effects for applications, wallpapers, carousels, and more.</p>
+<p>For more information, see the <a
+href="{@docRoot}guide/topics/graphics/renderscript.html">3D Rendering and Computation with
+Renderscript</a> documentation.</p></li>
</ul>
@@ -717,21 +747,23 @@
or <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
android:targetSdkVersion}</a> attribute's value to {@code "11"}.</p>
-<p>However, the following properties allow you to disable split touch events for
-specific view groups, certain activities, or the entire application.</p>
+<p>However, the following properties allow you to disable split touch events across views inside
+specific view groups and across windows.</p>
<ul>
<li>The {@link android.R.attr#splitMotionEvents android:splitMotionEvents} attribute for view groups
-allows you to disable split motion events for the children in a layout. For example:
+allows you to disable split touch events that occur between child views in a layout. For example:
<pre>
<LinearLayout android:splitMotionEvents="false" ... >
...
</LinearLayout>
</pre>
+<p>This way, child views in the linear layout cannot split touch events—only one view can
+receive touch events at a time.</p>
</li>
<li>The {@link android.R.attr#windowEnableSplitTouch android:windowEnableSplitTouch} style property
-allows you to disable split motion events across windows by applying it to a theme for the activity
+allows you to disable split touch events across windows, by applying it to a theme for the activity
or entire application. For example:
<pre>
<style name="NoSplitMotionEvents" parent="android:Theme.Holo">
@@ -744,7 +776,8 @@
href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>,
only touch events within the current activity window are accepted. For example, by disabling split
touch events across windows, the system bar cannot receive touch events at the same time as the
-activity.</p>
+activity. This does <em>not</em> affect whether views inside the activity can split touch
+events—by default, the activity can still split touch events across views.</p>
<p>For more information about creating a theme, read <a
href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a>.</p>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index bd903da..b2f4379 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -245,25 +245,80 @@
}
}
+ /**
+ * Possible bitmap configurations. A bitmap configuration describes
+ * how pixels are stored. This affects the quality (color depth) as
+ * well as the ability to display transparent/translucent colors.
+ */
public enum Config {
// these native values must match up with the enum in SkBitmap.h
+
+ /**
+ * Each pixel is stored as a single translucency (alpha) channel.
+ * This is very useful to efficiently store masks for instance.
+ * No color information is stored.
+ * With this configuration, each pixel requires 1 byte of memory.
+ */
ALPHA_8 (2),
+
+ /**
+ * Each pixel is stored on 2 bytes and only the RGB channels are
+ * encoded: red is stored with 5 bits of precision (32 possible
+ * values), green is stored with 6 bits of precision (64 possible
+ * values) and blue is stored with 5 bits of precision.
+ *
+ * This configuration can produce slight visual artifacts depending
+ * on the configuration of the source. For instance, without
+ * dithering, the result might show a greenish tint. To get better
+ * results dithering should be applied.
+ *
+ * This configuration may be useful when using opaque bitmaps
+ * that do not require high color fidelity.
+ */
RGB_565 (4),
+
+ /**
+ * Each pixel is stored on 2 bytes. The three RGB color channels
+ * and the alpha channel (translucency) are stored with a 4 bits
+ * precision (16 possible values.)
+ *
+ * This configuration is mostly useful if the application needs
+ * to store translucency information but also needs to save
+ * memory.
+ *
+ * It is recommended to use {@link #ARGB_8888} instead of this
+ * configuration.
+ *
+ * @deprecated Because of the poor quality of this configuration,
+ * it is advised to use {@link #ARGB_8888} instead.
+ */
+ @Deprecated
ARGB_4444 (5),
+
+ /**
+ * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
+ * for translucency) is stored with 8 bits of precision (256
+ * possible values.)
+ *
+ * This configuration is very flexible and offers the best
+ * quality. It should be used whenever possible.
+ */
ARGB_8888 (6);
- Config(int ni) {
- this.nativeInt = ni;
- }
final int nativeInt;
- /* package */ static Config nativeToConfig(int ni) {
- return sConfigs[ni];
- }
-
+ @SuppressWarnings({"deprecation"})
private static Config sConfigs[] = {
null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
};
+
+ Config(int ni) {
+ this.nativeInt = ni;
+ }
+
+ static Config nativeToConfig(int ni) {
+ return sConfigs[ni];
+ }
}
/**
@@ -473,6 +528,7 @@
case ALPHA_8:
newConfig = Config.ALPHA_8;
break;
+ //noinspection deprecation
case ARGB_4444:
case ARGB_8888:
default:
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 8214e7f..8c78d60 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -786,92 +786,92 @@
/** Defines video profiles and levels. */
public final class VideoProfile {
- /** MPEG4, Simple Profile, Level 0. */
- public static final int MPEG4_SP_LEVEL_0 = 0;
-
- /** MPEG4, Simple Profile, Level 0B. */
- public static final int MPEG4_SP_LEVEL_0B = 1;
-
- /** MPEG4, Simple Profile, Level 1. */
- public static final int MPEG4_SP_LEVEL_1 = 2;
-
- /** MPEG4, Simple Profile, Level 2. */
- public static final int MPEG4_SP_LEVEL_2 = 3;
-
- /** MPEG4, Simple Profile, Level 3. */
- public static final int MPEG4_SP_LEVEL_3 = 4;
-
/** H263, Profile 0, Level 10. */
- public static final int H263_PROFILE_0_LEVEL_10 = 5;
+ public static final int H263_PROFILE_0_LEVEL_10 = MediaProperties.H263_PROFILE_0_LEVEL_10;
/** H263, Profile 0, Level 20. */
- public static final int H263_PROFILE_0_LEVEL_20 = 6;
+ public static final int H263_PROFILE_0_LEVEL_20 = MediaProperties.H263_PROFILE_0_LEVEL_20;
/** H263, Profile 0, Level 30. */
- public static final int H263_PROFILE_0_LEVEL_30 = 7;
+ public static final int H263_PROFILE_0_LEVEL_30 = MediaProperties.H263_PROFILE_0_LEVEL_30;
/** H263, Profile 0, Level 40. */
- public static final int H263_PROFILE_0_LEVEL_40 = 8;
+ public static final int H263_PROFILE_0_LEVEL_40 = MediaProperties.H263_PROFILE_0_LEVEL_40;
/** H263, Profile 0, Level 45. */
- public static final int H263_PROFILE_0_LEVEL_45 = 9;
-
- /** MPEG4, Simple Profile, Level 4A. */
- public static final int MPEG4_SP_LEVEL_4A = 10;
+ public static final int H263_PROFILE_0_LEVEL_45 = MediaProperties.H263_PROFILE_0_LEVEL_45;
/** MPEG4, Simple Profile, Level 0. */
- public static final int MPEG4_SP_LEVEL_5 = 11;
+ public static final int MPEG4_SP_LEVEL_0 = MediaProperties.MPEG4_SP_LEVEL_0;
+
+ /** MPEG4, Simple Profile, Level 0B. */
+ public static final int MPEG4_SP_LEVEL_0B = MediaProperties.MPEG4_SP_LEVEL_0B;
+
+ /** MPEG4, Simple Profile, Level 1. */
+ public static final int MPEG4_SP_LEVEL_1 = MediaProperties.MPEG4_SP_LEVEL_1;
+
+ /** MPEG4, Simple Profile, Level 2. */
+ public static final int MPEG4_SP_LEVEL_2 = MediaProperties.MPEG4_SP_LEVEL_2;
+
+ /** MPEG4, Simple Profile, Level 3. */
+ public static final int MPEG4_SP_LEVEL_3 = MediaProperties.MPEG4_SP_LEVEL_3;
+
+ /** MPEG4, Simple Profile, Level 4A. */
+ public static final int MPEG4_SP_LEVEL_4A = MediaProperties.MPEG4_SP_LEVEL_4A;
+
+ /** MPEG4, Simple Profile, Level 0. */
+ public static final int MPEG4_SP_LEVEL_5 = MediaProperties.MPEG4_SP_LEVEL_5;
/** H264, Profile 0, Level 1. */
- public static final int H264_PROFILE_0_LEVEL_1 = 12;
+ public static final int H264_PROFILE_0_LEVEL_1 = MediaProperties.H264_PROFILE_0_LEVEL_1;
/** H264, Profile 0, Level 1b. */
- public static final int H264_PROFILE_0_LEVEL_1b = 13;
+ public static final int H264_PROFILE_0_LEVEL_1b = MediaProperties.H264_PROFILE_0_LEVEL_1B;
/** H264, Profile 0, Level 1.1 */
- public static final int H264_PROFILE_0_LEVEL_1_1 = 14;
+ public static final int H264_PROFILE_0_LEVEL_1_1 = MediaProperties.H264_PROFILE_0_LEVEL_1_1;
/** H264, Profile 0, Level 1.2 */
- public static final int H264_PROFILE_0_LEVEL_1_2 = 15;
+ public static final int H264_PROFILE_0_LEVEL_1_2 = MediaProperties.H264_PROFILE_0_LEVEL_1_2;
/** H264, Profile 0, Level 1.3 */
- public static final int H264_PROFILE_0_LEVEL_1_3 = 16;
+ public static final int H264_PROFILE_0_LEVEL_1_3 = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
/** H264, Profile 0, Level 2. */
- public static final int H264_PROFILE_0_LEVEL_2 = 17;
+ public static final int H264_PROFILE_0_LEVEL_2 = MediaProperties.H264_PROFILE_0_LEVEL_2;
/** H264, Profile 0, Level 2.1 */
- public static final int H264_PROFILE_0_LEVEL_2_1 = 18;
+ public static final int H264_PROFILE_0_LEVEL_2_1 = MediaProperties.H264_PROFILE_0_LEVEL_2_1;
/** H264, Profile 0, Level 2.2 */
- public static final int H264_PROFILE_0_LEVEL_2_2 = 19;
+ public static final int H264_PROFILE_0_LEVEL_2_2 = MediaProperties.H264_PROFILE_0_LEVEL_2_2;
/** H264, Profile 0, Level 3. */
- public static final int H264_PROFILE_0_LEVEL_3 = 20;
+ public static final int H264_PROFILE_0_LEVEL_3 = MediaProperties.H264_PROFILE_0_LEVEL_3;
/** H264, Profile 0, Level 3.1 */
- public static final int H264_PROFILE_0_LEVEL_3_1 = 21;
+ public static final int H264_PROFILE_0_LEVEL_3_1 = MediaProperties.H264_PROFILE_0_LEVEL_3_1;
/** H264, Profile 0, Level 3.2 */
- public static final int H264_PROFILE_0_LEVEL_3_2 = 22;
+ public static final int H264_PROFILE_0_LEVEL_3_2 = MediaProperties.H264_PROFILE_0_LEVEL_3_2;
/** H264, Profile 0, Level 4. */
- public static final int H264_PROFILE_0_LEVEL_4 = 23;
+ public static final int H264_PROFILE_0_LEVEL_4 = MediaProperties.H264_PROFILE_0_LEVEL_4;
/** H264, Profile 0, Level 4.1 */
- public static final int H264_PROFILE_0_LEVEL_4_1 = 24;
+ public static final int H264_PROFILE_0_LEVEL_4_1 = MediaProperties.H264_PROFILE_0_LEVEL_4_1;
/** H264, Profile 0, Level 4.2 */
- public static final int H264_PROFILE_0_LEVEL_4_2 = 25;
+ public static final int H264_PROFILE_0_LEVEL_4_2 = MediaProperties.H264_PROFILE_0_LEVEL_4_2;
/** H264, Profile 0, Level 5. */
- public static final int H264_PROFILE_0_LEVEL_5 = 26;
+ public static final int H264_PROFILE_0_LEVEL_5 = MediaProperties.H264_PROFILE_0_LEVEL_5;
/** H264, Profile 0, Level 5.1 */
- public static final int H264_PROFILE_0_LEVEL_5_1 = 27;
+ public static final int H264_PROFILE_0_LEVEL_5_1 = MediaProperties.H264_PROFILE_0_LEVEL_5_1;
/** Profile out of range. */
- public static final int OUT_OF_RANGE = 255;
+ public static final int OUT_OF_RANGE = MediaProperties.UNSUPPORTED_PROFILE_LEVEL;
}
/** Defines video frame sizes. */
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e368848..cb08023 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -399,6 +399,9 @@
if (mConnectingDataSource != NULL) {
LOGI("interrupting the connection process");
mConnectingDataSource->disconnect();
+ } else if (mConnectingRTSPController != NULL) {
+ LOGI("interrupting the connection process");
+ mConnectingRTSPController->disconnect();
}
if (mFlags & PREPARING_CONNECTED) {
@@ -409,7 +412,7 @@
}
if (mFlags & PREPARING) {
- LOGI("waiting until preparation is completes.");
+ LOGI("waiting until preparation is completed.");
}
while (mFlags & PREPARING) {
@@ -1633,7 +1636,13 @@
mLooper->start();
}
mRTSPController = new ARTSPController(mLooper);
+ mConnectingRTSPController = mRTSPController;
+
+ mLock.unlock();
status_t err = mRTSPController->connect(mUri.string());
+ mLock.lock();
+
+ mConnectingRTSPController.clear();
LOGI("ARTSPController::connect returned %d", err);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 797e5ca..98b8c05 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -205,6 +205,7 @@
sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController;
+ sp<ARTSPController> mConnectingRTSPController;
sp<LiveSession> mLiveSession;
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index a7563ff..1328d2e 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -69,7 +69,14 @@
void ARTSPController::disconnect() {
Mutex::Autolock autoLock(mLock);
- if (mState != CONNECTED) {
+ if (mState == CONNECTING) {
+ mState = DISCONNECTED;
+ mConnectionResult = ERROR_IO;
+ mCondition.broadcast();
+
+ mHandler.clear();
+ return;
+ } else if (mState != CONNECTED) {
return;
}
diff --git a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
index a72c283..4999e5d 100644
--- a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
+++ b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,29 +14,33 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
/>
-
- <VideoView
- android:id="@+id/video_view"
+
+ <ImageView android:id="@+id/overlay_layer"
+ android:layout_width="0dip"
+ android:layout_height="392dip"/>
+
+ <VideoView
+ android:id="@+id/video_view"
android:layout_width="320px"
android:layout_height="240px"
/>
-
+
</FrameLayout>
-
+
</LinearLayout>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index 9fb49b1..41f0e22 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,9 @@
import android.widget.VideoView;
import com.android.mediaframeworktest.MediaNames;
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
import java.io.File;
import java.io.FileDescriptor;
import java.net.InetAddress;
@@ -58,6 +61,8 @@
public static AssetFileDescriptor midiafd;
public static AssetFileDescriptor mp3afd;
+ public static Bitmap mDestBitmap;
+ public static ImageView mOverlayView;
public MediaFrameworkTest() {
}
@@ -69,6 +74,7 @@
super.onCreate(icicle);
setContentView(R.layout.surface_view);
mSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
+ mOverlayView = (ImageView)findViewById(R.id.overlay_layer);
ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
@@ -77,6 +83,9 @@
//Get the mp3 fd
mp3afd = this.getResources().openRawResourceFd(R.raw.testmp3);
+ mOverlayView.setLayoutParams(lp);
+ mDestBitmap = Bitmap.createBitmap((int)640, (int)480, Bitmap.Config.ARGB_8888);
+ mOverlayView.setImageBitmap(mDestBitmap);
}
public void startPlayback(String filename){
@@ -148,4 +157,9 @@
InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
return address.isReachable(10000);
}
+
+ public static void testInvalidateOverlay() {
+ mOverlayView.invalidate();
+ }
+
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 1862fd5..f3cf0f7 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -33,8 +33,11 @@
import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
import com.android.mediaframeworktest.functional.MediaVisualizerTest;
/*import for VideoEditor Test cases*/
+import com.android.mediaframeworktest.functional.MediaItemThumbnailTest;
+import com.android.mediaframeworktest.functional.MediaPropertiesTest;
import com.android.mediaframeworktest.functional.VideoEditorAPITest;
-
+import com.android.mediaframeworktest.functional.VideoEditorExportTest;
+import com.android.mediaframeworktest.functional.VideoEditorPreviewTest;
import junit.framework.TestSuite;
import android.test.InstrumentationTestRunner;
@@ -73,7 +76,11 @@
suite.addTestSuite(MediaVirtualizerTest.class);
suite.addTestSuite(MediaVisualizerTest.class);
/*Test for Video Editor*/
+ suite.addTestSuite(MediaItemThumbnailTest.class);
+ suite.addTestSuite(MediaPropertiesTest.class);
suite.addTestSuite(VideoEditorAPITest.class);
+ suite.addTestSuite(VideoEditorExportTest.class);
+ suite.addTestSuite(VideoEditorPreviewTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java
new file mode 100755
index 0000000..895ca25
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java
@@ -0,0 +1,954 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.graphics.Bitmap;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.VideoEditor;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class MediaItemThumbnailTest extends
+ ActivityInstrumentationTestCase<MediaFrameworkTest> {
+ private final String TAG = "MediaItemThumbailTest";
+
+ private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+ private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+ private VideoEditor mVideoEditor;
+
+ private VideoEditorHelper mVideoEditorHelper;
+
+ public MediaItemThumbnailTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ // setup for each test case.
+ super.setUp();
+ mVideoEditorHelper = new VideoEditorHelper();
+ // Create a random String which will be used as project path, where all
+ // project related files will be stored.
+ final String projectPath = mVideoEditorHelper.
+ createRandomFile(PROJECT_LOCATION);
+ mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+ // Clean the directory created as project path
+ mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+ System.gc();
+ super.tearDown();
+ }
+
+ protected void validateThumbnail(Bitmap thumbNailBmp, int outWidth,
+ int outHeight) throws Exception {
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Height", outHeight, thumbNailBmp.getHeight());
+ assertEquals("Thumbnail Width", outWidth, thumbNailBmp.getWidth());
+ thumbNailBmp.recycle();
+ }
+
+ // -----------------------------------------------------------------
+ // THUMBNAIL
+ // -----------------------------------------------------------------
+ /**
+ * To test thumbnail / frame extraction on H.263 QCIF.
+ */
+ // TODO : TC_TN_001
+ @LargeTest
+ public void testThumbnailForH263QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = mediaVideoItem.getHeight();
+
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on MPEG4 VGA .
+ */
+ // TODO : TC_TN_002
+ @LargeTest
+ public void testThumbnailForMPEG4VGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp";
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = mediaVideoItem.getHeight();
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on MPEG4 NTSC.
+ */
+ // TODO : TC_TN_003
+ @LargeTest
+ public void testThumbnailForMPEG4NTSC() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on MPEG4 WVGA.
+ */
+ // TODO : TC_TN_004
+ @LargeTest
+ public void testThumbnailForMPEG4WVGA() throws Exception {
+
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth() * 2;
+ final int outHeight = mediaVideoItem.getHeight();
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on MPEG4 QCIF.
+ */
+ // TODO : TC_TN_005
+ @LargeTest
+ public void testThumbnailForMPEG4QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth();
+ final int outHeight = mediaVideoItem.getHeight() * 2;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on H264 QCIF.
+ */
+ // TODO : TC_TN_006
+ @LargeTest
+ public void testThumbnailForH264QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp";
+
+ final int atTime = 0;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth() * 2;
+ final int outHeight = mediaVideoItem.getHeight() * 2;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on H264 VGA.
+ */
+ // TODO : TC_TN_007
+ @LargeTest
+ public void testThumbnailForH264VGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+ final int outWidth = 32;
+ final int outHeight = 32;
+ final int atTime = 0;
+
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+ /**
+ * To test thumbnail / frame extraction on H264 WVGA.
+ */
+ // TODO : TC_TN_008
+ @LargeTest
+ public void testThumbnailForH264WVGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int outWidth = 64;
+ final int outHeight = 64;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final long atTime = mediaVideoItem.getDuration() / 2;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on H264 854x480.
+ */
+ // TODO : TC_TN_009
+ @LargeTest
+ public void testThumbnailForH264854_480() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4";
+ final int outWidth = 128;
+ final int outHeight = 128;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ MediaVideoItem mediaVideoItem = null;
+ mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final long atTime = mediaVideoItem.getDuration() - 1000;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on H264 960x720.
+ */
+ // TODO : TC_TN_010
+ @LargeTest
+ public void testThumbnailForH264HD960() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4";
+ final int outWidth = 75;
+ final int outHeight = 75;
+
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final long atTime = mediaVideoItem.getDuration() - 1000;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on H264 1080x720 .
+ */
+ // TODO : TC_TN_011
+ @LargeTest
+ public void testThumbnailForH264HD1080() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final long atTime = mediaVideoItem.getDuration() / 4;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * Check the thumbnail / frame extraction precision at 0,100 and 200 ms
+ */
+ // TODO : TC_TN_012
+ @LargeTest
+ public void testThumbnailForH264VGADifferentDuration() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ final int atTime = 0;
+ final int atTime1 = 100;
+ final int atTime2 = 200;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth();
+ final int outHeight = mediaVideoItem.getHeight();
+
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+ // get Thumbnail @ 100ms
+ final Bitmap thumbNailBmpAt100 =
+ mediaVideoItem.getThumbnail(outWidth, outHeight, atTime1);
+ validateThumbnail(thumbNailBmpAt100, outWidth, outHeight);
+
+ // get Thumbnail @ 200ms
+ final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail(
+ outWidth, outHeight, atTime2);
+ validateThumbnail(thumbNailBmpAt200, outWidth, outHeight);
+ }
+
+ /**
+ *Check the thumbnail / frame extraction precision at
+ * FileDuration,FileDuration/2 + 100 andFileDuration/2 + 200 ms
+ */
+ // TODO : TC_TN_013
+ @LargeTest
+ public void testThumbnailForMP4VGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ final int outWidth = mediaVideoItem.getWidth();
+ final int outHeight = mediaVideoItem.getHeight();
+ final long atTime = mediaVideoItem.getDuration() / 2;
+ final long atTime1 = atTime + 100;
+ final long atTime2 = atTime + 200;
+
+ // get Thumbnail @ duration/2
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+ // get Thumbnail @ duration/2 + 100ms
+ final Bitmap thumbNailBmpAt100 = mediaVideoItem.getThumbnail(
+ outWidth, outHeight, atTime1);
+ validateThumbnail(thumbNailBmpAt100, outWidth, outHeight);
+
+ // get Thumbnail @ duration/2 + 200ms
+ final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail(
+ outWidth, outHeight, atTime2);
+ validateThumbnail(thumbNailBmpAt200, outWidth, outHeight);
+ }
+
+ /**
+ * Check the thumbnail / frame extraction on JPEG file
+ */
+ // TODO : TC_TN_014
+ @LargeTest
+ public void testThumbnailForImage() throws Exception {
+ final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final int mediaDuration = 1000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ int outWidth = 0;
+ int outHeight = 0;
+
+ final MediaImageItem mii = mVideoEditorHelper.createMediaItem(
+ mVideoEditor, "m1", imageItemFilename, mediaDuration, renderingMode);
+ assertNotNull("Media Image Item is Null", mii);
+ outWidth = mii.getWidth() / 2;
+ outHeight = mii.getHeight() / 2;
+
+ final Bitmap thumbNailBmp = mii.getThumbnail(outWidth,
+ outHeight, mediaDuration);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+ /**
+ *To test ThumbnailList for H263 QCIF
+ */
+ // TODO : TC_TN_015
+ @LargeTest
+ public void testThumbnailListH263QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ final int startTime = 0;
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = mediaVideoItem.getWidth() / 4;
+ final int outHeight = mediaVideoItem.getHeight() / 4;
+ final long endTime = mediaVideoItem.getDuration() / 2;
+
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+ outWidth, outHeight, startTime, endTime, tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for MPEG4 QCIF
+ */
+ // TODO : TC_TN_016
+ @LargeTest
+ public void testThumbnailListMPEG4QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final long startTime = mediaVideoItem.getDuration() / 2;
+ final long endTime = mediaVideoItem.getDuration();
+
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+ outWidth, outHeight, startTime, endTime, tnCount);
+
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for H264 VGA
+ */
+ // TODO : TC_TN_017
+ @LargeTest
+ public void testThumbnailListH264VGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final long startTime = mediaVideoItem.getDuration() / 3;
+ final long endTime = mediaVideoItem.getDuration() / 2;
+
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+ outWidth, outHeight, startTime, endTime, tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for H264 WVGA
+ */
+ // TODO : TC_TN_018
+ @LargeTest
+ public void testThumbnailListH264WVGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final long startTime = mediaVideoItem.getDuration() / 3;
+ final long endTime = mediaVideoItem.getDuration() / 2;
+
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(
+ outWidth, outHeight, startTime, endTime, tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for H264 VGA ,Time exceeding file duration
+ */
+ // TODO : TC_TN_019
+ @LargeTest
+ public void testThumbnailH264VGAExceedingFileDuration() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ boolean flagForException = false;
+ int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth() / 2;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ final long atTime = mediaVideoItem.getDuration() + 2000;
+ mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Exception in Extracting thumbanil with Invalid Time",
+ flagForException);
+ }
+
+ /**
+ *To test ThumbnailList for VGA Image
+ */
+ // TODO : TC_TN_020
+ @LargeTest
+ public void testThumbnailListVGAImage() throws Exception {
+ final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final int imageItemDuration = 10000;
+ final int startTime = 0;
+ final int endTime = 0;
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaImageItem mediaImageItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ imageItemFilename, imageItemDuration, renderingMode);
+ final int outWidth = mediaImageItem.getWidth() / 2;
+ final int outHeight = mediaImageItem.getHeight() / 2;
+
+ final Bitmap thumbNailBmp[] = mediaImageItem.getThumbnailList
+ (outWidth, outHeight, startTime, endTime, tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for Invalid file path
+ */
+ // TODO : TC_TN_021
+ @LargeTest
+ public void testThumbnailForInvalidFilePath() throws Exception {
+ final String imageItemFileName = INPUT_FILE_PATH + "/sdcard/abc.jpg";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try{
+ final MediaImageItem mii = new MediaImageItem(mVideoEditor, "m1",
+ imageItemFileName, 3000, renderingMode);
+ }catch (IllegalArgumentException e){
+ flagForException = true;
+ }
+ assertTrue(" Invalid File Path", flagForException);
+ }
+
+ /**
+ * To test thumbnail / frame extraction with setBoundaries
+ */
+ // TODO : TC_TN_022
+ @LargeTest
+ public void testThumbnailForMPEG4WVGAWithSetBoundaries() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+ final int atTime = 10000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ mediaVideoItem.setExtractBoundaries(1000,
+ (mediaVideoItem.getDuration() - 21000));
+
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ *To test ThumbnailList for H264 WVGA with setExtractboundaries
+ */
+ // TODO : TC_TN_023
+ @LargeTest
+ public void testThumbnailListForH264WVGAWithSetBoundaries() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_1_17.mp4";
+ final int thumbNailStartTime = 10000;
+ final int thumbNailEndTime = 12000;
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+
+ mediaVideoItem.setExtractBoundaries(10000, 12000);
+
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList
+ (outWidth, outHeight, thumbNailStartTime, thumbNailEndTime,
+ tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertTrue("Thumbnail Size", (thumbNailBmp.length > 0) ? true : false);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList for H264 WVGA with count > frame available
+ */
+ // TODO : TC_TN_024
+ @LargeTest
+ public void testThumbnailListForH264WVGAWithCount() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int tnCount = 100;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+ final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+ final long thumbNailEndTime = thumbNailStartTime + 4000;
+ Bitmap thumbNailBmp[] = null;
+ boolean flagForException = false;
+ try{
+ thumbNailBmp = mediaVideoItem.getThumbnailList(outWidth, outHeight,
+ thumbNailStartTime, thumbNailEndTime, tnCount);
+ }catch (Exception e){
+ assertTrue("Unable to get Thumbnail list", flagForException);
+ }
+ if (thumbNailBmp.length <= tnCount) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail count more than asked", flagForException);
+ }
+
+ /**
+ *To test ThumbnailList for H264 WVGA with startTime > End Time
+ */
+ // TODO : TC_TN_025
+ @LargeTest
+ public void testThumbnailListH264WVGAWithStartGreaterEnd() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final int tnCount = 10;
+ boolean flagForException = false;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+ final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+ final long thumbNailEndTime = thumbNailStartTime - 1000;
+ try{
+ mediaVideoItem.getThumbnailList(outWidth, outHeight,
+ thumbNailStartTime, thumbNailEndTime, tnCount);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail Extraction where start time > end time",
+ flagForException);
+ }
+
+ /**
+ *To test ThumbnailList TC_TN_026 for H264 WVGA with startTime = End Time
+ */
+ // TODO : TC_TN_026
+ @LargeTest
+ public void testThumbnailListH264WVGAWithStartEqualEnd() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int tnCount = 1;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+ final long thumbNailStartTime = mediaVideoItem.getDuration() / 2;
+ final long thumbNailEndTime = thumbNailStartTime;
+ final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(outWidth,
+ outHeight, thumbNailStartTime, thumbNailEndTime, tnCount);
+ assertNotNull("Thumbnail Retrived is Null", thumbNailBmp);
+ assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length);
+ for (int i = 0; i < thumbNailBmp.length; i++) {
+ validateThumbnail(thumbNailBmp[i], outWidth, outHeight);
+ thumbNailBmp[i] = null;
+ }
+ }
+
+ /**
+ *To test ThumbnailList TC_TN_027 for file where video duration is less
+ * than file duration.
+ */
+ // TODO : TC_TN_027
+ @LargeTest
+ public void testThumbnailForVideoDurationLessFileDuration() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = (mediaVideoItem.getHeight() / 2);
+ final long atTime = mediaVideoItem.getDuration() - 2000;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+
+ }
+
+ /**
+ *To test ThumbnailList TC_TN_028 for file which has video part corrupted
+ */
+ // TODO : TC_TN_028
+ @LargeTest
+ public void testThumbnailWithCorruptedVideoPart() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth();
+ final int outHeight = mediaVideoItem.getHeight() * 2;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail
+ (outWidth, outHeight, mediaVideoItem.getDuration()/2);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Corrupted File cannot be read", flagForException);
+ }
+
+ /**
+ * Check the thumbnail / frame list extraction for Height as Negative Value
+ */
+ // TODO : TC_TN_029
+ @LargeTest
+ public void testThumbnailWithNegativeHeight() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = -1;
+ final long thumbNailStartTime =
+ mediaVideoItem.getBoundaryBeginTime()/2;
+ final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime();
+ mediaVideoItem.getThumbnailList(outWidth, outHeight,
+ thumbNailStartTime, thumbNailEndTime, tnCount);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail List with negative Height", flagForException);
+ }
+
+ /**
+ * Check the thumbnail for Height as Zero
+ */
+ // TODO : TC_TN_030
+ @LargeTest
+ public void testThumbnailWithHeightAsZero() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int atTime = 100;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = -1;
+ mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail List with Zero Height", flagForException);
+ }
+
+ /**
+ * Check the thumbnail for Height = 10
+ */
+ // TODO : TC_TN_031
+ @LargeTest
+ public void testThumbnailWithHeight() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int atTime = 1000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = (mediaVideoItem.getWidth() / 2);
+ final int outHeight = 10;
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * Check the thumbnail / frame list extraction for Width as Negative Value
+ */
+ // TODO : TC_TN_032
+ @LargeTest
+ public void testThumbnailWithNegativeWidth() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int tnCount = 10;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = -1;
+ final int outHeight = mediaVideoItem.getHeight();
+ final long thumbNailStartTime =
+ mediaVideoItem.getBoundaryBeginTime()/2;
+ final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime();
+ mediaVideoItem.getThumbnailList(outWidth, outHeight, thumbNailStartTime,
+ thumbNailEndTime, tnCount);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail List with negative Height", flagForException);
+ }
+
+ /**
+ * Check the thumbnail / frame list extraction for Width zero
+ */
+ // TODO : TC_TN_033
+ @LargeTest
+ public void testThumbnailWithWidthAsZero() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int atTime = 1000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try {
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = 0;
+ final int outHeight = mediaVideoItem.getHeight() / 2;
+ mediaVideoItem.getThumbnail(outWidth, outHeight, atTime);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail List with Zero Width", flagForException);
+ }
+
+ /**
+ * Check the thumbnail for Width = 10
+ */
+ // TODO : TC_TN_034
+ @LargeTest
+ public void testThumbnailWithWidth() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ final int atTime = 1000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = 10;
+ final int outHeight = mediaVideoItem.getHeight();
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+ outHeight, atTime);
+ validateThumbnail(thumbNailBmp, outWidth, outHeight);
+ }
+
+ /**
+ * To test thumbnail / frame extraction on MPEG4 (time beyond file duration).
+ */
+ // TODO : TC_TN_035
+ @LargeTest
+ public void testThumbnailMPEG4withMorethanFileDuration() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp";
+ boolean flagForException = false;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename, renderingMode);
+ final int outWidth = mediaVideoItem.getWidth()/2;
+ final int outHeight = mediaVideoItem.getHeight()/2;
+ final long atTime = mediaVideoItem.getDuration() + 100;
+ try{
+ final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth,
+ outHeight, atTime);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Thumbnail duration is more than file duration",
+ flagForException);
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
new file mode 100755
index 0000000..3efa5b2
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.VideoEditor;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class MediaPropertiesTest extends
+ ActivityInstrumentationTestCase<MediaFrameworkTest> {
+ private final String TAG = "MediaPropertiesTest";
+
+ private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+ private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+ private VideoEditor mVideoEditor;
+
+ private VideoEditorHelper mVideoEditorHelper;
+
+ public MediaPropertiesTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ // setup for each test case.
+ super.setUp();
+ mVideoEditorHelper = new VideoEditorHelper();
+ // Create a random String which will be used as project path,
+ // where all project related files will be stored.
+ final String projectPath = mVideoEditorHelper.
+ createRandomFile(PROJECT_LOCATION);
+ mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+ // Clean the directory created as project path
+ mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+ System.gc();
+ super.tearDown();
+ }
+
+ protected void validateVideoProperties(int aspectRatio, int fileType,
+ int videoCodecType, int duration, int videoBitrate, int fps,
+ int videoProfile, int width, int height, int audioCodecType,
+ int audioSamplingFrequency, int audioChannel, int audioBitrate,
+ MediaVideoItem mvi) throws Exception {
+ assertEquals("Aspect Ratio Mismatch", aspectRatio, mvi.getAspectRatio());
+ assertEquals("File Type Mismatch", fileType, mvi.getFileType());
+ assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType());
+
+ assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange (
+ duration, mvi.getDuration(), 10));
+ assertEquals("Video Profile " + mvi.getVideoProfile(), videoProfile,
+ mvi.getVideoProfile());
+ assertEquals("Video height " + mvi.getHeight(), height, mvi.getHeight());
+ assertEquals("Video width " + mvi.getWidth(), width, mvi.getWidth());
+ /** Check FPS with 10% range */
+ assertTrue("fps Mismatch" + mvi.getFps(),
+ mVideoEditorHelper.checkRange(fps, mvi.getFps(), 10));
+
+ assertEquals("AudioType Mismatch ", audioCodecType, mvi.getAudioType());
+ assertEquals("Audio Sampling " + mvi.getAudioSamplingFrequency(),
+ audioSamplingFrequency, mvi.getAudioSamplingFrequency());
+ assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel,
+ mvi.getAudioChannels());
+ }
+
+ protected void validateAudioProperties(int audioCodecType, int duration,
+ int audioSamplingFrequency, int audioChannel, int audioBitrate,
+ AudioTrack aT) throws Exception {
+ assertEquals("AudioType Mismatch ", audioCodecType, aT.getAudioType());
+ assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange (
+ duration, aT.getDuration(), 10));
+ assertEquals("Audio Sampling " + aT.getAudioSamplingFrequency(),
+ audioSamplingFrequency, aT.getAudioSamplingFrequency());
+ assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel,
+ aT.getAudioChannels());
+ }
+
+ protected void validateImageProperties(int aspectRatio, int fileType,
+ int width, int height, MediaImageItem mii)
+ throws Exception {
+ assertEquals("Aspect Ratio Mismatch", aspectRatio, mii.getAspectRatio());
+ assertEquals("File Type Mismatch", fileType, mii.getFileType());
+ assertEquals("Image height " + mii.getHeight(), height, mii.getHeight());
+ assertEquals("Image width " + mii.getWidth(), width, mii.getWidth());
+ }
+
+
+ /**
+ *To test Media Properties for file MPEG4 854 x 480
+ */
+ // TODO : Remove TC_MP_001
+ @LargeTest
+ public void testPropertiesMPEG4854_480() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+ final int duration = 26933;
+ final int videoBitrate = 319000;
+ final int audioBitrate = 48000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 16000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+ final int width = 854;
+ final int height = MediaProperties.HEIGHT_480;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+
+ /**
+ *To test Media Properties for file MPEG4 WVGA
+ */
+ // TODO : Remove TC_MP_002
+ @LargeTest
+ public void testPropertiesMPEGWVGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+ final int duration = 26933;
+ final int videoBitrate = 384000;
+ final int audioBitrate = 12800;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+ final int audioSamplingFrequency = 8000;
+ final int audioChannel = 1;
+ final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+ final int width = 800;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test media properties for MPEG4 720x480 (NTSC) + AAC file.
+ */
+ // TODO : Remove TC_MP_003
+ @LargeTest
+ public void testPropertiesMPEGNTSC() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+ final int duration = 26866;
+ final int videoBitrate = 403000;
+ final int audioBitrate = 160000;
+ final int fps = 30;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 48000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+ final int width = 720;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file MPEG4 VGA
+ */
+ // TODO : Remove TC_MP_004
+ @LargeTest
+ public void testPropertiesMPEGVGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+ final int duration = 26933;
+ final int videoBitrate = 533000;
+ final int audioBitrate = 128000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 48000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+ final int width = 640;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file MPEG4 QCIF
+ */
+ // TODO : Remove TC_MP_005
+ @LargeTest
+ public void testPropertiesMPEGQCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9;
+ final int fileType = MediaProperties.FILE_3GP;
+ final int videoCodecType = MediaProperties.VCODEC_MPEG4;
+ final int duration = 27000;
+ final int videoBitrate = 384000;
+ final int audioBitrate = 12200;
+ final int fps = 12;
+ final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+ final int audioSamplingFrequency = 8000;
+ final int audioChannel = 1;
+ final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1;
+ final int width = 176;
+ final int height = MediaProperties.HEIGHT_144;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To To test media properties for H263 176x144 (QCIF) + AAC (mono) file.
+ */
+ // TODO : Remove TC_MP_006
+ @LargeTest
+ public void testPropertiesH263QCIF() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9;
+ final int fileType = MediaProperties.FILE_3GP;
+ final int videoCodecType = MediaProperties.VCODEC_H263;
+ final int duration = 26933;
+ final int videoBitrate = 384000;
+ final int audioBitrate = 64000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 16000;
+ final int audioChannel = 1;
+ final int videoProfile = MediaProperties.H263_PROFILE_0_LEVEL_10;
+ final int width = 176;
+ final int height = MediaProperties.HEIGHT_144;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file H264 VGA
+ */
+ // TODO : Remove TC_MP_007
+ @LargeTest
+ public void testPropertiesH264VGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ final int fileType = MediaProperties.FILE_3GP;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 77600;
+ final int videoBitrate = 745000;
+ final int audioBitrate = 64000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 48000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 640;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file H264 NTSC
+ */
+ // TODO : Remove TC_MP_008
+ @LargeTest
+ public void testPropertiesH264NTSC() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 26880;
+ final int videoBitrate = 244000;
+ final int audioBitrate = 12200;
+ final int fps = 25;
+ final int audioCodecType = MediaProperties.ACODEC_AMRNB;
+ final int audioSamplingFrequency = 8000;
+ final int audioChannel = 1;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 720;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test media properties for H264 800x480 (WVGA) + AAC file.
+ */
+ // TODO : Remove TC_MP_009
+ @LargeTest
+ public void testPropertiesH264WVGA() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 77466;
+ final int videoBitrate = 528000;
+ final int audioBitrate = 38000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 24000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 800;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file H264 HD1280
+ */
+ // TODO : Remove TC_MP_010
+ @LargeTest
+ public void testPropertiesH264HD1280() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 77600;
+ final int videoBitrate = 606000;
+ final int audioBitrate = 48000;
+ final int fps = 15;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 16000;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 1280;
+ final int height = MediaProperties.HEIGHT_720;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test media properties for H264 1080x720 + AAC file
+ */
+ // TODO : Remove TC_MP_011
+ @LargeTest
+ public void testPropertiesH264HD1080WithAudio() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 77500;
+ final int videoBitrate = 1190000;
+ final int audioBitrate = 64000;
+ final int fps = 10;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 44100;
+ final int audioChannel = 2;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 1080;
+ final int height = MediaProperties.HEIGHT_720;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for file WMV - Unsupported type
+ */
+ // TODO : Remove TC_MP_012
+ @LargeTest
+ public void testPropertiesWMVFile() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "WMV_V7_640x480_15fps_512Kbps_wma_V9_44khz_48Kbps_s_1_30.wmv";
+ boolean flagForException = false;
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for a WMV File -- Unsupported file type",
+ flagForException);
+ }
+
+ /**
+ *To test media properties for H.264 Main/Advanced profile. (unsupported profile input)
+ */
+ // TODO : Remove TC_MP_013
+ @LargeTest
+ public void testPropertiesH264MainLineProfile() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH
+ + "H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ //final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int videoCodecType = MediaProperties.VCODEC_H264MP;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+
+ try {
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+ assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType());
+ }catch (IllegalArgumentException e){
+ flagForException = true;
+ }
+ assertTrue("Unsupported Main Profile", flagForException);
+ }
+
+ /**
+ *To test Media Properties for non existing file.
+ */
+ // TODO : Remove TC_MP_014
+ @LargeTest
+ public void testPropertiesForNonExsitingFile() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH + "abc.3gp";
+ boolean flagForException = false;
+
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for non exsisting file", flagForException);
+ }
+
+ /**
+ *To test Media Properties for file H264 HD1080
+ */
+ // TODO : Remove TC_MP_015
+ @LargeTest
+ public void testPropertiesH264HD1080WithoutAudio() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+ final int fileType = MediaProperties.FILE_MP4;
+ final int videoCodecType = MediaProperties.VCODEC_H264BP;
+ final int duration = 77366;
+ final int videoBitrate = 859000;
+ final int audioBitrate = 0;
+ final int fps = 30;
+ final int audioCodecType = -1;
+ final int audioSamplingFrequency = 0;
+ final int audioChannel = 0;
+ final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
+ final int width = 1080;
+ final int height = MediaProperties.HEIGHT_720;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", videoItemFilename, renderingMode);
+
+ validateVideoProperties(aspectRatio, fileType, videoCodecType, duration,
+ videoBitrate, fps, videoProfile, width, height, audioCodecType,
+ audioSamplingFrequency, audioChannel, audioBitrate, mvi);
+ }
+
+ /**
+ *To test Media Properties for Image file of JPEG Type
+ */
+ // TODO : Remove TC_MP_016
+ @LargeTest
+ public void testPropertiesVGAImage() throws Exception {
+ final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final int imageItemDuration = 10000;
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ final int fileType = MediaProperties.FILE_JPEG;
+ final int width = 640;
+ final int height = MediaProperties.HEIGHT_480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+
+ final MediaImageItem mii = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", imageItemFilename, imageItemDuration,
+ renderingMode);
+ validateImageProperties(aspectRatio, fileType, width, height, mii);
+ }
+
+ /**
+ *To test Media Properties for Image file of PNG Type
+ */
+ // TODO : Remove TC_MP_017
+ @LargeTest
+ public void testPropertiesPNG() throws Exception {
+ final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.png";
+ final int imageItemDuration = 10000;
+ final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ final int fileType = MediaProperties.FILE_PNG;
+ final int width = 640;
+ final int height = 480;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final MediaImageItem mii = mVideoEditorHelper.createMediaItem
+ (mVideoEditor, "m1", imageItemFilename, imageItemDuration,
+ renderingMode);
+ validateImageProperties(aspectRatio, fileType, width, height, mii);
+ }
+
+ /**
+ *To test Media Properties for file GIF - Unsupported type
+ */
+ // TODO : Remove TC_MP_018
+ @LargeTest
+ public void testPropertiesGIFFile() throws Exception {
+
+ final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.gif";
+ final int imageItemDuration = 10000;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ try {
+ new MediaImageItem(mVideoEditor, "m1", imageItemFilename,
+ imageItemDuration, renderingMode);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for a GIF File -- Unsupported file type",
+ flagForException);
+ }
+
+ /**
+ *To test Media Properties for file Text file named as 3GP
+ */
+ // TODO : Remove TC_MP_019
+ @LargeTest
+ public void testPropertiesofDirtyFile() throws Exception {
+
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "Text_FileRenamedTo3gp.3gp";
+ boolean flagForException = false;
+
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for a Dirty File ",
+ flagForException);
+ }
+
+ /**
+ *To test Media Properties for file name as NULL
+ */
+ // TODO : Remove TC_MP_020
+ @LargeTest
+ public void testPropertieNULLFile() throws Exception {
+ final String videoItemFilename = null;
+ boolean flagForException = false;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+ renderingMode);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for NULL File ",
+ flagForException);
+ }
+
+ /**
+ *To test Media Properties for file which is of type MPEG2
+ */
+ // TODO : Remove TC_MP_021
+ @LargeTest
+ public void testPropertiesMPEG2File() throws Exception {
+ final String videoItemFilename = INPUT_FILE_PATH +
+ "MPEG2_640x480_30fps_192kbps_1_5.mp4";
+ boolean flagForException = false;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", videoItemFilename,
+ renderingMode);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Media Properties for a MPEG2 File --Unsupported file type",
+ flagForException);
+ }
+
+ /**
+ *To test Media Properties TC_MP_023 for file without Video only Audio
+ */
+ // TODO : Remove TC_MP_023
+ @LargeTest
+ public void testProperties3GPWithoutVideoMediaItem() throws Exception {
+ final String audioFilename = INPUT_FILE_PATH +
+ "AACLC_48KHz_256Kbps_s_1_17.3gp";
+ boolean flagForException = false;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ try {
+ new MediaVideoItem(mVideoEditor, "m1", audioFilename,
+ renderingMode);
+ } catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Exception in Creaing Media Video item object without video",
+ flagForException);
+ }
+
+ /**
+ *To test media properties for Audio Track file. (No Video, AAC Audio)
+ */
+ // TODO : Remove TC_MP_024
+ @LargeTest
+ public void testProperties3GPWithoutVideoAudioTrack() throws Exception {
+
+ final String audioFilename = INPUT_FILE_PATH +
+ "AACLC_44.1kHz_256kbps_s_1_17.mp4";
+ final int duration = 77554;
+ final int audioBitrate = 384000;
+ final int audioCodecType = MediaProperties.ACODEC_AAC_LC;
+ final int audioSamplingFrequency = 44100;
+ final int audioChannel = 2;
+
+ final AudioTrack audioTrack = mVideoEditorHelper.createAudio
+ (mVideoEditor, "a1", audioFilename);
+
+ validateAudioProperties(audioCodecType, duration, audioSamplingFrequency,
+ audioChannel, audioBitrate, audioTrack);
+ }
+
+ /**
+ *To test media properties for Audio Track file. MP3 file
+ */
+ // TODO : Remove TC_MP_025
+ @LargeTest
+ public void testPropertiesMP3AudioTrack() throws Exception {
+
+ final String audioFilename = INPUT_FILE_PATH +
+ "MP3_48KHz_128kbps_s_1_17.mp3";
+ final int duration = 77640;
+ final int audioBitrate = 128000;
+ final int audioCodecType = MediaProperties.ACODEC_MP3;
+ final int audioSamplingFrequency = 48000;
+ final int audioChannel = 2;
+
+ final AudioTrack audioTrack = mVideoEditorHelper.createAudio
+ (mVideoEditor, "a1", audioFilename);
+
+ validateAudioProperties(audioCodecType, duration, audioSamplingFrequency,
+ audioChannel, audioBitrate, audioTrack);
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java
index 6a87656..0dadaa5 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java
@@ -89,7 +89,6 @@
* To Test Creation of Media Video Item.
*/
// TODO : remove TC_API_001
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItem() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -132,7 +131,6 @@
* the Begin and End Time.
*/
// TODO : remove TC_API_002
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemExtractBoundaries() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -202,7 +200,6 @@
* To test creation of Media Video Item with Set and Get rendering Mode
*/
// TODO : remove TC_API_003
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemRenderingModes() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -247,7 +244,6 @@
* To Test the Media Video API : Set Audio Volume, Get Audio Volume and Mute
*/
// TODO : remove TC_API_005
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemAudioFeatures() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -306,7 +302,6 @@
*/
// TODO : remove TC_API_006
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemGetWaveformData() throws Exception {
@@ -349,7 +344,6 @@
*/
// TODO : remove TC_API_007
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemEffect() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -391,7 +385,6 @@
*/
// TODO : remove TC_API_008
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemTransitions() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -439,7 +432,6 @@
*/
// TODO : remove TC_API_009
- @SuppressWarnings("unused")
@LargeTest
public void testMediaVideoItemOverlays() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -1404,8 +1396,6 @@
final Rect endRect1 = new Rect(10, 10, mediaImageItem.getWidth() / 4,
mediaImageItem.getHeight() / 4);
- //kbEffectOnMediaItem.setStartRect(startRect1);
- //kbEffectOnMediaItem.setEndRect(endRect1);
/* Added newly to take care of removal set APIs */
kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_changed",
startRect1, endRect1, 500, 3000);
@@ -1417,7 +1407,6 @@
final Rect zeroRect = new Rect(0, 0, 0, 0);
try {
- //kbEffectOnMediaItem.setStartRect(zeroRect);
kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroStart",
zeroRect, endRect, 500, 3000);
@@ -1428,7 +1417,6 @@
flagForException = false;
try {
- //kbEffectOnMediaItem.setEndRect(zeroRect);
kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroEnd",
startRect, zeroRect, 500, 3000);
} catch (IllegalArgumentException e) {
@@ -1443,7 +1431,6 @@
*/
// TODO : remove TC_API_037
- @SuppressWarnings("unused")
@LargeTest
public void testTransitionFadeBlack() throws Exception {
@@ -1592,7 +1579,6 @@
*/
// TODO : remove TC_API_038
- @SuppressWarnings("unused")
@LargeTest
public void testTransitionCrossFade() throws Exception {
@@ -1744,7 +1730,6 @@
*/
// TODO : remove TC_API_039
- @SuppressWarnings("unused")
@LargeTest
public void testTransitionSliding() throws Exception {
final String videoItemFilename1 = INPUT_FILE_PATH +
@@ -1935,7 +1920,6 @@
*/
// TODO : remove TC_API_040
- @SuppressWarnings("unused")
@LargeTest
public void testTransitionAlpha() throws Exception {
@@ -2115,7 +2099,6 @@
*/
// TODO : remove TC_API_041
- @SuppressWarnings("unused")
@LargeTest
public void testFrameOverlayVideoItem() throws Exception {
final String videoItemFilename1 = INPUT_FILE_PATH +
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java
new file mode 100755
index 0000000..37b1f54
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import java.io.File;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.EffectColor;
+import android.media.videoeditor.EffectKenBurns;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.OverlayFrame;
+import android.media.videoeditor.Transition;
+import android.media.videoeditor.TransitionAlpha;
+import android.media.videoeditor.TransitionCrossfade;
+import android.media.videoeditor.TransitionFadeBlack;
+import android.media.videoeditor.TransitionSliding;
+import android.media.videoeditor.VideoEditor;
+import android.media.videoeditor.VideoEditor.ExportProgressListener;
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+
+
+import android.util.Log;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+public class VideoEditorExportTest extends
+ ActivityInstrumentationTestCase<MediaFrameworkTest> {
+ private final String TAG = "TransitionTest";
+
+ private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+ private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+ private VideoEditor mVideoEditor;
+
+ private VideoEditorHelper mVideoEditorHelper;
+
+ // Declares the annotation for Preview Test Cases
+ public @interface TransitionTests {
+ }
+
+ public VideoEditorExportTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ // setup for each test case.
+ super.setUp();
+ mVideoEditorHelper = new VideoEditorHelper();
+ // Create a random String which will be used as project path, where all
+ // project related files will be stored.
+ final String projectPath =
+ mVideoEditorHelper.createRandomFile(PROJECT_LOCATION);
+ mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+ // Clean the directory created as project path
+ mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+ System.gc();
+ super.tearDown();
+ }
+
+ /**
+ * To Test export : Merge and Trim different types of Video and Image files
+ */
+ // TODO :remove TC_EXP_001
+ @LargeTest
+ public void testExportMergeTrim() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String videoItemFilename2 = INPUT_FILE_PATH
+ + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final String videoItemFilename3 = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+ final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+ final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/")
+ + ".3gp";
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(2000, 7000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaImageItem mediaImageItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem2);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem3.setExtractBoundaries(0, 2000);
+ mVideoEditor.addMediaItem(mediaVideoItem3);
+
+ final MediaVideoItem mediaVideoItem4 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+ videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000,
+ mediaVideoItem4.getDuration());
+ mVideoEditor.addMediaItem(mediaVideoItem4);
+
+ final MediaImageItem mediaImageItem5 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+ imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem5);
+
+ final MediaImageItem mediaImageItem6 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+ imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem6);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ final long storyBoardDuration = mediaVideoItem1.getTimelineDuration()
+ + mediaImageItem2.getDuration() + mediaVideoItem3.getTimelineDuration()
+ + mediaVideoItem4.getTimelineDuration() + mediaImageItem5.getDuration()
+ + mediaImageItem6.getDuration();
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ *To Test export : With Effect and Overlays on Different Media Items
+ */
+ // TODO :remove TC_EXP_002
+ @LargeTest
+ public void testExportEffectOverlay() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String videoItemFilename2 = INPUT_FILE_PATH
+ + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp";
+ final String videoItemFilename3 = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+ final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+ final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+ final String overlayFile = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png";
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(2000, 7000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final EffectColor effectPink =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectPink",
+ 0, 2000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+ mediaVideoItem1.addEffect(effectPink);
+
+ final EffectColor effectNegative =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectNegative",
+ 3000, 4000, EffectColor.TYPE_NEGATIVE, 0);
+ mediaVideoItem1.addEffect(effectNegative);
+
+ final MediaImageItem mediaImageItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem2);
+
+ final EffectColor effectFifties =
+ mVideoEditorHelper.createEffectItem(mediaImageItem2, "effectFifties",
+ 0, 3000, EffectColor.TYPE_FIFTIES, 0);
+ mediaImageItem2.addEffect(effectFifties);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem3);
+ mediaVideoItem3.setExtractBoundaries(0, 8000);
+
+ final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile,
+ 640, 480);
+ final OverlayFrame overlayFrame =
+ mVideoEditorHelper.createOverlay(mediaVideoItem3, "overlay",
+ mBitmap, 2000, 5000);
+ mediaVideoItem3.addOverlay(overlayFrame);
+
+ final EffectColor effectGreen =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem3, "effectGreen",
+ 0, 2000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+ mediaVideoItem3.addEffect(effectGreen);
+
+ final MediaVideoItem mediaVideoItem4 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+ videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000,
+ mediaVideoItem4.getDuration());
+ mVideoEditor.addMediaItem(mediaVideoItem4);
+
+ final EffectColor effectSepia =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem4, "effectSepia",
+ 0, 2000, EffectColor.TYPE_SEPIA, 0);
+ mediaVideoItem4.addEffect(effectSepia);
+
+ final MediaImageItem mediaImageItem5 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+ imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem5);
+
+ final EffectColor effectGray =
+ mVideoEditorHelper.createEffectItem(mediaImageItem5, "effectGray",
+ 0, 2000, EffectColor.TYPE_COLOR, EffectColor.GRAY);
+ mediaImageItem5.addEffect(effectGray);
+
+ final MediaImageItem mediaImageItem6 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+ imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem6);
+
+ final EffectColor effectGradient =
+ mVideoEditorHelper.createEffectItem(mediaImageItem6,
+ "effectGradient", 0, 2000, EffectColor.TYPE_GRADIENT,
+ EffectColor.PINK);
+ mediaImageItem6.addEffect(effectGradient);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ final long storyBoardDuration = mediaVideoItem1.getTimelineDuration()
+ + mediaImageItem2.getDuration()
+ + mediaVideoItem3.getTimelineDuration()
+ + mediaVideoItem4.getTimelineDuration()
+ + mediaImageItem5.getDuration()
+ + mediaImageItem6.getDuration();
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ * To test export : with Image with KenBurnEffect
+ */
+ // TODO : remove TC_EXP_003
+ @LargeTest
+ public void testExportEffectKenBurn() throws Exception {
+ final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final int imageItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+ final MediaImageItem mediaImageItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1",
+ imageItemFileName, 5000, imageItemRenderingMode);
+ mVideoEditor.addMediaItem(mediaImageItem);
+
+ final Rect startRect = new Rect((mediaImageItem.getHeight() / 3),
+ (mediaImageItem.getWidth() / 3), (mediaImageItem.getHeight() / 2),
+ (mediaImageItem.getWidth() / 2));
+
+ final Rect endRect = new Rect(0, 0, mediaImageItem.getWidth(),
+ mediaImageItem.getHeight());
+
+ final EffectKenBurns kbEffectOnMediaItem = new EffectKenBurns(
+ mediaImageItem, "KBOnM2", startRect, endRect, 500, 3000);
+ assertNotNull("EffectKenBurns", kbEffectOnMediaItem);
+ mediaImageItem.addEffect(kbEffectOnMediaItem);
+
+ assertEquals("KenBurn Start Rect", startRect,
+ kbEffectOnMediaItem.getStartRect());
+ assertEquals("KenBurn End Rect", endRect,
+ kbEffectOnMediaItem.getEndRect());
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, mediaImageItem.getDuration(),
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ * To Test Export : With Video and Image and An Audio BackGround Track
+ */
+ // TODO : remove TC_EXP_004
+ @LargeTest
+ public void testExportAudio() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+ final String audioTrackFilename = INPUT_FILE_PATH +
+ "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final MediaImageItem mediaImageItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFileName, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem);
+
+ final AudioTrack audioTrack = mVideoEditorHelper.createAudio(
+ mVideoEditor, "a1", audioTrackFilename);
+ audioTrack.setExtractBoundaries(2000, 5000);
+ mVideoEditor.addAudioTrack(audioTrack);
+
+ audioTrack.disableDucking();
+ audioTrack.enableLoop();
+ audioTrack.setVolume(75);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, (mediaVideoItem.getTimelineDuration() +
+ mediaImageItem.getDuration()),
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ *To Test export : With Transition on Different Media Items
+ */
+ // TODO :remove TC_EXP_005
+ @LargeTest
+ public void testExportTransition() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String videoItemFilename2 = INPUT_FILE_PATH
+ + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final String videoItemFilename3 = INPUT_FILE_PATH +
+ "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+
+ final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg";
+ final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+ final String maskFilename = INPUT_FILE_PATH +
+ "TransitionSpiral_QVGA.jpg";
+
+ final MediaVideoItem mediaItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaItem1.setExtractBoundaries(2000, 7000);
+ mVideoEditor.addMediaItem(mediaItem1);
+
+ final TransitionAlpha transition1 =
+ mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1,
+ 2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true);
+ mVideoEditor.addTransition(transition1);
+
+ final MediaImageItem mediaItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem2);
+
+ final MediaVideoItem mediaItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaItem3.setExtractBoundaries(0, 8000);
+ mVideoEditor.addMediaItem(mediaItem3);
+
+ final TransitionSliding transition2And3 =
+ mVideoEditorHelper.createTSliding("transition2", mediaItem2,
+ mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST,
+ TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN);
+ mVideoEditor.addTransition(transition2And3);
+
+ final MediaVideoItem mediaItem4 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+ videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem4);
+ mediaItem4.setExtractBoundaries(0, 8000);
+
+ final TransitionCrossfade transition3And4 =
+ mVideoEditorHelper.createTCrossFade("transition3", mediaItem3,
+ mediaItem4, 3500, Transition.BEHAVIOR_MIDDLE_SLOW);
+ mVideoEditor.addTransition(transition3And4);
+
+ final MediaImageItem mediaItem5 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+ imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem5);
+
+ final TransitionFadeBlack transition4And5 =
+ mVideoEditorHelper.createTFadeBlack("transition4", mediaItem4,
+ mediaItem5, 3500, Transition.BEHAVIOR_SPEED_DOWN);
+ mVideoEditor.addTransition(transition4And5);
+
+ final MediaImageItem mediaItem6 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m6",
+ imageItemFilename3, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem6);
+
+ final TransitionSliding transition5And6 =
+ mVideoEditorHelper.createTSliding("transition5", mediaItem5,
+ mediaItem6, 1000/*4000*/, Transition.BEHAVIOR_SPEED_UP,
+ TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN);
+ mVideoEditor.addTransition(transition5And6);
+
+ final TransitionSliding transition6 =
+ mVideoEditorHelper.createTSliding("transition6", mediaItem6, null,
+ 1000 /*4000*/, Transition.BEHAVIOR_SPEED_UP,
+ TransitionSliding.DIRECTION_TOP_OUT_BOTTOM_IN);
+ mVideoEditor.addTransition(transition6);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ final long storyBoardDuration = mediaItem1.getTimelineDuration()
+ + mediaItem2.getTimelineDuration()
+ + mediaItem3.getTimelineDuration() - transition2And3.getDuration()
+ + mediaItem4.getTimelineDuration() - transition3And4.getDuration()
+ + mediaItem5.getTimelineDuration() - transition4And5.getDuration()
+ + mediaItem6.getTimelineDuration() - transition5And6.getDuration();
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, storyBoardDuration,
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ * To Test Export : Without any Media Items in the story Board
+ *
+ * @throws Exception
+ */
+ // TODO :remove TC_EXP_006
+ @LargeTest
+ public void testExportWithoutMediaItems() throws Exception {
+ boolean flagForException = false;
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (IllegalStateException e) {
+ flagForException = true;
+ }
+ assertTrue("Export without any MediaItems", flagForException);
+ }
+
+ /**
+ * To Test Export : With Media Items add and removed in the story Board
+ *
+ * @throws Exception
+ */
+ // TODO :remove TC_EXP_007
+ @LargeTest
+ public void testExportWithoutMediaItemsAddRemove() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH +
+ "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg";
+ boolean flagForException = false;
+
+ final MediaVideoItem mediaItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaItem1.setExtractBoundaries(0, 15000);
+ mVideoEditor.addMediaItem(mediaItem1);
+
+ final MediaImageItem mediaItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 15000,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem2);
+
+ final TransitionAlpha transition1 =
+ mVideoEditorHelper.createTAlpha("transition1", mediaItem1, mediaItem2,
+ 3000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, false);
+ mVideoEditor.addTransition(transition1);
+
+ final EffectColor effectColor =
+ mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 12000,
+ 3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+ mediaItem2.addEffect(effectColor);
+
+ mVideoEditor.removeMediaItem(mediaItem1.getId());
+ mVideoEditor.removeMediaItem(mediaItem2.getId());
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (IllegalStateException e) {
+ flagForException = true;
+ }
+ assertTrue("Export with MediaItem added and removed", flagForException);
+ }
+
+ /**
+ * To Test Export : With Video and Image : MMS use case
+ *
+ * @throws Exception
+ */
+ // TODO :remove TC_EXP_008
+ @LargeTest
+ public void testExportMMS() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String videoItemFilename2 = INPUT_FILE_PATH
+ + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+ final MediaVideoItem mediaItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaItem1.setExtractBoundaries(2000, 7000);
+ mVideoEditor.addMediaItem(mediaItem1);
+
+ final TransitionAlpha transition1 =
+ mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1,
+ 2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true);
+ mVideoEditor.addTransition(transition1);
+
+ final MediaImageItem mediaItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem2);
+
+ final MediaVideoItem mediaItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaItem3.setExtractBoundaries(0, 8000);
+ mVideoEditor.addMediaItem(mediaItem3);
+
+ final TransitionSliding transition2And3 =
+ mVideoEditorHelper.createTSliding("transition2", mediaItem2,
+ mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST,
+ TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN);
+ mVideoEditor.addTransition(transition2And3);
+
+ final TransitionCrossfade transition3 =
+ mVideoEditorHelper.createTCrossFade("transition3", mediaItem3, null,
+ 3500, Transition.BEHAVIOR_MIDDLE_SLOW);
+ mVideoEditor.addTransition(transition3);
+
+ final EffectColor effectColor =
+ mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 0,
+ 3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+ mediaItem2.addEffect(effectColor);
+
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_11_9);
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ final long storyBoardDuration = mediaItem1.getTimelineDuration()
+ + mediaItem2.getTimelineDuration() + mediaItem3.getTimelineDuration()
+ - transition2And3.getDuration();
+
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_144, 0, storyBoardDuration,
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ * To Test Export :Media Item having duration of 1 Hour
+ *
+ * @throws Exception
+ */
+ @LargeTest
+ public void testExportDuration1Hour() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH +
+ "H264_BP_640x480_15fps_384kbps_60_0.mp4";
+ final String outFilename = mVideoEditorHelper.createRandomFile(
+ mVideoEditor.getPath() + "/") + ".3gp";
+
+ final MediaVideoItem mediaItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaItem1);
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ }catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0, mediaItem1.getDuration(),
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+
+ /**
+ * To Test Export : Storage location having very less space (Less than 100
+ * KB)
+ *
+ * @throws Exception
+ */
+ @LargeTest
+ public void testExportWithStorageFull() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+ boolean flagForException = false;
+
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", videoItemFilename1,
+ MediaItem.RENDERING_MODE_BLACK_BORDER);
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ flagForException = true;
+ }
+ assertTrue("Error in exporting file due to lack of storage space",
+ flagForException);
+ }
+
+ /**
+ * To Test Export :Two Media Items added
+ *
+ * @throws Exception
+ */
+ @LargeTest
+ public void testExportTwoVideos() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ final String videoItemFileName1 = INPUT_FILE_PATH +
+ "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4";
+ final String outFilename = mVideoEditorHelper
+ .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp";
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ try {
+ final int[] progressUpdate = new int[100];
+ mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720,
+ MediaProperties.BITRATE_800K, new ExportProgressListener() {
+ int i = 0;
+ public void onProgress(VideoEditor ve, String outFileName,
+ int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ } catch (Exception e) {
+ assertTrue("Error in Export" + e.toString(), false);
+ }
+ mVideoEditorHelper.validateExport(mVideoEditor, outFilename,
+ MediaProperties.HEIGHT_720, 0,
+ (mediaVideoItem.getDuration()+ mediaVideoItem1.getDuration()),
+ MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC);
+ mVideoEditorHelper.checkDeleteExistingFile(outFilename);
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java
new file mode 100644
index 0000000..bd0a838
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java
@@ -0,0 +1,1161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.media.videoeditor.AudioTrack;
+import android.media.videoeditor.Effect;
+import android.media.videoeditor.EffectColor;
+import android.media.videoeditor.EffectKenBurns;
+import android.media.videoeditor.MediaImageItem;
+import android.media.videoeditor.MediaItem;
+import android.media.videoeditor.MediaProperties;
+import android.media.videoeditor.MediaVideoItem;
+import android.media.videoeditor.Overlay;
+import android.media.videoeditor.OverlayFrame;
+import android.media.videoeditor.Transition;
+import android.media.videoeditor.TransitionAlpha;
+import android.media.videoeditor.TransitionCrossfade;
+import android.media.videoeditor.TransitionFadeBlack;
+import android.media.videoeditor.TransitionSliding;
+import android.media.videoeditor.VideoEditor;
+import android.media.videoeditor.VideoEditor.ExportProgressListener;
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
+import android.media.videoeditor.VideoEditor.PreviewProgressListener;
+import android.media.videoeditor.VideoEditor.OverlayData;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase;
+import android.view.SurfaceHolder;
+
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.mediaframeworktest.VideoEditorHelper;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.concurrent.TimeUnit;
+
+import android.util.Log;
+
+public class VideoEditorPreviewTest extends
+ ActivityInstrumentationTestCase<MediaFrameworkTest> {
+ private final String TAG = "VideoEditorTest";
+
+ private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON;
+
+ private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON;
+
+ private final String PROJECT_CLASS_NAME =
+ "android.media.videoeditor.VideoEditorImpl";
+
+ private VideoEditor mVideoEditor;
+
+ private VideoEditorHelper mVideoEditorHelper;
+
+ private class EventHandler extends Handler {
+ public EventHandler( Looper lp)
+ {
+ super(lp);
+ }
+ public void handleMessage(Message msg)
+ {
+ switch (msg.what)
+ {
+ default:
+ MediaFrameworkTest.testInvalidateOverlay();
+ }
+ }
+ }
+ private EventHandler mEventHandler;
+
+ private boolean previewStart;
+ private boolean previewStop;
+
+ /* Minimum waiting time for Semaphore to wait for release */
+ private final long minWaitingTime = 1000;
+
+ // Declares the annotation for Preview Test Cases
+ public @interface Preview {
+ }
+
+ public VideoEditorPreviewTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(looper);
+
+ } else {
+ //Handle error when looper can not be created.
+ ;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ // setup for each test case.
+ super.setUp();
+ mVideoEditorHelper = new VideoEditorHelper();
+ // Create a random String which will be used as project path, where all
+ // project related files will be stored.
+ final String projectPath =
+ mVideoEditorHelper.createRandomFile(PROJECT_LOCATION);
+ mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mVideoEditorHelper.destroyVideoEditor(mVideoEditor);
+ // Clean the directory created as project path
+ mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath()));
+ System.gc();
+ super.tearDown();
+ }
+
+ protected void setPreviewStart() {
+ previewStart = true;
+ }
+ protected void setPreviewStop() {
+ previewStop = true;
+ }
+
+ protected void validatePreviewProgress(int startMs, int endMs,
+ boolean loop, long duration) throws Exception {
+
+ final int[] progressUpdate = new int[100];
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ int i = 0;
+ public void onProgress(Object item, int action, int progress) {
+ progressUpdate[i++] = progress;
+ }
+ });
+ mVideoEditorHelper.checkProgressCBValues(progressUpdate);
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+
+ long waitingTime = minWaitingTime;
+ if (endMs == -1) {
+ waitingTime += duration;
+ }
+ else {
+ waitingTime += (endMs - startMs);
+ }
+ blockTillPreviewCompletes.acquire();
+ try {
+ mVideoEditor.startPreview(surfaceHolder, startMs, endMs, loop, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+
+ if ( overlayData != null) {
+ if(overlayData.needsRendering()) {
+ overlayData.renderOverlay(MediaFrameworkTest.mDestBitmap);
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(1, 2, 3));
+ }
+ }
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ blockTillPreviewCompletes.release();
+ }
+ });
+ } catch (Exception e) {
+ blockTillPreviewCompletes.release();
+ }
+ blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+
+ mVideoEditor.stopPreview();
+ assertTrue("Preview Failed to start", previewStart);
+ assertTrue("Preview Failed to stop", previewStop);
+
+ blockTillPreviewCompletes.release();
+ }
+
+ // -----------------------------------------------------------------
+ // Preview
+ // -----------------------------------------------------------------
+
+ /**
+ *To test Preview : FULL Preview of current work (beginning till end)
+ */
+ // TODO : remove TC_PRV_001
+ @LargeTest
+ public void testPreviewTheStoryBoard() throws Exception {
+ final String videoItemFileName1 = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+ final String videoItemFileName2 = INPUT_FILE_PATH
+ + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+ final String videoItemFileName3 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ previewStart = false;
+ previewStop = false;
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaVideoItem mediaVideoItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2",
+ videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem2);
+ mediaVideoItem2.setExtractBoundaries(0, 10000);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3",
+ videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem3.setExtractBoundaries(0, 10000);
+
+ mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId());
+ List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0));
+ assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1));
+ assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2));
+
+ mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+ assertEquals("Media Item 1 Rendering Mode",
+ MediaItem.RENDERING_MODE_BLACK_BORDER,
+ mediaVideoItem1.getRenderingMode());
+
+ mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+ assertEquals("Media Item 2 Rendering Mode",
+ MediaItem.RENDERING_MODE_BLACK_BORDER,
+ mediaVideoItem2.getRenderingMode());
+
+ mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH);
+ assertEquals("Media Item 3 Rendering Mode",
+ MediaItem.RENDERING_MODE_STRETCH,
+ mediaVideoItem3.getRenderingMode());
+
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3);
+ assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3,
+ mVideoEditor.getAspectRatio());
+
+ validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ * To test Preview : Preview of start + 10 sec till end of story board
+ */
+ // TODO : remove TC_PRV_002
+ @LargeTest
+ public void testPreviewTheStoryBoardFromDuration() throws Exception {
+ final String videoItemFileName1 = INPUT_FILE_PATH
+ + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4";
+ final String videoItemFileName2 = INPUT_FILE_PATH +
+ "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4";
+ final String videoItemFileName3 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp";
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaVideoItem mediaVideoItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2",
+ videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem2.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem2);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3",
+ videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem3.setExtractBoundaries(0, 10000);
+
+ mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId());
+
+ List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0));
+ assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1));
+ assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2));
+ mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ assertEquals("Media Item 1 Rendering Mode",
+ MediaItem.RENDERING_MODE_BLACK_BORDER,
+ mediaVideoItem1.getRenderingMode());
+ mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ assertEquals("Media Item 2 Rendering Mode",
+ MediaItem.RENDERING_MODE_BLACK_BORDER,
+ mediaVideoItem2.getRenderingMode());
+ mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH);
+
+ assertEquals("Media Item 3 Rendering Mode",
+ MediaItem.RENDERING_MODE_STRETCH,
+ mediaVideoItem3.getRenderingMode());
+
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3);
+ assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3,
+ mVideoEditor.getAspectRatio());
+
+ validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ * To test Preview : Preview of current Effects applied
+ */
+ // TODO : remove TC_PRV_003
+ @LargeTest
+ public void testPreviewOfEffects() throws Exception {
+ final String videoItemFileName1 = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final EffectColor effectNegative =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1,
+ "effectNegative", 0, 2000, EffectColor.TYPE_NEGATIVE, 0);
+ mediaVideoItem1.addEffect(effectNegative);
+
+ final EffectColor effectGreen =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectGreen",
+ 2000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+ mediaVideoItem1.addEffect(effectGreen);
+
+ final EffectColor effectFifties =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1,
+ "effectFifties", 5000, 4000, EffectColor.TYPE_FIFTIES, 0);
+ mediaVideoItem1.addEffect(effectFifties);
+
+ List<Effect> effectList = mediaVideoItem1.getAllEffects();
+ assertEquals("Effect List Size", 3, effectList.size());
+ assertEquals("Effect negative", effectNegative, effectList.get(0));
+ assertEquals("Effect Green", effectGreen, effectList.get(1));
+ assertEquals("Effect Fifties", effectFifties, effectList.get(2));
+
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3);
+ assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_4_3,
+ mVideoEditor.getAspectRatio());
+
+ final long storyboardDuration = mVideoEditor.getDuration() ;
+ validatePreviewProgress(0, (int)(storyboardDuration/2), false, (storyboardDuration/2));
+
+ assertEquals("Removing Effect : Negative", effectNegative,
+ mediaVideoItem1.removeEffect(effectNegative.getId()));
+
+ effectList = mediaVideoItem1.getAllEffects();
+
+ assertEquals("Effect List Size", 2, effectList.size());
+ assertEquals("Effect Green", effectGreen, effectList.get(0));
+ assertEquals("Effect Fifties", effectFifties, effectList.get(1));
+
+ validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ *To test Preview : Preview of current Transitions applied (with multiple
+ * generatePreview)
+ */
+ // TODO : remove TC_PRV_004
+ @LargeTest
+ public void testPreviewWithTransition() throws Exception {
+
+ final String videoItemFileName1 = INPUT_FILE_PATH +
+ "H263_profile0_176x144_10fps_96kbps_0_25.3gp";
+ final String imageItemFileName1 = INPUT_FILE_PATH +
+ "IMG_1600x1200.jpg";
+ final String videoItemFileName2 = INPUT_FILE_PATH +
+ "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+ final String maskFilename = INPUT_FILE_PATH +
+ "TransitionSpiral_QVGA.jpg";
+ previewStart = false;
+ previewStop = false;
+
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaImageItem mediaImageItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFileName1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem1);
+
+ final MediaVideoItem mediaVideoItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem2.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem2);
+
+ final TransitionCrossfade transition1And2CrossFade =
+ mVideoEditorHelper.createTCrossFade("transition_1_2_CF",
+ mediaVideoItem1, mediaImageItem1, 2000,
+ Transition.BEHAVIOR_MIDDLE_FAST);
+ mVideoEditor.addTransition(transition1And2CrossFade);
+
+ final TransitionAlpha transition2And3Alpha =
+ mVideoEditorHelper.createTAlpha("transition_2_3", mediaImageItem1,
+ mediaVideoItem2, 4000, Transition.BEHAVIOR_SPEED_UP,
+ maskFilename, 50, true);
+ mVideoEditor.addTransition(transition2And3Alpha);
+
+ final TransitionFadeBlack transition1FadeBlack =
+ mVideoEditorHelper.createTFadeBlack("transition_1FB", null,
+ mediaVideoItem1, 2000, Transition.BEHAVIOR_MIDDLE_FAST);
+ mVideoEditor.addTransition(transition1FadeBlack);
+
+ List<Transition> transitionList = mVideoEditor.getAllTransitions();
+ assertEquals("Transition List Size", 3, transitionList.size());
+ assertEquals("Transition 1", transition1And2CrossFade,
+ transitionList.get(0));
+ assertEquals("Transition 2", transition2And3Alpha, transitionList.get(1));
+ assertEquals("Transition 3", transition1FadeBlack, transitionList.get(2));
+
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_3_2);
+
+ final int[] progressValues = new int[300];
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ int i = 0;
+
+ public void onProgress(Object item, int action, int progress) {
+ if (item instanceof TransitionCrossfade) {
+ progressValues[i] = progress;
+ assertEquals("Object", item, transition1And2CrossFade);
+ assertEquals("Action", action,
+ MediaProcessingProgressListener.ACTION_ENCODE);
+ } else if (item instanceof TransitionAlpha) {
+ progressValues[i] = progress;
+ assertEquals("Object", item, transition2And3Alpha);
+ assertEquals("Action", action,
+ MediaProcessingProgressListener.ACTION_ENCODE);
+ } else if (item instanceof TransitionFadeBlack) {
+ progressValues[i] = progress;
+ assertEquals("Object", item, transition1FadeBlack);
+ assertEquals("Action", action,
+ MediaProcessingProgressListener.ACTION_ENCODE);
+ }
+ i++;
+ }
+ });
+
+ mVideoEditorHelper.checkProgressCBValues(progressValues);
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+
+ long waitingTime = minWaitingTime + 10000;
+
+ blockTillPreviewCompletes.acquire();
+ try {
+ mVideoEditor.startPreview(surfaceHolder, 0, 10000, false, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ blockTillPreviewCompletes.release();
+ }
+ });
+ } catch (Exception e) {
+ blockTillPreviewCompletes.release();
+ }
+ blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+ mVideoEditor.stopPreview();
+ blockTillPreviewCompletes.release();
+ assertTrue("Preview Failed to start", previewStart);
+ assertTrue("Preview Failed to stop", previewStop);
+
+ assertEquals("Removing Transition " + transition1And2CrossFade.getId(),
+ transition1And2CrossFade,
+ mVideoEditor.removeTransition(transition1And2CrossFade.getId()));
+ transitionList = mVideoEditor.getAllTransitions();
+ assertEquals("Transition List Size", 2, transitionList.size());
+ assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0));
+ assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1));
+
+ validatePreviewProgress(0, -1, false, mVideoEditor.getDuration());
+
+
+ final TransitionSliding transition1And2Sliding =
+ mVideoEditorHelper.createTSliding("transition_1_2Sliding",
+ mediaVideoItem1, mediaImageItem1, 4000,
+ Transition.BEHAVIOR_MIDDLE_FAST,
+ TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN);
+ mVideoEditor.addTransition(transition1And2Sliding);
+
+ transitionList = mVideoEditor.getAllTransitions();
+ assertEquals("Transition List Size", 3, transitionList.size());
+ assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0));
+ assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1));
+ assertEquals("Transition 3", transition1And2Sliding,
+ transitionList.get(2));
+
+ validatePreviewProgress(5000, -1, false, (mVideoEditor.getDuration()));
+
+ }
+
+ /**
+ * To test Preview : Preview of current Overlay applied
+ */
+ // TODO : remove TC_PRV_005
+ @LargeTest
+ public void testPreviewWithOverlay() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH
+ + "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp";
+ final String overlayFilename1 = INPUT_FILE_PATH +
+ "IMG_640x480_Overlay1.png";
+ final String overlayFilename2 = INPUT_FILE_PATH +
+ "IMG_640x480_Overlay2.png";
+ final int previewFrom = 5000;
+ final int previewTo = 10000;
+ final boolean previewLoop = false;
+ final int previewCallbackFrameCount = 1;
+ final int setAspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+ boolean flagForException = false;
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, renderingMode);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+ mediaVideoItem.setExtractBoundaries(0, 10000);
+
+ final Bitmap mBitmap1 = mVideoEditorHelper.getBitmap(overlayFilename1,
+ 640, 480);
+ final OverlayFrame overlayOnMvi1 =
+ mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi1",
+ mBitmap1, 0, 5000);
+ mediaVideoItem.addOverlay(overlayOnMvi1);
+
+ final Bitmap mBitmap2 = mVideoEditorHelper.getBitmap(overlayFilename2,
+ 640, 480);
+ final OverlayFrame overlayOnMvi2 =
+ mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi2",
+ mBitmap2, 5000, 9000);
+ mediaVideoItem.addOverlay(overlayOnMvi2);
+
+ List<Overlay> overlayList = mediaVideoItem.getAllOverlays();
+ assertEquals("Overlay Size", 2, overlayList.size());
+ assertEquals("Overlay 1", overlayOnMvi1, overlayList.get(0));
+ assertEquals("Overlay 2", overlayOnMvi2, overlayList.get(1));
+
+ mVideoEditor.setAspectRatio(setAspectRatio);
+
+ validatePreviewProgress(0 /* previewFrom */, -1, previewLoop,
+ mVideoEditor.getDuration());
+ }
+
+ /**
+ * To test Preview : Preview of current Trim applied (with default aspect
+ * ratio)
+ */
+ // TODO : remove TC_PRV_006
+ @LargeTest
+ public void testPreviewWithTrim() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, MediaItem.RENDERING_MODE_CROPPING);
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ boolean flagForException = false;
+ previewStart = false;
+ previewStop = false;
+ mediaVideoItem.setExtractBoundaries(mediaVideoItem.getDuration() / 2,
+ mediaVideoItem.getDuration());
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ validatePreviewProgress(1000, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ * To test Preview : Preview of current work having Overlay and Effect
+ * applied
+ */
+
+ // TODO : remove TC_PRV_007
+ @LargeTest
+ public void testPreviewWithOverlayEffectKenBurn() throws Exception {
+
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+ final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String videoItemFileName1 = INPUT_FILE_PATH +
+ "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4";
+ final String overlayFilename = INPUT_FILE_PATH +
+ "IMG_640x480_Overlay1.png";
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaImageItem mediaImageItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFileName, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem2);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem3);
+
+ final EffectColor effectColor =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem1, "Effect1",
+ 1000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN);
+ mediaVideoItem1.addEffect(effectColor);
+
+ final Rect startRect = new Rect((mediaImageItem2.getHeight() / 3),
+ (mediaImageItem2.getWidth() / 3), (mediaImageItem2.getHeight() / 2),
+ (mediaImageItem2.getWidth() / 2));
+ final Rect endRect = new Rect(0, 0, mediaImageItem2.getWidth(),
+ mediaImageItem2.getHeight());
+
+ final EffectKenBurns kbeffectOnMI2 = new EffectKenBurns(mediaImageItem2,
+ "KBOnM2", startRect, endRect, 0, 10000);
+ assertNotNull("EffectKenBurns", kbeffectOnMI2);
+ mediaImageItem2.addEffect(kbeffectOnMI2);
+
+ final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFilename,
+ 640, 480);
+ final OverlayFrame overlayFrame =
+ mVideoEditorHelper.createOverlay(mediaVideoItem3, "OverlayID",
+ mBitmap, (mediaImageItem2.getDuration() / 4),
+ (mediaVideoItem3.getDuration() / 3));
+ mediaVideoItem3.addOverlay(overlayFrame);
+
+ validatePreviewProgress(5000, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ *To test Preview : Export during preview
+ */
+ // TODO : remove TC_PRV_008
+ @LargeTest
+ public void testPreviewDuringExport() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ previewStart = false;
+ previewStop = false;
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(0, 20000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ long waitingTime = minWaitingTime + mVideoEditor.getDuration();
+
+ blockTillPreviewCompletes.acquire();
+
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+ try {
+ mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1,
+ new PreviewProgressListener() {
+ final String fileName = mVideoEditor.getPath() + "\test.3gp";
+ final int height = MediaProperties.HEIGHT_360;
+ final int bitrate = MediaProperties.BITRATE_512K;
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ if (timeMs >= 10000)
+ try {
+ videoEditor.export(fileName, height, bitrate,
+ new ExportProgressListener() {
+ public void onProgress(VideoEditor ve,
+ String outFileName,int progress) {
+
+ }
+ });
+ } catch (IOException e) {
+ assertTrue("UnExpected Error in Export" +
+ e.toString(), false);
+ }
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ blockTillPreviewCompletes.release();
+ }
+ });
+ } catch (Exception e) {
+ blockTillPreviewCompletes.release();
+ }
+
+ blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+ mVideoEditor.stopPreview();
+ assertTrue("Preview Failed to start", previewStart);
+ assertTrue("Preview Failed to stop", previewStop);
+ blockTillPreviewCompletes.release();
+ }
+
+ /**
+ * To test Preview : Preview of current Effects applied (with from time >
+ * total duration)
+ */
+ // TODO : remove TC_PRV_009
+ @LargeTest
+ public void testPreviewWithDurationGreaterThanMediaDuration()
+ throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_192kbps_1_5.mp4";
+ final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
+ boolean flagForException = false;
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName, renderingMode);
+ try {
+ mediaVideoItem1.setExtractBoundaries(0, 20000);
+ } catch (Exception e) {
+ assertTrue("Exception during setExtract Boundaries", false);
+ }
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+ long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 30000);
+ if(waitingTime < 0)
+ {
+ waitingTime = minWaitingTime;
+ }
+
+ blockTillPreviewCompletes.acquire();
+ try {
+ mVideoEditor.startPreview(surfaceHolder, 30000, -1, true, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ blockTillPreviewCompletes.release();
+ }
+ });
+
+ } catch (IllegalArgumentException e) {
+ blockTillPreviewCompletes.release();
+ flagForException = true;
+ }
+ blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+ assertTrue("Expected Error in Preview", flagForException);
+ mVideoEditor.stopPreview();
+ blockTillPreviewCompletes.release();
+ }
+
+ /**
+ * To test Preview : Preview of current Effects applied (with Render Preview
+ * Frame)
+ */
+ // TODO : remove TC_PRV_010
+ @LargeTest
+ public void testPreviewWithRenderPreviewFrame() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ final Semaphore blockTillPreviewCompletes = new Semaphore(1);
+ boolean flagForException = false;
+ OverlayData overlayData1 = new OverlayData();
+ previewStart = false;
+ previewStop = false;
+
+ final String overlayFilename1 = INPUT_FILE_PATH +
+ "IMG_640x480_Overlay1.png";
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor,
+ "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final EffectColor effectPink =
+ mVideoEditorHelper.createEffectItem(mediaVideoItem,
+ "effectNegativeOnMvi", 1000, 3000, EffectColor.TYPE_COLOR,
+ EffectColor.PINK);
+ mediaVideoItem.addEffect(effectPink);
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+
+ assertEquals("Render preview Frame at 5 Sec", 5000,
+ mVideoEditor.renderPreviewFrame(surfaceHolder, 5000,
+ overlayData1));
+
+ assertEquals("Render preview Frame at 7 Sec", 7000,
+ mVideoEditor.renderPreviewFrame(surfaceHolder, 7000,
+ overlayData1));
+
+ long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 5000);
+
+ blockTillPreviewCompletes.acquire();
+ try {
+ mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ blockTillPreviewCompletes.release();
+ }
+ });
+ } catch (Exception e) {
+ blockTillPreviewCompletes.release();
+ }
+ blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS);
+ mVideoEditor.stopPreview();
+ assertTrue("Preview Failed to start", previewStart);
+ assertTrue("Preview Failed to stop", previewStop);
+ blockTillPreviewCompletes.release();
+ }
+
+ /**
+ * To test Preview : Preview of current work from selected jump location
+ * till end with Audio Track
+ */
+ // TODO : remove TC_PRV_011
+ @LargeTest
+ public void testPreviewWithEndAudioTrack() throws Exception {
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_640x480.jpg";
+ final String audioFilename = INPUT_FILE_PATH +
+ "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+
+ boolean flagForException = false;
+ previewStart = false;
+ previewStop = false;
+ final MediaImageItem mediaImageItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ imageItemFilename1, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem1);
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem.setExtractBoundaries(1000, 8000);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final MediaImageItem mediaImageItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem2);
+
+ final AudioTrack audioTrack =
+ mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename);
+ mVideoEditor.addAudioTrack(audioTrack);
+
+ List<AudioTrack> audioList = mVideoEditor.getAllAudioTracks();
+ assertEquals("Audio Track List size", 1, audioList.size());
+ assertEquals("Audio Track", audioTrack, audioList.get(0));
+ mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3);
+
+ validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration());
+ }
+
+ /**
+ * To test render Preview Frame
+ */
+ // TODO : remove TC_PRV_012
+ @LargeTest
+ public void testRenderPreviewFrame() throws Exception {
+ final String videoItemFileName1 = INPUT_FILE_PATH
+ + "H264_BP_1080x720_30fps_800kbps_1_17.mp4";
+ final String videoItemFileName2 = INPUT_FILE_PATH
+ + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4";
+ final String videoItemFileName3 = INPUT_FILE_PATH
+ + "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ final String imageItemFilename1 = INPUT_FILE_PATH
+ + "IMG_1600x1200.jpg";
+ final String imageItemFilename2 = INPUT_FILE_PATH
+ + "IMG_176x144.jpg";
+ final String audioFilename = INPUT_FILE_PATH
+ + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp";
+ OverlayData overlayData1 = new OverlayData();
+ previewStart = false;
+ previewStop = false;
+ final MediaVideoItem mediaVideoItem1 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(0, 10000);
+ mVideoEditor.addMediaItem(mediaVideoItem1);
+
+ final MediaVideoItem mediaVideoItem2 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 4,
+ mediaVideoItem2.getDuration() / 2);
+ mVideoEditor.addMediaItem(mediaVideoItem2);
+
+ final MediaVideoItem mediaVideoItem3 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m3",
+ videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 2,
+ mediaVideoItem2.getDuration());
+ mVideoEditor.addMediaItem(mediaVideoItem3);
+
+ final MediaImageItem mediaImageItem4 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m4",
+ imageItemFilename1, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ final MediaImageItem mediaImageItem5 =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m5",
+ imageItemFilename2, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+
+ List<MediaItem> mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item List Size", 3, mediaList.size());
+
+ mVideoEditor.insertMediaItem(mediaImageItem4, mediaVideoItem2.getId());
+ mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item List Size", 4, mediaList.size());
+ assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0));
+ assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1));
+ assertEquals("Media item 4", mediaImageItem4, mediaList.get(2));
+ assertEquals("Media item 3", mediaVideoItem3, mediaList.get(3));
+
+ mVideoEditor.insertMediaItem(mediaImageItem5, mediaImageItem4.getId());
+ mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item List Size", 5, mediaList.size());
+ assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0));
+ assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1));
+ assertEquals("Media item 4", mediaImageItem4, mediaList.get(2));
+ assertEquals("Media item 5", mediaImageItem5, mediaList.get(3));
+ assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4));
+
+ mVideoEditor.moveMediaItem(mediaVideoItem1.getId(),
+ mediaImageItem5.getId());
+ mediaList = mVideoEditor.getAllMediaItems();
+ assertEquals("Media Item List Size", 5, mediaList.size());
+ assertEquals("Media item 2", mediaVideoItem2, mediaList.get(0));
+ assertEquals("Media item 4", mediaImageItem4, mediaList.get(1));
+ assertEquals("Media item 5", mediaImageItem5, mediaList.get(2));
+ assertEquals("Media item 1", mediaVideoItem1, mediaList.get(3));
+ assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4));
+
+ final TransitionCrossfade transition2And4CrossFade =
+ mVideoEditorHelper.createTCrossFade("transition2And4CrossFade",
+ mediaVideoItem2, mediaImageItem4, 2000,
+ Transition.BEHAVIOR_MIDDLE_FAST);
+ mVideoEditor.addTransition(transition2And4CrossFade);
+
+ final TransitionCrossfade transition1And3CrossFade =
+ mVideoEditorHelper.createTCrossFade("transition1And3CrossFade",
+ mediaVideoItem1, mediaVideoItem3, 5000,
+ Transition.BEHAVIOR_MIDDLE_FAST);
+ mVideoEditor.addTransition(transition1And3CrossFade);
+
+ final AudioTrack audioTrack =
+ mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename);
+ audioTrack.setExtractBoundaries(0, 2000);
+ mVideoEditor.addAudioTrack(audioTrack);
+
+ audioTrack.enableLoop();
+
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+
+ mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/4, overlayData1);
+ Thread.sleep(1000);
+ mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/2, overlayData1);
+ Thread.sleep(1000);
+ mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration(), overlayData1);
+
+ }
+
+ /**
+ * To Test Preview : Without any Media Items in the story Board
+ */
+ // TODO : remove TC_PRV_013
+ @LargeTest
+ public void testStartPreviewWithoutMediaItems() throws Exception {
+ boolean flagForException = false;
+
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+ try{
+ mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ }
+ });
+ }catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Preview without Media Items", flagForException);
+ }
+
+ /**
+ * To Test Preview : Add Media and Remove Media Item (Without any Media
+ * Items in the story Board)
+ */
+ // TODO : remove TC_PRV_014
+ @LargeTest
+ public void testStartPreviewAddRemoveMediaItems() throws Exception {
+ final String videoItemFilename1 = INPUT_FILE_PATH
+ + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp";
+ final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
+ final String alphaFilename = INPUT_FILE_PATH +
+ "TransitionSpiral_QVGA.jpg";
+ boolean flagForException = false;
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m1",
+ videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mediaVideoItem.setExtractBoundaries(0, 15000);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final MediaImageItem mediaImageItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor, "m2",
+ imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaImageItem);
+
+ final TransitionAlpha transition1And2 =
+ mVideoEditorHelper.createTAlpha("transition", mediaVideoItem,
+ mediaImageItem, 3000, Transition.BEHAVIOR_SPEED_UP,
+ alphaFilename, 10, false);
+ mVideoEditor.addTransition(transition1And2);
+
+ final EffectColor effectColor =
+ mVideoEditorHelper.createEffectItem(mediaImageItem, "effect", 5000,
+ 3000, EffectColor.TYPE_COLOR, EffectColor.PINK);
+ mediaImageItem.addEffect(effectColor);
+
+ assertEquals("removing Media item 1", mediaVideoItem,
+ mVideoEditor.removeMediaItem(mediaVideoItem.getId()));
+ assertEquals("removing Media item 2", mediaImageItem,
+ mVideoEditor.removeMediaItem(mediaImageItem.getId()));
+
+ try{
+ mVideoEditor.generatePreview(new MediaProcessingProgressListener() {
+ public void onProgress(Object item, int action, int progress) {
+ }
+ });
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+ mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1,
+ new PreviewProgressListener() {
+ public void onProgress(VideoEditor videoEditor, long timeMs,
+ OverlayData overlayData) {
+ }
+ public void onStart(VideoEditor videoEditor) {
+ setPreviewStart();
+ }
+ public void onStop(VideoEditor videoEditor) {
+ setPreviewStop();
+ }
+ });
+ }catch (IllegalArgumentException e) {
+ flagForException = true;
+ }
+ assertTrue("Preview with removed Media Items", flagForException);
+
+ }
+
+ /**
+ * To test Preview : Preview of current Effects applied (with Render Preview
+ * Frame)
+ */
+ // TODO : remove TC_PRV_015
+ @LargeTest
+ public void testPreviewWithRenderPreviewFrameWithoutGenerate() throws Exception {
+ final String videoItemFileName = INPUT_FILE_PATH +
+ "H264_BP_640x480_30fps_256kbps_1_17.mp4";
+ boolean flagForException = false;
+ long duration = 0;
+ OverlayData overlayData1 = new OverlayData();
+
+ final MediaVideoItem mediaVideoItem =
+ mVideoEditorHelper.createMediaItem(mVideoEditor,
+ "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER);
+ mVideoEditor.addMediaItem(mediaVideoItem);
+
+ final SurfaceHolder surfaceHolder =
+ MediaFrameworkTest.mSurfaceView.getHolder();
+ duration = mVideoEditor.getDuration();
+ /* RenderPreviewFrame returns -1 to indicate last frame */
+ try {
+ assertEquals("Render preview Frame at item duration", -1,
+ mVideoEditor.renderPreviewFrame(surfaceHolder, duration,
+ overlayData1));
+ } catch ( Exception e) {
+ assertTrue (" Render Preview Frame without generate", false);
+ }
+ duration = mVideoEditor.getDuration() + 1000;
+ try {
+ mVideoEditor.renderPreviewFrame(surfaceHolder, duration,
+ overlayData1);
+ } catch ( IllegalStateException e) {
+ flagForException = true;
+ }
+ assertTrue (" Preview time greater than duration", flagForException);
+ }
+
+}
diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4
new file mode 100755
index 0000000..be050dc
--- /dev/null
+++ b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4
Binary files differ
diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png
old mode 100644
new mode 100755
index 6611986..0f32131a
--- a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png
+++ b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png
Binary files differ
diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp
deleted file mode 100644
index bd8b079..0000000
--- a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_96kbps_44kHz_s_1_17.3gp
+++ /dev/null
Binary files differ
diff --git a/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp
new file mode 100644
index 0000000..02103c6
--- /dev/null
+++ b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp
@@ -0,0 +1 @@
+This is a text file
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index 3fdfdbb..c358e13 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -40,6 +40,9 @@
android:layout_alignParentTop="true"
android:layout_marginLeft="123dip"
android:layout_marginTop="16dip"
+ android:maxWidth="64dip"
+ android:maxHeight="64dip"
+ android:adjustViewBounds="true"
/>
<View android:id="@+id/recents_callout_line"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
index b1e74ad..ce0848b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
@@ -90,8 +90,8 @@
if (imi2 == null) return 0;
if (imi1 == null) return 1;
if (mPackageManager != null) {
- CharSequence imiId1 = imi1.loadLabel(mPackageManager);
- CharSequence imiId2 = imi2.loadLabel(mPackageManager);
+ CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId();
+ CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId();
if (imiId1 != null && imiId2 != null) {
return imiId1.toString().compareTo(imiId2.toString());
}
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 6c6c2cc8..018fe0c 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -361,10 +361,12 @@
/** {@inheritDoc} */
public void cleanUp() {
+ if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
mUpdateMonitor.removeCallback(this);
mLockPatternUtils = null;
mUpdateMonitor = null;
mCallback = null;
+ mLockPatternView.setOnPatternListener(null);
}
@Override
@@ -406,6 +408,7 @@
mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
} else {
+ boolean reportFailedAttempt = false;
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
}
@@ -413,9 +416,10 @@
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
mTotalFailedPatternAttempts++;
mFailedPatternAttemptsSinceLastTimeout++;
- mCallback.reportFailedUnlockAttempt();
+ reportFailedAttempt = true;
}
- if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+ if (mFailedPatternAttemptsSinceLastTimeout
+ >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
@@ -427,6 +431,12 @@
mCancelPatternRunnable,
PATTERN_CLEAR_TIMEOUT_MS);
}
+
+ // Because the following can result in cleanUp() being called on this screen,
+ // member variables reset in cleanUp() shouldn't be accessed after this call.
+ if (reportFailedAttempt) {
+ mCallback.reportFailedUnlockAttempt();
+ }
}
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index d25b9c8..ba0f31b 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1949,14 +1949,7 @@
private boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
if (subtype == null) return true;
- String[] extraValues = subtype.getExtraValue().split(",");
- final int N = extraValues.length;
- for (int i = 0; i < N; ++i) {
- if (SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME.equals(extraValues[i])) {
- return false;
- }
- }
- return true;
+ return subtype.containsExtraValueKey(SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME);
}
private void saveCurrentInputMethodAndSubtypeToHistory() {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b7a276f..b662c55 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5889,7 +5889,7 @@
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
token = new Binder();
- mDragState = new DragState(token, surface, flags, winBinder);
+ mDragState = new DragState(token, surface, /*flags*/ 0, winBinder);
mDragState.mSurface = surface;
token = mDragState.mToken = new Binder();
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index ff5f989..f24f96c 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -90,7 +90,7 @@
private BroadcastReceiver mStateReceiver;
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
- private static final String USB_NETMASK = "255.255.255.0";
+ private static final int USB_PREFIX_LENGTH = 24;
// USB is 192.168.42.1 and 255.255.255.0
// Wifi is 192.168.43.1 and 255.255.255.0
@@ -568,8 +568,7 @@
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR);
- InetAddress mask = InetAddress.getByName(USB_NETMASK);
- ifcg.addr = new LinkAddress(addr, mask);
+ ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
if (enabled) {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {