Merge "Code cleaning"
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 968b8ed6..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"
@@ -265620,7 +265635,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
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/app/Activity.java b/core/java/android/app/Activity.java
index b7b0ef58..67e4806 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -349,7 +349,7 @@
* section for more information on how the lifecycle of a process is tied
* to the activities it is hosting. Note that it is important to save
* persistent data in {@link #onPause} instead of {@link #onSaveInstanceState}
- * because the later is not part of the lifecycle callbacks, so will not
+ * because the latter is not part of the lifecycle callbacks, so will not
* be called in every situation as described in its documentation.</p>
*
* <p class="note">Be aware that these semantics will change slightly between
diff --git a/core/java/android/app/package.html b/core/java/android/app/package.html
index 5137600..f37f1dc 100644
--- a/core/java/android/app/package.html
+++ b/core/java/android/app/package.html
@@ -32,8 +32,9 @@
action bar.</p>
<p>For information about using some the classes in this package, see the following
-documents: <a href="{@docRoot}guide/topics/fundamentals/index.html">Application
-Fundamentals</a> (for activities, services, and fragments), <a
+documents: <a href="{@docRoot}guide/topics/fundamentals/activities.html">Activities</a>, <a
+href="{@docRoot}guide/topics/fundamentals/services.html">Services</a>, <a
+href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a>, <a
href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a>, <a
href="{@docRoot}guide/topics/ui/dialogs.html">Creating Dialogs</a>, and <a
href="{@docRoot}guide/topics/ui/notifiers/index.html">Notifying the User</a>.</p>
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 659b937..ebc1882 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -237,10 +237,28 @@
private BroadcastReceiver mConnectivityIntentReceiver =
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- sendCheckAlarmsMessage();
+ final boolean wasConnected = mDataConnectionIsConnected;
+
+ // don't use the intent to figure out if network is connected, just check
+ // ConnectivityManager directly.
+ mDataConnectionIsConnected = isNetworkConnected();
+ if (mDataConnectionIsConnected) {
+ if (!wasConnected) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Reconnection detected: clearing all backoffs");
+ }
+ mSyncStorageEngine.clearAllBackoffs(mSyncQueue);
+ }
+ sendCheckAlarmsMessage();
+ }
}
};
+ private boolean isNetworkConnected() {
+ NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
+ return (networkInfo != null) && networkInfo.isConnected();
+ }
+
private BroadcastReceiver mShutdownIntentReceiver =
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -1411,8 +1429,7 @@
// to have the most recent value used.
try {
waitUntilReadyToRun();
- NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
- mDataConnectionIsConnected = (networkInfo != null) && networkInfo.isConnected();
+ mDataConnectionIsConnected = isNetworkConnected();
mSyncManagerWakeLock.acquire();
// Always do this first so that we be sure that any periodic syncs that
// are ready to run have been converted into pending syncs. This allows the
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index c8ca6189..a1e174b 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -525,7 +525,7 @@
}
}
- public void clearAllBackoffs() {
+ public void clearAllBackoffs(SyncQueue syncQueue) {
boolean changed = false;
synchronized (mAuthorities) {
for (AccountInfo accountInfo : mAccounts.values()) {
@@ -541,6 +541,7 @@
}
authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
+ syncQueue.onBackoffChanged(accountInfo.account, authorityInfo.authority, 0);
changed = true;
}
}
diff --git a/core/java/android/content/pm/package.html b/core/java/android/content/pm/package.html
index b18adb2..d55f0f7 100644
--- a/core/java/android/content/pm/package.html
+++ b/core/java/android/content/pm/package.html
@@ -5,7 +5,7 @@
permissions, services, signatures, and providers.</p>
<p>Most of the information about an application package is defined by its manifest file. For
more information, see the <a
-href="{@docRoot}guide/topics/manifest/guide/topics/manifest/manifest-intro.html">AndroidManifest.xml
-File</a> documentation.</p>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml File</a>
+documentation.</p>
</BODY>
</HTML>
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f079e42..dd4096b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -380,6 +380,58 @@
/*-----------------------------------------------------------------------*/
+ private class SensorEventPool {
+ private final int mPoolSize;
+ private final SensorEvent mPool[];
+ private int mNumItemsInPool;
+
+ private SensorEvent createSensorEvent() {
+ // maximal size for all legacy events is 3
+ return new SensorEvent(3);
+ }
+
+ SensorEventPool(int poolSize) {
+ mPoolSize = poolSize;
+ mNumItemsInPool = poolSize;
+ mPool = new SensorEvent[poolSize];
+ }
+
+ SensorEvent getFromPool() {
+ SensorEvent t = null;
+ synchronized (this) {
+ if (mNumItemsInPool > 0) {
+ // remove the "top" item from the pool
+ final int index = mPoolSize - mNumItemsInPool;
+ t = mPool[index];
+ mPool[index] = null;
+ mNumItemsInPool--;
+ }
+ }
+ if (t == null) {
+ // the pool was empty or this item was removed from the pool for
+ // the first time. In any case, we need to create a new item.
+ t = createSensorEvent();
+ }
+ return t;
+ }
+
+ void returnToPool(SensorEvent t) {
+ synchronized (this) {
+ // is there space left in the pool?
+ if (mNumItemsInPool < mPoolSize) {
+ // if so, return the item to the pool
+ mNumItemsInPool++;
+ final int index = mPoolSize - mNumItemsInPool;
+ mPool[index] = t;
+ }
+ }
+ }
+ }
+
+ private static SensorEventPool sPool;
+
+ /*-----------------------------------------------------------------------*/
+
static private class SensorThread {
Thread mThread;
@@ -485,10 +537,9 @@
/*-----------------------------------------------------------------------*/
private class ListenerDelegate {
- final SensorEventListener mSensorEventListener;
+ private final SensorEventListener mSensorEventListener;
private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
private final Handler mHandler;
- private SensorEvent mValuesPool;
public SparseBooleanArray mSensors = new SparseBooleanArray();
public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
public SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -527,40 +578,12 @@
}
mSensorEventListener.onSensorChanged(t);
- returnToPool(t);
+ sPool.returnToPool(t);
}
};
addSensor(sensor);
}
- protected SensorEvent createSensorEvent() {
- // maximal size for all legacy events is 3
- return new SensorEvent(3);
- }
-
- protected SensorEvent getFromPool() {
- SensorEvent t = null;
- synchronized (this) {
- // remove the array from the pool
- t = mValuesPool;
- mValuesPool = null;
- }
- if (t == null) {
- // the pool was empty, we need a new one
- t = createSensorEvent();
- }
- return t;
- }
-
- protected void returnToPool(SensorEvent t) {
- synchronized (this) {
- // put back the array into the pool
- if (mValuesPool == null) {
- mValuesPool = t;
- }
- }
- }
-
Object getListener() {
return mSensorEventListener;
}
@@ -582,7 +605,7 @@
}
void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
- SensorEvent t = getFromPool();
+ SensorEvent t = sPool.getFromPool();
final float[] v = t.values;
v[0] = values[0];
v[1] = values[1];
@@ -644,6 +667,7 @@
}
} while (i>0);
+ sPool = new SensorEventPool( sFullSensorsList.size()*2 );
sSensorThread = new SensorThread();
}
}
@@ -1970,7 +1994,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/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 97f96da..b3f3988 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -159,17 +159,7 @@
*/
public static InetAddress numericToInetAddress(String addrString)
throws IllegalArgumentException {
- // TODO - do this for real, using a hidden method on InetAddress that aborts
- // instead of doing dns step
- if (!InetAddress.isNumeric(addrString)) {
- throw new IllegalArgumentException("numericToInetAddress with non numeric: '" +
- addrString + "'");
- }
- try {
- return InetAddress.getByName(addrString);
- } catch (UnknownHostException e) {
- throw new IllegalArgumentException(e);
- }
+ return InetAddress.parseNumericAddress(addrString);
}
/**
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 622bcdb..8c56fda 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -702,4 +702,28 @@
return null;
}
}
+
+ /**
+ * To change the Secure Element Card Emulation state (ON/OFF)
+ * @hide
+ */
+ public void changeNfcSecureElementCardEmulationState(boolean state)
+ {
+ int seId = 11259375;
+ if(state){
+ /* Enable card emulation */
+ try {
+ sService.selectSecureElement(seId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Enable card emulation failed", e);
+ }
+ }else{
+ /* Disable card emulation */
+ try {
+ sService.deselectSecureElement();
+ } catch (RemoteException e) {
+ Log.e(TAG, " card emulation failed", e);
+ }
+ }
+ }
}
diff --git a/core/java/android/nfc/tech/package.html b/core/java/android/nfc/tech/package.html
new file mode 100644
index 0000000..a99828f
--- /dev/null
+++ b/core/java/android/nfc/tech/package.html
@@ -0,0 +1,13 @@
+<HTML>
+<BODY>
+<p>
+These classes provide access to a tag technology's features, which vary by the type
+of tag that is scanned. A scanned tag can support multiple technologies, and you can find
+out what they are by calling {@link android.nfc.Tag#getTechList getTechList()}.</p>
+
+<p>For more information on dealing with tag technologies and handling the ones that you care about, see
+<a href="{@docRoot}guide/topics/nfc/index.html#dispatch">The Tag Dispatch System</a>.
+The {@link android.nfc.tech.TagTechnology} interface provides an overview of the
+supported technologies.</p>
+</BODY>
+</HTML>
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/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 0412285..0825bf3 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -212,6 +212,10 @@
Object ret1 = null;
for (int i = 0; i < spanCount; i++) {
+ if (kind != null && !kind.isInstance(spans[i])) {
+ continue;
+ }
+
int spanStart = data[i * COLUMNS + START];
int spanEnd = data[i * COLUMNS + END];
@@ -231,10 +235,6 @@
}
}
- if (kind != null && !kind.isInstance(spans[i])) {
- continue;
- }
-
if (count == 0) {
ret1 = spans[i];
count++;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 378eb21..b982c7b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2138,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;
@@ -2234,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;
@@ -8355,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);
}
@@ -8597,7 +8607,7 @@
public void buildDrawingCache() {
buildDrawingCache(false);
}
-
+
/**
* <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
*
@@ -8624,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);
@@ -8649,6 +8660,7 @@
(width * height * (opaque && !use32BitCache ? 2 : 4) >
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
destroyDrawingCache();
+ mCachingFailed = true;
return;
}
@@ -8701,6 +8713,7 @@
} else {
mUnscaledDrawingCache = null;
}
+ mCachingFailed = true;
return;
}
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 efbcd58..3aebd00 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -805,8 +805,11 @@
if (mWebView.getWebViewCore() != null) {
// we always force, in case our height changed, in which case we
// still want to send the notification over to webkit.
- setZoomScale(Math.max(mActualScale, getZoomOverviewScale()),
- mUpdateTextWrap, true);
+ // Keep overview mode unchanged when rotating.
+ final float zoomOverviewScale = getZoomOverviewScale();
+ final float newScale = (mInZoomOverview) ?
+ zoomOverviewScale : Math.max(mActualScale, zoomOverviewScale);
+ setZoomScale(newScale, mUpdateTextWrap, true);
// update the zoom buttons as the scale can be changed
updateZoomPicker();
}
@@ -846,23 +849,33 @@
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))) {
+ // fit the content width to the current view for the first new picture
+ // after first layout.
+ boolean scaleHasDiff = exceedsMinScaleIncrement(newZoomOverviewScale, mActualScale);
+ if (!mWebView.drawHistory() && mInitialZoomOverview && scaleHasDiff) {
mInitialZoomOverview = false;
- setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
+ setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
!mWebView.getSettings().getUseFixedViewport());
+ } else if (scaleHasDiff) {
+ mInZoomOverview = false;
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3f38f2e..eb53e56 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
@@ -3266,9 +3267,10 @@
final int scrollY = mScrollY;
if (!mEdgeGlowTop.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
+ final int width = getWidth() - mListPadding.left - mListPadding.right;
- canvas.translate(0, Math.min(0, scrollY + mFirstPositionDistanceGuess));
+ canvas.translate(mListPadding.left,
+ Math.min(0, scrollY + mFirstPositionDistanceGuess));
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
invalidate();
@@ -3277,10 +3279,11 @@
}
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
+ final int width = getWidth() - mListPadding.left - mListPadding.right;
final int height = getHeight();
- canvas.translate(-width, Math.max(height, scrollY + mLastPositionDistanceGuess));
+ canvas.translate(-width + mListPadding.left,
+ Math.max(height, scrollY + mLastPositionDistanceGuess));
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
@@ -4169,7 +4172,7 @@
if (mScrollingCacheEnabled && !mCachingStarted) {
setChildrenDrawnWithCacheEnabled(true);
setChildrenDrawingCacheEnabled(true);
- mCachingStarted = true;
+ mCachingStarted = mCachingActive = true;
}
}
@@ -4178,7 +4181,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/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 13f0890..d75748f 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -1422,11 +1422,11 @@
final int scrollX = mScrollX;
if (!mEdgeGlowLeft.isFinished()) {
final int restoreCount = canvas.save();
- final int height = getHeight();
+ final int height = getHeight() - mPaddingTop - mPaddingBottom;
canvas.rotate(270);
- canvas.translate(-height, Math.min(0, scrollX));
- mEdgeGlowLeft.setSize(getHeight(), getWidth());
+ canvas.translate(-height + mPaddingTop, Math.min(0, scrollX));
+ mEdgeGlowLeft.setSize(height, getWidth());
if (mEdgeGlowLeft.draw(canvas)) {
invalidate();
}
@@ -1435,10 +1435,10 @@
if (!mEdgeGlowRight.isFinished()) {
final int restoreCount = canvas.save();
final int width = getWidth();
- final int height = getHeight();
+ final int height = getHeight() - mPaddingTop - mPaddingBottom;
canvas.rotate(90);
- canvas.translate(0,
+ canvas.translate(-mPaddingTop,
-(Math.max(getScrollRange(), scrollX) + width));
mEdgeGlowRight.setSize(height, width);
if (mEdgeGlowRight.draw(canvas)) {
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/ScrollView.java b/core/java/android/widget/ScrollView.java
index d1cfcec..4cc4a27 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1470,9 +1470,9 @@
final int scrollY = mScrollY;
if (!mEdgeGlowTop.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
+ final int width = getWidth() - mPaddingLeft - mPaddingRight;
- canvas.translate(0, Math.min(0, scrollY));
+ canvas.translate(mPaddingLeft, Math.min(0, scrollY));
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
invalidate();
@@ -1481,10 +1481,11 @@
}
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth();
+ final int width = getWidth() - mPaddingLeft - mPaddingRight;
final int height = getHeight();
- canvas.translate(-width, Math.max(getScrollRange(), scrollY) + height);
+ canvas.translate(-width + mPaddingLeft,
+ Math.max(getScrollRange(), scrollY) + height);
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index f051b77..22edcd0 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -394,7 +394,6 @@
if (mIconifiedByDefault == iconified) return;
mIconifiedByDefault = iconified;
updateViewsVisibility(iconified);
- setImeVisibility(!iconified);
}
/**
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/TimePicker.java b/core/java/android/widget/TimePicker.java
index 18d1825..029d690 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -43,8 +43,8 @@
* or 'P' to pick. For a dialog using this view, see
* {@link android.app.TimePickerDialog}.
*<p>
- * See the <a href="{@docRoot}
- * resources/tutorials/views/hello-timepicker.html">Time Picker tutorial</a>.
+ * See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.
* </p>
*/
@Widget
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_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 58b5686..fc806a5 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -31,25 +31,6 @@
static jboolean sScanModeActive = false;
-//TODO: check general errors in addition to overflow on snprintf
-
-/*
- * The following remembers the jfieldID's of the fields
- * of the DhcpInfo Java object, so that we don't have
- * to look them up every time.
- */
-static struct fieldIds {
- jclass dhcpInfoClass;
- jmethodID constructorId;
- jfieldID ipaddress;
- jfieldID gateway;
- jfieldID netmask;
- jfieldID dns1;
- jfieldID dns2;
- jfieldID serverAddress;
- jfieldID leaseDuration;
-} dhcpInfoFieldIds;
-
static int doCommand(const char *cmd, char *replybuf, int replybuflen)
{
size_t reply_len = replybuflen - 1;
@@ -575,29 +556,6 @@
return doBooleanCommand(cmdstr, "OK");
}
-
-static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
-{
- jint ipaddr, gateway, mask, dns1, dns2, server, lease;
- jboolean succeeded = ((jboolean)::do_dhcp_request(&ipaddr, &gateway, &mask,
- &dns1, &dns2, &server, &lease) == 0);
- if (succeeded && dhcpInfoFieldIds.dhcpInfoClass != NULL) {
- env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr);
- env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway);
- env->SetIntField(info, dhcpInfoFieldIds.netmask, mask);
- env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1);
- env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2);
- env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server);
- env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease);
- }
- return succeeded;
-}
-
-static jstring android_net_wifi_getDhcpError(JNIEnv* env, jobject clazz)
-{
- return env->NewStringUTF(::get_dhcp_error_string());
-}
-
// ----------------------------------------------------------------------------
/*
@@ -665,8 +623,6 @@
(void*) android_net_wifi_setSuspendOptimizationsCommand},
{ "setCountryCodeCommand", "(Ljava/lang/String;)Z",
(void*) android_net_wifi_setCountryCodeCommand},
- { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
- { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
};
int register_android_net_wifi_WifiManager(JNIEnv* env)
@@ -674,18 +630,6 @@
jclass wifi = env->FindClass(WIFI_PKG_NAME);
LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME);
- dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo");
- if (dhcpInfoFieldIds.dhcpInfoClass != NULL) {
- dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V");
- dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I");
- dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I");
- dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I");
- dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I");
- dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I");
- dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I");
- dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I");
- }
-
return AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2dc0d31..8e8383a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -343,7 +343,7 @@
android:description="@string/permdesc_bluetooth"
android:label="@string/permlab_bluetooth" />
- <!-- Allows applications to directly communicate over NFC -->
+ <!-- Allows applications to perform I/O operations over NFC -->
<permission android:name="android.permission.NFC"
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="dangerous"
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/docs/html/guide/developing/building/building-cmdline.jd b/docs/html/guide/developing/building/building-cmdline.jd
index a5ab07d..81c1178 100644
--- a/docs/html/guide/developing/building/building-cmdline.jd
+++ b/docs/html/guide/developing/building/building-cmdline.jd
@@ -1,4 +1,6 @@
page.title=Building and Running from the Command Line
+parent.title=Building and Running
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/building/building-eclipse.jd b/docs/html/guide/developing/building/building-eclipse.jd
index 85c82a9..6ebc49e 100644
--- a/docs/html/guide/developing/building/building-eclipse.jd
+++ b/docs/html/guide/developing/building/building-eclipse.jd
@@ -1,4 +1,6 @@
page.title=Building and Running from Eclipse with ADT
+parent.title=Building and Running
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/debugging/ddms.jd b/docs/html/guide/developing/debugging/ddms.jd
index 8f5e706..0d47ae5 100644
--- a/docs/html/guide/developing/debugging/ddms.jd
+++ b/docs/html/guide/developing/debugging/ddms.jd
@@ -1,4 +1,6 @@
page.title=Using DDMS
+parent.title=Debugging
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/debugging/debugging-devtools.jd b/docs/html/guide/developing/debugging/debugging-devtools.jd
index 67605ae..157d62e 100644
--- a/docs/html/guide/developing/debugging/debugging-devtools.jd
+++ b/docs/html/guide/developing/debugging/debugging-devtools.jd
@@ -1,4 +1,6 @@
page.title=Using the Dev Tools App
+parent.title=Debugging
+parent.link=index.html
@jd:body
<p>The Dev Tools application is installed by default on all system images included with the SDK,
diff --git a/docs/html/guide/developing/debugging/debugging-log.jd b/docs/html/guide/developing/debugging/debugging-log.jd
index ff73c17..295772d 100644
--- a/docs/html/guide/developing/debugging/debugging-log.jd
+++ b/docs/html/guide/developing/debugging/debugging-log.jd
@@ -1,4 +1,6 @@
page.title=Reading and Writing Logs
+parent.title=Debugging
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/debugging/debugging-projects-cmdline.jd b/docs/html/guide/developing/debugging/debugging-projects-cmdline.jd
index 6800a5d..3b5ceab 100644
--- a/docs/html/guide/developing/debugging/debugging-projects-cmdline.jd
+++ b/docs/html/guide/developing/debugging/debugging-projects-cmdline.jd
@@ -1,4 +1,6 @@
-page.title=Debugging from other IDEs
+page.title=Debugging from Other IDEs
+parent.title=Debugging
+parent.link=index.html
@jd:body
diff --git a/docs/html/guide/developing/debugging/debugging-projects.jd b/docs/html/guide/developing/debugging/debugging-projects.jd
index deb4341..2283f8b 100644
--- a/docs/html/guide/developing/debugging/debugging-projects.jd
+++ b/docs/html/guide/developing/debugging/debugging-projects.jd
@@ -1,4 +1,6 @@
page.title=Debugging from Eclipse with ADT
+parent.title=Debugging
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/debugging/debugging-tracing.jd b/docs/html/guide/developing/debugging/debugging-tracing.jd
index 0401966..72f6498 100644
--- a/docs/html/guide/developing/debugging/debugging-tracing.jd
+++ b/docs/html/guide/developing/debugging/debugging-tracing.jd
@@ -1,4 +1,6 @@
page.title=Profiling with Traceview and dmtracedump
+parent.title=Debugging
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/debugging/debugging-ui.jd b/docs/html/guide/developing/debugging/debugging-ui.jd
index 9b02d34..d2c42d5 100644
--- a/docs/html/guide/developing/debugging/debugging-ui.jd
+++ b/docs/html/guide/developing/debugging/debugging-ui.jd
@@ -1,4 +1,6 @@
page.title=Debugging and Profiling User Interfaces
+parent.title=Debugging
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
@@ -7,162 +9,498 @@
<ol>
<li>
- <a href="#hierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy
- Viewer</a>
- <ol>
- <li><a href="#layoutview">Layout View</a></li>
- <li><a href="#pixelperfect">Pixel Perfect View</a></li>
- </ol>
+ <a href="#HierarchyViewer">
+ Debugging and Optimizing User Interfaces with Hierarchy Viewer
+ </a>
+ <ol>
+ <li><a href="#runhv">Running Hierarchy Viewer and choosing a window</a></li>
+ <li><a href="#viewhierarchy">About the View Hierarchy window</a></li>
+ <li><a href="#indiView">Working with an individual View in Tree View</a></li>
+ <li><a href="#hvdebugging">Debugging with View Hierarchy</a></li>
+ <li><a href="#hvoptimize">Optimizing with View Hierarchy</a></li>
+ </ol>
</li>
-
+ <li>
+ <a href="#pixelperfect">
+ Examining and Designing User Interfaces with Pixel Perfect
+ </a>
+ <ol>
+ <li><a href="#aboutpixelperfect">About the Pixel Perfect window</a></li>
+ <li><a href="#overlays">Working with Pixel Perfect overlays</a></li>
+ </ol>
+ </li>
<li><a href="#layoutopt">Optimizing Layouts with <code>layoutopt</code></a></li>
</ol>
</div>
</div>
- <p>Sometimes your application's layout can slow down your application.
- To help debug issues in your layout, the Android SDK provides the Hierarchy Viewer and
+ <p>Sometimes your application's layout can slow down your application.
+ To help debug issues in your layout, the Android SDK provides the Hierarchy Viewer and
<code>layoutopt</code> tools.
</p>
-
+
<p>The Hierarchy Viewer application allows you to debug and optimize your user interface. It
- provides a visual representation of the layout's View hierarchy (the Layout View) and a magnified
- inspector of the display (the Pixel Perfect View).</p>
+ provides a visual representation of the layout's View hierarchy (the View Hierarchy window)
+ and a magnified view of the display (the Pixel Perfect window).</p>
<p><code>layoutopt</code> is a command-line tool that helps you optimize the layouts and layout
hierarchies of your applications. You can run it against your layout files or resource
directories to quickly check for inefficiencies or other types of problems that could be
affecting the performance of your application.</p>
- <h2 id="hierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy Viewer</h2>
+<h2 id="HierarchyViewer">Debugging and Optimizing User Interfaces with Hierarchy Viewer</h2>
- <p>To get the Hierarchy Viewer started:</p>
-
- <ol>
- <li>Connect your device or launch an emulator.</li>
-
- <li>From a terminal, launch <code>hierarchyviewer</code> from the <code><sdk>/tools/</code>
- directory.</li>
-
- <li>In the window that opens, you'll see a list of <strong>Devices</strong>. When a device is
- selected, a list of currently active <strong>Windows</strong> is displayed on the right. The
- <em><Focused Window></em> is the window currently in the foreground, and also the default
- window loaded if you do not select another.</li>
-
- <li>Select the window that you'd like to inspect and click <strong>Load View
- Hierarchy</strong>. The Layout View will be loaded. You can then load the Pixel Perfect View by
- clicking the second icon at the bottom-left of the window.</li>
- </ol>
-
- <p>If you've navigated to a different window on the device, press <strong>Refresh
- Windows</strong> to refresh the list of available windows on the right.</p>
-
-
- <h3 id="layoutview">Layout View</h3>
-
- <p>The Layout View offers a look at the View layout and properties. It has three views:</p>
-
- <ul>
- <li>Tree View: a hierarchy diagram of the Views, on the left.</li>
-
- <li>Properties View: a list of the selected View's properties, on the top-right.</li>
-
- <li>Wire-frame View: a wire-frame drawing of the layout, on the bottom-right.</li>
- </ul><br />
- <img src="{@docRoot}images/hierarchyviewer-layout.png"
+<h3 id="runhv">Running Hierarchy Viewer and choosing a window</h3>
+<p>
+ To run Hierarchy Viewer, follow these steps:</p>
+<ol>
+ <li>
+ Connect your device or launch an emulator.
+ <p>
+ To preserve security, Hierarchy Viewer can only connect to devices running a
+ developer version of the Android system.
+ </p>
+ </li>
+ <li>
+ If you have not done so already, install the application you want to work with.
+ </li>
+ <li>
+ Run the application, and ensure that its UI is visible.
+ </li>
+ <li>
+ From a terminal, launch <code>hierarchyviewer</code> from the
+ <code><sdk>/tools/</code>
+ directory.
+ </li>
+ <li>
+ The first window you see displays a list of devices and emulators. To expand the list
+ of Activity objects for a device or emulator, click the arrow on the left. This displays a
+ list of the Activity objects whose UI is currently visible on the device or emulator. The
+ objects are listed by their Android component name. The list includes both your application
+ Activity and system Activity objects. A screenshot of this window appears in
+ figure 1.
+ </li>
+ <li>
+ Select the name of your Activity from the list. You can now look at its view
+ hierarchy using the View Hierarchy window, or look at a magnified image of the UI using
+ the Pixel Perfect window.
+ </li>
+</ol>
+<p>
+ To learn how to use the View Hierarchy window, go to
+ <a href="#viewhierarchy">About the View Hierarchy window</a>. To learn how to use the
+ Pixel Perfect window, go to <a href="#pixelperfect">About the Pixel Perfect window</a>.
+</p>
+<img id="Fig1" src="{@docRoot}images/developing/hv_device_window.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 1.</strong> Hierarchy Viewer device window</p>
+<h3 id="viewhierarchy">About the View Hierarchy window</h3>
+<p>
+ The View Hierarchy window displays the View objects that form the UI of the
+ Activity that is running on your device or emulator. You use it to look at individual
+ View objects within the context of the entire View tree. For each View object, the View
+ Hierarchy window also displays rendering performance data.
+</p>
+<p>
+ To see the View Hierarchy window, run Hierarchy Viewer as described in
+ the section <a href="#runhv">Running Hierarchy Viewer and choosing a window</a>. Next, click
+ <strong>View Hierarchy</strong> at the top of the device window.
+</p>
+<p>
+ You should see four panes:
+</p>
+<ul>
+ <li>
+ <strong>Tree View</strong>: The left-hand pane displays the Tree View,
+ a diagram of the Activity object's hierarchy of views. Use Tree View to examine individual
+ View objects and see the relationships between View objects in your UI.
+ <p>
+ To zoom in on the pane, use the slider at the bottom of the pane, or use your mouse
+ scroll wheel. To move around in the pane or reveal View objects that are not currently
+ visible, click and drag the pane.
+ </p>
+ <p>
+ To highlight the nodes in the tree whose class or ID match a search string, enter the
+ string in the <strong>Filter by class or id:</strong> edit box at the bottom of the
+ window. The background of nodes that match the search string will change from gray to
+ bright blue.
+ </p>
+ <p>
+ To save a screenshot of Tree View to a PNG file, click <strong>Save As PNG</strong> at
+ the top of the View Hierarchy window. This displays a dialog in which you can choose
+ a directory and file name.
+ </p>
+ <p>
+ To save a layered screenshot of your device or emulator to an Adobe Photoshop (PSD)
+ file, click <strong>Capture Layers</strong> at the top of the View Hierarchy window.
+ This displays a dialog in which you can choose a directory or file name.
+ Each View in the UI is saved as a separate Photoshop layer.
+ </p>
+ <p>
+ In Photoshop (or similar program that accepts .psd files), you can hide, show or edit a
+ layer independently of others. When you save a layered screenshot, you can examine and
+ modify the image of an individual View object. This helps you experiment with design
+ changes.
+ </p>
+ </li>
+ <li>
+ The upper right-hand pane displays the <strong>Tree Overview</strong>, a smaller map
+ representation of the entire Tree View window. Use Tree Overview to identify the part of the
+ view tree that is being displayed in Tree View.
+ <p>
+ You can also use Tree Overview to move around in the Tree View pane. Click and drag
+ the shaded rectangle over an area to reveal it in Tree View.
+ </p>
+ </li>
+ <li>
+ The middle right-hand pane displays the <strong>Properties View</strong>,
+ a list of the properties for a selected View object. With Properties View, you can
+ examine all the properties without having to look at your application source.
+ <p>
+ The properties are organized by category. To find an individual property, expand
+ a category name by clicking the arrow on its left. This reveals all the properties
+ in that category.
+ </p>
+ </li>
+ <li>
+ The lower right-hand pane displays the <strong>Layout View</strong>,
+ a block representation of the UI. Layout View is another way to navigate through your UI.
+ When you click on a View object in Tree View, its position in the UI is highlighted.
+ Conversely, when you click in an area of Layout View, the View object for that area is
+ highlighted in Tree View.
+ <p>
+ The outline colors of blocks in Layout View provide additional information:
+ </p>
+ <ul>
+ <li>
+ Bold red: The block represents the the View that is currently selected in
+ Tree View.
+ </li>
+ <li>
+ Light red: The block represents the parent of the block outlined in bold red.
+ </li>
+ <li>
+ White: The block represents a visible View that is not a parent or child of the
+ View that is currently selected in Tree View.
+ </li>
+ </ul>
+ </li>
+</ul>
+<p>
+ When the UI of the current Activity changes, the View Hierarchy window is not automatically
+ updated. To update it, click <strong>Load View Hierarchy</strong> at the top of the window.
+</p>
+<p>
+ Also, the window is not updated if you switch to a new Activity. To update it, start by
+ clicking the window selection icon in the bottom left-hand corner of the window. This
+ navigates back to the Window Selection window. From this window, click the Android
+ component name of the new Activity and then click <strong>Load View Hierarchy</strong>
+ at the top of the window.
+</p>
+<p>
+ A screenshot of the View Hierarchy window appears in figure 2.
+</p>
+<img id="Fig2" src="{@docRoot}images/developing/hv_view_hierarchy_window.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 2.</strong> The View Hierarchy window</p>
+<h3 id="indiView">Working with an individual View in Tree View</h3>
+<p>
+ Each node in Tree View represents a single View. Some information is always visible. Starting
+ at the top of the node, you see the following:
+</p>
+<ol>
+ <li>
+ View class: The View object's class.
+ </li>
+ <li>
+ View object address: A pointer to View object.
+ </li>
+ <li>
+ View object ID: The value of the
+ <code><a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">android:id</a>
+ </code> attribute.
+ </li>
+ <li>
+ Performance indicators: A set of three colored dots that indicate the rendering
+ speed of this View relative to other View objects in the tree. The three dots
+ represent (from left to right) the measure, layout, and draw times of the rendering.
+ <p>
+ The colors indicate the following relative performance:
+ </p>
+ <ul>
+ <li>
+ Green: For this part of the render time, this View is in the faster 50% of all
+ the View objects in the tree. For example, a green dot for the measure time means
+ that this View has a faster measure time than 50% of the View objects in the tree.
+ </li>
+ <li>
+ Yellow: For this part of the render time, this View is in the slower 50% of all
+ the View objects in the tree. For example, a yellow dot for the layout time means
+ that this View has a slower layout time than 50% of the View objects in the tree.
+ </li>
+ <li>
+ Red: For this part of the render time, this View is the slowest one in the tree.
+ For example, a red dot for the draw time means that this View takes the most
+ time to draw of all the View objects in the tree.
+ </li>
+ </ul>
+ </li>
+ <li>
+ View index: The zero-based index of the View in its parent View. If it is the only child,
+ this is 0.
+ </li>
+</ol>
+<p>
+ When you select a node, additional information for the View appears in a small window above
+ the node. When you click one of the nodes, you see the following:
+</p>
+<ul>
+ <li>
+ Image: The actual image of the View, as it would appear in the emulator. If the View has
+ children, these are also displayed.
+ </li>
+ <li>
+ View count: The number of View objects represented by this node. This includes the View
+ itself and a count of its children. For example, this value is 4 for a View that has 3
+ children.
+ </li>
+ <li>
+ Render times: The actual measure, layout, and draw times for the View rendering, in
+ milliseconds. These represent the same values as the performance indicators mentioned in
+ the preceding section.
+ </li>
+</ul>
+<p>
+ An annotated screenshot of an individual node in the Tree View window appears in figure 3.
+</p>
+<img id="Fig3" src="{@docRoot}images/developing/hv_treeview_screenshot.png" alt="" height="600"/>
+<p class="img-caption"><strong>Figure 3.</strong> An annotated node in Tree View</p>
+<h3 id="hvdebugging">Debugging with View Hierarchy</h3>
+<p>
+ The View Hierarchy window helps you debug an application by providing a static display
+ of the UI. The display starts with your application's opening screen. As you step through
+ your application, the display remains unchanged until you redraw it by invalidating and
+ then requesting layout for a View.
+</p>
+<p>
+ To redraw a View in the display:
+</p>
+ <ul>
+ <li>
+ Select a View in Tree View. As you move up towards the root of the tree (to the
+ left in the Tree View), you see the highest-level View objects. Redrawing a high-level
+ object usually forces the lower-level objects to redraw as well.
+ </li>
+ <li>
+ Click <strong>Invalidate</strong> at the top of the window. This marks the View as
+ invalid, and schedules it for a redraw at the next point that a layout is requested.
+ </li>
+ <li>
+ Click <strong>Request Layout</strong> to request a layout. The View and its children
+ are redrawn, as well as any other View objects that need to be redrawn.
+ </li>
+ </ul>
+<p>
+ Manually redrawing a View allows you to watch the View object tree and examine the properties of
+ individual View objects one step at a time as you go through breakpoints in your code.
+</p>
+<h3 id="hvoptimize">Optimizing with View Hierarchy</h3>
+<p>
+ View Hierarchy also helps you identify slow render performance. You start by looking at the
+ View nodes with red or yellow performance indicators to identify the slower View objects. As you
+ step through your application, you can judge if a View is consistently slow or slow only in
+ certain circumstances.
+</p>
+<p>
+ Remember that slow performance is not necessarily evidence of a problem, especially for
+ ViewGroup objects. View objects that have more children and more complex View objects render
+ more slowly.
+</p>
+<p>
+ The View Hierarchy window also helps you find performance issues. Just by looking at the
+ performance indicators (the dots) for each View node, you can see which View objects are the
+ slowest to measure, layout, and draw. From that, you can quickly identify the problems you
+ should look at first.
+</p>
+<h2 id="pixelperfect">Examining and Designing User Interfaces with Pixel Perfect</h2>
+<p>
+ Pixel Perfect is a tool for examining pixel properties and laying out UIs from a design drawing.
+</p>
+<h3 id="aboutpixelperfect">About the Pixel Perfect window</h3>
+<p>
+ The Pixel Perfect window displays a magnified image of the screen that is currently
+ visible on the emulator or device. In it, you can examine the properties
+ of individual pixels in the screen image. You can also use the Pixel Perfect window
+ to help you lay out your application UI based on a bitmap design.
+</p>
+<p>
+ To see the Pixel Perfect window, run Hierarchy Viewer, as described in
+ the section <a href="#runhv">Running Hierarchy Viewer and choosing a window</a>. Next, click
+ <strong>Inspect Screenshot</strong> at the top of the device window. The Pixel Perfect window
+ appears.
+</p>
+<p>
+ In it, you see three panes:
+</p>
+<ul>
+ <li>
+ View Object pane: This is a hierarchical list of the View objects that are currently
+ visible on the device or emulator screen, including both the ones in your application and
+ the ones generated by the system. The objects are listed by their View class.
+ To see the class names of a View object's children, expand the View by clicking the
+ arrow to its left. When you click a View, its position is highlighted in the Pixel Perfect
+ pane on the right.
+ </li>
+ <li>
+ Pixel Perfect Loupe pane: This is the magnified screen image. It is overlaid by a grid in
+ which each square represents one pixel. To look at the information for a pixel, click in its
+ square. Its color and X,Y coordinates appear at the bottom of the pane.
+ <p>
+ The magenta crosshair in the pane corresponds to the positioning
+ crosshair in the next pane. It only moves when you move the crosshair in the next pane.
+ </p>
+ <p>
+ To zoom in or out on the image, use the <strong>Zoom</strong> slider at the bottom of
+ the pane, or use your mouse's scroll wheel.
+ </p>
+ <p>
+ When you select a pixel in the Loupe pane, you see the following information at the
+ bottom of the pane:
+ </p>
+ <ul>
+ <li>
+ Pixel swatch: A rectangle filled with the same color as the pixel.
+ </li>
+ <li>
+ HTML color code: The hexadecimal RGB code corresponding to the pixel color
+ </li>
+ <li>
+ RGB color values: A list of the (R), green (G), and blue (B) color values of the
+ pixel color. Each value is in the range 0-255.
+ </li>
+ <li>
+ X and Y coordinates: The pixel's coordinates, in device-specific pixel units.
+ The values are 0-based, with X=0 at the left of the screen and Y=0 at the top.
+ </li>
+ </ul>
+ </li>
+ <li>
+ Pixel Perfect pane: This displays the currently visible screen as it would appear in the
+ emulator.
+ <p>
+ You use the cyan crosshair to do coarse positioning. Drag the crosshair in the image,
+ and the Loupe crosshair will move accordingly. You can also click on a point in the
+ Pixel Perfect pane, and the crosshair will move to that point.
+ </p>
+ <p>
+ The image corresponding to the View object selected in the View Object pane is
+ outlined in a box that indicates the View object's position on the screen. For the
+ selected object, the box is bold red. Sibling and parent View objects have a light
+ red box. View objects that are neither parents nor siblings are in white.
+ </p>
+ <p>
+ The layout box may have other rectangles either inside or outside it, each of which
+ indicates part of the View. A purple or green rectangle indicates the View bounding box.
+ A white or black box inside the layout box represents the <strong>padding</strong>, the
+ defined distance between the View object's content and its bounding box. An outer white
+ or black rectangle represents the <strong>margins</strong>, the distance between the
+ View bounding box and adjacent View objects. The padding and margin boxes are white if
+ the layout background is black, and vice versa.
+ </p>
+ <p>
+ You can save the screen image being displayed in the Pixel Perfect pane as a PNG file.
+ This produces a screenshot of the current screen. To do this, click
+ <strong>Save as PNG</strong> at the top of the window. This displays a dialog,
+ in which you can choose a directory and filename for the file.
+ </p>
+ </li>
+</ul>
+<p>
+ The panes are not automatically refreshed when you change one of the View objects or go to
+ another Activity. To refresh the Pixel Perfect pane and the Loupe pane, click
+ <strong>Refresh Screenshot</strong> at the top of the window. This will change the panes
+ to reflect the current screen image. You still may need to refresh the View Object pane;
+ to do this, click <strong>Refresh Tree</strong> at the top of the window.
+</p>
+<p>
+ To automatically refresh the panes while you are debugging, set
+ <strong>Auto Refresh</strong> at the top of the window, and then set a refresh rate
+ with the <strong>Refresh Rate</strong> slider at the bottom of the Loupe pane.
+</p>
+<h3 id="overlays">Working with Pixel Perfect overlays</h3>
+<p>
+ You often construct a UI based on a design done as a bitmap image. The Pixel Perfect window
+ helps you match up your View layout to a bitmap image by allowing you to load the bitmap as an
+ <strong>overlay</strong> on the screen image.
+</p>
+<p>
+ To use a bitmap image as an overlay:
+</p>
+<ul>
+ <li>
+ Start your application in a device or emulator and navigate to the Activity whose UI you
+ want to work with.
+ </li>
+ <li>
+ Start Hierarchy Viewer and navigate to the Pixel Perfect window.
+ </li>
+ <li>
+ At the top of the window, click <strong>Load Overlay</strong>. A dialog opens, prompting
+ for the image file to load. Load the image file.
+ </li>
+ <li>
+ Pixel Perfect displays the overlay over the screen image in the Pixel Perfect pane. The
+ lower left corner of the bitmap image (X=0, Y=<em>max value</em>) is anchored on the lower
+ leftmost pixel (X=0, Y=<em>max screen</em>) of the screen.
+ <p>
+ By default, the overlay has a 50% transparency, which allows you to see the screen
+ image underneath. You can adjust this with the <strong>Overlay:</strong> slider at the
+ bottom of the Loupe pane.
+ </p>
+ <p>
+ Also by default, the overlay is not displayed in the Loupe pane. To display it,
+ set <strong>Show in Loupe</strong> at the top of the window.
+ </p>
+ </li>
+</ul>
+<p>
+ The overlay is not saved as part of the screenshot when you save the screen image as a PNG
+ file.
+</p>
+<p>
+ A screenshot of the Pixel Perfect window appears in figure 4.
+</p>
+<img id="Fig4" src="{@docRoot}images/developing/hv_pixelperfect.png"
alt=""
- height="509"
- width="700" />
- <p class="img-caption"><strong>Figure 1.</strong> Screenshot of Hierarchy Viewer</p>
+ height="600"/>
+<p class="img-caption"><strong>Figure 4.</strong> The Pixel Perfect window</p>
+<h2 id="layoutopt">Optimizing layouts with layoutopt</h2>
+<p>
+ The <code>layoutopt</code> tool lets you analyze the XML files that define your
+ application's UI to find inefficiencies in the view hierarchy.</p>
- <p>Select a node in the Tree View to display the properties of that element in the Properties
- View. When a node is selected, the Wire-frame View also indicates the bounds of the element with
- a red rectangle. Double click a node in the tree (or select it, and click <strong>Display
- View</strong>) to open a new window with a rendering of that element.</p>
-
- <p>The Layout View includes a couple other helpful features for debugging your layout:
- <strong>Invalidate</strong> and <strong>Request Layout</strong>. These buttons execute the
- respective View calls, {@link android.view.View#invalidate()} and {@link
- android.view.View#requestLayout()}, on the View element currently selected in the tree. Calling
- these methods on any View can be very useful when simultaneously running a debugger on your
- application.</p>
-
- <p>The Tree View can be resized by adjusting the zoom slider, below the diagram. The number of
- View elements in the window is also given here. You should look for ways to minimize the number
- of Views. The fewer View elements there are in a window, the faster it will perform.</p>
-
- <p>If you interact with the device and change the focused View, the diagram will not
- automatically refresh. You must reload the Layout View by clicking <strong>Load View
- Hierarchy</strong>.</p>
-
- <h3 id="pixelperfect">Pixel Perfect View</h3>
-
- <p>The Pixel Perfect View provides a magnified look at the current device window. It helps you
- design your UI better by giving you a closer look at your UI's image quality, alignment, and other
- aesthetic qualities. It has three views:</p>
-
- <ul>
- <li>Explorer View: shows the View hierarchy as a list, on the left.</li>
-
- <li>Normal View: a normal view of the device window, in the middle.</li>
-
- <li>Loupe View: a magnified, pixel-grid view of the device window, on the right.</li>
- </ul><br />
- <img src="{@docRoot}images/hierarchyviewer-pixelperfect.png"
- alt=""
- height="509"
- width="700" />
-
- <p>Click on an element in the Explorer View and a "layout box" will be drawn in the Normal View
- to indicate the layout position of that element. The layout box uses multiple rectangles, to
- indicate the normal bounds, the padding and the margin (as needed). The purple or green rectangle
- indicates the normal bounds of the element (the height and width). The inner white or black
- rectangle indicates the content bounds, when padding is present. A black or white rectangle
- outside the normal purple/green rectangle indicates any present margins. (There are two colors
- for each rectangle, in order to provide the best contrast based on the colors currently in the
- background.)</p>
-
- <p>A very handy feature for designing your UI is the ability to overlay an image in the Normal
- and Loupe Views. For example, you might have a mock-up image of how you'd like to layout your
- interface. By selecting <strong>Load...</strong> from the controls in the Normal View, you can
- choose the image from your computer and it will be placed atop the preview. Your chosen image
- will anchor at the bottom left corner of the screen. You can then adjust the opacity of the
- overlay and begin fine-tuning your layout to match the mock-up.</p>
-
- <p>The Normal View and Loupe View refresh at regular intervals (5 seconds by default), but the
- Explorer View does not. If you navigate away and focus on a different View, then you should
- refresh the Explorer's hierarchy by clicking <strong>Load View Hierarchy</strong>. This is even
- true when you're working in a window that holds multiple Views that are not always visible. If
- you do not, although the previews will refresh, clicking a View in the Explorer will not provide
- the proper layout box in the Normal View, because the hierarchy believes you are still focused on
- the prior View.</p>
-
- <p>Optional controls include:</p>
-
- <ul>
- <li><strong>Overlay</strong>: Load an overlay image onto the view and adjust its opacity.</li>
-
- <li><strong>Refresh Rate</strong>: Adjust how often the Normal and Loupe View refresh their
- display.</li>
-
- <li><strong>Zoom</strong>: Adjust the zoom level of the Loupe View.</li>
- </ul>
-
- <h2 id="layoutopt">Optimizing layouts with layoutopt</h2>
- <p>The <code>layoutopt</code> tool lets you analyze the XML files that represent your application's layout
- and finds ineffiencies in the view hierarchy.</p>
-
- <p>To run the tool, open a terminal and launch <code>layoutopt <resources></code> from your
- SDK <code>tools/</code> directory. In the command, supply a list of uncompiled resource xml files
- or directories that you want to analyze.</p>
-
- <p>When run, the tool loads the specified XML files and analyzes their layout structures and
- hierarchies according to a set of predefined rules. If it detects issues, it outputs information
- about the issues, giving filename, line numbers, description of issue, and for some types of
- issues a suggested resolution.</p>
-
- <p>Here's an example of the output:</p>
- <pre>
+<p>
+ To run the tool, open a terminal and launch <code>layoutopt <xmlfiles></code>
+ from your SDK <code>tools/</code> directory. The <xmlfiles> argument is a space-
+ delimited list of resources you want to analyze, either uncompiled resource xml files or
+ directories of such files.
+</p>
+<p>
+ The tool loads the specified XML files and analyzes their definitions and
+ hierarchies according to a set of predefined rules. For every issue it detects, it
+ displays the following information:
+</p>
+<ul>
+ <li>
+ The filename in which the issue was detected.
+ </li>
+ <li>
+ The line number for the issue.
+ </li>
+ <li>
+ A description of the issue, and for some types of issues it also suggests a resolution.
+ </li>
+</ul>
+<p>The following is a sample of the output from the tool:</p>
+<pre>
$ layoutopt samples/
samples/compound.xml
7:23 The root-level <FrameLayout/> can be replaced with <merge/>
@@ -188,15 +526,3 @@
7:19 The root-level <FrameLayout/> can be replaced with <merge/>
11:17 This LinearLayout layout or its FrameLayout parent is useless
</pre>
-
-<p>
-For more information on running the tool, see the
-<a href="${@docRoot}guide/developing/debugging/debugging-ui.html#layoutopt">layoutopt</a> reference.</p>
-
-
-
-
-
-
-
-
diff --git a/docs/html/guide/developing/devices/emulator.jd b/docs/html/guide/developing/devices/emulator.jd
index f61a437..fe00531 100644
--- a/docs/html/guide/developing/devices/emulator.jd
+++ b/docs/html/guide/developing/devices/emulator.jd
@@ -1,4 +1,6 @@
page.title=Using the Android Emulator
+parent.title=Managing Virtual Devices
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/devices/managing-avds-cmdline.jd b/docs/html/guide/developing/devices/managing-avds-cmdline.jd
index 02fd750..6feeeb6 100644
--- a/docs/html/guide/developing/devices/managing-avds-cmdline.jd
+++ b/docs/html/guide/developing/devices/managing-avds-cmdline.jd
@@ -1,4 +1,6 @@
page.title=Managing AVDs from the Command Line
+parent.title=Managing Virtual Devices
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/devices/managing-avds.jd b/docs/html/guide/developing/devices/managing-avds.jd
index 2913ac9..1817ce7 100644
--- a/docs/html/guide/developing/devices/managing-avds.jd
+++ b/docs/html/guide/developing/devices/managing-avds.jd
@@ -1,4 +1,6 @@
page.title=Managing AVDs with AVD Manager
+parent.title=Managing Virtual Devices
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/projects/projects-cmdline.jd b/docs/html/guide/developing/projects/projects-cmdline.jd
index b12864e..7c3e40e 100644
--- a/docs/html/guide/developing/projects/projects-cmdline.jd
+++ b/docs/html/guide/developing/projects/projects-cmdline.jd
@@ -1,4 +1,6 @@
page.title=Managing Projects from the Command Line
+parent.title=Managing Projects
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/projects/projects-eclipse.jd b/docs/html/guide/developing/projects/projects-eclipse.jd
index aa49663..40c17ed 100644
--- a/docs/html/guide/developing/projects/projects-eclipse.jd
+++ b/docs/html/guide/developing/projects/projects-eclipse.jd
@@ -1,4 +1,6 @@
page.title=Managing Projects from Eclipse with ADT
+parent.title=Managing Projects
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/testing/testing_eclipse.jd b/docs/html/guide/developing/testing/testing_eclipse.jd
index 2a5b771..4e9ecca 100644
--- a/docs/html/guide/developing/testing/testing_eclipse.jd
+++ b/docs/html/guide/developing/testing/testing_eclipse.jd
@@ -1,4 +1,6 @@
page.title=Testing from Eclipse with ADT
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
<div id="qv">
@@ -16,8 +18,8 @@
basic processes for creating and running applications with ADT, as described in
<a href="{@docRoot}guide/developing/projects/projects-eclipse.html">Managing Projects from
Eclipse</a>
- and <a href="guide/developing/building/building-eclipse.html">Building and Running from
-Eclipse</a>.
+ and <a href="{@docRoot}guide/developing/building/building-eclipse.html">Building and Running
+from Eclipse</a>.
You may also want to read
<a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
which provides an overview of the Android testing framework.
diff --git a/docs/html/guide/developing/testing/testing_otheride.jd b/docs/html/guide/developing/testing/testing_otheride.jd
index 50cc4fa..93af979 100644
--- a/docs/html/guide/developing/testing/testing_otheride.jd
+++ b/docs/html/guide/developing/testing/testing_otheride.jd
@@ -1,4 +1,6 @@
page.title=Testing from Other IDEs
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/tools/MonkeyDevice.jd b/docs/html/guide/developing/tools/MonkeyDevice.jd
index 34bbba9..abcf8fd 100644
--- a/docs/html/guide/developing/tools/MonkeyDevice.jd
+++ b/docs/html/guide/developing/tools/MonkeyDevice.jd
@@ -1,4 +1,6 @@
page.title=MonkeyDevice
+parent.title=monkeyrunner
+parent.link=index.html
@jd:body
<style>
h4.jd-details-title {background-color: #DEE8F1;}
diff --git a/docs/html/guide/developing/tools/MonkeyImage.jd b/docs/html/guide/developing/tools/MonkeyImage.jd
index ae85cb5..2efc373 100644
--- a/docs/html/guide/developing/tools/MonkeyImage.jd
+++ b/docs/html/guide/developing/tools/MonkeyImage.jd
@@ -1,4 +1,6 @@
page.title=MonkeyImage
+parent.title=monkeyrunner
+parent.link=index.html
@jd:body
<style>
h4.jd-details-title {background-color: #DEE8F1;}
diff --git a/docs/html/guide/developing/tools/MonkeyRunner.jd b/docs/html/guide/developing/tools/MonkeyRunner.jd
index 871e06d..ea8d69e 100644
--- a/docs/html/guide/developing/tools/MonkeyRunner.jd
+++ b/docs/html/guide/developing/tools/MonkeyRunner.jd
@@ -1,5 +1,8 @@
page.title=MonkeyRunner
+parent.title=monkeyrunner
+parent.link=index.html
@jd:body
+
<style>
h4.jd-details-title {background-color: #DEE8F1;}
</style>
diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd
index 26839a1..e891ccd 100644
--- a/docs/html/guide/developing/tools/adb.jd
+++ b/docs/html/guide/developing/tools/adb.jd
@@ -1,4 +1,6 @@
page.title=Android Debug Bridge
+parent.title=Tools
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/tools/android.jd b/docs/html/guide/developing/tools/android.jd
index 15cf8e0..a67012f 100644
--- a/docs/html/guide/developing/tools/android.jd
+++ b/docs/html/guide/developing/tools/android.jd
@@ -1,4 +1,6 @@
page.title=android
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>{@code android} is an important development tool that lets you:</p>
diff --git a/docs/html/guide/developing/tools/bmgr.jd b/docs/html/guide/developing/tools/bmgr.jd
index 57deb25..d63dcf2 100644
--- a/docs/html/guide/developing/tools/bmgr.jd
+++ b/docs/html/guide/developing/tools/bmgr.jd
@@ -1,4 +1,6 @@
page.title=bmgr
+parent.title=Tools
+parent.link=index.html
@jd:body
<!-- quickview box content here -->
diff --git a/docs/html/guide/developing/tools/dmtracedump.jd b/docs/html/guide/developing/tools/dmtracedump.jd
index 492a049..cb9ad26 100644
--- a/docs/html/guide/developing/tools/dmtracedump.jd
+++ b/docs/html/guide/developing/tools/dmtracedump.jd
@@ -1,4 +1,6 @@
page.title=dmtracedump
+parent.title=Tools
+parent.link=index.html
@jd:body
diff --git a/docs/html/guide/developing/tools/draw9patch.jd b/docs/html/guide/developing/tools/draw9patch.jd
index 4d8043b..1d9de4f 100644
--- a/docs/html/guide/developing/tools/draw9patch.jd
+++ b/docs/html/guide/developing/tools/draw9patch.jd
@@ -1,4 +1,6 @@
page.title=Draw 9-patch
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>The Draw 9-patch tool allows you to easily create a
diff --git a/docs/html/guide/developing/tools/emulator.jd b/docs/html/guide/developing/tools/emulator.jd
index 748a8b1..ff667f2 100644
--- a/docs/html/guide/developing/tools/emulator.jd
+++ b/docs/html/guide/developing/tools/emulator.jd
@@ -1,6 +1,8 @@
page.title=Android Emulator
+parent.title=Tools
+parent.link=index.html
@jd:body
-<p>
+
<p>The Android SDK includes a mobile device emulator — a virtual mobile device
that runs on your computer. The emulator lets you develop and test
Android applications without using a physical device.</p>
diff --git a/docs/html/guide/developing/tools/hierarchy-viewer.jd b/docs/html/guide/developing/tools/hierarchy-viewer.jd
index ce660fc..3d3191b 100644
--- a/docs/html/guide/developing/tools/hierarchy-viewer.jd
+++ b/docs/html/guide/developing/tools/hierarchy-viewer.jd
@@ -1,4 +1,6 @@
page.title=Hierarchy Viewer
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>Hierarchy Viewer allows you to debug and optimize your user
diff --git a/docs/html/guide/developing/tools/hprof-conv.jd b/docs/html/guide/developing/tools/hprof-conv.jd
index 27000b4..f96def2 100644
--- a/docs/html/guide/developing/tools/hprof-conv.jd
+++ b/docs/html/guide/developing/tools/hprof-conv.jd
@@ -1,4 +1,6 @@
page.title=HPROF Converter
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>
diff --git a/docs/html/guide/developing/tools/layoutopt.jd b/docs/html/guide/developing/tools/layoutopt.jd
index e8f623d..cb0b505 100644
--- a/docs/html/guide/developing/tools/layoutopt.jd
+++ b/docs/html/guide/developing/tools/layoutopt.jd
@@ -1,4 +1,6 @@
page.title=layoutopt
+parent.title=Tools
+parent.link=index.html
@jd:body
<p><code>layoutopt</code> is a command-line tool that helps you optimize the
diff --git a/docs/html/guide/developing/tools/logcat.jd b/docs/html/guide/developing/tools/logcat.jd
index 9603b9f..d4ee68a 100644
--- a/docs/html/guide/developing/tools/logcat.jd
+++ b/docs/html/guide/developing/tools/logcat.jd
@@ -1,4 +1,6 @@
page.title=logcat
+parent.title=Tools
+parent.link=index.html
@jd:body
<div></div>
diff --git a/docs/html/guide/developing/tools/mksdcard.jd b/docs/html/guide/developing/tools/mksdcard.jd
index 0ec69d2..0a80454 100644
--- a/docs/html/guide/developing/tools/mksdcard.jd
+++ b/docs/html/guide/developing/tools/mksdcard.jd
@@ -1,4 +1,6 @@
page.title=mksdcard
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>The <code>mksdcard</code> tool lets you quickly create a FAT32 disk image that you can load in the
diff --git a/docs/html/guide/developing/tools/monkey.jd b/docs/html/guide/developing/tools/monkey.jd
index 6c05934..a7e539c 100644
--- a/docs/html/guide/developing/tools/monkey.jd
+++ b/docs/html/guide/developing/tools/monkey.jd
@@ -1,4 +1,6 @@
page.title=UI/Application Exerciser Monkey
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>The Monkey is a program that runs on your
diff --git a/docs/html/guide/developing/tools/monkeyrunner_concepts.jd b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd
index 658ff75..97c7c1f 100644
--- a/docs/html/guide/developing/tools/monkeyrunner_concepts.jd
+++ b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd
@@ -1,4 +1,6 @@
page.title=monkeyrunner
+parent.title=Tools
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/tools/proguard.jd b/docs/html/guide/developing/tools/proguard.jd
index e75a8cd..b97babe 100644
--- a/docs/html/guide/developing/tools/proguard.jd
+++ b/docs/html/guide/developing/tools/proguard.jd
@@ -1,4 +1,6 @@
page.title=ProGuard
+parent.title=Tools
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/developing/tools/sqlite3.jd b/docs/html/guide/developing/tools/sqlite3.jd
index 71e614f..9cc7e98 100644
--- a/docs/html/guide/developing/tools/sqlite3.jd
+++ b/docs/html/guide/developing/tools/sqlite3.jd
@@ -1,4 +1,6 @@
page.title=sqlite3
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>From a remote shell to your device or from your host machine, you can use the <a href=
diff --git a/docs/html/guide/developing/tools/traceview.jd b/docs/html/guide/developing/tools/traceview.jd
index 422fe00..aa37481 100644
--- a/docs/html/guide/developing/tools/traceview.jd
+++ b/docs/html/guide/developing/tools/traceview.jd
@@ -1,4 +1,6 @@
page.title=Traceview
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>Traceview is a graphical viewer for execution logs saved by your application.
diff --git a/docs/html/guide/developing/tools/zipalign.jd b/docs/html/guide/developing/tools/zipalign.jd
index 6231798..ebf177b 100644
--- a/docs/html/guide/developing/tools/zipalign.jd
+++ b/docs/html/guide/developing/tools/zipalign.jd
@@ -1,4 +1,6 @@
page.title=zipalign
+parent.title=Tools
+parent.link=index.html
@jd:body
<p>zipalign is an archive alignment tool that provides important
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 24ccfdb..e0b67f62 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -60,14 +60,14 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/fundamentals/activities.html">
<span class="en">Activities</span>
- </a></div>
+ </a> <span class="new">new!</span></div>
<ul>
<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">
+ <li><a href="<?cs var:toroot ?>guide/topics/fundamentals/loaders.html">
<span class="en">Loaders</span>
- </a><span class="new">new!</span></li>
+ </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>
@@ -76,11 +76,11 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/fundamentals/services.html">
<span class="en">Services</span>
- </a></div>
+ </a> <span class="new">new!</span></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/fundamentals/bound-services.html">
<span class="en">Bound Services</span>
- </a></li>
+ </a> <span class="new">new!</span></li>
</ul>
</li>
<li><a href="<?cs var:toroot ?>guide/topics/providers/content-providers.html">
@@ -91,7 +91,7 @@
</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/fundamentals/processes-and-threads.html">
<span class="en">Processes and Threads</span>
- </a></li>
+ </a> <span class="new">new!</span></li>
</ul>
@@ -419,7 +419,7 @@
<li class="toggle-list">
<div>
<a href="<?cs var:toroot ?>guide/developing/devices/index.html">
- <span class="en">Creating and Managing Virtual Devices</span>
+ <span class="en">Managing Virtual Devices</span>
</a>
</div>
<ul>
@@ -430,7 +430,7 @@
</li>
<li>
<a href="<?cs var:toroot ?>guide/developing/devices/managing-avds-cmdline.html">
- <span class="en">On the Command Line</span>
+ <span class="en">From the Command Line</span>
</a>
</li>
<li>
@@ -460,7 +460,7 @@
</li>
<li>
<a href="<?cs var:toroot ?>guide/developing/projects/projects-cmdline.html">
- <span class="en">On the Command Line</span>
+ <span class="en">From the Command Line</span>
</a>
</li>
</ul>
@@ -490,12 +490,12 @@
<ul>
<li>
<a href="<?cs var:toroot ?>guide/developing/debugging/debugging-projects.html">
- <span class="en">In Eclipse with ADT</span>
+ <span class="en">From Eclipse with ADT</span>
</a>
</li>
<li>
<a href="<?cs var:toroot ?>guide/developing/debugging/debugging-projects-cmdline.html">
- <span class="en">In Other IDEs</span>
+ <span class="en">From Other IDEs</span>
</a>
</li>
<li>
@@ -505,7 +505,7 @@
</li>
<li>
<a href="<?cs var:toroot ?>guide/developing/debugging/debugging-log.html">
- <span class="en">Reading and Writing Log Messages</span>
+ <span class="en">Reading and Writing Logs</span>
</a>
</li>
<li>
diff --git a/docs/html/guide/market/billing/billing_about.jd b/docs/html/guide/market/billing/billing_about.jd
index 9c027ca..dac9738 100755
--- a/docs/html/guide/market/billing/billing_about.jd
+++ b/docs/html/guide/market/billing/billing_about.jd
@@ -1,4 +1,6 @@
page.title=About this Release
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_admin.jd b/docs/html/guide/market/billing/billing_admin.jd
index cc5cb59..cd8f960 100755
--- a/docs/html/guide/market/billing/billing_admin.jd
+++ b/docs/html/guide/market/billing/billing_admin.jd
@@ -1,4 +1,6 @@
page.title=Administering In-app Billing
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_best_practices.jd b/docs/html/guide/market/billing/billing_best_practices.jd
index fd67e80..4743e88 100755
--- a/docs/html/guide/market/billing/billing_best_practices.jd
+++ b/docs/html/guide/market/billing/billing_best_practices.jd
@@ -1,4 +1,6 @@
page.title=Security and Design
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd
index 0f081a5..0cac2eb 100755
--- a/docs/html/guide/market/billing/billing_integrate.jd
+++ b/docs/html/guide/market/billing/billing_integrate.jd
@@ -1,4 +1,6 @@
page.title=Implementing In-app Billing
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_overview.jd b/docs/html/guide/market/billing/billing_overview.jd
index 675fe2a..b899b9b 100755
--- a/docs/html/guide/market/billing/billing_overview.jd
+++ b/docs/html/guide/market/billing/billing_overview.jd
@@ -1,4 +1,6 @@
-page.title=Overview of In-app Billing
+page.title=In-app Billing Overview
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_reference.jd b/docs/html/guide/market/billing/billing_reference.jd
index c91ca89..2e5c9c6 100755
--- a/docs/html/guide/market/billing/billing_reference.jd
+++ b/docs/html/guide/market/billing/billing_reference.jd
@@ -1,4 +1,6 @@
page.title=In-app Billing Reference
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/billing_testing.jd b/docs/html/guide/market/billing/billing_testing.jd
index 4a36588..5ced9c7 100755
--- a/docs/html/guide/market/billing/billing_testing.jd
+++ b/docs/html/guide/market/billing/billing_testing.jd
@@ -1,4 +1,6 @@
page.title=Testing In-app Billing
+parent.title=In-app Billing
+parent.link=index.html
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/market/billing/index.jd b/docs/html/guide/market/billing/index.jd
index f5d001d..6985179 100755
--- a/docs/html/guide/market/billing/index.jd
+++ b/docs/html/guide/market/billing/index.jd
@@ -1,4 +1,4 @@
-page.title=Adding In-app Billing to Your Applications
+page.title=In-app Billing
@jd:body
<style type="text/css">
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
index 5551384..4184ecb 100644
--- a/docs/html/guide/publishing/licensing.jd
+++ b/docs/html/guide/publishing/licensing.jd
@@ -1,4 +1,4 @@
-page.title=Licensing Your Applications
+page.title=Application Licensing
@jd:body
<div id="qv-wrapper">
@@ -518,7 +518,7 @@
</ol>
<p>If you are not familiar with AVDs or how to use them, see <a
-href="{@docRoot}guide/developing/devices/index.html">Creating and Managing Virtual Devices</a>.</p>
+href="{@docRoot}guide/developing/devices/index.html">Managing Virtual Devices</a>.</p>
<h4 id="project-update">Updating your project configuration</h4>
@@ -629,7 +629,7 @@
<p style="margin-top:.5em;">If you aren't familiar with library projects or how
to use them, see <a href="{@docRoot}guide/developing/projects/index.html#LibraryProjects">
-Creating and Managing Projects</a>.
+Managing Projects</a>.
</p>
</div>
</div>
@@ -666,8 +666,9 @@
no further configuration is needed. </p>
<p>For more information about how to create an application project or work with
-library projects in Eclipse, see <a href="{@docRoot}guide/developing/projects/projects-eclipse.html">
-Creating and Managing Projects in Eclipse</a></p>.
+library projects in Eclipse, see <a
+href="{@docRoot}guide/developing/projects/projects-eclipse.html">Managing Projects from
+Eclipse with ADT</a></p>.
<h4>Copying the LVL sources to your application</h4>
@@ -698,8 +699,8 @@
Select the "Android" properties group and click <strong>Add</strong>, then
choose the LVL library project (com_android_vending_licensing) and click
<strong>OK</strong>. For more information, see
-<a href="{@docRoot}developing/projects/projects-eclipse.html#SettingUpLibraryProject">
-Creating and Managing Projects in Eclipse</a></p>.
+<a href="{@docRoot}guide/developing/projects/projects-eclipse.html#SettingUpLibraryProject">
+Managing Projects from Eclipse with ADT</a></p>.
<div style="margin-bottom:2em;">
@@ -727,8 +728,8 @@
</pre>
<p>For more information about working with library projects,
-see <a href="{@docRoot}developing/projects/projects-cmdline.html#SettingUpLibraryProject">
-Creating and Managing Projects on the Command Line</a></p>.
+see <a href="{@docRoot}guide/developing/projects/projects-cmdline.html#SettingUpLibraryProject">
+Managing Projects from the Command Line</a></p>.
<h2 id="app-integration">Integrating the LVL with Your Application</h2>
diff --git a/docs/html/guide/topics/advanced/aidl.jd b/docs/html/guide/topics/advanced/aidl.jd
index fef46ec..419048a 100644
--- a/docs/html/guide/topics/advanced/aidl.jd
+++ b/docs/html/guide/topics/advanced/aidl.jd
@@ -14,9 +14,11 @@
<p class="note"><strong>Note:</strong> Using AIDL is necessary only if you allow clients from
different applications to access your service for IPC and want to handle multithreading in your
service. If you do not need to perform IPC across
-different applications, you should create your interface <a href="Binder">implementing a
+different applications, you should create your interface <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html#Binder">implementing a
Binder</a> or, if you want to perform IPC, but do not need to handle multithreading, then you
-should implement your interface <a href="#Messenger">using a Messenger</a>.</p>
+should implement your interface <a
+href="{@docRoot}guide/topics/fundamentals/bound-services.html#Messenger">using a Messenger</a>.</p>
<p>Before you begin designing your AIDL interface, be aware that calls on to an AIDL interface are
direct function calls. You can not generally make assumptions about the thread in which the call
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 3fdbd95..661f5cb 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -422,7 +422,7 @@
resources. However, you should create specialized layouts for certain screen sizes and provide
specialized images for certain densities, using alternative layout resources, and by declaring in
your manifest exactly which screen sizes your application supports with the <a
-href="{@docRoot}guide/topics/manifest/supports-screens.html">{@code
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
<supports-screens>}</a> element.</p>
<p>For more information, see the <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>
@@ -453,7 +453,8 @@
href="{@docRoot}guide/appendix/api-levels.html">API Level</a> (for example, Android 1.0 is API Level
1 and Android 2.3 is API Level 9). If you use any APIs that were added to the platform after
version 1.0, you should declare the minimum API Level in which those APIs were introduced using the
-<a href="{@docRoot}guide/topics/manifest/uses-sdk.html">{@code <uses-sdk>}</a> element.</dd>
+<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a>
+element.</dd>
</dl>
<p>It's important that you declare all such requirements for your application, because, when you
diff --git a/docs/html/guide/topics/fundamentals/activities.jd b/docs/html/guide/topics/fundamentals/activities.jd
index 9079ef6..5cc1b45 100644
--- a/docs/html/guide/topics/fundamentals/activities.jd
+++ b/docs/html/guide/topics/fundamentals/activities.jd
@@ -40,7 +40,7 @@
<h2>See also</h2>
<ol>
<li><a href="{@docRoot}resources/tutorials/hello-world.html">Hello World Tutorial</a></li>
- <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack">Tasks and Back
+ <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Stack</a></li>
</ol>
@@ -594,13 +594,14 @@
definition in table 1 might still be killed by the system—but that would happen only in
extreme circumstances when there is no other recourse. When an activity might be killed is
discussed more in the <a
-href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html">Processes and
+href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
Threading</a> document.</p>
<h3 id="SavingActivityState">Saving activity state</h3>
-<p>The introduction to <a href="Lifecycle">Managing the Activity Lifecycle</a> briefly mentions that
+<p>The introduction to <a href="#Lifecycle">Managing the Activity Lifecycle</a> briefly mentions
+that
when an activity is paused or stopped, the state of the activity is retained. This is true because
the {@link android.app.Activity} object is still held in memory when it is paused or
stopped—all information about its members and current state is still alive. Thus, any changes
diff --git a/docs/html/guide/topics/fundamentals/fragments.jd b/docs/html/guide/topics/fundamentals/fragments.jd
index 7ca990e..f780960 100644
--- a/docs/html/guide/topics/fundamentals/fragments.jd
+++ b/docs/html/guide/topics/fundamentals/fragments.jd
@@ -743,7 +743,8 @@
based on the screen configuration.</p>
<p class="note"><strong>Note:</strong> The complete source code for this activity is available in
-<a href="resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code
+<a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code
FragmentLayout.java}</a>.</p>
<p>The main activity applies a layout in the usual way, during {@link
diff --git a/docs/html/guide/topics/providers/loaders.jd b/docs/html/guide/topics/fundamentals/loaders.jd
similarity index 97%
rename from docs/html/guide/topics/providers/loaders.jd
rename to docs/html/guide/topics/fundamentals/loaders.jd
index c54656c..d31f090 100644
--- a/docs/html/guide/topics/providers/loaders.jd
+++ b/docs/html/guide/topics/fundamentals/loaders.jd
@@ -1,4 +1,6 @@
-page.title=Using Loaders
+page.title=Loaders
+parent.title=Activities
+parent.link=activities.html
@jd:body
<div id="qv-wrapper">
<div id="qv">
@@ -29,8 +31,11 @@
<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>
+ <li> <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.html">FragmentListCursorLoader</a></li>
+ <li> <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
+LoaderThrottle</a></li>
</ol>
</div>
</div>
@@ -480,7 +485,7 @@
<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
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.html">FragmentListCursorLoader</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>
diff --git a/docs/html/guide/topics/fundamentals/services.jd b/docs/html/guide/topics/fundamentals/services.jd
index d0c924a..a9dd315 100644
--- a/docs/html/guide/topics/fundamentals/services.jd
+++ b/docs/html/guide/topics/fundamentals/services.jd
@@ -121,7 +121,7 @@
android.app.Activity#onStart onStart()}, then stop it in {@link android.app.Activity#onStop
onStop()}. Also consider using {@link android.os.AsyncTask} or {@link android.os.HandlerThread},
instead of the traditional {@link java.lang.Thread} class. See the <a
-href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html#Threads">Processes and
+href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes and
Threading</a> document for more information about threads.</p>
<p>Remember that if you do use a service, it still runs in your application's main thread by
default, so you should still create a new thread within the service if it performs intensive or
@@ -183,7 +183,7 @@
available again (though this also depends on the value you return from {@link
android.app.Service#onStartCommand onStartCommand()}, as discussed later). For more information
about when the system might destroy a service, see the <a
-href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html">Processes and Threading</a>
+href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and Threading</a>
document.</p>
<p>In the following sections, you'll see how you can create each type of service and how to use
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index 6594568..618cdf8 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -1,5 +1,5 @@
page.title=2D Graphics
-parent.title=2D and 3D Graphics
+parent.title=Graphics
parent.link=index.html
@jd:body
diff --git a/docs/html/guide/topics/graphics/animation.jd b/docs/html/guide/topics/graphics/animation.jd
index cd74efa..3b1716c 100644
--- a/docs/html/guide/topics/graphics/animation.jd
+++ b/docs/html/guide/topics/graphics/animation.jd
@@ -1,4 +1,6 @@
page.title=Property Animation
+parent.title=Graphics
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index 4d0b223..1f51c2d 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -1,5 +1,5 @@
page.title=3D with OpenGL
-parent.title=2D and 3D Graphics
+parent.title=Graphics
parent.link=index.html
@jd:body
diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd
index 0ef8a22..54737ad 100644
--- a/docs/html/guide/topics/graphics/renderscript.jd
+++ b/docs/html/guide/topics/graphics/renderscript.jd
@@ -1,4 +1,6 @@
page.title=3D Rendering and Computation with Renderscript
+parent.title=Graphics
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/graphics/view-animation.jd b/docs/html/guide/topics/graphics/view-animation.jd
index ad27e1c..eff6f70 100644
--- a/docs/html/guide/topics/graphics/view-animation.jd
+++ b/docs/html/guide/topics/graphics/view-animation.jd
@@ -1,4 +1,6 @@
page.title=View Animation
+parent.title=Graphics
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/nfc/index.jd b/docs/html/guide/topics/nfc/index.jd
index 3992099..c4917b4 100644
--- a/docs/html/guide/topics/nfc/index.jd
+++ b/docs/html/guide/topics/nfc/index.jd
@@ -31,29 +31,33 @@
</div>
</div>
- <p>Near Field Communication (NFC) is a set of short-range wireless technologies, similar to RFID.
- It typically requires a distance of 4 cm or less and operates at 13.56mhz and at rates ranging
- from 106 kbit/s to 848 kbit/s. NFC communication always involves an initiator and a target. The
- initiator actively generates an RF field that can power a passive target. This enables NFC
- targets to take very simple form factors such as tags, stickers or cards that do not require
- power. NFC peer-to-peer communication is also possible, where both devices are powered.</p>
-
- <p>Compared to other wireless technologies such as Bluetooth or WiFi, NFC provides much lower
- bandwidth and range, but provides low-cost, un-powered targets and do not require discovery or
- pairing. Users interact with NFC tags with just a tap. Targets can range in complexity. Simple
- tags just offer read and write capabilities, sometimes with one-time programmable areas to make
- the card read-only. More complex tags offer math operations, and have cryptographic hardware to
- authenticate access to a sector. The most sophisticated tags contain operating environments,
- allowing complex interactions with applets that are running on the tag.</p>
-
- <p>An Android device with NFC hardware typically acts as an initiator. This mode is also known as
- NFC reader/writer. The device actively looks for NFC tags and starts activities to handle them in
- this mode. In Android 2.3.3, devices also have some limited peer-to-peer support.</p>
+ <p>Near Field Communication (NFC) is a set of short-range wireless technologies, typically
+ requiring a distance of 4cm or less. NFC operates at 13.56mhz, and at rates ranging
+ from 106 kbit/s to 848 kbit/s. NFC communication always involves an initiator and a target.
+ The initiator actively generates an RF field that can power a passive target. This
+ enables NFC targets to take very simple form factors such as tags, stickers or cards that do
+ not require power. NFC peer-to-peer communication is also possible, where both devices
+ are powered.
+ <p>
+ Compared to other wireless technologies such as Bluetooth or WiFi, NFC provides much lower
+ bandwidth and range, but enables low-cost, un-powered targets
+ and does not require discovery or pairing. Interactions can be initiated with just a tap.
+ <p>
+ An Android device with NFC hardware will typically act as an initiator when the screen is
+ on. This mode is also known as NFC reader/writer. It will actively look for NFC tags and start
+ activities to handle them. Android 2.3.3 also has some limited P2P support.
+ <p>
+ Tags can range in complexity, simple tags just offer read/write semantics, sometimes
+ with one-time-programmable areas to make the card read-only. More complex tags offer
+ math operations, and have cryptographic hardware to authenticate access to a sector.
+ The most sophisticated tags contain operating environments, allowing
+ complex interactions with code executing on the tag.
<h2 id="api">API Overview</h2>
- <p>The {@link android.nfc} package contain the high-level classes to interact with the local
- device's NFC adapter, to represent discovered tags, and to use the NDEF data format.</p>
+ <p>The {@link android.nfc} package contains the high-level classes to interact
+ with the local device's NFC adapter, to represent discovered tags, and to use
+ the NDEF data format.
<table>
<tr>
@@ -65,44 +69,52 @@
<tr>
<td>{@link android.nfc.NfcManager}</td>
- <td>A high level manager class that enumerates the NFC adapters on this Android device. Since
- most Android devices only have one NFC adapter, you can just use the static helper {@link
- android.nfc.NfcAdapter#getDefaultAdapter()} for most situations.</td>
+
+ <td>A high level manager class that enumerates the NFC adapters on this Android device.
+ Since most Android devices only have one NFC adapter, you can just use the static helper
+ {@link android.nfc.NfcAdapter#getDefaultAdapter(Context)} for most situations.</td>
</tr>
<tr>
<td>{@link android.nfc.NfcAdapter}</td>
- <td>Represents the local NFC adapter and defines the Intents that are used in the tag
- dispatch system. It provides methods to register for foreground tag dispatching and
- foreground NDEF pushing. Foreground NDEF push is the only peer-to-peer support that is
- currently provided in Android.</td>
+ <td>Represents the local NFC adapter. Defines the intent's used to request
+ tag dispatch to your activity, and provides methods to register for foreground
+ tag dispatch and foreground NDEF push. Foreground NDEF push is the only
+ peer-to-peer support that is currently provided in Android.</td>
</tr>
<tr>
<td>{@link android.nfc.NdefMessage} and {@link android.nfc.NdefRecord}</td>
- <td>NDEF is an NFC Forum defined data structure, designed to efficiently store data on NFC
- tags, such as Text, URLs, and other MIME types. An {@link android.nfc.NdefMessage} acts as a
+ <td>NDEF is an NFC Forum defined data structure, designed to efficiently
+ store data on NFC tags, such as text, URL's, and other MIME types. A
+ {@link android.nfc.NdefMessage} acts as a
container for the data that you want to transmit or read. One {@link android.nfc.NdefMessage}
- object contains zero or more {@link android.nfc.NdefRecord}s. Each NDEF Record has a type
- such as Text, URL, Smart Poster, or any MIME type. The type of the first NDEF Record in the
- NDEF message is used to dispatch a tag to an Activity.</td>
+ object contains zero or more {@link android.nfc.NdefRecord}s. Each NDEF record
+ has a type such as text, URL, smart poster, or any MIME data. The type of the
+ first NDEF record in the NDEF message is used to dispatch a tag to an activity
+ on Android.</td>
</tr>
<tr>
<td>{@link android.nfc.Tag}</td>
- <td>Represents a passive NFC target. These can come in many form factors such as a tag, card,
- FOB, or an even more complex device doing card emulation. When a tag is discovered, a {@link
- android.nfc.Tag} object is created and wrapped inside an Intent. The dispatch system sends
- the Intent to a compatible Activity <code>startActivity()</code>. You can use the {@link
+ <td>Represents a passive NFC target. These can come in many form factors such as
+ a tag, card, key fob, or even a phone doing card emulation. When a tag is
+ discovered, a {@link android.nfc.Tag} object is created and wrapped inside an
+ Intent. The NFC dispatch system sends the intent to a compatible actvitiy
+ using <code>startActivity()</code>. You can use the {@link
android.nfc.Tag#getTechList getTechList()} method to determine the technologies supported by
this tag and create the corresponding {@link android.nfc.tech.TagTechnology} object with one
of classes provided by {@link android.nfc.tech}.</td>
</tr>
</table>
+ <p>The {@link android.nfc.tech} package contains classes to query properties
+ and perform I/O operations on a tag. The classes are divided to represent different
+ NFC technologies that can be available on a tag.
+
<p>The {@link android.nfc.tech} package contains classes to query properties and perform I/O
operations on a tag. The classes are divided to represent different NFC technologies that can be
available on a Tag:</p>
@@ -117,7 +129,7 @@
<tr>
<td>{@link android.nfc.tech.TagTechnology}</td>
- <td>The interface that all Tag Technology classes must implement.</td>
+ <td>The interface that all tag technology classes must implement.</td>
</tr>
<tr>
@@ -153,8 +165,8 @@
<tr>
<td>{@link android.nfc.tech.Ndef}</td>
- <td>Provides access to NDEF data and operations on NFC Tags that have been formatted as
- NDEF.</td>
+ <td>Provides access to NDEF data and operations on NFC tags that have been formatted as NDEF.
+ </td>
</tr>
<tr>
@@ -166,15 +178,15 @@
<tr>
<td>{@link android.nfc.tech.MifareClassic}</td>
- <td>Provides access to MIFARE Classic properties and I/O operations. Not all Android devices
- provide implementations for this class.</td>
+ <td>Provides access to MIFARE Classic properties and I/O operations, if this
+ Android device supports MIFARE.</td>
</tr>
<tr>
<td>{@link android.nfc.tech.MifareUltralight}</td>
- <td>Provides access to MIFARE Ultralight properties and I/O operations. Not all Android
- devices provide implementations for this class.</td>
+ <td>Provides access to MIFARE Ultralight properties and I/O operations, if this
+ Android device supports MIFARE.</td>
</tr>
</table>
@@ -191,12 +203,13 @@
</li>
<li>The minimum SDK version that your application can support. API level 9 only supports
- limited tag dispatching with {@link android.nfc.NfcAdapter#ACTION_TAG_DISCOVERED}, and only
- gives access to NDEF messages via the {@link android.nfc.NfcAdapter#EXTRA_NDEF_MESSAGES} extra.
- No other tag properties or I/O operations are accessible. API level 10 adds comprehensive
- reader/writer support, so you probably want to use this for more functionality.
- <pre class="pretty-print">
-<uses-sdk android:minSdkVersion="9|10"/>
+ limited tag dispatch via {@link android.nfc.NfcAdapter#ACTION_TAG_DISCOVERED},
+ and only gives access to NDEF messages via the {@link android.nfc.NfcAdapter#EXTRA_NDEF_MESSAGES}
+ extra. No other tag properties or I/O operations are accessible. You probably want
+ to use API level 10 which includes comprehensive reader/writer support.
+
+<pre class="pretty-print">
+<uses-sdk android:minSdkVersion="10"/>
</pre>
</li>
diff --git a/docs/html/guide/topics/testing/activity_testing.jd b/docs/html/guide/topics/testing/activity_testing.jd
index 6392ad7..51121f4 100644
--- a/docs/html/guide/topics/testing/activity_testing.jd
+++ b/docs/html/guide/topics/testing/activity_testing.jd
@@ -1,4 +1,6 @@
page.title=Activity Testing
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/testing/contentprovider_testing.jd b/docs/html/guide/topics/testing/contentprovider_testing.jd
index e42e631..edaae8c 100644
--- a/docs/html/guide/topics/testing/contentprovider_testing.jd
+++ b/docs/html/guide/topics/testing/contentprovider_testing.jd
@@ -1,4 +1,6 @@
page.title=Content Provider Testing
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/testing/service_testing.jd b/docs/html/guide/topics/testing/service_testing.jd
index 77884779..eae8607 100644
--- a/docs/html/guide/topics/testing/service_testing.jd
+++ b/docs/html/guide/topics/testing/service_testing.jd
@@ -1,4 +1,6 @@
page.title=Service Testing
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/testing/testing_android.jd b/docs/html/guide/topics/testing/testing_android.jd
index 6f3048c..c8a3f6e 100755
--- a/docs/html/guide/topics/testing/testing_android.jd
+++ b/docs/html/guide/topics/testing/testing_android.jd
@@ -1,4 +1,6 @@
page.title=Testing Fundamentals
+parent.title=Testing
+parent.link=index.html
@jd:body
<div id="qv-wrapper">
@@ -653,10 +655,12 @@
It guides you through a more complex testing scenario that you develop against a
more realistic application.
</li>
+ <!-- sample is not available
<li>
- The sample test package <a href="{@docRoot}resources/samples/AlarmServiceTest"}>
+ The sample test package <a href="{@docRoot}resources/samples/AlarmServiceTest/index.html">
Alarm Service Test</a> is an example of testing a {@link android.app.Service}. It contains
a set of unit tests for the Alarm Service sample application's {@link android.app.Service}.
</li>
+ -->
</ul>
diff --git a/docs/html/guide/topics/testing/what_to_test.jd b/docs/html/guide/topics/testing/what_to_test.jd
index e8a27da..99061df 100644
--- a/docs/html/guide/topics/testing/what_to_test.jd
+++ b/docs/html/guide/topics/testing/what_to_test.jd
@@ -1,4 +1,6 @@
page.title=What To Test
+parent.title=Testing
+parent.link=index.html
@jd:body
<p>
As you develop Android applications, knowing what to test is as important as knowing how to
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index b8d1928..065f95a 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -38,7 +38,15 @@
<li>{@link android.app.ActionBar}</li>
<li>{@link android.view.Menu}</li>
</ol>
-
+
+ <h2>Related samples</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#ActionBar">API
+ Demos</a></li>
+ <li><a
+href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a></li>
+ </ol>
+
<h2>See also</h2>
<ol>
<li><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a></li>
@@ -363,7 +371,7 @@
in the overflow menu as a normal menu item and you must respond to it from the {@link
android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} callback method. (For a
guide to providing search functionality, see the <a
-href="{@docRoot}gudie/topics/search/index.html">Search</a> documentation.)</p>
+href="{@docRoot}guide/topics/search/index.html">Search</a> documentation.)</p>
<p>When the activity first starts, the system populates the Action Bar and overflow menu by calling
{@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()}.
@@ -397,7 +405,7 @@
<img src="{@docRoot}images/ui/actionbar-tabs.png" alt="" />
<p class="img-caption"><strong>Figure 6.</strong> Screenshot of tabs in the
Action Bar, from the <a
-href="{@docRoot}resources/samples/Honeycomb-Gallery/index.html">Honeycomb Gallery</a> sample
+href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> sample
application.</p>
</div>
diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd
index 46ccdf8..c33c507 100644
--- a/docs/html/guide/topics/ui/drag-drop.jd
+++ b/docs/html/guide/topics/ui/drag-drop.jd
@@ -289,14 +289,14 @@
<p>
To get the action type, a listener calls {@link android.view.DragEvent#getAction()}. There
are six possible values, defined by constants in the {@link android.view.DragEvent} class. These
- are listed in <a href="table1">table 1</a>.
+ are listed in <a href="#table1">table 1</a>.
</p>
<p>
The {@link android.view.DragEvent} object also contains the data that your application provided
to the system in the call to
{@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
Some of the data is valid only for certain action types. The data that is valid for each action
- type is summarized in <a href="table2">table 2</a>. It is also described in detail with
+ type is summarized in <a href="#table2">table 2</a>. It is also described in detail with
the event for which it is valid in the section
<a href="#DesignDragOperation">Designing a Drag and Drop Operation</a>.
</p>
diff --git a/docs/html/images/developing/hv_device_window.png b/docs/html/images/developing/hv_device_window.png
new file mode 100644
index 0000000..2bb80a8
--- /dev/null
+++ b/docs/html/images/developing/hv_device_window.png
Binary files differ
diff --git a/docs/html/images/developing/hv_pixelperfect.png b/docs/html/images/developing/hv_pixelperfect.png
new file mode 100644
index 0000000..6d19119
--- /dev/null
+++ b/docs/html/images/developing/hv_pixelperfect.png
Binary files differ
diff --git a/docs/html/images/developing/hv_treeview_screenshot.png b/docs/html/images/developing/hv_treeview_screenshot.png
new file mode 100644
index 0000000..c0e7ac5
--- /dev/null
+++ b/docs/html/images/developing/hv_treeview_screenshot.png
Binary files differ
diff --git a/docs/html/images/developing/hv_view_hierarchy_window.png b/docs/html/images/developing/hv_view_hierarchy_window.png
new file mode 100644
index 0000000..0d8b263
--- /dev/null
+++ b/docs/html/images/developing/hv_view_hierarchy_window.png
Binary files differ
diff --git a/docs/html/resources/articles/avoiding-memory-leaks.jd b/docs/html/resources/articles/avoiding-memory-leaks.jd
index 3361bc1..395f590 100644
--- a/docs/html/resources/articles/avoiding-memory-leaks.jd
+++ b/docs/html/resources/articles/avoiding-memory-leaks.jd
@@ -1,4 +1,6 @@
page.title=Avoiding Memory Leaks
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/backward-compatibility.jd b/docs/html/resources/articles/backward-compatibility.jd
index ad64dfc..f96d587 100644
--- a/docs/html/resources/articles/backward-compatibility.jd
+++ b/docs/html/resources/articles/backward-compatibility.jd
@@ -1,4 +1,6 @@
page.title=Backward Compatibility for Applications
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/can-i-use-this-intent.jd b/docs/html/resources/articles/can-i-use-this-intent.jd
index a726189..c527331 100644
--- a/docs/html/resources/articles/can-i-use-this-intent.jd
+++ b/docs/html/resources/articles/can-i-use-this-intent.jd
@@ -1,4 +1,6 @@
page.title=Can I Use this Intent?
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Android offers a very powerful and yet easy-to-use message type called
diff --git a/docs/html/resources/articles/contacts.jd b/docs/html/resources/articles/contacts.jd
index c837dc3..8365d29 100644
--- a/docs/html/resources/articles/contacts.jd
+++ b/docs/html/resources/articles/contacts.jd
@@ -1,4 +1,6 @@
page.title=Using the Contacts API
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Starting from Android 2.0 (API Level 5), the Android platform provides an
diff --git a/docs/html/resources/articles/creating-input-method.jd b/docs/html/resources/articles/creating-input-method.jd
index 6f932df..e4b77f4 100644
--- a/docs/html/resources/articles/creating-input-method.jd
+++ b/docs/html/resources/articles/creating-input-method.jd
@@ -1,4 +1,6 @@
page.title=Creating an Input Method
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/drawable-mutations.jd b/docs/html/resources/articles/drawable-mutations.jd
index f979829..c5818fc 100644
--- a/docs/html/resources/articles/drawable-mutations.jd
+++ b/docs/html/resources/articles/drawable-mutations.jd
@@ -1,4 +1,6 @@
page.title=Drawable Mutations
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Android's drawables are extremely useful to easily build applications. A
diff --git a/docs/html/resources/articles/faster-screen-orientation-change.jd b/docs/html/resources/articles/faster-screen-orientation-change.jd
index f82e592..52531bb 100644
--- a/docs/html/resources/articles/faster-screen-orientation-change.jd
+++ b/docs/html/resources/articles/faster-screen-orientation-change.jd
@@ -1,4 +1,6 @@
page.title=Faster Screen Orientation Change
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/future-proofing.jd b/docs/html/resources/articles/future-proofing.jd
index ee98186..b8aeedf 100644
--- a/docs/html/resources/articles/future-proofing.jd
+++ b/docs/html/resources/articles/future-proofing.jd
@@ -1,4 +1,6 @@
page.title=Future-Proofing Your Apps
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>It's important to implement your application so that it will not break as new
diff --git a/docs/html/resources/articles/gestures.jd b/docs/html/resources/articles/gestures.jd
index 8711645..5b8d760 100644
--- a/docs/html/resources/articles/gestures.jd
+++ b/docs/html/resources/articles/gestures.jd
@@ -1,4 +1,6 @@
page.title=Gestures
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Touch screens are a great way to interact with applications on
diff --git a/docs/html/resources/articles/glsurfaceview.jd b/docs/html/resources/articles/glsurfaceview.jd
index 57403ea..45407a9 100644
--- a/docs/html/resources/articles/glsurfaceview.jd
+++ b/docs/html/resources/articles/glsurfaceview.jd
@@ -1,4 +1,6 @@
page.title=Introducing GLSurfaceView
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/index.jd b/docs/html/resources/articles/index.jd
index d2b7645..220a4ed 100644
--- a/docs/html/resources/articles/index.jd
+++ b/docs/html/resources/articles/index.jd
@@ -1,4 +1,6 @@
page.title=Technical Articles
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<dl>
diff --git a/docs/html/resources/articles/layout-tricks-efficiency.jd b/docs/html/resources/articles/layout-tricks-efficiency.jd
index b7b5761..00b4147 100644
--- a/docs/html/resources/articles/layout-tricks-efficiency.jd
+++ b/docs/html/resources/articles/layout-tricks-efficiency.jd
@@ -1,4 +1,6 @@
page.title=Layout Tricks: Creating Efficient Layouts
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>The Android UI toolkit offers several layout managers that are
diff --git a/docs/html/resources/articles/layout-tricks-merge.jd b/docs/html/resources/articles/layout-tricks-merge.jd
index 95409e4..0ca0317 100644
--- a/docs/html/resources/articles/layout-tricks-merge.jd
+++ b/docs/html/resources/articles/layout-tricks-merge.jd
@@ -1,4 +1,6 @@
page.title=Layout Tricks: Merging Layouts
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>The articles showed you how to use the <code><include /></code> tag in XML layouts, to
diff --git a/docs/html/resources/articles/layout-tricks-reuse.jd b/docs/html/resources/articles/layout-tricks-reuse.jd
index 396e2127..179c1d8 100644
--- a/docs/html/resources/articles/layout-tricks-reuse.jd
+++ b/docs/html/resources/articles/layout-tricks-reuse.jd
@@ -1,4 +1,6 @@
page.title=Layout Tricks: Creating Reusable UI Components
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>The Android platform offers a wide variety of UI <em>widgets</em>, small
diff --git a/docs/html/resources/articles/layout-tricks-stubs.jd b/docs/html/resources/articles/layout-tricks-stubs.jd
index 88bcb78..64f07f9 100644
--- a/docs/html/resources/articles/layout-tricks-stubs.jd
+++ b/docs/html/resources/articles/layout-tricks-stubs.jd
@@ -1,4 +1,6 @@
page.title=Layout Tricks: Using ViewStubs
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Sharing and reusing UI components is very easy with Android, thanks to the <a
diff --git a/docs/html/resources/articles/listview-backgrounds.jd b/docs/html/resources/articles/listview-backgrounds.jd
index f4c6998..c4037ba 100644
--- a/docs/html/resources/articles/listview-backgrounds.jd
+++ b/docs/html/resources/articles/listview-backgrounds.jd
@@ -1,4 +1,6 @@
page.title=ListView Backgrounds: An Optimization
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>{@link android.widget.ListView} is one of Android's most widely used widgets.
diff --git a/docs/html/resources/articles/live-folders.jd b/docs/html/resources/articles/live-folders.jd
index be974f4..aeab997 100644
--- a/docs/html/resources/articles/live-folders.jd
+++ b/docs/html/resources/articles/live-folders.jd
@@ -1,4 +1,6 @@
page.title=Live Folders
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Live folders, introduced in Android 1.5 (API Level 3), let you display any source of data
diff --git a/docs/html/resources/articles/live-wallpapers.jd b/docs/html/resources/articles/live-wallpapers.jd
index ea67fed..bfbbb34 100644
--- a/docs/html/resources/articles/live-wallpapers.jd
+++ b/docs/html/resources/articles/live-wallpapers.jd
@@ -1,4 +1,6 @@
page.title=Live Wallpapers
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
@@ -27,35 +29,36 @@
<img src="images/live_wallpapers_small.png" style="align:center" />
<p>Creating your own live wallpaper is easy, especially if you have had
-previous experience with <a
-href="../../../reference/android/view/SurfaceView.html"><code>SurfaceView</code></a> or <a
-href="../../../reference/android/graphics/Canvas.html"><code>Canvas</code></a>.
+previous experience with {@link android.view.SurfaceView} or {@link
+android.graphics.Canvas}.
To learn how to create a live wallpaper, you should check out the <a
href="../samples/CubeLiveWallpaper/index.html">CubeLiveWallpaper sample code</a>.</p>
-<p>In terms of implementation, a live wallpaper is very similar to a regular
-Android <a href="../../../reference/android/app/Service.html">service</a>. The
-only difference is the addition of a new method, <a
-href="../../../reference/android/service/wallpaper/WallpaperService.html#onCreateEngine()">{@code
-onCreateEngine()}</a>, whose goal is to create a <a
-href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html">
-<code>WallpaperService.Engine</code></a>. The engine is responsible for
+<p>In terms of implementation, a live wallpaper is very similar to a {@link android.app.Service}.
+The only difference is the addition of a new method, {@link
+android.service.wallpaper.WallpaperService#onCreateEngine()}, whose goal is to create a {@link
+android.service.wallpaper.WallpaperService.Engine}. The engine is responsible for
handling the lifecycle and drawing of a wallpaper. The system provides a surface
-on which you can draw, just like you would with a <code>SurfaceView</code></a>.
+on which you can draw, just like you would with a {@link android.view.SurfaceView}.
Drawing a wallpaper can be very expensive so you should optimize your code
as much as possible to avoid using too much CPU, not only for battery life
but also to avoid slowing down the rest of the system. That is also why the
-most important part of the lifecycle of a wallpaper is <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onVisibilityChanged%28boolean%29">when it becomes invisible</a>.
+most important part of the lifecycle of a wallpaper is when it becomes visible, as indicated
+by a call to {@link android.service.wallpaper.WallpaperService.Engine#onVisibilityChanged
+onVisibilityChanged()}.
When invisible, such as when the user launches an application that covers
the home screen, a wallpaper must stop all activity.</p>
<p>The engine can also implement several methods to interact with the user
or the home application. For instance, if you want your wallpaper to scroll
-along when the user swipes from one home screen to another, you can use <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onOffsetsChanged%28float,%20float,%20float,%20float,%20int,%20int%29"><code>onOffsetsChanged()</code></a>.
-To react to touch events, simply implement <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onTouchEvent%28android.view.MotionEvent%29"><code>onTouchEvent(MotionEvent)</code></a>.
+along when the user swipes from one home screen to another, you can use
+{@link android.service.wallpaper.WallpaperService.Engine#onOffsetsChanged
+onOffsetsChanged()}.
+To react to touch events, simply implement {@link
+android.service.wallpaper.WallpaperService.Engine#onTouchEvent onTouchEvent()}.
Finally, applications can send arbitrary commands to the live wallpaper.
-Currently, only the standard home application sends commands to the <a
-href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onCommand%28java.lang.String,%20int,%20int,%20int,%20android.os.Bundle,%20boolean%29"><code>onCommand()</code></a>
+Currently, only the standard home application sends commands to the
+{@link android.service.wallpaper.WallpaperService.Engine#onCommand onCommand()}
method of the live wallpaper:</p>
<ul>
@@ -78,9 +81,9 @@
<ul>
<li><code><uses-sdk android:minSdkVersion="7" /></code>, which indicates
to Android Market and the platform that your application requires Android 2.1 or
-higher. For more information, see the <a href="../../../guide/appendix/api-levels.html">API
+higher. For more information, see the <a href="{@docRoot}guide/appendix/api-levels.html">API
Levels</a> and the documentation for the
-<a href="../../../guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
+<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
element.</li>
<li><code><uses-feature android:name="android.software.live_wallpaper" /></code>,
which tells Android Market that your application includes a live wallpaper
@@ -89,7 +92,9 @@
displays your application only to users whose devices support live wallpapers,
while hiding it from other devices on which it would not be able to run. For
more information, see the documentation for the
-<a href="../../../guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a>
+<a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code
+<uses-feature></a>
element.</li>
</ul>
diff --git a/docs/html/resources/articles/on-screen-inputs.jd b/docs/html/resources/articles/on-screen-inputs.jd
index 30b4c84..6a028c8 100644
--- a/docs/html/resources/articles/on-screen-inputs.jd
+++ b/docs/html/resources/articles/on-screen-inputs.jd
@@ -1,4 +1,6 @@
page.title=Onscreen Input Methods
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/painless-threading.jd b/docs/html/resources/articles/painless-threading.jd
index 17cec35..fea7ee2 100644
--- a/docs/html/resources/articles/painless-threading.jd
+++ b/docs/html/resources/articles/painless-threading.jd
@@ -1,4 +1,6 @@
page.title=Painless Threading
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>This article discusses the threading model used by Android applications and how applications can ensure best UI performance by spawning worker threads to handle long-running operations, rather than handling them in the main thread. The article also explains the API that your application can use to interact with Android UI toolkit components running on the main thread and spawn managed worker threads. </p>
diff --git a/docs/html/resources/articles/qsb.jd b/docs/html/resources/articles/qsb.jd
index d47ba54..01fb115 100644
--- a/docs/html/resources/articles/qsb.jd
+++ b/docs/html/resources/articles/qsb.jd
@@ -1,4 +1,6 @@
page.title=Quick Search Box
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/speech-input.jd b/docs/html/resources/articles/speech-input.jd
index 282b619..d42bd59 100644
--- a/docs/html/resources/articles/speech-input.jd
+++ b/docs/html/resources/articles/speech-input.jd
@@ -1,4 +1,6 @@
page.title=Speech Input
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p> People love their mobile phones because they can stay in touch wherever they
diff --git a/docs/html/resources/articles/timed-ui-updates.jd b/docs/html/resources/articles/timed-ui-updates.jd
index 863387c..7a0804f 100644
--- a/docs/html/resources/articles/timed-ui-updates.jd
+++ b/docs/html/resources/articles/timed-ui-updates.jd
@@ -1,4 +1,6 @@
page.title=Updating the UI from a Timer
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<img style="margin: 1.5em; float: right;" src="images/JFlubber.png" alt="" id="BLOGGER_PHOTO_ID_5135098660116808706" border="0">
diff --git a/docs/html/resources/articles/touch-mode.jd b/docs/html/resources/articles/touch-mode.jd
index e340062..5eae9b9 100644
--- a/docs/html/resources/articles/touch-mode.jd
+++ b/docs/html/resources/articles/touch-mode.jd
@@ -1,4 +1,6 @@
page.title=Touch Mode
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>This article explains the <em>touch mode</em>, one of the most
diff --git a/docs/html/resources/articles/track-mem.jd b/docs/html/resources/articles/track-mem.jd
index d580e82..c4184b5 100644
--- a/docs/html/resources/articles/track-mem.jd
+++ b/docs/html/resources/articles/track-mem.jd
@@ -1,4 +1,6 @@
page.title=Tracking Memory Allocations
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Writing efficient mobile applications is not always straightforward. In
diff --git a/docs/html/resources/articles/tts.jd b/docs/html/resources/articles/tts.jd
index e3fad91..7d07a89 100644
--- a/docs/html/resources/articles/tts.jd
+++ b/docs/html/resources/articles/tts.jd
@@ -1,4 +1,6 @@
page.title=Using Text-to-Speech
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Starting with Android 1.6 (API Level 4), the Android platform includes a new
diff --git a/docs/html/resources/articles/ui-1.5.jd b/docs/html/resources/articles/ui-1.5.jd
index c10cf52..2edaa2e 100644
--- a/docs/html/resources/articles/ui-1.5.jd
+++ b/docs/html/resources/articles/ui-1.5.jd
@@ -1,4 +1,6 @@
page.title=UI Framework Changes in Android 1.5
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/ui-1.6.jd b/docs/html/resources/articles/ui-1.6.jd
index 10cb524..09108dd 100644
--- a/docs/html/resources/articles/ui-1.6.jd
+++ b/docs/html/resources/articles/ui-1.6.jd
@@ -1,4 +1,6 @@
page.title=UI Framework Changes in Android 1.6
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Android 1.6 introduces numerous enhancements and bug fixes in the UI
diff --git a/docs/html/resources/articles/using-webviews.jd b/docs/html/resources/articles/using-webviews.jd
index 3a1f34c..3a2430b 100644
--- a/docs/html/resources/articles/using-webviews.jd
+++ b/docs/html/resources/articles/using-webviews.jd
@@ -1,4 +1,6 @@
page.title=Using WebViews
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>A small application called <a title="WebViewDemo"
diff --git a/docs/html/resources/articles/wikinotes-intents.jd b/docs/html/resources/articles/wikinotes-intents.jd
index bc64544..78fe62e 100644
--- a/docs/html/resources/articles/wikinotes-intents.jd
+++ b/docs/html/resources/articles/wikinotes-intents.jd
@@ -1,4 +1,6 @@
page.title=WikiNotes: Routing Intents
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
diff --git a/docs/html/resources/articles/wikinotes-linkify.jd b/docs/html/resources/articles/wikinotes-linkify.jd
index 21b1f13..fb49f86 100644
--- a/docs/html/resources/articles/wikinotes-linkify.jd
+++ b/docs/html/resources/articles/wikinotes-linkify.jd
@@ -1,4 +1,6 @@
page.title=WikiNotes: Linkify your Text!
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<img style="margin-left: 1.5em; margin-bottom:1.5em; float: right;"
diff --git a/docs/html/resources/articles/window-bg-speed.jd b/docs/html/resources/articles/window-bg-speed.jd
index bd7a303..c5e5e90 100644
--- a/docs/html/resources/articles/window-bg-speed.jd
+++ b/docs/html/resources/articles/window-bg-speed.jd
@@ -1,4 +1,6 @@
page.title=Window Backgrounds & UI Speed
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>Some Android applications require to squeeze every bit of performance out of
diff --git a/docs/html/resources/articles/zipalign.jd b/docs/html/resources/articles/zipalign.jd
index 013d0fe..9e767aa 100644
--- a/docs/html/resources/articles/zipalign.jd
+++ b/docs/html/resources/articles/zipalign.jd
@@ -1,4 +1,6 @@
page.title=Zipalign: an Easy Optimization
+parent.title=Articles
+parent.link=../browser.html?tag=article
@jd:body
<p>The Android SDK includes a tool called <a
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/resources/samples/get.jd b/docs/html/resources/samples/get.jd
index 1b6d137..86ec836 100644
--- a/docs/html/resources/samples/get.jd
+++ b/docs/html/resources/samples/get.jd
@@ -1,4 +1,6 @@
page.title=Getting the Samples
+parent.title=Sample Code
+parent.link=../browser.html?tag=sample
@jd:body
diff --git a/docs/html/resources/tutorials/hello-world.jd b/docs/html/resources/tutorials/hello-world.jd
index 67c2521..020c738 100644
--- a/docs/html/resources/tutorials/hello-world.jd
+++ b/docs/html/resources/tutorials/hello-world.jd
@@ -1,4 +1,6 @@
page.title=Hello, World
+parent.title=Tutorials
+parent.link=../browser.html?tag=tutorial
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/resources/tutorials/localization/index.jd b/docs/html/resources/tutorials/localization/index.jd
index 8a60814..de4433b 100755
--- a/docs/html/resources/tutorials/localization/index.jd
+++ b/docs/html/resources/tutorials/localization/index.jd
@@ -1,4 +1,6 @@
page.title=Hello, L10N
+parent.title=Tutorials
+parent.link=../../browser.html?tag=tutorial
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/resources/tutorials/notepad/index.jd b/docs/html/resources/tutorials/notepad/index.jd
index f569314..dd92184 100644
--- a/docs/html/resources/tutorials/notepad/index.jd
+++ b/docs/html/resources/tutorials/notepad/index.jd
@@ -1,4 +1,6 @@
page.title=Notepad Tutorial
+parent.title=Tutorials
+parent.link=../../browser.html?tag=tutorial
@jd:body
diff --git a/docs/html/resources/tutorials/testing/activity_test.jd b/docs/html/resources/tutorials/testing/activity_test.jd
index c94e8ab..4b861e2 100644
--- a/docs/html/resources/tutorials/testing/activity_test.jd
+++ b/docs/html/resources/tutorials/testing/activity_test.jd
@@ -1,4 +1,6 @@
page.title=Activity Testing
+parent.title=Tutorials
+parent.link=../../browser.html?tag=tutorial
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/resources/tutorials/testing/helloandroid_test.jd b/docs/html/resources/tutorials/testing/helloandroid_test.jd
index b47c334..4d949c8 100644
--- a/docs/html/resources/tutorials/testing/helloandroid_test.jd
+++ b/docs/html/resources/tutorials/testing/helloandroid_test.jd
@@ -1,4 +1,6 @@
page.title=Hello, Testing
+parent.title=Tutorials
+parent.link=../../browser.html?tag=tutorial
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/resources/tutorials/views/index.jd b/docs/html/resources/tutorials/views/index.jd
index 6ea7683..bba8330 100644
--- a/docs/html/resources/tutorials/views/index.jd
+++ b/docs/html/resources/tutorials/views/index.jd
@@ -1,4 +1,6 @@
page.title=Hello, Views
+parent.title=Tutorials
+parent.link=../../browser.html?tag=tutorial
@jd:body
<style>
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index 6842c82..8088e32 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -294,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
@@ -351,10 +351,10 @@
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
+href="{@docRoot}guide/topics/fundamentals/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/FragmentListCursorLoader.html">FragmentListCursorLoader</a>
+and <a
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
LoaderThrottle</a> samples.</p>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index c283167..355465b 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -116,22 +116,27 @@
<ul>
<li>The tools now automatically generate Java Programming Language source files (in the <code>gen/</code> directory) and
bytecode (in the <code>res/raw/</code> directory) from your <code>.rs</code> files.</li>
- <li>A Binary XML editor has been added.</li>
+ <li>A Binary XML editor has been added (<a href="http://tools.android.com/recent/binaryxmleditor">details</a>).</li>
<li>Traceview is now integrated into the Eclipse UI (<a href="http://tools.android.com/recent/traceviewineclipse">details</a>).</li>
<li>The "Go To Declaration" feature for XML and <code>.java</code> files quickly show all the matches in the project
- and allows you jump to specific items such as string translations or <code>onClick</code> handlers.</li>
- <li>The Resource Chooser can create items such as dimensions, integers, ids, and booleans.</li>
+ and allows you jump to specific items such as string translations or <code>onClick</code> handlers
+ (<a href="http://tools.android.com/recent/gotodeclarationimprovements">details</a>).</li>
+ <li>The Resource Chooser can create items such as dimensions, integers, ids, and booleans
+ (<a href="http://tools.android.com/recent/resourcechoosercannowcreatearbitraryvalues">details</a>).</li>
<li>Improvements to the Visual Layout Editor:
<ul>
<li>A new Palette with categories and rendering previews
(<a href="http://tools.android.com/recent/newpalette">details</a>).</li>
- <li>A Layout action bar.</li>
+ <li>A Layout Actions bar that provides quick access to common layout operations
+ (<a href="http://tools.android.com/recent/layoutactionsbar">details</a>).</li>
<li>When the Android 3.0 rendering library is selected, layouts render more like they do on devices.
This includes rendering of status and title bars to more accurately reflect the actual
- screen space available to applications.</li>
+ screen space available to applications
+ (<a href="http://tools.android.com/recent/systembarandactionbar">details</a>).</li>
<li>Zoom improvements such as fit to view, persistent scale, and keyboard access.
(<a href="http://tools.android.com/recent/zoomimprovements">details</a>).</li>
- <li>Further improvements to <code><merge></code> layouts, as well as layouts with gesture overlays.</li>
+ <li>Further improvements to <code><merge></code> layouts, as well as layouts with gesture overlays
+ (<a href="http://tools.android.com/recent/improvedsupportformergetags">details</a>).</li>
<li>Improved rendering error diagnostics.</li>
</ul>
</li>
diff --git a/docs/html/sdk/preview/start.jd b/docs/html/sdk/preview/start.jd
index 3bf70b3..d6e442e 100644
--- a/docs/html/sdk/preview/start.jd
+++ b/docs/html/sdk/preview/start.jd
@@ -46,7 +46,7 @@
the samples are located in <code><sdk_root>/samples/android-Honeycomb</code>. The list of links
below helps you find samples for the features you are interested in:</p>
<ul>
- <li><a href="{@docRoot}resources/samples/Honeycomb-Gallery/index.html">Honeycomb Gallery</a> -
+ <li><a href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> -
A demo application highlighting how to use some of the new APIs in Honeycomb, including fragments, the action bar,
drag and drop, transition animations, and a stack widget.</li>
<li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">Fragments</a>
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 31a549c..66dfff6 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -38,6 +38,7 @@
extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_AUDIO_FLAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
diff --git a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
index 2cc1f8e..7730eb1 100644
--- a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
@@ -157,7 +157,7 @@
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
const int self_pid = getpid();
- if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) {
+ if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
// we're called from a different process, do the real check
if (!checkCallingPermission(
String16("android.permission.ACCESS_SURFACE_FLINGER")))
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/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 439e4ce..60bdd62 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -732,18 +732,14 @@
return TEST_PLAYER;
}
- char value[PROPERTY_VALUE_MAX];
- if (!property_get("media.httplive.disable-nuplayer", value, NULL)
- || (strcasecmp(value, "true") && strcmp(value, "1"))) {
- if (!strncasecmp("http://", url, 7)) {
- size_t len = strlen(url);
- if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
- return NU_PLAYER;
- }
+ if (!strncasecmp("http://", url, 7)) {
+ size_t len = strlen(url);
+ if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
+ return NU_PLAYER;
+ }
- if (strstr(url,"m3u8")) {
- return NU_PLAYER;
- }
+ if (strstr(url,"m3u8")) {
+ return NU_PLAYER;
}
}
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
new file mode 100644
index 0000000..4203b6e
--- /dev/null
+++ b/media/libstagefright/AACExtractor.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AACExtractor"
+#include <utils/Log.h>
+
+#include "include/AACExtractor.h"
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+#define ADTS_HEADER_LENGTH 7
+
+class AACSource : public MediaSource {
+public:
+ AACSource(const sp<DataSource> &source,
+ const sp<MetaData> &meta,
+ const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~AACSource();
+
+private:
+ static const size_t kMaxFrameSize;
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+
+ off64_t mOffset;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+ MediaBufferGroup *mGroup;
+
+ Vector<uint64_t> mOffsetVector;
+ int64_t mFrameDurationUs;
+
+ AACSource(const AACSource &);
+ AACSource &operator=(const AACSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Returns the sample rate based on the sampling frequency index
+uint32_t get_sample_rate(const uint8_t sf_index)
+{
+ static const uint32_t sample_rates[] =
+ {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+
+ if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) {
+ return sample_rates[sf_index];
+ }
+
+ return 0;
+}
+
+static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) {
+ size_t frameSize = 0;
+
+ uint8_t syncword[2];
+ if (source->readAt(0, &syncword, 2) != 2) {
+ return 0;
+ }
+ if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) {
+ return 0;
+ }
+
+ uint8_t protectionAbsent;
+ if (source->readAt(offset + 1, &protectionAbsent, 1) < 1) {
+ return 0;
+ }
+ protectionAbsent &= 0x1;
+
+ uint8_t header[3];
+ if (source->readAt(offset + 3, &header, 3) < 3) {
+ return 0;
+ }
+
+ frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
+ frameSize += ADTS_HEADER_LENGTH + protectionAbsent ? 0 : 2;
+
+ return frameSize;
+}
+
+AACExtractor::AACExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mInitCheck(NO_INIT),
+ mFrameDurationUs(0) {
+ String8 mimeType;
+ float confidence;
+ if (!SniffAAC(mDataSource, &mimeType, &confidence, NULL)) {
+ return;
+ }
+
+ uint8_t profile, sf_index, channel, header[2];
+ if (mDataSource->readAt(2, &header, 2) < 2) {
+ return;
+ }
+
+ profile = (header[0] >> 6) & 0x3;
+ sf_index = (header[0] >> 2) & 0xf;
+ uint32_t sr = get_sample_rate(sf_index);
+ if (sr == 0) {
+ return;
+ }
+ channel = (header[0] & 0x1) << 2 | (header[1] >> 6);
+
+ mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);
+
+ off64_t offset = 0;
+ off64_t streamSize, numFrames = 0;
+ size_t frameSize = 0;
+ int64_t duration = 0;
+
+ if (mDataSource->getSize(&streamSize) == OK) {
+ while (offset < streamSize) {
+ if ((frameSize = getFrameSize(source, offset)) == 0) {
+ return;
+ }
+
+ mOffsetVector.push(offset);
+
+ offset += frameSize;
+ numFrames ++;
+ }
+
+ // Round up and get the duration
+ mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
+ duration = numFrames * mFrameDurationUs;
+ mMeta->setInt64(kKeyDuration, duration);
+ }
+
+ mInitCheck = OK;
+}
+
+AACExtractor::~AACExtractor() {
+}
+
+sp<MetaData> AACExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+
+ if (mInitCheck != OK) {
+ return meta;
+ }
+
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS);
+
+ return meta;
+}
+
+size_t AACExtractor::countTracks() {
+ return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> AACExtractor::getTrack(size_t index) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs);
+}
+
+sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return mMeta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// 8192 = 2^13, 13bit AAC frame size (in bytes)
+const size_t AACSource::kMaxFrameSize = 8192;
+
+AACSource::AACSource(
+ const sp<DataSource> &source, const sp<MetaData> &meta,
+ const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us)
+ : mDataSource(source),
+ mMeta(meta),
+ mOffset(0),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL),
+ mOffsetVector(offset_vector),
+ mFrameDurationUs(frame_duration_us) {
+}
+
+AACSource::~AACSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t AACSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mOffset = 0;
+ mCurrentTimeUs = 0;
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mStarted = true;
+
+ return OK;
+}
+
+status_t AACSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ return OK;
+}
+
+sp<MetaData> AACSource::getFormat() {
+ return mMeta;
+}
+
+status_t AACSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ if (mFrameDurationUs > 0) {
+ int64_t seekFrame = seekTimeUs / mFrameDurationUs;
+ mCurrentTimeUs = seekFrame * mFrameDurationUs;
+
+ mOffset = mOffsetVector.itemAt(seekFrame);
+ }
+ }
+
+ size_t frameSize, frameSizeWithoutHeader;
+ if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH;
+ if (mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(),
+ frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ buffer->set_range(0, frameSizeWithoutHeader);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+
+ mOffset += frameSize;
+ mCurrentTimeUs += mFrameDurationUs;
+
+ *out = buffer;
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffAAC(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+ uint8_t header[2];
+
+ if (source->readAt(0, &header, 2) != 2) {
+ return false;
+ }
+
+ // ADTS syncword
+ if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
+ *confidence = 0.2;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 029b238..53435f8 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -5,6 +5,7 @@
LOCAL_SRC_FILES:= \
ACodec.cpp \
+ AACExtractor.cpp \
AMRExtractor.cpp \
AMRWriter.cpp \
AudioPlayer.cpp \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index cb08023..8866750 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -44,7 +44,6 @@
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
-#include "include/LiveSession.h"
#define USE_SURFACE_ALLOC 1
#define FRAME_DROP_FREQ 7
@@ -233,17 +232,6 @@
mUri = uri;
- if (!strncmp("http://", uri, 7)) {
- // Hack to support http live.
-
- size_t len = strlen(uri);
- if (!strcasecmp(&uri[len - 5], ".m3u8")
- || strstr(&uri[7], "m3u8") != NULL) {
- mUri = "httplive://";
- mUri.append(&uri[7]);
- }
- }
-
if (headers) {
mUriHeaders = *headers;
}
@@ -376,14 +364,11 @@
}
void AwesomePlayer::reset() {
- LOGI("reset");
-
Mutex::Autolock autoLock(mLock);
reset_l();
}
void AwesomePlayer::reset_l() {
- LOGI("reset_l");
mDisplayWidth = 0;
mDisplayHeight = 0;
@@ -411,10 +396,6 @@
}
}
- if (mFlags & PREPARING) {
- LOGI("waiting until preparation is completed.");
- }
-
while (mFlags & PREPARING) {
mPreparedCondition.wait(mLock);
}
@@ -438,8 +419,6 @@
}
mAudioSource.clear();
- LOGI("audio source cleared");
-
mTimeSource = NULL;
delete mAudioPlayer;
@@ -457,11 +436,6 @@
mRTSPController.clear();
}
- if (mLiveSession != NULL) {
- mLiveSession->disconnect();
- mLiveSession.clear();
- }
-
if (mVideoSource != NULL) {
mVideoSource->stop();
@@ -476,8 +450,6 @@
IPCThreadState::self()->flushCommands();
}
- LOGI("video source cleared");
-
mDurationUs = -1;
mFlags = 0;
mExtractorFlags = 0;
@@ -494,8 +466,6 @@
mFileSource.clear();
mBitrate = -1;
-
- LOGI("reset_l completed");
}
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -668,35 +638,6 @@
postBufferingEvent_l();
}
-void AwesomePlayer::partial_reset_l() {
- // Only reset the video renderer and shut down the video decoder.
- // Then instantiate a new video decoder and resume video playback.
-
- mVideoRenderer.clear();
-
- if (mVideoBuffer) {
- mVideoBuffer->release();
- mVideoBuffer = NULL;
- }
-
- {
- mVideoSource->stop();
-
- // The following hack is necessary to ensure that the OMX
- // component is completely released by the time we may try
- // to instantiate it again.
- wp<MediaSource> tmp = mVideoSource;
- mVideoSource.clear();
- while (tmp.promote() != NULL) {
- usleep(1000);
- }
- IPCThreadState::self()->flushCommands();
- }
-
- CHECK_EQ((status_t)OK,
- initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData));
-}
-
void AwesomePlayer::onStreamDone() {
// Posted whenever any stream finishes playing.
@@ -706,21 +647,7 @@
}
mStreamDoneEventPending = false;
- if (mStreamDoneStatus == INFO_DISCONTINUITY) {
- // This special status is returned because an http live stream's
- // video stream switched to a different bandwidth at this point
- // and future data may have been encoded using different parameters.
- // This requires us to shutdown the video decoder and reinstantiate
- // a fresh one.
-
- LOGV("INFO_DISCONTINUITY");
-
- CHECK(mVideoSource != NULL);
-
- partial_reset_l();
- postVideoEvent_l();
- return;
- } else if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
+ if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
notifyListener_l(
@@ -1606,29 +1533,6 @@
LOGI("Prepare cancelled while waiting for initial cache fill.");
return UNKNOWN_ERROR;
}
- } else if (!strncasecmp(mUri.string(), "httplive://", 11)) {
- String8 uri("http://");
- uri.append(mUri.string() + 11);
-
- if (mLooper == NULL) {
- mLooper = new ALooper;
- mLooper->setName("httplive");
- mLooper->start();
- }
-
- mLiveSession = new LiveSession;
- mLooper->registerHandler(mLiveSession);
-
- mLiveSession->connect(uri.string());
- dataSource = mLiveSession->getDataSource();
-
- sp<MediaExtractor> extractor =
- MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
-
- static_cast<MPEG2TSExtractor *>(extractor.get())
- ->setLiveSession(mLiveSession);
-
- return setDataSource_l(extractor);
} else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
if (mLooper == NULL) {
mLooper = new ALooper;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index e06fa81..8f9c150 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -24,6 +24,7 @@
#include "include/NuHTTPDataSource.h"
#include "include/DRMExtractor.h"
#include "include/FLACExtractor.h"
+#include "include/AACExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -109,6 +110,7 @@
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
+ RegisterSniffer(SniffAAC);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index b50af89..0be7261 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -36,6 +36,7 @@
const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
+const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 08ed206..dbd0829 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -27,6 +27,7 @@
#include "include/DRMExtractor.h"
#include "include/WVMExtractor.h"
#include "include/FLACExtractor.h"
+#include "include/AACExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -98,6 +99,8 @@
return new MPEG2TSExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
return new WVMExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
+ return new AACExtractor(source);
}
return NULL;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index fccd68c..5d502e7 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3289,7 +3289,7 @@
}
status_t OMXCodec::stop() {
- CODEC_LOGI("stop mState=%d", mState);
+ CODEC_LOGV("stop mState=%d", mState);
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index fa12cf0..95cf2d3 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -329,5 +329,52 @@
return foundIDR;
}
+sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration) {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ CHECK_LE(sampling_freq_index, 11u);
+ static const int32_t kSamplingFreq[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000
+ };
+ meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
+ meta->setInt32(kKeyChannelCount, channel_configuration);
+
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05, 2,
+ // AudioSpecificInfo follows
+
+ // oooo offf fccc c000
+ // o - audioObjectType
+ // f - samplingFreqIndex
+ // c - channelConfig
+ };
+ sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+ memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+ csd->data()[sizeof(kStaticESDS)] =
+ ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+ csd->data()[sizeof(kStaticESDS) + 1] =
+ ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+ meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+
+ return meta;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 0bed3ca..f7a9085 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -168,18 +168,6 @@
CHECK_GT(mBandwidthItems.size(), 0u);
mBandwidthItems.sort(SortByBandwidth);
-
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.httplive.disable-nuplayer", value, NULL)
- && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
- // The "legacy" player cannot deal with audio format changes,
- // some streams use different audio encoding parameters for
- // their lowest bandwidth stream.
- if (mBandwidthItems.size() > 1) {
- // XXX Remove the lowest bitrate stream for now...
- mBandwidthItems.removeAt(0);
- }
- }
}
postMonitorQueue();
diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h
new file mode 100644
index 0000000..8e5657b
--- /dev/null
+++ b/media/libstagefright/include/AACExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAC_EXTRACTOR_H_
+
+#define AAC_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct AMessage;
+class String8;
+
+class AACExtractor : public MediaExtractor {
+public:
+ AACExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~AACExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+ status_t mInitCheck;
+
+ Vector<uint64_t> mOffsetVector;
+ int64_t mFrameDurationUs;
+
+ AACExtractor(const AACExtractor &);
+ AACExtractor &operator=(const AACExtractor &);
+};
+
+bool SniffAAC(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // AAC_EXTRACTOR_H_
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 98b8c05..1497732 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -43,8 +43,6 @@
class DrmManagerClinet;
class DecryptHandle;
-struct LiveSession;
-
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -207,8 +205,6 @@
sp<ARTSPController> mRTSPController;
sp<ARTSPController> mConnectingRTSPController;
- sp<LiveSession> mLiveSession;
-
DrmManagerClient *mDrmManagerClient;
DecryptHandle *mDecryptHandle;
@@ -219,7 +215,6 @@
status_t setDataSource_l(const sp<DataSource> &dataSource);
status_t setDataSource_l(const sp<MediaExtractor> &extractor);
void reset_l();
- void partial_reset_l();
status_t seekTo_l(int64_t timeUs);
status_t pause_l(bool at_eos = false);
void initRenderer_l();
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 3aeb07f..0218755 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -52,6 +52,10 @@
const char *AVCProfileToString(uint8_t profile);
+sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration);
+
} // namespace android
#endif // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 4335b99..6056739 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -371,21 +371,6 @@
mBuffer->setRange(0, 0);
switch (type) {
- case DISCONTINUITY_HTTPLIVE:
- {
- mQueue.clear(true);
-
- if (mStreamType == 0x1b) {
- // Don't signal discontinuities on audio streams.
- if (mSource != NULL) {
- mSource->queueDiscontinuity(type);
- } else {
- deferDiscontinuity(type);
- }
- }
- break;
- }
-
case DISCONTINUITY_SEEK:
case DISCONTINUITY_FORMATCHANGE:
{
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index ec3be84..455f9d5 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -34,7 +34,6 @@
struct ATSParser : public RefBase {
enum DiscontinuityType {
DISCONTINUITY_NONE,
- DISCONTINUITY_HTTPLIVE,
DISCONTINUITY_SEEK,
DISCONTINUITY_FORMATCHANGE
};
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 73efdfe..dcaf9f7 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -341,54 +341,6 @@
return timeUs;
}
-// static
-sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration) {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
- CHECK_LE(sampling_freq_index, 11u);
- static const int32_t kSamplingFreq[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000
- };
- meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
- meta->setInt32(kKeyChannelCount, channel_configuration);
-
- static const uint8_t kStaticESDS[] = {
- 0x03, 22,
- 0x00, 0x00, // ES_ID
- 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
-
- 0x04, 17,
- 0x40, // Audio ISO/IEC 14496-3
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-
- 0x05, 2,
- // AudioSpecificInfo follows
-
- // oooo offf fccc c000
- // o - audioObjectType
- // f - samplingFreqIndex
- // c - channelConfig
- };
- sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
- memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
-
- csd->data()[sizeof(kStaticESDS)] =
- ((profile + 1) << 3) | (sampling_freq_index >> 1);
-
- csd->data()[sizeof(kStaticESDS) + 1] =
- ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
-
- meta->setData(kKeyESDS, 0, csd->data(), csd->size());
-
- return meta;
-}
-
struct NALPosition {
size_t nalOffset;
size_t nalSize;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 5b7957e..d081995 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -61,10 +61,6 @@
// returns its timestamp in us (or -1 if no time information).
int64_t fetchTimestamp(size_t size);
- static sp<MetaData> MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration);
-
DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
};
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index a1f0796..dfec47f 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -202,20 +202,13 @@
LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
}
-static bool isDiscontinuity(const uint8_t *data, ssize_t size) {
- return size == 188 && data[0] == 0x00;
-}
-
status_t MPEG2TSExtractor::feedMore() {
Mutex::Autolock autoLock(mLock);
uint8_t packet[kTSPacketSize];
ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
- if (isDiscontinuity(packet, n)) {
- LOGI("XXX discontinuity detected");
- mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE);
- } else if (n < (ssize_t)kTSPacketSize) {
+ if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
} else {
mParser->feedTSPacket(packet, kTSPacketSize);
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 13988cd..9f6bd29 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -104,7 +104,7 @@
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
- mIsGeneric = desc.startsWith("mpeg4-generic/");
+ mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14);
if (mIsGeneric) {
AString value;
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index f0b858d..679dcab 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -637,7 +637,7 @@
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
- } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+ } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
AString val;
if (!GetAttribute(params.c_str(), "mode", &val)
|| (strcasecmp(val.c_str(), "AAC-lbr")
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 601f569..47de4e09 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -123,7 +123,7 @@
struct sockaddr_in addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(*rtpSocket,
@@ -340,6 +340,8 @@
}
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
+ LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP");
+
CHECK(!s->mIsInjected);
sp<ABuffer> buffer = new ABuffer(65536);
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 893a387..84c666f 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -67,7 +67,7 @@
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
- || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
+ || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
mIssueFIRRequests = true;
} else {
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 3e710dc..f03f7a2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -71,6 +71,11 @@
line.setTo(desc, i, eolPos - i);
}
+ if (line.empty()) {
+ i = eolPos + 1;
+ continue;
+ }
+
if (line.size() < 2 || line.c_str()[1] != '=') {
return false;
}
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index ba7c1b2..d15d9c5 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -38,6 +38,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
+#include <netdb.h>
// If no access units are received within 5 secs, assume that the rtp
// stream has ended and signal end of stream.
@@ -121,9 +122,10 @@
// want to transmit user/pass in cleartext.
AString host, path, user, pass;
unsigned port;
- if (ARTSPConnection::ParseURL(
- mSessionURL.c_str(), &host, &port, &path, &user, &pass)
- && user.size() > 0) {
+ CHECK(ARTSPConnection::ParseURL(
+ mSessionURL.c_str(), &host, &port, &path, &user, &pass));
+
+ if (user.size() > 0) {
mSessionURL.clear();
mSessionURL.append("rtsp://");
mSessionURL.append(host);
@@ -133,6 +135,8 @@
LOGI("rewritten session url: '%s'", mSessionURL.c_str());
}
+
+ mSessionHost = host;
}
void connect(const sp<AMessage> &doneMsg) {
@@ -248,34 +252,64 @@
// In case we're behind NAT, fire off two UDP packets to the remote
// rtp/rtcp ports to poke a hole into the firewall for future incoming
// packets. We're going to send an RR/SDES RTCP packet to both of them.
- void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+ bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+ struct sockaddr_in addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+ addr.sin_family = AF_INET;
+
AString source;
AString server_port;
if (!GetAttribute(transport.c_str(),
"source",
- &source)
- || !GetAttribute(transport.c_str(),
+ &source)) {
+ LOGW("Missing 'source' field in Transport response. Using "
+ "RTSP endpoint address.");
+
+ struct hostent *ent = gethostbyname(mSessionHost.c_str());
+ if (ent == NULL) {
+ LOGE("Failed to look up address of session host '%s'",
+ mSessionHost.c_str());
+
+ return false;
+ }
+
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ } else {
+ addr.sin_addr.s_addr = inet_addr(source.c_str());
+ }
+
+ if (!GetAttribute(transport.c_str(),
"server_port",
&server_port)) {
- return;
+ LOGI("Missing 'server_port' field in Transport response.");
+ return false;
}
int rtpPort, rtcpPort;
if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
|| rtpPort <= 0 || rtpPort > 65535
|| rtcpPort <=0 || rtcpPort > 65535
- || rtcpPort != rtpPort + 1
- || (rtpPort & 1) != 0) {
- return;
+ || rtcpPort != rtpPort + 1) {
+ LOGE("Server picked invalid RTP/RTCP port pair %s,"
+ " RTP port must be even, RTCP port must be one higher.",
+ server_port.c_str());
+
+ return false;
}
- struct sockaddr_in addr;
- memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(source.c_str());
+ if (rtpPort & 1) {
+ LOGW("Server picked an odd RTP port, it should've picked an "
+ "even one, we'll let it pass for now, but this may break "
+ "in the future.");
+ }
if (addr.sin_addr.s_addr == INADDR_NONE) {
- return;
+ return true;
+ }
+
+ if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) {
+ // No firewalls to traverse on the loopback interface.
+ return true;
}
// Make up an RR/SDES RTCP packet.
@@ -289,16 +323,26 @@
ssize_t n = sendto(
rtpSocket, buf->data(), buf->size(), 0,
(const sockaddr *)&addr, sizeof(addr));
- CHECK_EQ(n, (ssize_t)buf->size());
+
+ if (n < (ssize_t)buf->size()) {
+ LOGE("failed to poke a hole for RTP packets");
+ return false;
+ }
addr.sin_port = htons(rtcpPort);
n = sendto(
rtcpSocket, buf->data(), buf->size(), 0,
(const sockaddr *)&addr, sizeof(addr));
- CHECK_EQ(n, (ssize_t)buf->size());
+
+ if (n < (ssize_t)buf->size()) {
+ LOGE("failed to poke a hole for RTCP packets");
+ return false;
+ }
LOGV("successfully poked holes.");
+
+ return true;
}
virtual void onMessageReceived(const sp<AMessage> &msg) {
@@ -381,6 +425,7 @@
response->mContent->size());
if (!mSessionDesc->isValid()) {
+ LOGE("Failed to parse session description.");
result = ERROR_MALFORMED;
} else {
ssize_t i = response->mHeaders.indexOfKey("content-base");
@@ -395,6 +440,25 @@
}
}
+ if (!mBaseURL.startsWith("rtsp://")) {
+ // Some misbehaving servers specify a relative
+ // URL in one of the locations above, combine
+ // it with the absolute session URL to get
+ // something usable...
+
+ LOGW("Server specified a non-absolute base URL"
+ ", combining it with the session URL to "
+ "get something usable...");
+
+ AString tmp;
+ CHECK(MakeURL(
+ mSessionURL.c_str(),
+ mBaseURL.c_str(),
+ &tmp));
+
+ mBaseURL = tmp;
+ }
+
CHECK_GT(mSessionDesc->countTracks(), 1u);
setupTrack(1);
}
@@ -455,9 +519,12 @@
if (!track->mUsingInterleavedTCP) {
AString transport = response->mHeaders.valueAt(i);
- pokeAHole(track->mRTPSocket,
- track->mRTCPSocket,
- transport);
+ // We are going to continue even if we were
+ // unable to poke a hole into the firewall...
+ pokeAHole(
+ track->mRTPSocket,
+ track->mRTCPSocket,
+ transport);
}
mRTPConn->addStream(
@@ -853,17 +920,16 @@
case 'tiou':
{
if (!mReceivedFirstRTCPPacket) {
- if (mTryFakeRTCP) {
- LOGW("Never received any data, disconnecting.");
- (new AMessage('abor', id()))->post();
- } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) {
+ if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
LOGW("We received RTP packets but no RTCP packets, "
"using fake timestamps.");
mTryFakeRTCP = true;
mReceivedFirstRTCPPacket = true;
- } else {
+
+ fakeTimestamps();
+ } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
LOGW("Never received any data, switching transports.");
mTryTCPInterleaving = true;
@@ -871,6 +937,9 @@
sp<AMessage> msg = new AMessage('abor', id());
msg->setInt32("reconnect", true);
msg->post();
+ } else {
+ LOGW("Never received any data, disconnecting.");
+ (new AMessage('abor', id()))->post();
}
}
break;
@@ -1016,6 +1085,7 @@
sp<ASessionDescription> mSessionDesc;
AString mOriginalSessionURL; // This one still has user:pass@
AString mSessionURL;
+ AString mSessionHost;
AString mBaseURL;
AString mSessionID;
bool mSetupTracksSuccessful;
@@ -1158,6 +1228,12 @@
return true;
}
+ void fakeTimestamps() {
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ onTimeUpdate(i, 0, 0ll);
+ }
+ }
+
void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
trackIndex, rtpTime, ntpTime);
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index fb1b073..4ea8849 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -38,6 +38,7 @@
namespace android {
+#if 0
static bool isMtpDevice(uint16_t vendor, uint16_t product) {
// Sandisk Sansa Fuze
if (vendor == 0x0781 && product == 0x74c2)
@@ -47,6 +48,7 @@
return true;
return false;
}
+#endif
MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
struct usb_device *device = usb_device_new(deviceName, fd);
@@ -91,7 +93,9 @@
LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
free(manufacturerName);
free(productName);
- } else {
+ }
+#if 0
+ else {
// look for special cased devices based on vendor/product ID
// we are doing this mainly for testing purposes
uint16_t vendor = usb_device_get_vendor_id(device);
@@ -119,7 +123,7 @@
printf("no MTP string\n");
}
}
-
+#endif
// if we got here, then we have a likely MTP or PTP device
// interface should be followed by three endpoints
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/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/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index e775dac..5ed67a9 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -26,6 +26,7 @@
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import android.media.AudioManager;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -70,12 +71,12 @@
private boolean mKeyguardBypassEnabled;
- private boolean mDevicePluggedIn;
-
private boolean mDeviceProvisioned;
private int mBatteryLevel;
+ private int mBatteryStatus;
+
private CharSequence mTelephonyPlmn;
private CharSequence mTelephonySpn;
@@ -203,7 +204,7 @@
// take a guess to start
mSimState = IccCard.State.READY;
- mDevicePluggedIn = true;
+ mBatteryStatus = BATTERY_STATUS_FULL;
mBatteryLevel = 100;
mTelephonyPlmn = getDefaultPlmn();
@@ -283,13 +284,12 @@
/**
* Handle {@link #MSG_BATTERY_UPDATE}
*/
- private void handleBatteryUpdate(int pluggedInStatus, int batteryLevel) {
+ private void handleBatteryUpdate(int batteryStatus, int batteryLevel) {
if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
- final boolean pluggedIn = isPluggedIn(pluggedInStatus);
-
- if (isBatteryUpdateInteresting(pluggedIn, batteryLevel)) {
+ if (isBatteryUpdateInteresting(batteryStatus, batteryLevel)) {
+ mBatteryStatus = batteryStatus;
mBatteryLevel = batteryLevel;
- mDevicePluggedIn = pluggedIn;
+ final boolean pluggedIn = isPluggedIn(batteryStatus);;
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onRefreshBatteryInfo(
shouldShowBatteryInfo(), pluggedIn, batteryLevel);
@@ -336,26 +336,34 @@
return status == BATTERY_STATUS_CHARGING || status == BATTERY_STATUS_FULL;
}
- private boolean isBatteryUpdateInteresting(boolean pluggedIn, int batteryLevel) {
+ private boolean isBatteryUpdateInteresting(int batteryStatus, int batteryLevel) {
// change in plug is always interesting
- if (mDevicePluggedIn != pluggedIn) {
+ final boolean isPluggedIn = isPluggedIn(batteryStatus);
+ final boolean wasPluggedIn = isPluggedIn(mBatteryStatus);
+ final boolean stateChangedWhilePluggedIn =
+ wasPluggedIn == true && isPluggedIn == true && (mBatteryStatus != batteryStatus);
+ if (wasPluggedIn != isPluggedIn || stateChangedWhilePluggedIn) {
return true;
}
// change in battery level while plugged in
- if (pluggedIn && mBatteryLevel != batteryLevel) {
+ if (isPluggedIn && mBatteryLevel != batteryLevel) {
return true;
}
- if (!pluggedIn) {
+ if (!isPluggedIn) {
// not plugged in and below threshold
- if (batteryLevel < LOW_BATTERY_THRESHOLD && batteryLevel != mBatteryLevel) {
+ if (isBatteryLow(batteryLevel) && batteryLevel != mBatteryLevel) {
return true;
}
}
return false;
}
+ private boolean isBatteryLow(int batteryLevel) {
+ return batteryLevel < LOW_BATTERY_THRESHOLD;
+ }
+
/**
* @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
* @return The string to use for the plmn, or null if it should not be shown.
@@ -485,7 +493,12 @@
}
public boolean isDevicePluggedIn() {
- return mDevicePluggedIn;
+ return isPluggedIn(mBatteryStatus);
+ }
+
+ public boolean isDeviceCharged() {
+ return mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL
+ || mBatteryLevel >= 100; // in case a particular device doesn't flag it
}
public int getBatteryLevel() {
@@ -493,7 +506,7 @@
}
public boolean shouldShowBatteryInfo() {
- return mDevicePluggedIn || mBatteryLevel < LOW_BATTERY_THRESHOLD;
+ return isPluggedIn(mBatteryStatus) || isBatteryLow(mBatteryLevel);
}
public CharSequence getTelephonyPlmn() {
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/policy/src/com/android/internal/policy/impl/StatusView.java b/policy/src/com/android/internal/policy/impl/StatusView.java
index da7bbb8..2aff4a8 100644
--- a/policy/src/com/android/internal/policy/impl/StatusView.java
+++ b/policy/src/com/android/internal/policy/impl/StatusView.java
@@ -51,6 +51,7 @@
private LockPatternUtils mLockPatternUtils;
private int mHelpMessageId;
private int mHelpIconId;
+ private KeyguardUpdateMonitor mUpdateMonitor;
private View findViewById(int id) {
return mView.findViewById(id);
@@ -97,6 +98,7 @@
mHasDate = (mDate != null);
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
mLockPatternUtils = lockPatternUtils;
+ mUpdateMonitor = updateMonitor;
refreshTimeAndDateDisplay();
@@ -186,7 +188,7 @@
// Battery status
if (mPluggedIn) {
// Charging or charged
- if (mBatteryLevel >= 100) {
+ if (mUpdateMonitor.isDeviceCharged()) {
mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
} else {
mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in,
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index d25b9c8..51e9b00 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/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index 41f3b23..a9efc98 100755
--- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -305,8 +305,15 @@
fileIds = mPbrFile.mFileIds.get(recNum);
if (fileIds == null || fileIds.isEmpty()) return;
+
+ int extEf = 0;
+ // Only call fileIds.get while EFEXT1_TAG is available
+ if (fileIds.containsKey(USIM_EFEXT1_TAG)) {
+ extEf = fileIds.get(USIM_EFEXT1_TAG);
+ }
+
mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG),
- fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
+ extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
try {
mLock.wait();
} catch (InterruptedException e) {
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 030048f..d920108 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -163,10 +163,6 @@
public native static String startWpsWithPinFromDeviceCommand(String bssid);
- public native static boolean doDhcpRequest(DhcpInfo results);
-
- public native static String getDhcpError();
-
public native static boolean setSuspendOptimizationsCommand(boolean enabled);
public native static boolean setCountryCodeCommand(String countryCode);