diff options
113 files changed, 6793 insertions, 3589 deletions
diff --git a/api/current.xml b/api/current.xml index 35fb3ef..54aae7f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -26658,6 +26658,17 @@ visibility="public" > </field> +<field name="EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS" + type="java.lang.String" + transient="false" + volatile="false" + value=""extra_click_download_ids"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="PAUSED_QUEUED_FOR_WIFI" type="int" transient="false" @@ -27617,6 +27628,17 @@ visibility="public" > </method> +<method name="onDestroyOptionsMenu" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="onDestroyView" return="void" abstract="false" @@ -50109,6 +50131,17 @@ visibility="public" > </field> +<field name="CATEGORY_HE_DESK_DOCK" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.category.HE_DESK_DOCK"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CATEGORY_HOME" type="java.lang.String" transient="false" @@ -50142,6 +50175,17 @@ visibility="public" > </field> +<field name="CATEGORY_LE_DESK_DOCK" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.category.LE_DESK_DOCK"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CATEGORY_MONKEY" type="java.lang.String" transient="false" @@ -50361,6 +50405,28 @@ visibility="public" > </field> +<field name="EXTRA_DOCK_STATE_HE_DESK" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOCK_STATE_LE_DESK" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_DOCK_STATE_UNDOCKED" type="int" transient="false" @@ -83600,6 +83666,17 @@ visibility="public" > </method> +<method name="getColor" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getOpacity" return="int" abstract="false" @@ -83624,6 +83701,19 @@ <parameter name="alpha" type="int"> </parameter> </method> +<method name="setColor" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="color" type="int"> +</parameter> +</method> <method name="setColorFilter" return="void" abstract="false" @@ -145048,6 +145138,23 @@ <parameter name="key" type="java.lang.CharSequence"> </parameter> </method> +<method name="finishPreferencePanel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="caller" type="android.app.Fragment"> +</parameter> +<parameter name="resultCode" type="int"> +</parameter> +<parameter name="resultData" type="android.content.Intent"> +</parameter> +</method> <method name="getPreferenceManager" return="android.preference.PreferenceManager" abstract="false" @@ -145276,7 +145383,7 @@ <parameter name="push" type="boolean"> </parameter> </method> -<method name="startPreferenceFragment" +<method name="startPreferencePanel" return="void" abstract="false" native="false" @@ -145286,9 +145393,17 @@ deprecated="not deprecated" visibility="public" > -<parameter name="fragment" type="android.app.Fragment"> +<parameter name="fragmentClass" type="java.lang.String"> </parameter> -<parameter name="ft" type="android.app.FragmentTransaction"> +<parameter name="args" type="android.os.Bundle"> +</parameter> +<parameter name="titleRes" type="int"> +</parameter> +<parameter name="titleText" type="java.lang.CharSequence"> +</parameter> +<parameter name="resultTo" type="android.app.Fragment"> +</parameter> +<parameter name="resultRequestCode" type="int"> </parameter> </method> <method name="startWithFragment" @@ -145305,6 +145420,10 @@ </parameter> <parameter name="args" type="android.os.Bundle"> </parameter> +<parameter name="resultTo" type="android.app.Fragment"> +</parameter> +<parameter name="resultRequestCode" type="int"> +</parameter> </method> <method name="switchToHeader" return="void" @@ -245494,7 +245613,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="t" type="T"> +<parameter name="arg0" type="T"> </parameter> </method> </interface> @@ -347567,7 +347686,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="toCopyIn" type="E[]"> +<parameter name="array" type="E[]"> </parameter> </constructor> <method name="add" @@ -347595,7 +347714,7 @@ > <parameter name="index" type="int"> </parameter> -<parameter name="element" type="E"> +<parameter name="e" type="E"> </parameter> </method> <method name="addAll" @@ -347723,7 +347842,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="o" type="java.lang.Object"> +<parameter name="e" type="E"> +</parameter> +<parameter name="index" type="int"> </parameter> </method> <method name="indexOf" @@ -347736,9 +347857,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="e" type="E"> -</parameter> -<parameter name="index" type="int"> +<parameter name="o" type="java.lang.Object"> </parameter> </method> <method name="isEmpty" @@ -347773,7 +347892,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="o" type="java.lang.Object"> +<parameter name="e" type="E"> +</parameter> +<parameter name="index" type="int"> </parameter> </method> <method name="lastIndexOf" @@ -347786,9 +347907,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="e" type="E"> -</parameter> -<parameter name="index" type="int"> +<parameter name="o" type="java.lang.Object"> </parameter> </method> <method name="listIterator" @@ -347879,7 +347998,7 @@ > <parameter name="index" type="int"> </parameter> -<parameter name="element" type="E"> +<parameter name="e" type="E"> </parameter> </method> <method name="size" diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 37c8ad0..b5fddfa 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -389,11 +389,13 @@ public final class Bmgr { if (err == 0) { observer.waitForCompletion(); sets = observer.sets; - for (RestoreSet s : sets) { - if (s.token == token) { - System.out.println("Scheduling restore: " + s.name); - didRestore = (mRestore.restoreAll(token, observer) == 0); - break; + if (sets != null) { + for (RestoreSet s : sets) { + if (s.token == token) { + System.out.println("Scheduling restore: " + s.name); + didRestore = (mRestore.restoreAll(token, observer) == 0); + break; + } } } } diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java index 1741e60..2242462 100644 --- a/core/java/android/animation/KeyframeSet.java +++ b/core/java/android/animation/KeyframeSet.java @@ -28,12 +28,18 @@ class KeyframeSet { private int mNumKeyframes; - ArrayList<Keyframe> mKeyframes; + Keyframe mFirstKeyframe; + Keyframe mLastKeyframe; + TimeInterpolator mInterpolator; // only used in the 2-keyframe case + ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes public KeyframeSet(Keyframe... keyframes) { + mNumKeyframes = keyframes.length; mKeyframes = new ArrayList<Keyframe>(); mKeyframes.addAll(Arrays.asList(keyframes)); - mNumKeyframes = mKeyframes.size(); + mFirstKeyframe = mKeyframes.get(0); + mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); + mInterpolator = mLastKeyframe.getInterpolator(); } public static KeyframeSet ofInt(int... values) { @@ -125,32 +131,40 @@ class KeyframeSet { * @return The animated value. */ public Object getValue(float fraction, TypeEvaluator evaluator) { - // TODO: special-case 2-keyframe common case + + // Special-case optimization for the common case of only two keyframes + if (mNumKeyframes == 2) { + if (mInterpolator != null) { + fraction = mInterpolator.getInterpolation(fraction); + } + return evaluator.evaluate(fraction, mFirstKeyframe.getValue(), + mLastKeyframe.getValue()); + } if (fraction <= 0f) { - final Keyframe prevKeyframe = mKeyframes.get(0); final Keyframe nextKeyframe = mKeyframes.get(1); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } - float intervalFraction = (fraction - prevKeyframe.getFraction()) / - (nextKeyframe.getFraction() - prevKeyframe.getFraction()); - return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), + final float prevFraction = mFirstKeyframe.getFraction(); + float intervalFraction = (fraction - prevFraction) / + (nextKeyframe.getFraction() - prevFraction); + return evaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(), nextKeyframe.getValue()); } else if (fraction >= 1f) { final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2); - final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1); - final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); + final TimeInterpolator interpolator = mLastKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } - float intervalFraction = (fraction - prevKeyframe.getFraction()) / - (nextKeyframe.getFraction() - prevKeyframe.getFraction()); + final float prevFraction = prevKeyframe.getFraction(); + float intervalFraction = (fraction - prevFraction) / + (mLastKeyframe.getFraction() - prevFraction); return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), - nextKeyframe.getValue()); + mLastKeyframe.getValue()); } - Keyframe prevKeyframe = mKeyframes.get(0); + Keyframe prevKeyframe = mFirstKeyframe; for (int i = 1; i < mNumKeyframes; ++i) { Keyframe nextKeyframe = mKeyframes.get(i); if (fraction < nextKeyframe.getFraction()) { @@ -158,14 +172,15 @@ class KeyframeSet { if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } - float intervalFraction = (fraction - prevKeyframe.getFraction()) / - (nextKeyframe.getFraction() - prevKeyframe.getFraction()); + final float prevFraction = prevKeyframe.getFraction(); + float intervalFraction = (fraction - prevFraction) / + (nextKeyframe.getFraction() - prevFraction); return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), nextKeyframe.getValue()); } prevKeyframe = nextKeyframe; } - // shouldn't get here - return mKeyframes.get(mNumKeyframes - 1).getValue(); + // shouldn't reach here + return mLastKeyframe.getValue(); } } diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 7427651..5b8c91d 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -376,9 +376,14 @@ public final class ObjectAnimator extends ValueAnimator { */ @Override public void setTarget(Object target) { - mTarget = target; - // New property/values/target should cause re-initialization prior to starting - mInitialized = false; + if (mTarget != target) { + mTarget = target; + if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) { + return; + } + // New target type should cause re-initialization prior to starting + mInitialized = false; + } } @Override diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index ad8c971..a0b70b5 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -1142,7 +1142,7 @@ public class ValueAnimator extends Animator { switch (mPlayingState) { case RUNNING: case SEEKED: - float fraction = (float)(currentTime - mStartTime) / mDuration; + float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (fraction >= 1f) { if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 66038d8..b359ce6 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -299,7 +299,7 @@ public abstract class ActionBar { * * <p>Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the * {@link #DISPLAY_SHOW_HOME} option. - * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_HIDE_HOME | DISPLAY_USE_LOGO) + * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO) * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}. * * @param options A combination of the bits defined by the DISPLAY_ constants diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 45ce860..b9ac848 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -27,6 +27,14 @@ import android.content.res.Configuration; * AndroidManifest.xml's <application> tag, which will cause that class * to be instantiated for you when the process for your application/package is * created. + * + * <p class="note">There is normally no need to subclass Application. In + * most situation, static singletons can provide the same functionality in a + * more modular way. If your singleton needs a global context (for example + * to register broadcast receivers), the function to retrieve it can be + * given a {@link android.content.Context} which internally uses + * {@link android.content.Context#getApplicationContext() Context.getApplicationContext()} + * when first constructing the singleton.</p> */ public class Application extends ContextWrapper implements ComponentCallbacks { @@ -46,12 +54,10 @@ public class Application extends ContextWrapper implements ComponentCallbacks { } /** - * Called when the application is stopping. There are no more application - * objects running and the process will exit. <em>Note: never depend on - * this method being called; in many cases an unneeded application process - * will simply be killed by the kernel without executing any application - * code.</em> - * If you override this method, be sure to call super.onTerminate(). + * This method is for use in emulated process environments. It will + * never be called on a production Android device, where processes are + * removed by simply killing them; no user code (including this callback) + * is executed when doing so. */ public void onTerminate() { } diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 570e494..592a63b 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -272,6 +272,14 @@ public class DownloadManager { */ public static final String EXTRA_DOWNLOAD_ID = "extra_download_id"; + /** + * When clicks on multiple notifications are received, the following + * provides an array of download ids corresponding to the download notification that was + * clicked. It can be retrieved by the receiver of this + * Intent using {@link android.content.Intent#getLongArrayExtra(String)}. + */ + public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids"; + // this array must contain all public columns private static final String[] COLUMNS = new String[] { COLUMN_ID, diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index f27a15e..eaf1aee 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1027,6 +1027,16 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener } /** + * Called when this fragment's option menu items are no longer being + * included in the overall options menu. Receiving this call means that + * the menu needed to be rebuilt, but this fragment's items were not + * included in the newly built menu (its {@link #onCreateOptionsMenu(Menu, MenuInflater)} + * was not called). + */ + public void onDestroyOptionsMenu() { + } + + /** * This hook is called whenever an item in your options menu is selected. * The default implementation simply returns false to have the normal * processing happen (calling the item's Runnable or sending a message to diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java index 22e0747..e924c1c 100644 --- a/core/java/android/app/FragmentBreadCrumbs.java +++ b/core/java/android/app/FragmentBreadCrumbs.java @@ -16,6 +16,7 @@ package android.app; +import android.animation.LayoutTransition; import android.app.FragmentManager.BackStackEntry; import android.content.Context; import android.util.AttributeSet; @@ -69,6 +70,7 @@ public class FragmentBreadCrumbs extends ViewGroup addView(mContainer); a.getFragmentManager().addOnBackStackChangedListener(this); updateCrumbs(); + setLayoutTransition(new LayoutTransition()); } /** diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 45f9325..512ca16 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -268,6 +268,7 @@ final class FragmentManagerImpl implements FragmentManager { ArrayList<Fragment> mAdded; ArrayList<Integer> mAvailIndices; ArrayList<BackStackRecord> mBackStack; + ArrayList<Fragment> mCreatedMenus; // Must be accessed while locked. ArrayList<BackStackRecord> mBackStackIndices; @@ -1325,15 +1326,32 @@ final class FragmentManagerImpl implements FragmentManager { public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { boolean show = false; + ArrayList<Fragment> newMenus = null; if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); if (f != null && !f.mHidden && f.mHasMenu) { show = true; f.onCreateOptionsMenu(menu, inflater); + if (newMenus == null) { + newMenus = new ArrayList<Fragment>(); + } + newMenus.add(f); } } } + + if (mCreatedMenus != null) { + for (int i=0; i<mCreatedMenus.size(); i++) { + Fragment f = mCreatedMenus.get(i); + if (newMenus == null || !newMenus.contains(f)) { + f.onDestroyOptionsMenu(); + } + } + } + + mCreatedMenus = newMenus; + return show; } diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index 0c8e4d9..138e7f2 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -773,6 +773,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: transitionTo(mIncomingHandsfree); + break; case CONNECT_A2DP_INCOMING: transitionTo(mIncomingA2dp); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 8c36aa6..e14282c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -533,6 +533,8 @@ import java.util.Set; * <li> {@link #CATEGORY_TEST} * <li> {@link #CATEGORY_CAR_DOCK} * <li> {@link #CATEGORY_DESK_DOCK} + * <li> {@link #CATEGORY_LE_DESK_DOCK} + * <li> {@link #CATEGORY_HE_DESK_DOCK} * <li> {@link #CATEGORY_CAR_MODE} * <li> {@link #CATEGORY_APP_MARKET} * </ul> @@ -549,6 +551,8 @@ import java.util.Set; * <li> {@link #EXTRA_CHANGED_COMPONENT_NAME} * <li> {@link #EXTRA_DATA_REMOVED} * <li> {@link #EXTRA_DOCK_STATE} + * <li> {@link #EXTRA_DOCK_STATE_HE_DESK} + * <li> {@link #EXTRA_DOCK_STATE_LE_DESK} * <li> {@link #EXTRA_DOCK_STATE_CAR} * <li> {@link #EXTRA_DOCK_STATE_DESK} * <li> {@link #EXTRA_DOCK_STATE_UNDOCKED} @@ -1822,6 +1826,36 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.HEADSET_PLUG"; /** + * Broadcast Action: An analog audio speaker/headset plugged in or unplugged. + * + * <p>The intent will have the following extra values: + * <ul> + * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> + * <li><em>name</em> - Headset type, human readable string </li> + * </ul> + * </ul> + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_ANLG_HEADSET_PLUG = + "android.intent.action.DOCK_HEADSET_PLUG"; + + /** + * Broadcast Action: An analog audio speaker/headset plugged in or unplugged. + * + * <p>The intent will have the following extra values: + * <ul> + * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> + * <li><em>name</em> - Headset type, human readable string </li> + * </ul> + * </ul> + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USB_DGTL_HEADSET_PLUG = + "android.intent.action.DOCK_HEADSET_PLUG"; + + /** * Broadcast Action: An outgoing call is about to be placed. * * <p>The Intent will have the following extra value: @@ -2060,6 +2094,21 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK"; + /** + * An activity to run when device is inserted into a analog (low end) dock. + * Used with {@link #ACTION_MAIN} to launch an activity. For more + * information, see {@link android.app.UiModeManager}. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK"; + + /** + * An activity to run when device is inserted into a digital (high end) dock. + * Used with {@link #ACTION_MAIN} to launch an activity. For more + * information, see {@link android.app.UiModeManager}. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK"; /** * Used to indicate that the activity can be used in a car environment. @@ -2208,7 +2257,9 @@ public class Intent implements Parcelable, Cloneable { * intents to request the dock state. Possible values are * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED}, * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or - * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}. + * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}, or + * {@link android.content.Intent#EXTRA_DOCK_STATE_LE_DESK}, or + * {@link android.content.Intent#EXTRA_DOCK_STATE_HE_DESK}. */ public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE"; @@ -2231,6 +2282,18 @@ public class Intent implements Parcelable, Cloneable { public static final int EXTRA_DOCK_STATE_CAR = 2; /** + * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE} + * to represent that the phone is in a analog (low end) dock. + */ + public static final int EXTRA_DOCK_STATE_LE_DESK = 3; + + /** + * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE} + * to represent that the phone is in a digital (high end) dock. + */ + public static final int EXTRA_DOCK_STATE_HE_DESK = 4; + + /** * Boolean that can be supplied as meta-data with a dock activity, to * indicate that the dock should take over the home key when it is active. */ diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 0c6a237..97c957d 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -825,7 +825,7 @@ public abstract class PreferenceActivity extends ListActivity implements /** * Called when the user selects an item in the header list. The default - * implementation will call either {@link #startWithFragment(String, Bundle)} + * implementation will call either {@link #startWithFragment(String, Bundle, Fragment, int)} * or {@link #switchToHeader(Header)} as appropriate. * * @param header The header that was selected. @@ -834,7 +834,7 @@ public abstract class PreferenceActivity extends ListActivity implements public void onHeaderClick(Header header, int position) { if (header.fragment != null) { if (mSinglePane) { - startWithFragment(header.fragment, header.fragmentArguments); + startWithFragment(header.fragment, header.fragmentArguments, null, 0); } else { switchToHeader(header); } @@ -852,13 +852,18 @@ public abstract class PreferenceActivity extends ListActivity implements * @param fragmentName The name of the fragment to display. * @param args Optional arguments to supply to the fragment. */ - public void startWithFragment(String fragmentName, Bundle args) { + public void startWithFragment(String fragmentName, Bundle args, + Fragment resultTo, int resultRequestCode) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClass(this, getClass()); intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName); intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); intent.putExtra(EXTRA_NO_HEADERS, true); - startActivity(intent); + if (resultTo == null) { + startActivity(intent); + } else { + resultTo.startActivityForResult(intent, resultRequestCode); + } } /** @@ -923,9 +928,15 @@ public abstract class PreferenceActivity extends ListActivity implements * @param header The new header to display. */ public void switchToHeader(Header header) { - int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader); - switchToHeaderInner(header.fragment, header.fragmentArguments, direction); - setSelectedHeader(header); + if (mCurHeader == header) { + // This is the header we are currently displaying. Just make sure + // to pop the stack up to its root state. + getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); + } else { + int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader); + switchToHeaderInner(header.fragment, header.fragmentArguments, direction); + setSelectedHeader(header); + } } Header findBestMatchingHeader(Header cur, ArrayList<Header> from) { @@ -982,7 +993,7 @@ public abstract class PreferenceActivity extends ListActivity implements */ public void startPreferenceFragment(Fragment fragment, boolean push) { FragmentTransaction transaction = getFragmentManager().openTransaction(); - startPreferenceFragment(fragment, transaction); + transaction.replace(com.android.internal.R.id.prefs, fragment); if (push) { transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.addToBackStack(BACK_STACK_PREFS); @@ -993,25 +1004,74 @@ public abstract class PreferenceActivity extends ListActivity implements } /** - * Start a new fragment. - * - * @param fragment The fragment to start - * @param ft The FragmentTransaction in which to perform this operation. - * Will not be added to the back stack or committed for you; you use do that. + * Start a new fragment containing a preference panel. If the prefences + * are being displayed in multi-pane mode, the given fragment class will + * be instantiated and placed in the appropriate pane. If running in + * single-pane mode, a new activity will be launched in which to show the + * fragment. + * + * @param fragmentClass Full name of the class implementing the fragment. + * @param args Any desired arguments to supply to the fragment. + * @param titleRes Optional resource identifier of the title of this + * fragment. + * @param titleText Optional text of the title of this fragment. + * @param resultTo Optional fragment that result data should be sent to. + * If non-null, resultTo.onActivityResult() will be called when this + * preference panel is done. The launched panel must use + * {@link #finishPreferencePanel(Fragment, int, Intent)} when done. + * @param resultRequestCode If resultTo is non-null, this is the caller's + * request code to be received with the resut. */ - public void startPreferenceFragment(Fragment fragment, FragmentTransaction ft) { - ft.replace(com.android.internal.R.id.prefs, fragment); + public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes, + CharSequence titleText, Fragment resultTo, int resultRequestCode) { + if (mSinglePane) { + startWithFragment(fragmentClass, args, resultTo, resultRequestCode); + } else { + Fragment f = Fragment.instantiate(this, fragmentClass, args); + if (resultTo != null) { + f.setTargetFragment(resultTo, resultRequestCode); + } + FragmentTransaction transaction = getFragmentManager().openTransaction(); + transaction.replace(com.android.internal.R.id.prefs, f); + if (titleRes != 0) { + transaction.setBreadCrumbTitle(titleRes); + } else if (titleText != null) { + transaction.setBreadCrumbTitle(titleText); + } + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + transaction.addToBackStack(BACK_STACK_PREFS); + transaction.commit(); + } } - + + /** + * Called by a preference panel fragment to finish itself. + * + * @param caller The fragment that is asking to be finished. + * @param resultCode Optional result code to send back to the original + * launching fragment. + * @param resultData Optional result data to send back to the original + * launching fragment. + */ + public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) { + if (mSinglePane) { + setResult(resultCode, resultData); + finish(); + } else { + if (caller != null) { + if (caller.getTargetFragment() != null) { + caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(), + resultCode, resultData); + } + } + // XXX be smarter about popping the stack. + onBackPressed(); + } + } + @Override public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { - Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras()); - FragmentTransaction transaction = getFragmentManager().openTransaction(); - startPreferenceFragment(f, transaction); - transaction.setBreadCrumbTitle(pref.getTitle()); - transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); - transaction.addToBackStack(BACK_STACK_PREFS); - transaction.commit(); + startPreferencePanel(pref.getFragment(), pref.getExtras(), 0, pref.getTitle(), null, 0); return true; } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 7289012..ec8f031 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -385,11 +385,23 @@ public class BluetoothService extends IBluetooth.Stub { // Allow 3 seconds for profiles to gracefully disconnect // TODO: Introduce a callback mechanism so that each profile can notify // BluetoothService when it is done shutting down + disconnectDevices(); + mHandler.sendMessageDelayed( mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000); return true; } + private synchronized void disconnectDevices() { + // Disconnect devices handled by BluetoothService. + for (BluetoothDevice device: getConnectedInputDevices()) { + disconnectInputDevice(device); + } + + for (BluetoothDevice device: getConnectedPanDevices()) { + disconnectPanDevice(device); + } + } private synchronized void finishDisable(boolean saveSetting) { if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 87e03cf..a9f0780 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6296,8 +6296,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (invalidateCache) { mPrivateFlags &= ~DRAWING_CACHE_VALID; } - final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; + final ViewParent p = mParent; + if (p != null && ai != null && ai.mHardwareAccelerated) { + // fast-track for GL-enabled applications; just invalidate the whole hierarchy + // with a null dirty rect, which tells the ViewRoot to redraw everything + p.invalidateChild(this, null); + return; + } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); @@ -6321,7 +6327,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ @ViewDebug.ExportedProperty(category = "drawing") public boolean isOpaque() { - return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK; + return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK && + (mAlpha >= 1.0f - ViewConfiguration.ALPHA_THRESHOLD); } private void computeOpaqueFlags() { @@ -8618,7 +8625,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ @RemotableViewMethod public void setBackgroundColor(int color) { - setBackgroundDrawable(new ColorDrawable(color)); + if (mBGDrawable instanceof ColorDrawable) { + ((ColorDrawable) mBGDrawable).setColor(color); + } else { + setBackgroundDrawable(new ColorDrawable(color)); + } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index b881bdd..aef13ad 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2101,6 +2101,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int cr = child.mRight; final int cb = child.mBottom; + final boolean childHasIdentityMatrix = child.hasIdentityMatrix(); + final int flags = mGroupFlags; if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) { @@ -2182,7 +2184,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - concatMatrix |= !child.hasIdentityMatrix(); + concatMatrix |= !childHasIdentityMatrix; // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations @@ -2231,40 +2233,41 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) { - int transX = 0; - int transY = 0; - - if (hasNoCache) { - transX = -sx; - transY = -sy; - } + if (transformToApply != null || !childHasIdentityMatrix) { + int transX = 0; + int transY = 0; - if (transformToApply != null) { - if (concatMatrix) { - // Undo the scroll translation, apply the transformation matrix, - // then redo the scroll translate to get the correct result. - canvas.translate(-transX, -transY); - canvas.concat(transformToApply.getMatrix()); - canvas.translate(transX, transY); - mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; + if (hasNoCache) { + transX = -sx; + transY = -sy; } - float transformAlpha = transformToApply.getAlpha(); - if (transformAlpha < 1.0f) { - alpha *= transformToApply.getAlpha(); - mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; + if (transformToApply != null) { + if (concatMatrix) { + // Undo the scroll translation, apply the transformation matrix, + // then redo the scroll translate to get the correct result. + canvas.translate(-transX, -transY); + canvas.concat(transformToApply.getMatrix()); + canvas.translate(transX, transY); + mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; + } + + float transformAlpha = transformToApply.getAlpha(); + if (transformAlpha < 1.0f) { + alpha *= transformToApply.getAlpha(); + mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; + } } - } - if (!child.hasIdentityMatrix()) { - canvas.translate(-transX, -transY); - canvas.concat(child.getMatrix()); - canvas.translate(transX, transY); + if (!childHasIdentityMatrix) { + canvas.translate(-transX, -transY); + canvas.concat(child.getMatrix()); + canvas.translate(transX, transY); + } } if (alpha < 1.0f) { mGroupFlags |= FLAG_CLEAR_TRANSFORMATION; - if (hasNoCache) { final int multipliedAlpha = (int) (255 * alpha); if (!child.onSetAlpha(multipliedAlpha)) { @@ -3209,19 +3212,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - final int[] location = attachInfo.mInvalidateChildLocation; - location[CHILD_LEFT_INDEX] = child.mLeft; - location[CHILD_TOP_INDEX] = child.mTop; - Matrix childMatrix = child.getMatrix(); - if (!childMatrix.isIdentity()) { - RectF boundingRect = attachInfo.mTmpTransformRect; - boundingRect.set(dirty); - childMatrix.mapRect(boundingRect); - dirty.set((int) boundingRect.left, (int) boundingRect.top, - (int) (boundingRect.right + 0.5f), - (int) (boundingRect.bottom + 0.5f)); - } - // If the child is drawing an animation, we want to copy this flag onto // ourselves and the parent to make sure the invalidate request goes // through @@ -3229,45 +3219,95 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Check whether the child that requests the invalidate is fully opaque final boolean isOpaque = child.isOpaque() && !drawAnimation && - child.getAnimation() != null; + child.getAnimation() == null; // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; - do { - View view = null; - if (parent instanceof View) { - view = (View) parent; - } + if (dirty == null) { + do { + View view = null; + if (parent instanceof View) { + view = (View) parent; + if ((view.mPrivateFlags & DIRTY_MASK) != 0) { + // already marked dirty - we're done + break; + } + } - if (drawAnimation) { - if (view != null) { - view.mPrivateFlags |= DRAW_ANIMATION; - } else if (parent instanceof ViewRoot) { - ((ViewRoot) parent).mIsAnimating = true; + if (drawAnimation) { + if (view != null) { + view.mPrivateFlags |= DRAW_ANIMATION; + } else if (parent instanceof ViewRoot) { + ((ViewRoot) parent).mIsAnimating = true; + } } - } - // If the parent is dirty opaque or not dirty, mark it dirty with the opaque - // flag coming from the child that initiated the invalidate - if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { - view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; + if (parent instanceof ViewRoot) { + ((ViewRoot) parent).invalidate(); + parent = null; + } else if (view != null) { + if ((mPrivateFlags & DRAWN) == DRAWN) { + view.mPrivateFlags &= ~DRAWING_CACHE_VALID; + if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { + view.mPrivateFlags = + (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; + } + parent = view.mParent; + } else { + parent = null; + } + } + } while (parent != null); + } else { + final int[] location = attachInfo.mInvalidateChildLocation; + location[CHILD_LEFT_INDEX] = child.mLeft; + location[CHILD_TOP_INDEX] = child.mTop; + Matrix childMatrix = child.getMatrix(); + if (!childMatrix.isIdentity()) { + RectF boundingRect = attachInfo.mTmpTransformRect; + boundingRect.set(dirty); + childMatrix.mapRect(boundingRect); + dirty.set((int) boundingRect.left, (int) boundingRect.top, + (int) (boundingRect.right + 0.5f), + (int) (boundingRect.bottom + 0.5f)); } - parent = parent.invalidateChildInParent(location, dirty); - if (view != null) { - // Account for transform on current parent - Matrix m = view.getMatrix(); - if (!m.isIdentity()) { - RectF boundingRect = attachInfo.mTmpTransformRect; - boundingRect.set(dirty); - m.mapRect(boundingRect); - dirty.set((int) boundingRect.left, (int) boundingRect.top, - (int) (boundingRect.right + 0.5f), - (int) (boundingRect.bottom + 0.5f)); + do { + View view = null; + if (parent instanceof View) { + view = (View) parent; } - } - } while (parent != null); + + if (drawAnimation) { + if (view != null) { + view.mPrivateFlags |= DRAW_ANIMATION; + } else if (parent instanceof ViewRoot) { + ((ViewRoot) parent).mIsAnimating = true; + } + } + + // If the parent is dirty opaque or not dirty, mark it dirty with the opaque + // flag coming from the child that initiated the invalidate + if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { + view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; + } + + parent = parent.invalidateChildInParent(location, dirty); + if (view != null) { + // Account for transform on current parent + Matrix m = view.getMatrix(); + if (!m.isIdentity()) { + RectF boundingRect = attachInfo.mTmpTransformRect; + boundingRect.set(dirty); + m.mapRect(boundingRect); + dirty.set((int) boundingRect.left, (int) boundingRect.top, + (int) (boundingRect.right + 0.5f), + (int) (boundingRect.bottom + 0.5f)); + } + } + } while (parent != null); + } } } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 06261bb..22a7773 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -543,6 +543,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn public void invalidateChild(View child, Rect dirty) { checkThread(); if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); + if (dirty == null) { + // Fast invalidation for GL-enabled applications; GL must redraw everything + invalidate(); + return; + } if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); dirty = mTempRect; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e5f4b08..c657a1c 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -794,7 +794,17 @@ public interface WindowManager extends ViewManager { public int softInputMode; /** - * Placement of window within the screen as per {@link Gravity} + * Placement of window within the screen as per {@link Gravity}. Both + * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int, + * android.graphics.Rect) Gravity.apply} and + * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect) + * Gravity.applyDisplay} are used during window layout, with this value + * given as the desired gravity. For example you can specify + * {@link Gravity#DISPLAY_CLIP_HORIZONTAL Gravity.DISPLAY_CLIP_HORIZONTAL} and + * {@link Gravity#DISPLAY_CLIP_VERTICAL Gravity.DISPLAY_CLIP_VERTICAL} here + * to control the behavior of + * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect) + * Gravity.applyDisplay}. * * @see Gravity */ @@ -802,13 +812,19 @@ public interface WindowManager extends ViewManager { /** * The horizontal margin, as a percentage of the container's width, - * between the container and the widget. + * between the container and the widget. See + * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int, + * android.graphics.Rect) Gravity.apply} for how this is used. This + * field is added with {@link #x} to supply the <var>xAdj</var> parameter. */ public float horizontalMargin; /** * The vertical margin, as a percentage of the container's height, - * between the container and the widget. + * between the container and the widget. See + * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int, + * android.graphics.Rect) Gravity.apply} for how this is used. This + * field is added with {@link #y} to supply the <var>yAdj</var> parameter. */ public float verticalMargin; @@ -1168,14 +1184,22 @@ public interface WindowManager extends ViewManager { sb.append('x'); sb.append((height== MATCH_PARENT ?"fill":(height==WRAP_CONTENT?"wrap":height))); sb.append(")"); - if (softInputMode != 0) { - sb.append(" sim=#"); - sb.append(Integer.toHexString(softInputMode)); + if (horizontalMargin != 0) { + sb.append(" hm="); + sb.append(horizontalMargin); + } + if (verticalMargin != 0) { + sb.append(" vm="); + sb.append(verticalMargin); } if (gravity != 0) { sb.append(" gr=#"); sb.append(Integer.toHexString(gravity)); } + if (softInputMode != 0) { + sb.append(" sim=#"); + sb.append(Integer.toHexString(softInputMode)); + } sb.append(" ty="); sb.append(type); sb.append(" fl=#"); @@ -1190,6 +1214,18 @@ public interface WindowManager extends ViewManager { sb.append(" or="); sb.append(screenOrientation); } + if (alpha != 1.0f) { + sb.append(" alpha="); + sb.append(alpha); + } + if (screenBrightness != BRIGHTNESS_OVERRIDE_NONE) { + sb.append(" sbrt="); + sb.append(screenBrightness); + } + if (buttonBrightness != BRIGHTNESS_OVERRIDE_NONE) { + sb.append(" bbrt="); + sb.append(buttonBrightness); + } if ((flags & FLAG_COMPATIBLE_WINDOW) != 0) { sb.append(" compatible=true"); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 1fd31a3..1a341e1 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -544,16 +544,18 @@ public interface WindowManagerPolicy { * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. * @param whenNanos The event time in uptime nanoseconds. + * @param action The key event action. + * @param flags The key event flags. * @param keyCode The key code. - * @param down True if the key is down. + * @param scanCode The key's scan code. * @param policyFlags The policy flags associated with the key. * @param isScreenOn True if the screen is already on * * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags. */ - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags, - boolean isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn); /** * Called from the input dispatcher thread before a key is dispatched to a window. @@ -571,6 +573,7 @@ public interface WindowManagerPolicy { * @param action The key event action. * @param flags The key event flags. * @param keyCode The key code. + * @param scanCode The key's scan code. * @param metaState bit mask of meta keys that are held. * @param repeatCount Number of times a key down has repeated. * @param policyFlags The policy flags associated with the key. @@ -578,7 +581,7 @@ public interface WindowManagerPolicy { * not be further dispatched. */ public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int metaState, int repeatCount, int policyFlags); + int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags); /** * Called when layout of the windows is about to start. diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 3bcd483..14dc61d 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -370,6 +370,7 @@ import java.util.ArrayList; int candEnd = EditableInputConnection.getComposingSpanEnd(sp); imm.updateSelection(this, selStart, selEnd, candStart, candEnd); } + updateCursorControllerPositions(); } @Override diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 72b4e36..f5affe5 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3477,7 +3477,9 @@ public class WebView extends AbsoluteLayout if (AUTO_REDRAW_HACK && mAutoRedraw) { invalidate(); } - if (inEditingMode()) mWebTextView.onDrawSubstitute(); + if (inEditingMode()) { + mWebTextView.onDrawSubstitute(); + } mWebViewCore.signalRepaintDone(); // paint the highlight in the end diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 9a873b6..9edb267 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -472,6 +472,11 @@ final class WebViewCore { */ private native int nativeRecordContent(Region invalRegion, Point wh); + /** + * Update the layers' content + */ + private native int nativeUpdateLayers(); + private native boolean nativeFocusBoundsChanged(); /** @@ -798,6 +803,7 @@ final class WebViewCore { "FREE_MEMORY", // = 145 "VALID_NODE_BOUNDS", // = 146 "SAVE_WEBARCHIVE", // = 147 + "WEBKIT_DRAW_LAYERS", // = 148; }; class EventHub { @@ -868,6 +874,9 @@ final class WebViewCore { // Load and save web archives static final int SAVE_WEBARCHIVE = 147; + // Update layers + static final int WEBKIT_DRAW_LAYERS = 148; + // Network-based messaging static final int CLEAR_SSL_PREF_TABLE = 150; @@ -953,6 +962,10 @@ final class WebViewCore { webkitDraw(); break; + case WEBKIT_DRAW_LAYERS: + webkitDrawLayers(); + break; + case DESTROY: // Time to take down the world. Cancel all pending // loads and destroy the native view and frame. @@ -1800,6 +1813,7 @@ final class WebViewCore { // Used to avoid posting more than one draw message. private boolean mDrawIsScheduled; + private boolean mDrawLayersIsScheduled; // Used to avoid posting more than one split picture message. private boolean mSplitPictureIsScheduled; @@ -1839,6 +1853,20 @@ final class WebViewCore { boolean mFocusSizeChanged; } + // Only update the layers' content, not the base surface + // PictureSet. + private void webkitDrawLayers() { + mDrawLayersIsScheduled = false; + if (mDrawIsScheduled) { + removeMessages(EventHub.WEBKIT_DRAW); + webkitDraw(); + return; + } + DrawData draw = new DrawData(); + draw.mBaseLayer = nativeUpdateLayers(); + webkitDraw(draw); + } + private void webkitDraw() { mDrawIsScheduled = false; DrawData draw = new DrawData(); @@ -1848,6 +1876,10 @@ final class WebViewCore { if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); return; } + webkitDraw(draw); + } + + private void webkitDraw(DrawData draw) { if (mWebView != null) { draw.mFocusSizeChanged = nativeFocusBoundsChanged(); draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight); @@ -1957,6 +1989,15 @@ final class WebViewCore { } } + // called from JNI + void layersDraw() { + synchronized (this) { + if (mDrawLayersIsScheduled) return; + mDrawLayersIsScheduled = true; + mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS)); + } + } + // called by JNI private void contentScrollBy(int dx, int dy, boolean animate) { if (!mBrowserFrame.firstLayoutDone()) { diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 39b1377..be1234d 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -413,33 +413,46 @@ public class MediaController extends FrameLayout { @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); - if (event.getRepeatCount() == 0 && event.isDown() && ( - keyCode == KeyEvent.KEYCODE_HEADSETHOOK || - keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || - keyCode == KeyEvent.KEYCODE_SPACE)) { - doPauseResume(); - show(sDefaultTimeout); - if (mPauseButton != null) { - mPauseButton.requestFocus(); + final boolean uniqueDown = event.getRepeatCount() == 0 + && event.getAction() == KeyEvent.ACTION_DOWN; + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE + || keyCode == KeyEvent.KEYCODE_SPACE) { + if (uniqueDown) { + doPauseResume(); + show(sDefaultTimeout); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + } + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (uniqueDown && !mPlayer.isPlaying()) { + mPlayer.start(); + updatePausePlay(); + show(sDefaultTimeout); } return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { - if (mPlayer.isPlaying()) { + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (uniqueDown && mPlayer.isPlaying()) { mPlayer.pause(); updatePausePlay(); + show(sDefaultTimeout); } return true; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || - keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // don't show the controls for volume adjustment return super.dispatchKeyEvent(event); } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) { - hide(); - + if (uniqueDown) { + hide(); + } return true; - } else { - show(sDefaultTimeout); } + + show(sDefaultTimeout); return super.dispatchKeyEvent(event); } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 35c50fd..6ba7b44 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -132,6 +132,25 @@ public class RemoteViews implements Parcelable, Filter { // here return; } + + protected boolean startIntentSafely(Context context, PendingIntent pendingIntent, + Intent fillInIntent) { + try { + // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? + context.startIntentSender( + pendingIntent.getIntentSender(), fillInIntent, + Intent.FLAG_ACTIVITY_NEW_TASK, + Intent.FLAG_ACTIVITY_NEW_TASK, 0); + } catch (IntentSender.SendIntentException e) { + android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); + return false; + } catch (Exception e) { + android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " + + "unknown exception: ", e); + return false; + } + return true; + } } private class SetEmptyView extends Action { @@ -236,15 +255,7 @@ public class RemoteViews implements Parcelable, Filter { rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); fillInIntent.setSourceBounds(rect); - try { - // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? - v.getContext().startIntentSender( - pendingIntent.getIntentSender(), fillInIntent, - Intent.FLAG_ACTIVITY_NEW_TASK, - Intent.FLAG_ACTIVITY_NEW_TASK, 0); - } catch (IntentSender.SendIntentException e) { - android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); - } + startIntentSafely(v.getContext(), pendingIntent, fillInIntent); } }; @@ -326,16 +337,7 @@ public class RemoteViews implements Parcelable, Filter { final Intent intent = new Intent(); intent.setSourceBounds(rect); intent.putExtras(extras); - - try { - // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? - v.getContext().startIntentSender( - pendingIntent.getIntentSender(), intent, - Intent.FLAG_ACTIVITY_NEW_TASK, - Intent.FLAG_ACTIVITY_NEW_TASK, 0); - } catch (IntentSender.SendIntentException e) { - android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); - } + startIntentSafely(v.getContext(), pendingIntent, intent); } }; @@ -441,15 +443,7 @@ public class RemoteViews implements Parcelable, Filter { final Intent intent = new Intent(); intent.setSourceBounds(rect); - try { - // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? - v.getContext().startIntentSender( - pendingIntent.getIntentSender(), intent, - Intent.FLAG_ACTIVITY_NEW_TASK, - Intent.FLAG_ACTIVITY_NEW_TASK, 0); - } catch (IntentSender.SendIntentException e) { - android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); - } + startIntentSafely(v.getContext(), pendingIntent, intent); } }; target.setOnClickListener(listener); diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 36adacd..ce4e8e5 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -115,7 +115,6 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { } private void initTabWidget() { - setOrientation(LinearLayout.HORIZONTAL); mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER; final Context context = mContext; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9855d6e..2fcae1c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4277,6 +4277,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener canvas.restore(); + updateCursorControllerPositions(); + } + + /** + * Update the positions of the CursorControllers. Needed by WebTextView, + * which does not draw. + * @hide + */ + protected void updateCursorControllerPositions() { if (mInsertionPointCursorController != null && mInsertionPointCursorController.isShowing()) { mInsertionPointCursorController.updatePosition(); diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 3d9cde4..2be7bca 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -529,10 +529,19 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mMediaController.hide(); } return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (mMediaPlayer.isPlaying()) { + start(); + mMediaController.hide(); + } + return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP - && mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (!mMediaPlayer.isPlaying()) { + pause(); + mMediaController.show(); + } + return true; } else { toggleMediaControlsVisiblity(); } diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml index 7a9a3b9..edf1948 100644 --- a/core/res/res/anim/fragment_close_enter.xml +++ b/core/res/res/anim/fragment_close_enter.xml @@ -26,7 +26,7 @@ android:duration="@android:integer/config_mediumAnimTime"/> <objectAnimator android:interpolator="@anim/decelerate_interpolator" - android:valueFrom="-400" + android:valueFrom="-100" android:valueTo="0" android:valueType="floatType" android:propertyName="translationX" diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml index 0743577..fbba476 100644 --- a/core/res/res/anim/fragment_close_exit.xml +++ b/core/res/res/anim/fragment_close_exit.xml @@ -27,7 +27,7 @@ <objectAnimator android:interpolator="@anim/accelerate_interpolator" android:valueFrom="0" - android:valueTo="400" + android:valueTo="100" android:valueType="floatType" android:propertyName="translationX" android:duration="@android:integer/config_mediumAnimTime"/> diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml index ac60494..334f4ef 100644 --- a/core/res/res/anim/fragment_open_enter.xml +++ b/core/res/res/anim/fragment_open_enter.xml @@ -24,7 +24,7 @@ android:propertyName="alpha" android:duration="@android:integer/config_mediumAnimTime"/> <objectAnimator - android:valueFrom="400" + android:valueFrom="100" android:valueTo="0" android:valueType="floatType" android:propertyName="translationX" diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml index 3bf1ad4..764fc39 100644 --- a/core/res/res/anim/fragment_open_exit.xml +++ b/core/res/res/anim/fragment_open_exit.xml @@ -25,7 +25,7 @@ android:duration="@android:integer/config_mediumAnimTime"/> <objectAnimator android:valueFrom="0" - android:valueTo="-400" + android:valueTo="-100" android:valueType="floatType" android:propertyName="translationX" android:duration="@android:integer/config_mediumAnimTime"/> diff --git a/core/res/res/layout/tab_content.xml b/core/res/res/layout/tab_content.xml index 0ee87ce..79147fb 100644 --- a/core/res/res/layout/tab_content.xml +++ b/core/res/res/layout/tab_content.xml @@ -22,8 +22,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> - <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" - android:layout_height="wrap_content" android:layout_weight="0" /> + <TabWidget android:id="@android:id/tabs" + android:orientation="horizontal" android:layout_width="match_parent" + android:layout_height="wrap_content" android:layout_weight="0" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1"/> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 077ce26..80072f4 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -572,7 +572,7 @@ <style name="Theme.IconMenu"> <!-- Menu/item attributes --> <item name="android:itemTextAppearance">@android:style/TextAppearance.Widget.IconMenu.Item</item> - <item name="android:selectableItemBackground">@android:drawable/menu_selector</item> + <item name="android:itemBackground">@android:drawable/menu_selector</item> <item name="android:itemIconDisabledAlpha">?android:attr/disabledAlpha</item> <item name="android:horizontalDivider">@android:drawable/divider_horizontal_dark</item> <item name="android:verticalDivider">@android:drawable/divider_vertical_dark</item> diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb Binary files differnew file mode 100644 index 0000000..373b8e4 --- /dev/null +++ b/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb Binary files differnew file mode 100644 index 0000000..aa531d8 --- /dev/null +++ b/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb diff --git a/core/tests/coretests/res/raw/obb_file1.obb b/core/tests/coretests/res/raw/obb_file1.obb Binary files differnew file mode 100644 index 0000000..e71a680 --- /dev/null +++ b/core/tests/coretests/res/raw/obb_file1.obb diff --git a/core/tests/coretests/res/raw/obb_file2.obb b/core/tests/coretests/res/raw/obb_file2.obb Binary files differnew file mode 100644 index 0000000..1c397df --- /dev/null +++ b/core/tests/coretests/res/raw/obb_file2.obb diff --git a/core/tests/coretests/res/raw/obb_file2_nosign.obb b/core/tests/coretests/res/raw/obb_file2_nosign.obb Binary files differnew file mode 100644 index 0000000..8292361 --- /dev/null +++ b/core/tests/coretests/res/raw/obb_file2_nosign.obb diff --git a/core/tests/coretests/res/raw/obb_file3.obb b/core/tests/coretests/res/raw/obb_file3.obb Binary files differnew file mode 100644 index 0000000..7f97a88 --- /dev/null +++ b/core/tests/coretests/res/raw/obb_file3.obb diff --git a/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb b/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb Binary files differnew file mode 100644 index 0000000..baa714a --- /dev/null +++ b/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java index 340137c..8df37ad 100644 --- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java @@ -83,6 +83,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes + protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes // Just a few popular file types used to return from a download protected enum DownloadFileType { @@ -180,7 +181,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { * @return A Set<Long> with the ids of the completed downloads. */ public Set<Long> getDownloadIds() { - synchronized(downloadIds) { + synchronized(this) { Set<Long> returnIds = new HashSet<Long>(downloadIds); return returnIds; } @@ -224,6 +225,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE); NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected()); return info.isConnected(); } } @@ -511,6 +513,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { * @param enable true if it should be enabled, false if it should be disabled */ protected void setWiFiStateOn(boolean enable) throws Exception { + Log.i(LOG_TAG, "Setting WiFi State to: " + enable); WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); manager.setWifiEnabled(enable); @@ -528,7 +531,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { while (receiver.getWiFiIsOn() != enable && !timedOut) { try { - receiver.wait(DEFAULT_MAX_WAIT_TIME); + receiver.wait(DEFAULT_WAIT_POLL_TIME); if (SystemClock.elapsedRealtime() > timeoutTime) { timedOut = true; diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java index ddf138f..e0b28d0 100644 --- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java @@ -24,7 +24,6 @@ import android.app.DownloadManager.Request; import android.database.Cursor; import android.net.Uri; import android.os.ParcelFileDescriptor; -import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -38,6 +37,7 @@ public class DownloadManagerStressTest extends DownloadManagerBaseTest { public void setUp() throws Exception { super.setUp(); mServer.play(0); + setWiFiStateOn(true); removeAllCurrentDownloads(); } @@ -71,7 +71,8 @@ public class DownloadManagerStressTest extends DownloadManagerBaseTest { } // wait for the download to complete or timeout - waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); + waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, + MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME); cursor = mDownloadManager.query(new Query()); assertEquals(NUM_FILES, cursor.getCount()); Log.i(LOG_TAG, "Verified number of downloads in download manager is what we expect."); @@ -130,10 +131,11 @@ public class DownloadManagerStressTest extends DownloadManagerBaseTest { } /** - * Tests trying to download a large file (~300M bytes) when there's not enough space in cache + * Tests trying to download a large file (~600M bytes) when there's not enough space in cache */ public void testInsufficientSpace() throws Exception { - long fileSize = 300000000L; + // @TODO: Rework this to fill up cache partition with a dynamically calculated size + long fileSize = 600000000L; File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null); Cursor cursor = null; diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java new file mode 100644 index 0000000..90cb9a5 --- /dev/null +++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.storage; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.os.Environment; +import android.os.SystemClock; +import android.test.InstrumentationTestCase; +import android.util.Log; +import android.os.Environment; +import android.os.FileUtils; +import android.os.storage.OnObbStateChangeListener; +import android.os.storage.StorageManager; + +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.InputStream; +import java.io.IOException; +import java.io.StringReader; + +public class StorageManagerBaseTest extends InstrumentationTestCase { + + protected Context mContext = null; + protected StorageManager mSm = null; + private static String LOG_TAG = "StorageManagerBaseTest"; + protected static final long MAX_WAIT_TIME = 120*1000; + protected static final long WAIT_TIME_INCR = 5*1000; + protected static String OBB_FILE_1 = "obb_file1.obb"; + protected static String OBB_FILE_1_CONTENTS_1 = "OneToOneThousandInts.bin"; + protected static String OBB_FILE_2 = "obb_file2.obb"; + protected static String OBB_FILE_3 = "obb_file3.obb"; + protected static String OBB_FILE_1_PASSWORD = "password1"; + protected static String OBB_FILE_1_ENCRYPTED = "obb_enc_file100_orig1.obb"; + protected static String OBB_FILE_2_UNSIGNED = "obb_file2_nosign.obb"; + protected static String OBB_FILE_3_PASSWORD = "password3"; + protected static String OBB_FILE_3_ENCRYPTED = "obb_enc_file100_orig3.obb"; + protected static String OBB_FILE_3_BAD_PACKAGENAME = "obb_file3_bad_packagename.obb"; + + protected static boolean FORCE = true; + protected static boolean DONT_FORCE = false; + + private static final String SAMPLE1_TEXT = "This is sample text.\n\nTesting 1 2 3."; + + private static final String SAMPLE2_TEXT = + "We the people of the United States, in order to form a more perfect union,\n" + + "establish justice, insure domestic tranquility, provide for the common\n" + + "defense, promote the general welfare, and secure the blessings of liberty\n" + + "to ourselves and our posterity, do ordain and establish this Constitution\n" + + "for the United States of America.\n\n"; + + class MountingObbThread extends Thread { + boolean mStop = false; + volatile boolean mFileOpenOnObb = false; + private String mObbFilePath = null; + private String mPathToContentsFile = null; + private String mOfficialObbFilePath = null; + + /** + * Constructor + * + * @param obbFilePath path to the OBB image file + * @param pathToContentsFile path to a file on the mounted OBB volume to open after the OBB + * has been mounted + */ + public MountingObbThread (String obbFilePath, String pathToContentsFile) { + assertTrue("obbFilePath cannot be null!", obbFilePath != null); + mObbFilePath = obbFilePath; + assertTrue("path to contents file cannot be null!", pathToContentsFile != null); + mPathToContentsFile = pathToContentsFile; + } + + /** + * Runs the thread + * + * Mounts OBB_FILE_1, and tries to open a file on the mounted OBB (specified in the + * constructor). Once it's open, it waits until someone calls its doStop(), after which it + * closes the opened file. + */ + public void run() { + // the official OBB file path and the mount-request file path should be the same, but + // let's distinguish the two as they may make for some interesting tests later + mOfficialObbFilePath = mountObb(mObbFilePath); + assertEquals("Expected and actual OBB file paths differ!", mObbFilePath, + mOfficialObbFilePath); + + // open a file on OBB 1... + DataInputStream inputFile = openFileOnMountedObb(mOfficialObbFilePath, + mPathToContentsFile); + assertTrue("Failed to open file!", inputFile != null); + + synchronized (this) { + mFileOpenOnObb = true; + notifyAll(); + } + + while (!mStop) { + try { + Thread.sleep(WAIT_TIME_INCR); + } catch (InterruptedException e) { + // nothing special to be done for interruptions + } + } + try { + inputFile.close(); + } catch (IOException e) { + fail("Failed to close file on OBB due to error: " + e.toString()); + } + } + + /** + * Tells whether a file has yet been successfully opened on the OBB or not + * + * @return true if the specified file on the OBB was opened; false otherwise + */ + public boolean isFileOpenOnObb() { + return mFileOpenOnObb; + } + + /** + * Returns the official path of the OBB file that was mounted + * + * This is not the mount path, but the normalized path to the actual OBB file + * + * @return a {@link String} representation of the path to the OBB file that was mounted + */ + public String officialObbFilePath() { + return mOfficialObbFilePath; + } + + /** + * Requests the thread to stop running + * + * Closes the opened file and returns + */ + public void doStop() { + mStop = true; + } + } + + public class ObbListener extends OnObbStateChangeListener { + private String LOG_TAG = "StorageManagerBaseTest.ObbListener"; + + String mOfficialPath = null; + boolean mDone = false; + int mState = -1; + + /** + * {@inheritDoc} + */ + @Override + public void onObbStateChange(String path, int state) { + Log.i(LOG_TAG, "Storage state changing to: " + state); + + synchronized (this) { + Log.i(LOG_TAG, "OfficialPath is now: " + path); + mState = state; + mOfficialPath = path; + mDone = true; + notifyAll(); + } + } + + /** + * Tells whether we are done or not (system told us the OBB has changed state) + * + * @return true if the system has told us this OBB's state has changed, false otherwise + */ + public boolean isDone() { + return mDone; + } + + /** + * The last state of the OBB, according to the system + * + * @return A {@link String} representation of the state of the OBB + */ + public int state() { + return mState; + } + + /** + * The normalized, official path to the OBB file (according to the system) + * + * @return A {@link String} representation of the official path to the OBB file + */ + public String officialPath() { + return mOfficialPath; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setUp() throws Exception { + mContext = getInstrumentation().getContext(); + mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE); + + } + + /** + * Helper to copy a raw resource file to an actual specified file + * + * @param rawResId The raw resource ID of the OBB resource file + * @param outFile A File representing the file we want to copy the OBB to + * @throws NotFoundException If the resource file could not be found + */ + private void copyRawToFile(int rawResId, File outFile) throws NotFoundException { + Resources res = mContext.getResources(); + InputStream is = null; + try { + is = res.openRawResource(rawResId); + } catch (NotFoundException e) { + Log.i(LOG_TAG, "Failed to load resource with id: " + rawResId); + throw e; + } + FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IRWXO, -1, -1); + assertTrue(FileUtils.copyToFile(is, outFile)); + FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IRWXO, -1, -1); + } + + /** + * Creates an OBB file (with the given name), into the app's standard files directory + * + * @param name The name of the OBB file we want to create/write to + * @param rawResId The raw resource ID of the OBB file in the package + * @return A {@link File} representing the file to write to + */ + protected File createObbFile(String name, int rawResId) { + File outFile = null; + try { + final File filesDir = mContext.getFilesDir(); + outFile = new File(filesDir, name); + copyRawToFile(rawResId, outFile); + } catch (NotFoundException e) { + if (outFile != null) { + outFile.delete(); + } + } + return outFile; + } + + /** + * Mounts an OBB file and opens a file located on it + * + * @param obbPath Path to OBB image + * @param fileName The full name and path to the file on the OBB to open once the OBB is mounted + * @return The {@link DataInputStream} representing the opened file, if successful in opening + * the file, or null of unsuccessful. + */ + protected DataInputStream openFileOnMountedObb(String obbPath, String fileName) { + + // get mSm obb mount path + assertTrue("Cannot open file when OBB is not mounted!", mSm.isObbMounted(obbPath)); + + String path = mSm.getMountedObbPath(obbPath); + assertTrue("Path should not be null!", path != null); + + File inFile = new File(path, fileName); + DataInputStream inStream = null; + try { + inStream = new DataInputStream(new FileInputStream(inFile)); + Log.i(LOG_TAG, "Opened file: " + fileName + " for read at path: " + path); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, e.toString()); + return null; + } catch (SecurityException e) { + Log.e(LOG_TAG, e.toString()); + return null; + } + return inStream; + } + + /** + * Mounts an OBB file + * + * @param obbFilePath The full path to the OBB file to mount + * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption + * @param expectedState The expected state resulting from trying to mount the OBB + * @return A {@link String} representing the normalized path to OBB file that was mounted + */ + protected String mountObb(String obbFilePath, String key, int expectedState) { + return doMountObb(obbFilePath, key, expectedState); + } + + /** + * Mounts an OBB file with default options (no encryption, mounting succeeds) + * + * @param obbFilePath The full path to the OBB file to mount + * @return A {@link String} representing the normalized path to OBB file that was mounted + */ + protected String mountObb(String obbFilePath) { + return doMountObb(obbFilePath, null, OnObbStateChangeListener.MOUNTED); + } + + /** + * Synchronously waits for an OBB listener to be signaled of a state change, but does not throw + * + * @param obbListener The listener for the OBB file + * @return true if the listener was signaled of a state change by the system, else returns + * false if we time out. + */ + protected boolean doWaitForObbStateChange(ObbListener obbListener) { + synchronized(obbListener) { + long waitTimeMillis = 0; + while (!obbListener.isDone()) { + try { + Log.i(LOG_TAG, "Waiting for listener..."); + obbListener.wait(WAIT_TIME_INCR); + Log.i(LOG_TAG, "Awoke from waiting for listener..."); + waitTimeMillis += WAIT_TIME_INCR; + if (waitTimeMillis > MAX_WAIT_TIME) { + fail("Timed out waiting for OBB state to change!"); + } + } catch (InterruptedException e) { + Log.i(LOG_TAG, e.toString()); + } + } + return obbListener.isDone(); + } + } + + /** + * Synchronously waits for an OBB listener to be signaled of a state change + * + * @param obbListener The listener for the OBB file + * @return true if the listener was signaled of a state change by the system; else a fail() + * is triggered if we timed out + */ + protected String doMountObb_noThrow(String obbFilePath, String key, int expectedState) { + Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key); + assertTrue ("Null path was passed in for OBB file!", obbFilePath != null); + assertTrue ("Null path was passed in for OBB file!", obbFilePath != null); + + ObbListener obbListener = new ObbListener(); + boolean success = mSm.mountObb(obbFilePath, key, obbListener); + success &= obbFilePath.equals(doWaitForObbStateChange(obbListener)); + success &= (expectedState == obbListener.state()); + + if (OnObbStateChangeListener.MOUNTED == expectedState) { + success &= obbFilePath.equals(obbListener.officialPath()); + success &= mSm.isObbMounted(obbListener.officialPath()); + } else { + success &= !mSm.isObbMounted(obbListener.officialPath()); + } + + if (success) { + return obbListener.officialPath(); + } else { + return null; + } + } + + /** + * Mounts an OBB file without throwing and synchronously waits for it to finish mounting + * + * @param obbFilePath The full path to the OBB file to mount + * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption + * @param expectedState The expected state resulting from trying to mount the OBB + * @return A {@link String} representing the actual normalized path to OBB file that was + * mounted, or null if the mounting failed + */ + protected String doMountObb(String obbFilePath, String key, int expectedState) { + Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key); + assertTrue ("Null path was passed in for OBB file!", obbFilePath != null); + + ObbListener obbListener = new ObbListener(); + assertTrue("mountObb call failed", mSm.mountObb(obbFilePath, key, obbListener)); + assertTrue("Failed to get OBB mount status change for file: " + obbFilePath, + doWaitForObbStateChange(obbListener)); + assertEquals("OBB mount state not what was expected!", expectedState, obbListener.state()); + + if (OnObbStateChangeListener.MOUNTED == expectedState) { + assertEquals(obbFilePath, obbListener.officialPath()); + assertTrue("Obb should be mounted, but SM reports it is not!", + mSm.isObbMounted(obbListener.officialPath())); + } else if (OnObbStateChangeListener.UNMOUNTED == expectedState) { + assertFalse("Obb should not be mounted, but SM reports it is!", + mSm.isObbMounted(obbListener.officialPath())); + } + + assertEquals("Mount state is not what was expected!", expectedState, obbListener.state()); + return obbListener.officialPath(); + } + + /** + * Unmounts an OBB file without throwing, and synchronously waits for it to finish unmounting + * + * @param obbFilePath The full path to the OBB file to mount + * @param force true if we shuold force the unmount, false otherwise + * @return true if the unmount was successful, false otherwise + */ + protected boolean unmountObb_noThrow(String obbFilePath, boolean force) { + Log.i(LOG_TAG, "doUnmountObb_noThrow() on " + obbFilePath); + assertTrue ("Null path was passed in for OBB file!", obbFilePath != null); + boolean success = true; + + ObbListener obbListener = new ObbListener(); + assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener)); + + boolean stateChanged = doWaitForObbStateChange(obbListener); + if (force) { + success &= stateChanged; + success &= (OnObbStateChangeListener.UNMOUNTED == obbListener.state()); + success &= !mSm.isObbMounted(obbFilePath); + } + return success; + } + + /** + * Unmounts an OBB file and synchronously waits for it to finish unmounting + * + * @param obbFilePath The full path to the OBB file to mount + * @param force true if we shuold force the unmount, false otherwise + */ + protected void unmountObb(String obbFilePath, boolean force) { + Log.i(LOG_TAG, "doUnmountObb() on " + obbFilePath); + assertTrue ("Null path was passed in for OBB file!", obbFilePath != null); + + ObbListener obbListener = new ObbListener(); + assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener)); + + boolean stateChanged = doWaitForObbStateChange(obbListener); + if (force) { + assertTrue("Timed out waiting to unmount OBB file " + obbFilePath, stateChanged); + assertEquals("OBB failed to unmount", OnObbStateChangeListener.UNMOUNTED, + obbListener.state()); + assertFalse("Obb should NOT be mounted, but SM reports it is!", mSm.isObbMounted( + obbFilePath)); + } + } + + /** + * Helper to validate the contents of an "int" file in an OBB. + * + * The format of the files are sequential int's, in the range of: [start..end) + * + * @param path The full path to the file (path to OBB) + * @param filename The filename containing the ints to validate + * @param start The first int expected to be found in the file + * @param end The last int + 1 expected to be found in the file + */ + protected void doValidateIntContents(String path, String filename, int start, int end) { + File inFile = new File(path, filename); + DataInputStream inStream = null; + Log.i(LOG_TAG, "Validating file " + filename + " at " + path); + try { + inStream = new DataInputStream(new FileInputStream(inFile)); + + for (int i = start; i < end; ++i) { + if (inStream.readInt() != i) { + fail("Unexpected value read in OBB file"); + } + } + if (inStream != null) { + inStream.close(); + } + Log.i(LOG_TAG, "Successfully validated file " + filename); + } catch (FileNotFoundException e) { + fail("File " + inFile + " not found: " + e.toString()); + } catch (IOException e) { + fail("IOError with file " + inFile + ":" + e.toString()); + } + } + + /** + * Helper to validate the contents of a text file in an OBB + * + * @param path The full path to the file (path to OBB) + * @param filename The filename containing the ints to validate + * @param contents A {@link String} containing the expected contents of the file + */ + protected void doValidateTextContents(String path, String filename, String contents) { + File inFile = new File(path, filename); + BufferedReader fileReader = null; + BufferedReader textReader = null; + Log.i(LOG_TAG, "Validating file " + filename + " at " + path); + try { + fileReader = new BufferedReader(new FileReader(inFile)); + textReader = new BufferedReader(new StringReader(contents)); + String actual = null; + String expected = null; + while ((actual = fileReader.readLine()) != null) { + expected = textReader.readLine(); + if (!actual.equals(expected)) { + fail("File " + filename + " in OBB " + path + " does not match expected value"); + } + } + fileReader.close(); + textReader.close(); + Log.i(LOG_TAG, "File " + filename + " successfully verified."); + } catch (IOException e) { + fail("IOError with file " + inFile + ":" + e.toString()); + } + } + + /** + * Helper to validate the contents of a "long" file on our OBBs + * + * The format of the files are sequential 0's of type long + * + * @param path The full path to the file (path to OBB) + * @param filename The filename containing the ints to validate + * @param size The number of zero's expected in the file + * @param checkContents If true, the contents of the file are actually verified; if false, + * we simply verify that the file can be opened + */ + protected void doValidateZeroLongFile(String path, String filename, long size, + boolean checkContents) { + File inFile = new File(path, filename); + DataInputStream inStream = null; + Log.i(LOG_TAG, "Validating file " + filename + " at " + path); + try { + inStream = new DataInputStream(new FileInputStream(inFile)); + + if (checkContents) { + for (long i = 0; i < size; ++i) { + if (inStream.readLong() != 0) { + fail("Unexpected value read in OBB file" + filename); + } + } + } + + if (inStream != null) { + inStream.close(); + } + Log.i(LOG_TAG, "File " + filename + " successfully verified for " + size + " zeros"); + } catch (IOException e) { + fail("IOError with file " + inFile + ":" + e.toString()); + } + } + + /** + * Helper to synchronously wait until we can get a path for a given OBB file + * + * @param filePath The full normalized path to the OBB file + * @return The mounted path of the OBB, used to access contents in it + */ + protected String doWaitForPath(String filePath) { + String path = null; + + long waitTimeMillis = 0; + assertTrue("OBB " + filePath + " is not currently mounted!", mSm.isObbMounted(filePath)); + while (path == null) { + try { + Thread.sleep(WAIT_TIME_INCR); + waitTimeMillis += WAIT_TIME_INCR; + if (waitTimeMillis > MAX_WAIT_TIME) { + fail("Timed out waiting to get path of OBB file " + filePath); + } + } catch (InterruptedException e) { + // do nothing + } + path = mSm.getMountedObbPath(filePath); + } + Log.i(LOG_TAG, "Got OBB path: " + path); + return path; + } + + /** + * Verifies the pre-defined contents of our first OBB (OBB_FILE_1) + * + * The OBB contains 4 files and no subdirectories + * + * @param filePath The normalized path to the already-mounted OBB file + */ + protected void verifyObb1Contents(String filePath) { + String path = null; + path = doWaitForPath(filePath); + + // Validate contents of 2 files in this obb + doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000); + doValidateIntContents(path, "SevenHundredInts.bin", 0, 700); + doValidateZeroLongFile(path, "FiveLongs.bin", 5, true); + } + + /** + * Verifies the pre-defined contents of our second OBB (OBB_FILE_2) + * + * The OBB contains 2 files and no subdirectories + * + * @param filePath The normalized path to the already-mounted OBB file + */ + protected void verifyObb2Contents(String filename) { + String path = null; + path = doWaitForPath(filename); + + // Validate contents of file + doValidateTextContents(path, "sample.txt", SAMPLE1_TEXT); + doValidateTextContents(path, "sample2.txt", SAMPLE2_TEXT); + } + + /** + * Verifies the pre-defined contents of our third OBB (OBB_FILE_3) + * + * The OBB contains nested files and subdirectories + * + * @param filePath The normalized path to the already-mounted OBB file + */ + protected void verifyObb3Contents(String filename) { + String path = null; + path = doWaitForPath(filename); + + // Validate contents of file + doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000); + doValidateZeroLongFile(path, "TwoHundredLongs", 200, true); + + // validate subdirectory 1 + doValidateZeroLongFile(path + File.separator + "subdir1", "FiftyLongs", 50, true); + + // validate subdirectory subdir2/ + doValidateIntContents(path + File.separator + "subdir2", "OneToOneThousandInts", 0, 1000); + + // validate subdirectory subdir2/subdir2a/ + doValidateZeroLongFile(path + File.separator + "subdir2" + File.separator + "subdir2a", + "TwoHundredLongs", 200, true); + + // validate subdirectory subdir2/subdir2a/subdir2a1/ + doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a" + + File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000); + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java new file mode 100644 index 0000000..71772d9 --- /dev/null +++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.storage; + +import android.content.Context; +import android.os.Environment; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import com.android.frameworks.coretests.R; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; + +import junit.framework.AssertionFailedError; + +public class StorageManagerIntegrationTest extends StorageManagerBaseTest { + + private static String LOG_TAG = "StorageManagerBaseTest.StorageManagerIntegrationTest"; + protected File mFile = null; + + /** + * {@inheritDoc} + */ + @Override + public void setUp() throws Exception { + super.setUp(); + mContext = getInstrumentation().getContext(); + mFile = null; + } + + /** + * {@inheritDoc} + */ + @Override + protected void tearDown() throws Exception { + if (mFile != null) { + mFile.delete(); + mFile = null; + } + super.tearDown(); + } + + /** + * Tests mounting a single OBB file and verifies its contents. + */ + @LargeTest + public void testMountSingleObb() { + mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath); + verifyObb1Contents(filePath); + unmountObb(filePath, DONT_FORCE); + } + + /** + * Tests mounting several OBB files and verifies its contents. + */ + @LargeTest + public void testMountMultipleObb() { + File file1 = null; + File file2 = null; + File file3 = null; + try { + file1 = createObbFile(OBB_FILE_1, R.raw.obb_file1); + String filePath1 = file1.getAbsolutePath(); + mountObb(filePath1); + verifyObb1Contents(filePath1); + + file2 = createObbFile(OBB_FILE_2, R.raw.obb_file2); + String filePath2 = file2.getAbsolutePath(); + mountObb(filePath2); + verifyObb2Contents(filePath2); + + file3 = createObbFile(OBB_FILE_3, R.raw.obb_file3); + String filePath3 = file3.getAbsolutePath(); + mountObb(filePath3); + verifyObb3Contents(filePath3); + + unmountObb(filePath1, DONT_FORCE); + unmountObb(filePath2, DONT_FORCE); + unmountObb(filePath3, DONT_FORCE); + } finally { + if (file1 != null) { + file1.delete(); + } + if (file2 != null) { + file2.delete(); + } + if (file3 != null) { + file3.delete(); + } + } + } + + /** + * Tests mounting a single encrypted OBB file and verifies its contents. + */ + @LargeTest + public void testMountSingleEncryptedObb() { + mFile = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED); + verifyObb3Contents(filePath); + unmountObb(filePath, DONT_FORCE); + } + + /** + * Tests mounting a single encrypted OBB file using an invalid password. + */ + @LargeTest + public void testMountSingleEncryptedObbInvalidPassword() { + mFile = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); + unmountObb(filePath, DONT_FORCE); + } + + /** + * Tests simultaneously mounting 2 encrypted OBBs with different keys and verifies contents. + */ + @LargeTest + public void testMountTwoEncryptedObb() { + File file3 = null; + File file1 = null; + try { + file3 = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3); + String filePath3 = file3.getAbsolutePath(); + mountObb(filePath3, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED); + verifyObb3Contents(filePath3); + + file1 = createObbFile(OBB_FILE_1_ENCRYPTED, R.raw.obb_enc_file100_orig1); + String filePath1 = file1.getAbsolutePath(); + mountObb(filePath1, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.MOUNTED); + verifyObb1Contents(filePath1); + + unmountObb(filePath3, DONT_FORCE); + unmountObb(filePath1, DONT_FORCE); + } finally { + if (file3 != null) { + file3.delete(); + } + if (file1 != null) { + file1.delete(); + } + } + } + + /** + * Tests that we can not force unmount when a file is currently open on the OBB. + */ + @LargeTest + public void testUnmount_DontForce() { + mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1); + String obbFilePath = mFile.getAbsolutePath(); + + MountingObbThread mountingThread = new MountingObbThread(obbFilePath, + OBB_FILE_1_CONTENTS_1); + + try { + mountingThread.start(); + + long waitTime = 0; + while (!mountingThread.isFileOpenOnObb()) { + synchronized (mountingThread) { + Log.i(LOG_TAG, "Waiting for file to be opened on OBB..."); + mountingThread.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + if (waitTime > MAX_WAIT_TIME) { + fail("Timed out waiting for file file to be opened on OBB!"); + } + } + } + + unmountObb(obbFilePath, DONT_FORCE); + + // verify still mounted + assertTrue("mounted path should not be null!", obbFilePath != null); + assertTrue("mounted path should still be mounted!", mSm.isObbMounted(obbFilePath)); + + // close the opened file + mountingThread.doStop(); + + // try unmounting again (should succeed this time) + unmountObb(obbFilePath, DONT_FORCE); + assertFalse("mounted path should no longer be mounted!", + mSm.isObbMounted(obbFilePath)); + } catch (InterruptedException e) { + fail("Timed out waiting for file on OBB to be opened..."); + } + } + + /** + * Tests mounting a single OBB that isn't signed. + */ + @LargeTest + public void testMountUnsignedObb() { + mFile = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath, OBB_FILE_2_UNSIGNED, OnObbStateChangeListener.ERROR_INTERNAL); + } + + /** + * Tests mounting a single OBB that is signed with a different package. + */ + @LargeTest + public void testMountBadPackageNameObb() { + mFile = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath, OBB_FILE_3_BAD_PACKAGENAME, + OnObbStateChangeListener.ERROR_PERMISSION_DENIED); + } + + /** + * Tests remounting a single OBB that has already been mounted. + */ + @LargeTest + public void testRemountObb() { + mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1); + String filePath = mFile.getAbsolutePath(); + mountObb(filePath); + verifyObb1Contents(filePath); + mountObb(filePath, null, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); + verifyObb1Contents(filePath); + unmountObb(filePath, DONT_FORCE); + } +}
\ No newline at end of file diff --git a/docs/html/guide/developing/tools/MonkeyDevice.jd b/docs/html/guide/developing/tools/MonkeyDevice.jd new file mode 100644 index 0000000..34bbba9 --- /dev/null +++ b/docs/html/guide/developing/tools/MonkeyDevice.jd @@ -0,0 +1,1353 @@ +page.title=MonkeyDevice +@jd:body +<style> + h4.jd-details-title {background-color: #DEE8F1;} +</style> +<p> + A monkeyrunner class that represents a device or emulator accessible by the workstation running +<code><a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a></code>. +</p> +<p> + This class is used to control an Android device or emulator. The methods send UI events, + retrieve information, install and remove applications, and run applications. +</p> +<p> + You normally do not have to create an instance of <code>MonkeyDevice</code>. Instead, you + use +<code><a href="{@docRoot}guide/developing/tools/MonkeyRunner.html#waitForConnection"> +MonkeyRunner.waitForConnection()</a></code> to create a new object from a connection to a device or +emulator. For example, instead of +using:</p> +<pre> +newdevice = MonkeyDevice() +</pre> +<p> + you would use: +</p> +<pre> +newdevice = MonkeyRunner.waitForConnection() +</pre> +<h2>Summary</h2> + <table id="constants" class="jd-sumtable" style="background-color: white;"> + <tr> + <th colspan="12" style="background-color: #E2E2E2">Constants</th> + </tr> + <tr class="api" style="background-color: white;"> + <td class="jd-typecol"><em>string</em></td> + <td class="jd-linkcol"><a href="#ACTION_DOWN">DOWN</a></td> + <td class="jd-descrcol" width="100%"> + Use this with the <code>type</code> argument of + <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a> + </code> + to send a DOWN event. + </td> + </tr> + <tr class="api" style="background-color: white;"> + <td class="jd-typecol"><em>string</em></td> + <td class="jd-linkcol"><a href="#ACTION_UP">UP</a></td> + <td class="jd-descrcol" width="100%"> + Use this with the <code>type</code> argument of + <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a> + </code> + to send an UP event. + </td> + </tr> + <tr class="api" style="background-color: white;"> + <td class="jd-typecol"><em>string</em></td> + <td class="jd-linkcol"><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></td> + <td class="jd-descrcol" width="100%"> + Use this with the <code>type</code> argument of + <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a> + </code> + to send a DOWN event immediately followed by an UP event. + </td> + </tr> + </table> +<table id="pubmethods" class="jd-sumtable"> + <tr> + <th colspan="12" style="background-color: #E2E2E2">Methods</th> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#broadcastIntent">broadcastIntent</a> + </span> + (<em>string</em> uri, + <em>string</em> action, + <em>string</em> data, + <em>string</em> mimetype, + <em>iterable</em> categories + <em>dictionary</em> extras, + <em>component</em> component, + <em>iterable</em> flags) + </nobr> + <div class="jd-descrdiv"> + Broadcasts an Intent to this device, as if the Intent were coming from an + application. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#drag">drag</a> + </span> + (<em>tuple</em> start, + <em>tuple</em> end, + <em>float</em> duration, + <em>integer</em> steps) + </nobr> + <div class="jd-descrdiv"> + Simulates a drag gesture (touch, hold, and move) on this device's screen. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>object</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#getProperty">getProperty</a> + </span> + (<em>string</em> key) + </nobr> + <div class="jd-descrdiv"> + Given the name of a system environment variable, returns its value for this device. + The available variable names are listed in the <a href="#getProperty"> + detailed description</a> of this method. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>object</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#getSystemProperty">getSystemProperty</a> + </span> + (<em>string</em> key) + </nobr> + <div class="jd-descrdiv"> +. The API equivalent of <code>adb shell getprop <key>. This is provided for use + by platform developers. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#installPackage">installPackage</a> + </span> + (<em>string</em> path) + </nobr> + <div class="jd-descrdiv"> + Installs the Android application or test package contained in packageFile onto this + device. If the application or test package is already installed, it is replaced. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>dictionary</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#instrument">instrument</a> + </span> + (<em>string</em> className, + <em>dictionary</em> args) + </nobr> + <div class="jd-descrdiv"> + Runs the specified component under Android instrumentation, and returns the results + in a dictionary whose exact format is dictated by the component being run. The + component must already be present on this device. + </div> + </td> + </tr> + <tr class="api"> + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#press">press</a> + </span> + (<em>string</em> name, + <em>dictionary</em> type) + </nobr> + <div class="jd-descrdiv"> + Sends the key event specified by type to the key specified by + keycode. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#reboot">reboot</a> + </span> + (<em>string</em> into) + </nobr> + <div class="jd-descrdiv"> + Reboots this device into the bootloader specified by bootloadType. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#removePackage">removePackage</a> + </span> + (<em>string</em> package) + </nobr> + <div class="jd-descrdiv"> + Deletes the specified package from this device, including its data and cache. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>object</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#shell">shell</a> + </span> + (<em>string</em> cmd) + </nobr> + <div class="jd-descrdiv"> + Executes an <code>adb</code> shell command and returns the result, if any. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#startActivity">startActivity</a> + </span> + (<em>string</em> uri, + <em>string</em> action, + <em>string</em> data, + <em>string</em> mimetype, + <em>iterable</em> categories + <em>dictionary</em> extras, + <em>component</em> component, + <em>flags</em>) + </nobr> + <div class="jd-descrdiv"> + Starts an Activity on this device by sending an Intent constructed from the + supplied arguments. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html"> + MonkeyImage + </a> + </code> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#takeSnapshot">takeSnapshot</a>() + </span> + </nobr> + <div class="jd-descrdiv"> + Captures the entire screen buffer of this device, yielding a + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html"> + MonkeyImage + </a> + </code> object containing a screen capture of the current display. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#touch">touch</a> + </span> + (<em>integer</em> x, + <em>integer</em> y, + <em>integer</em> type) + </nobr> + <div class="jd-descrdiv"> + Sends a touch event specified by type to the screen location specified + by x and y. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#touch">type</a> + </span> + (<em>string</em> message) + </nobr> + <div class="jd-descrdiv"> + Sends the characters contained in message to this device, as if they + had been typed on the device's keyboard. This is equivalent to calling + <code><a href="#press">press()</a></code> for each keycode in <code>message</code> + using the key event type <code><a href="#ACTION_DOWN_AND_UP"></a>DOWN_AND_UP</code>. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#touch">wake</a> + </span> + () + </nobr> + <div class="jd-descrdiv"> + Wakes the screen of this device. + </div> + </td> + </tr> +</table> +<!-- ========= ENUM CONSTANTS DETAIL ======== --> +<h2>Constants</h2> +<A NAME="ACTION_DOWN"></a> +<div class="jd-details api"> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + DOWN + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + <code><a href="#press">press()</a></code> or + <code><a href="#press">touch()</a></code> value. + Specifies that a DOWN event type should be sent to the device, corresponding to + pressing down on a key or touching the screen. + </p> + </div> + </div> +</div> +<A NAME="ACTION_UP"></A> +<div class="jd-details api"> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + UP + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + <code><a href="#press">press()</a></code> or + <code><a href="#press">touch()</a></code> value. + Specifies that an UP event type should be sent to the device, corresponding to + releasing a key or lifting up from the screen. + </p> + </div> + </div> +</div> +<A NAME="ACTION_DOWN_AND_UP"></A> + +<div class="jd-details api"> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + DOWN_AND_UP + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + <code><a href="#press">press()</a></code>, + <code><a href="#press">touch()</a></code> or + <code><a href="#type">type()</a></code> value. + Specifies that a DOWN event type followed by an UP event type should be sent to the + device, corresponding to typing a key or clicking the screen. + </p> + </div> + </div> +</div> +<!-- ========= METHOD DETAIL ======== --> +<!-- Public methods --> +<h2>Public Methods</h2> +<A NAME="broadcastIntent"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">broadcastIntent</span> + <span class="normal"> + ( + <em>string</em> uri, + <em>string</em> action, + <em>string</em> data, + <em>string</em> mimetype, + <em>iterable</em> categories + <em>dictionary</em> extras, + <em>component</em> component, + <em>iterable</em> flags) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Broadcasts an Intent to this device, as if the Intent were coming from an + application. See {@link android.content.Intent Intent} for more information about the + arguments. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>uri</th> + <td> + The URI for the Intent. + (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}). + </td> + </tr> + <tr> + <th>action</th> + <td> + The action for this Intent + (see {@link android.content.Intent#setAction(java.lang.String) Intent.setAction()}). + </td> + </tr> + <tr> + <th>data</th> + <td> + The data URI for this Intent + (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}). + </td> + </tr> + <tr> + <th>mimetype</th> + <td> + The MIME type for the Intent + (see {@link android.content.Intent#setType(java.lang.String) Intent.setType()}). + </td> + </tr> + <tr> + <th>categories</th> + <td> + An iterable data structure containing strings that define categories for this + Intent + (see + {@link android.content.Intent#addCategory(java.lang.String) Intent.addCategory()}). + </td> + </tr> + <tr> + <th>extras</th> + <td> + A dictionary of extra data for this Intent + (see {@link android.content.Intent#putExtra(java.lang.String,java.lang.String) + Intent.putExtra()} + for an example). + <p> + The key for each dictionary item should be a <em>string</em>. The item's value + can be any simple or structured data type. + </p> + </td> + </tr> + <tr> + <th>component</th> + <td> + The component for this Intent (see {@link android.content.ComponentName}). + Using this argument will direct the Intent to a specific class within a specific + Android package. + </td> + </tr> + <tr> + <th>flags</th> + <td> + An iterable data structure containing flags that control how the Intent is handled + (see {@link android.content.Intent#setFlags(int) Intent.setFlags()}). + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="drag"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">drag</span> + <span class="normal"> + ( + <em>tuple</em> start, + <em>tuple</em> end, + <em>float</em> duration, + <em>integer</em> steps) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Simulates a drag gesture (touch, hold, and move) on this device's screen. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>start</th> + <td> + The starting point of the drag gesture, in the form of a <em>tuple</em> + (x,y) where x and y are <em>integers</em>. + </td> + </tr> + <tr> + <th>end</th> + <td> + The end point of the drag gesture, in the form of a <em>tuple</em> (x,y) + where x and y are <em>integers</em>. + </td> + </tr> + <tr> + <th>duration</th> + <td>The duration of the drag gesture in seconds. The default is 1.0 seconds.</td> + </tr> + <tr> + <th>steps</th> + <td>The number of steps to take when interpolating points. The default is 10.</td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="getProperty"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>object</em> + </span> + <span class="sympad">getProperty</span> + <span class="normal"> + (<em>string</em> key) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Given the name of a system environment variable, returns its value for this device. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>key</th> + <td> + The name of the system environment variable. The available variable names are listed in + <a href="#table1">Table 1. Property variable names</a> at the end of this topic. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + The value of the variable. The data format varies according to the variable requested. + </li> + </ul> + </div> + </div> +</div> +<A NAME="getSystemProperty"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>object</em> + </span> + <span class="sympad">getSystemProperty</span> + <span class="normal"> + (<em>string</em> key) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Synonym for <code><a href="#getProperty">getProperty()</a></code>. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>key</th> + <td> + The name of the system environment variable. The available variable names are listed in + <a href="#table1">Table 1. Property Variable Names</a>. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + The value of the variable. The data format varies according to the variable requested. + </li> + </ul> + </div> + </div> +</div> +<A NAME="installPackage"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">installPackage</span> + <span class="normal"> + (<em>string</em> path) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Installs the Android application or test package contained in packageFile + onto this device. If the application or test package is already installed, it is + replaced. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>path</th> + <td> + The fully-qualified path and filename of the <code>.apk</code> file to install. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="instrument"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>dictionary</em> + </span> + <span class="sympad">instrument</span> + <span class="normal"> + ( + <em>string</em> className, + <em>dictionary</em> args) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Runs the specified component with Android instrumentation, and returns the results + in a dictionary whose exact format is dictated by the component being run. The + component must already be present on this device. + </p> + <p> + Use this method to start a test case that uses one of Android's test case classes. + See <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing + Fundamentals</a> to learn more about unit testing with the Android testing + framework. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>className</th> + <td> + The name of an Android component that is already installed on this device, in the + standard form packagename/classname, where packagename is the + Android package name of a <code>.apk</code> file on this device, and + classname is the class name of an Android component (Activity, + ContentProvider, Service, or BroadcastReceiver) in that file. Both + packagename and classname must be fully qualified. See + {@link android.content.ComponentName} for more details. + </td> + </tr> + <tr> + <th>args</th> + <td> + A dictionary containing flags and their values. These are passed to the component as it + is started. If the flag does not take a value, set its dictionary value to an empty + string. + </td> + </tr> + </table> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + <p> + A dictionary containing the component's output. The contents of the dictionary + are defined by the component itself. + </p> + <p> + If you use {@link android.test.InstrumentationTestRunner} as the class name in + the componentName argument, then the result dictionary contains + the single key "stream". The value of "stream" is a <em>string</em> containing + the test output, as if <code>InstrumentationTestRunner</code> was run from the + command line. The format of this output is described in + <a href="{@docRoot}guide/developing/testing/testing_otheride.html"> + Testing in Other IDEs</a>. + </p> + </li> + </ul> + </div> + </div> + </div> +</div> +<A NAME="press"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">press</span> + <span class="normal"> + (<em>string</em> name, + <em>integer</em> type) + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Sends the key event specified by <code>type</code> to the key specified by + <code>keycode</code>. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>name</th> + <td> + The name of the keycode to send. See {@link android.view.KeyEvent} for a list of + keycode names. Use the keycode name, not its integer value. + </td> + </tr> + <tr> + <th>type</th> + <td> + The type of key event to send. The allowed values are <code><a href="#ACTION_DOWN"> + DOWN</a></code>, <code><a href="#ACTION_UP">UP</a></code>, and + <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="reboot"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">reboot</span> + <span class="normal"> + (<em>string</em> bootloadType) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Reboots this device into the bootloader specified by <code>bootloadType</code>. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>into</th> + <td> + The type of bootloader to reboot into. The allowed values are + "bootloader", "recovery", or "None". + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="removePackage"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">removePackage</span> + <span class="normal"> + (<em>string</em> package) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Deletes the specified package from this device, including its data and cache. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>package</th> + <td> + The Android package name of an <code>.apk</code> file on this device. + </td> + </table> + </div> + </div> +</div> +<A NAME="shell"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>object</em> + </span> + <span class="sympad">shell</span> + <span class="normal"> + (<em>string</em> cmd) + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Executes an <code>adb</code> shell command and returns the result, if any. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>cmd</th> + <td> + The command to execute in the <code>adb</code> shell. The form of these commands is + described in the topic <a href="{@docRoot}guide/developing/tools/adb.html">Android + Debug Bridge</a>. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + The results of the command, if any. The format of the results is determined by the + command. + </li> + </ul> + </div> + </div> +</div> +<A NAME="startActivity"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">startActivity</span> + <span class="normal"> + ( + <em>string</em> uri, + <em>string</em> action, + <em>string</em> data, + <em>string</em> mimetype, + <em>iterable</em> categories + <em>dictionary</em> extras, + <em>component</em> component, + <em>iterable</em> flags) + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Starts an Activity on this device by sending an Intent constructed from the + supplied arguments. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>uri</th> + <td> + The URI for the Intent. + (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}). + </td> + </tr> + <tr> + <th>action</th> + <td> + The action for the Intent + (see {@link android.content.Intent#setAction(java.lang.String) Intent.setAction()}). + </td> + </tr> + <tr> + <th>data</th> + <td> + The data URI for the Intent + (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}). + </td> + </tr> + <tr> + <th>mimetype</th> + <td> + The MIME type for the Intent + (see {@link android.content.Intent#setType(java.lang.String) Intent.setType()}). + </td> + </tr> + <tr> + <th>categories</th> + <td> + An iterable data structure containing strings that define categories for the + Intent + (see + {@link android.content.Intent#addCategory(java.lang.String) Intent.addCategory()}). + </td> + </tr> + <tr> + <th>extras</th> + <td> + A dictionary of extra data for the Intent + (see + {@link android.content.Intent#putExtra(java.lang.String,java.lang.String) + Intent.putExtra()} + for an example). + <p> + The key for each dictionary item should be a <em>string</em>. The item's value + can be any simple or structured data type. + </p> + </td> + </tr> + <tr> + <th>component</th> + <td> + The component for the Intent + (see {@link android.content.ComponentName}). Using this argument will direct the + Intent to a specific class within a specific Android package. + </td> + </tr> + <tr> + <th>flags</th> + <td> + An iterable data structure containing flags that control how the Intent is handled + (see {@link android.content.Intent#setFlags(int) Intent.setFlags()}). + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="takeSnapshot"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html"> + MonkeyImage + </a> + </code> + </span> + <span class="sympad">takeSnapshot</span> + <span class="normal"> + () + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Captures the entire screen buffer of this device, yielding a + screen capture of the current display. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + A <a href="{@docRoot}guide/developing/tools/MonkeyImage.html"> + MonkeyImage</a> object containing the image of the current display. + </li> + </ul> + </div> + </div> +</div> +<A NAME="touch"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">touch</span> + <span class="normal"> + ( + <em>integer</em> x, + <em>integer</em> y, + <em>string</em> type) + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Sends a touch event specified by type to the screen location specified + by x and y. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>x</th> + <td> + The horizontal position of the touch in actual device pixels, starting from the left of + the screen in its current orientation. + </td> + </tr> + <tr> + <th>y</th> + <td> + The vertical position of the touch in actual device pixels, starting from the top of + the screen in its current orientation. + </td> + </tr> + <tr> + <th>type</th> + <td> + The type of key event to send. The allowed values are <code><a href="#ACTION_DOWN"> + DOWN</a></code>, <code><a href="#ACTION_UP">UP</a></code>, and + <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="type"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">type</span> + <span class="normal"> + (<em>string</em> message) + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Sends the characters contained in message to this device, as if they + had been typed on the device's keyboard. This is equivalent to calling + <code><a href="#press">press()</a></code> for each keycode in <code>message</code> + using the key event type <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>message</th> + <td> + A string containing the characters to send. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="wake"></A> +<div class="jd-details api"> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">wake</span> + <span class="normal"> + () + </span> + </h4> + <div class="jd-details-descr"> + <div class="jd-tagdata jd-tagdescr"> + <p> + Wakes the screen of this device. + </p> + </div> + </div> +</div> +<hr></hr> +<h2>Appendix</h2> + <p class="table-caption" id="table1"> + <strong>Table 1.</strong>Property variable names used with + <span class="sympad"><a href="#getProperty">getProperty()</a></span> and + <span class="sympad"><a href="#getSystemProperty">getSystemProperty()</a></span>. + </p> + <table> + <tr> + <th> + Property Group + </th> + <th> + Property + </th> + <th> + Description + </th> + <th> + Notes + </th> + </tr> + <tr> + <td rowspan="17"><code>build</code></td> + <td><code>board</code></td> + <td>Code name for the device's system board</td> + <td rowspan="17"> + See {@link android.os.Build} + </td> + </tr> + <tr> + <td><code>brand</code></td> + <td>The carrier or provider for which the OS is customized.</td> + </tr> + <tr> + <td><code>device</code></td> + <td>The device design name.</td> + </tr> + <tr> + <td><code>fingerprint</code></td> + <td>A unique identifier for the currently-running build.</td> + </tr> + <tr> + <td><code>host</code></td> + <td></td> + </tr> + <tr> + <td><code>ID</code></td> + <td>A changelist number or label.</td> + </tr> + <tr> + <td><code>model</code></td> + <td>The end-user-visible name for the device.</td> + </tr> + <tr> + <td><code>product</code></td> + <td>The overall product name.</td> + </tr> + <tr> + <td><code>tags</code></td> + <td>Comma-separated tags that describe the build, such as "unsigned" and "debug".</td> + </tr> + <tr> + <td><code>type</code></td> + <td>The build type, such as "user" or "eng".</td> + </tr> + <tr> + <td><code>user</code></td> + <td></td> + </tr> + <tr> + <td><code>CPU_ABI</code></td> + <td> + The name of the native code instruction set, in the form CPU type plus + ABI convention. + </td> + </tr> + <tr> + <td><code>manufacturer</code></td> + <td>The product/hardware manufacturer.</td> + </tr> + <tr> + <td><code>version.incremental</code></td> + <td> + The internal code used by the source control system to represent this version + of the software. + </td> + </tr> + <tr> + <td><code>version.release</code></td> + <td>The user-visible name of this version of the software.</td> + </tr> + <tr> + <td><code>version.sdk</code></td> + <td>The user-visible SDK version associated with this version of the OS.</td> + </tr> + <tr> + <td><code>version.codename</code></td> + <td> + The current development codename, or "REL" if this version of the software has been + released. + </td> + </tr> + <tr> + <td rowspan="3"><code>display</code></td> + <td><code>width</code></td> + <td>The device's display width in pixels.</td> + <td rowspan="3"> + See + {@link android.util.DisplayMetrics} for details. + </td> + </tr> + <tr> + <td><code>height</code></td> + <td>The device's display height in pixels.</td> + </tr> + <tr> + <td><code>density</code></td> + <td> + The logical density of the display. This is a factor that scales + DIP (Density-Independent Pixel) units to the device's resolution. DIP is adjusted so + that 1 DIP is equivalent to one pixel on a 160 pixel-per-inch display. For example, + on a 160-dpi screen, density = 1.0, while on a 120-dpi screen, density = .75. + <p> + The value does not exactly follow the real screen size, but is adjusted to + conform to large changes in the display DPI. See + {@link android.util.DisplayMetrics#density} for more details. + </p> + </td> + </tr> + <tr> + <td rowspan="6"><code>am.current</code></td> + <td><code>package</code></td> + <td>The Android package name of the currently running package.</td> + <td rowspan="6"> + The <code>am.current</code> keys return information about the currently-running + Activity. + </td> + </tr> + <tr> + <td><code>action</code></td> + <td> + The current activity's action. This has the same format as the <code>name</code> + attribute of the <code>action</code> element in a package manifest. + </td> + </tr> + <tr> + <td><code>comp.class</code></td> + <td> + The class name of the component that started the current Activity. See + <code><a href="#comppackage">comp.package</a></code> for more details.</td> + </tr> + <tr> + <td><a name="comppackage"><code>comp.package</code></a></td> + <td> + The package name of the component that started the current Activity. A component + is specified by a package name and the name of class that the package contains. + </td> + </tr> + <tr> + <td><code>data</code></td> + <td>The data (if any) contained in the Intent that started the current Activity.</td> + </tr> + <tr> + <td><code>categories</code></td> + <td>The categories specified by the Intent that started the current Activity.</td> + </tr> + <tr> + <td rowspan="3"><code>clock</code></td> + <td><code>realtime</code></td> + <td> + The number of milliseconds since the device rebooted, including deep-sleep + time. + </td> + <td rowspan="3"> + See {@link android.os.SystemClock} for more information. + </td> + </tr> + <tr> + <td><code>uptime</code></td> + <td> + The number of milliseconds since the device rebooted, <em>not</em> including + deep-sleep time + </td> + </tr> + <tr> + <td><code>millis</code></td> + <td>current time since the UNIX epoch, in milliseconds.</td> + </tr> + </table> diff --git a/docs/html/guide/developing/tools/MonkeyImage.jd b/docs/html/guide/developing/tools/MonkeyImage.jd new file mode 100644 index 0000000..ae85cb5 --- /dev/null +++ b/docs/html/guide/developing/tools/MonkeyImage.jd @@ -0,0 +1,435 @@ +page.title=MonkeyImage +@jd:body +<style> + h4.jd-details-title {background-color: #DEE8F1;} +</style> + +<p> + A monkeyrunner class to hold an image of the device or emulator's screen. The image is + copied from the screen buffer during a screenshot. This object's methods allow you to + convert the image into various storage formats, write the image to a file, copy parts of + the image, and compare this object to other <code>MonkeyImage</code> objects. +</p> +<p> + You do not need to create new instances of <code>MonkeyImage</code>. Instead, use +<code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html#takeSnapshot"> +MonkeyDevice.takeSnapshot()</a></code> to create a new instance from a screenshot. For example, use: +</p> +<pre> +newimage = MonkeyDevice.takeSnapshot() +</pre> +<h2>Summary</h2> +<table id="pubmethods" class="jd-sumtable"> + <tr> + <th colspan="12" style="background-color: #E2E2E2">Methods</th> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>string</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#convertToBytes">convertToBytes</a> + </span> + (<em>string</em> format) + </nobr> + <div class="jd-descrdiv"> + Converts the current image to a particular format and returns it as a + <em>string</em> that you can then access as an <em>iterable</em> of binary bytes. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>tuple</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#getRawPixel">getRawPixel</a> + </span> + (<em>integer</em> x, + <em>integer</em> y) + </nobr> + <div class="jd-descrdiv"> + Returns the single pixel at the image location (x,y), as an + a <em>tuple</em> of <em>integer</em>, in the form (a,r,g,b). + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>integer</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#getRawPixelInt">getRawPixelInt</a> + </span> + (<em>integer</em> x, + <em>integer</em> y) + </nobr> + <div class="jd-descrdiv"> + Returns the single pixel at the image location (x,y), as + a 32-bit <em>integer</em>. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a> + </code> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#getSubImage">getSubImage</a> + </span> + (<em>tuple</em> rect) + </nobr> + <div class="jd-descrdiv"> + Creates a new <code>MonkeyImage</code> object from a rectangular selection of the + current image. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>boolean</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#sameAs">sameAs</a> + </span> + (<code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code> + other, + <em>float</em> percent) + </nobr> + <div class="jd-descrdiv"> + Compares this <code>MonkeyImage</code> object to another and returns the result of + the comparison. The <code>percent</code> argument specifies the percentage + difference that is allowed for the two images to be "equal". + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>void</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#writeToFile">writeToFile</a> + </span> + (<em>string</em> path, + <em>string</em> format) + </nobr> + <div class="jd-descrdiv"> + Writes the current image to the file specified by <code>filename</code>, in the + format specified by <code>format</code>. + </div> + </td> + </tr> +</table> +<!-- ========= METHOD DETAIL ======== --> +<!-- Public methods --> +<h2>Public Methods</h2> +<A NAME="convertToBytes"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + <span class="sympad">convertToBytes</span> + <span class="normal"> + ( + <em>string</em> format) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Converts the current image to a particular format and returns it as a <em>string</em> + that you can then access as an <em>iterable</em> of binary bytes. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>format</th> + <td> + The desired output format. All of the common raster output formats are supported. + The default value is "png" (Portable Network Graphics). + </td> + </tr> + </table> + </div> +</div> +</div> +<A NAME="getRawPixel"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>tuple</em> + </span> + <span class="sympad">getRawPixel</span> + <span class="normal"> + (<em>integer</em> x, + <em>integer</em> y) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Returns the single pixel at the image location (x,y), as an + a <em>tuple</em> of <em>integer</em>, in the form (a,r,g,b). + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>x</th> + <td> + The horizontal position of the pixel, starting with 0 at the left of the screen in the + orientation it had when the screenshot was taken. + </td> + </tr> + <tr> + <th>y</th> + <td> + The vertical position of the pixel, starting with 0 at the top of the screen in the + orientation it had when the screenshot was taken. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + A tuple of integers representing the pixel, in the form (a,r,g,b) where + a is the alpha channel value, and r, g, and b are the red, green, and blue values, + respectively. + </li> + </ul> + </div> + </div> +</div> +<A NAME="getRawPixelInt"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>tuple</em> + </span> + <span class="sympad">getRawPixelInt</span> + <span class="normal"> + (<em>integer</em> x, + <em>integer</em> y) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Returns the single pixel at the image location (x,y), as an + an <em>integer</em>. Use this method to economize on memory. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>x</th> + <td> + The horizontal position of the pixel, starting with 0 at the left of the screen in the + orientation it had when the screenshot was taken. + </td> + </tr> + <tr> + <th>y</th> + <td> + The vertical position of the pixel, starting with 0 at the top of the screen in the + orientation it had when the screenshot was taken. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + The a,r,g, and b values of the pixel as 8-bit values combined into a 32-bit + integer, with a as the leftmost 8 bits, r the next rightmost, and so forth. + </li> + </ul> + </div> + </div> +</div> +<A NAME="getSubImage"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a> + </code> + </span> + <span class="sympad">getSubImage</span> + <span class="normal"> + (<em>tuple</em> rect) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Creates a new <code>MonkeyImage</code> object from a rectangular selection of the + current image. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>rect</th> + <td> + A tuple (x, y, w, h) specifying the selection. x and y specify the 0-based pixel + position of the upper left-hand corner of the selection. w specifies the width of the + region, and h specifies its height, both in units of pixels. + <p> + The image's orientation is the same as the screen orientation at the time the + screenshot was made. + </p> + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + A new <code>MonkeyImage</code> object containing the selection. + </li> + </ul> + </div> + </div> +</div> +<A NAME="sameAs"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>boolean</em> + </span> + <span class="sympad">sameAs</span> + <span class="normal"> + ( + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a> + </code> otherImage, + <em>float</em> percent + ) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Compares this <code>MonkeyImage</code> object to another and returns the result of + the comparison. The <code>percent</code> argument specifies the percentage + difference that is allowed for the two images to be "equal". + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>other</th> + <td> + Another <code>MonkeyImage</code> object to compare to this one. + </td> + </tr> + <tr> + <th> + percent + </th> + <td> + A float in the range 0.0 to 1.0, inclusive, indicating + the percentage of pixels that need to be the same for the method to return + <code>true</code>. The default is 1.0, indicating that all the pixels + must match. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + Boolean <code>true</code> if the images match, or boolean <code>false</code> otherwise. + </li> + </ul> + </div> + </div> +</div> +<A NAME="writeToFile"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">writeToFile</span> + <span class="normal"> + (<em>string</em> filename, + <em>string</em> format) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Writes the current image to the file specified by <code>filename</code>, in the + format specified by <code>format</code>. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>path</th> + <td> + The fully-qualified filename and extension of the output file. + </td> + </tr> + <tr> + <th> + format + </th> + <td> + The output format to use for the file. If no format is provided, then the + method tries to guess the format from the filename's extension. If no + extension is provided and no format is specified, then the default format of + "png" (Portable Network Graphics) is used. + </td> + </tr> + </table> + </div> + </div> +</div> diff --git a/docs/html/guide/developing/tools/MonkeyRunner.jd b/docs/html/guide/developing/tools/MonkeyRunner.jd new file mode 100644 index 0000000..871e06d --- /dev/null +++ b/docs/html/guide/developing/tools/MonkeyRunner.jd @@ -0,0 +1,445 @@ +page.title=MonkeyRunner +@jd:body +<style> + h4.jd-details-title {background-color: #DEE8F1;} +</style> +<p> + A monkeyrunner class that contains static utility methods. +</p> +<h2>Summary</h2> +<table id="pubmethods" class="jd-sumtable"> + <tr> + <th colspan="12" style="background-color: #E2E2E2">Methods</th> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#alert">alert</a> + </span> + (<em>string</em> message, + <em>string</em> title, + <em>string</em> okTitle) + </nobr> + <div class="jd-descrdiv"> + Displays an alert dialog to the process running the current + program. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>integer</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#choice">choice</a> + </span> + (<em>string</em> message, + <em>iterable</em> choices, + <em>string</em> title) + </nobr> + <div class="jd-descrdiv"> + Displays a dialog with a list of choices to the process running the current program. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#help">help</a> + </span> + (<em>string</em> format) + </nobr> + <div class="jd-descrdiv"> + Displays the monkeyrunner API reference in a style similar to that of Python's + <code>pydoc</code> tool, using the specified format. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <em>string</em> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#input">input</a> + </span> + (<em>string</em> message, + <em>string</em> initialValue, + <em>string</em> title, + <em>string</em> okTitle, + <em>string</em> cancelTitle) + </nobr> + <div class="jd-descrdiv"> + Displays a dialog that accepts input. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + void + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#sleep">sleep</a> + </span> + (<em>float</em> seconds) + </nobr> + <div class="jd-descrdiv"> + Pauses the current program for the specified number of seconds. + </div> + </td> + </tr> + <tr class="api" > + <td class="jd-typecol"> + <nobr> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a> + </code> + </nobr> + </td> + <td class="jd-linkcol" width="100%"> + <nobr> + <span class="sympad"> + <a href="#waitForConnection">waitForConnection</a> + </span> + (<em>float</em> timeout, + <em>string</em> deviceId) + </nobr> + <div class="jd-descrdiv"> + Tries to make a connection between the <code>monkeyrunner</code> backend and the + specified device or emulator. + </div> + </td> + </tr> +</table> +<!-- ========= METHOD DETAIL ======== --> +<!-- Public methods --> +<h2>Public Methods</h2> +<A NAME="alert"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + <span class="sympad">alert</span> + <span class="normal"> + ( + <em>string</em> message, + <em>string</em> title, + <em>string</em> okTitle) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Displays an alert dialog to the process running the current + program. The dialog is modal, so the program pauses until the user clicks the dialog's + button. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>message</th> + <td> + The message to display in the dialog. + </td> + </tr> + <tr> + <th>title</th> + <td> + The dialog's title. The default value is "Alert". + </td> + </tr> + <tr> + <th>okTitle</th> + <td> + The text displayed in the dialog button. The default value is "OK". + </td> + </tr> + </table> + </div> +</div> +</div> +<A NAME="choice"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>integer</em> + </span> + <span class="sympad">choice</span> + <span class="normal"> + (<em>string</em> message, + <em>iterable</em> choices, + <em>string</em> title) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Displays a dialog with a list of choices to the process running the current program. The + dialog is modal, so the program pauses until the user clicks one of the dialog's + buttons. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>message</th> + <td> + The prompt message displayed in the dialog. + </td> + </tr> + <tr> + <th>choices</th> + <td> + A Python iterable containing one or more objects that are displayed as strings. The + recommended form is an array of strings. + </td> + </tr> + <tr> + <th> + title + </th> + <td> + The dialog's title. The default is "Input". + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + If the user makes a selection and clicks the "OK" button, the method returns + the 0-based index of the selection within the iterable. + If the user clicks the "Cancel" button, the method returns -1. + </li> + </ul> + </div> + </div> +</div> +<A NAME="help"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">help</span> + <span class="normal"> + (<em>string</em> format) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Displays the monkeyrunner API reference in a style similar to that of Python's + <code>pydoc</code> tool, using the specified format. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>format</th> + <td> + The markup format to use in the output. The possible values are "text" for plain text + or "html" for HTML. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="input"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <em>string</em> + </span> + <span class="sympad">input</span> + <span class="normal"> + (<em>string</em> message + <em>string</em> initialValue, + <em>string</em> title, + <em>string</em> okTitle, + <em>string</em> cancelTitle) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Displays a dialog that accepts input and returns it to the program. The dialog is + modal, so the program pauses until the user clicks one of the dialog's buttons. + </p> + <p> + The dialog contains two buttons, one of which displays the okTitle value + and the other the cancelTitle value. If the user clicks the okTitle button, + the current value of the input box is returned. If the user clicks the cancelTitle + button, an empty string is returned. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>message</th> + <td> + The prompt message displayed in the dialog. + </td> + </tr> + <tr> + <th>initialValue</th> + <td> + The initial value to display in the dialog. The default is an empty string. + </td> + </tr> + <tr> + <th>title</th> + <td> + The dialog's title. The default is "Input". + </td> + </tr> + <tr> + <th>okTitle</th> + <td> + The text displayed in the okTitle button. The default is "OK". + </td> + </tr> + <tr> + <th>cancelTitle</th> + <td> + The text displayed in the cancelTitle button. The default is "Cancel". + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + If the user clicks the okTitle button, then the method returns the current value of + the dialog's input box. If the user clicks the cancelTitle button, the method returns + an empty string. + </li> + </ul> + </div> + </div> +</div> +<A NAME="sleep"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + void + </span> + <span class="sympad">sleep</span> + <span class="normal"> + ( + <em>float</em> seconds + ) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Pauses the current program for the specified number of seconds. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>seconds</th> + <td> + The number of seconds to pause. + </td> + </tr> + </table> + </div> + </div> +</div> +<A NAME="waitForConnection"></A> +<div class="jd-details api "> + <h4 class="jd-details-title"> + <span class="normal"> + <code> + <a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a> + </code> + </span> + <span class="sympad">waitForConnection</span> + <span class="normal"> + (<em>float</em> timeout, + <em>string</em> deviceId) + </span> + </h4> + <div class="jd-details-descr"> + + <div class="jd-tagdata jd-tagdescr"> + <p> + Tries to make a connection between the <code>monkeyrunner</code> backend and the + specified device or emulator. + </p> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Arguments</h5> + <table class="jd-tagtable"> + <tr> + <th>timeout</th> + <td> + The number of seconds to wait for a connection. The default is to wait forever. + </td> + </tr> + <tr> + <th> + deviceId + </th> + <td> + A regular expression that specifies the serial number of the device or emulator. See + the topic + <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a> + for a description of device and emulator serial numbers. + </td> + </tr> + </table> + </div> + <div class="jd-tagdata"> + <h5 class="jd-tagtitle">Returns</h5> + <ul class="nolist"> + <li> + A <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code> + instance for the device or emulator. Use this object to control and communicate with the + device or emulator. + </li> + </ul> + </div> + </div> +</div> diff --git a/docs/html/guide/developing/tools/index.jd b/docs/html/guide/developing/tools/index.jd index 6e9fde1..0e10377 100644 --- a/docs/html/guide/developing/tools/index.jd +++ b/docs/html/guide/developing/tools/index.jd @@ -3,27 +3,27 @@ page.title=Tools Overview <img src="{@docRoot}assets/images/android_wrench.png" alt="" align="right"> -<p>The Android SDK includes a variety of custom tools that help you develop mobile -applications on the Android platform. The most important of these are the Android -Emulator and the Android Development Tools plugin for Eclipse, but the SDK also -includes a variety of other tools for debugging, packaging, and installing your +<p>The Android SDK includes a variety of custom tools that help you develop mobile +applications on the Android platform. The most important of these are the Android +Emulator and the Android Development Tools plugin for Eclipse, but the SDK also +includes a variety of other tools for debugging, packaging, and installing your applications on the emulator. </p> - + <dl> <dt><a href="adt.html">Android Development Tools Plugin</a> (for the Eclipse IDE)</dt> - <dd>The ADT plugin adds powerful extensions to the Eclipse integrated environment, - making creating and debugging your Android applications easier and faster. If you - use Eclipse, the ADT plugin gives you an incredible boost in developing Android + <dd>The ADT plugin adds powerful extensions to the Eclipse integrated environment, + making creating and debugging your Android applications easier and faster. If you + use Eclipse, the ADT plugin gives you an incredible boost in developing Android applications.</dd> <dt><a href="emulator.html">Android Emulator</a></dt> - <dd>A QEMU-based device-emulation tool that you can use to design, + <dd>A QEMU-based device-emulation tool that you can use to design, debug, and test your applications in an actual Android run-time environment. </dd> <dt><a href="avd.html">Android Virtual Devices (AVDs)</a></dt> <dd>Virtual device configurations that you create, to model device characteristics in the Android Emulator. In each configuration, you can specify the Android platform to run, the hardware options, and the - emulator skin to use. Each AVD functions as an independent device with + emulator skin to use. Each AVD functions as an independent device with it's own storage for user data, SD card, and so on. </dd> <dt><a href="hierarchy-viewer.html">Hierarchy Viewer</a></dt> @@ -37,53 +37,53 @@ applications on the emulator. </p> efficiency. </dd> - <dt><a href="draw9patch.html">Draw 9-patch</a></dt> - <dd>The Draw 9-patch tool allows you to easily create a - {@link android.graphics.NinePatch} graphic using a WYSIWYG editor. It also previews stretched - versions of the image, and highlights the area in which content is allowed. - </dd> + <dt><a href="draw9patch.html">Draw 9-patch</a></dt> + <dd>The Draw 9-patch tool allows you to easily create a + {@link android.graphics.NinePatch} graphic using a WYSIWYG editor. It also previews stretched + versions of the image, and highlights the area in which content is allowed. + </dd> - <dt><a href="ddms.html" >Dalvik Debug Monitor + <dt><a href="ddms.html" >Dalvik Debug Monitor Service</a> (ddms)</dt> - <dd>Integrated with Dalvik, the Android platform's custom VM, this tool - lets you manage processes on an emulator or device and assists in debugging. - You can use it to kill processes, select a specific process to debug, - generate trace data, view heap and thread information, take screenshots - of the emulator or device, and more. </dd> - + <dd>Integrated with Dalvik, the Android platform's custom VM, this tool + lets you manage processes on an emulator or device and assists in debugging. + You can use it to kill processes, select a specific process to debug, + generate trace data, view heap and thread information, take screenshots + of the emulator or device, and more. </dd> + <dt><a href="adb.html" >Android Debug Bridge</a> (adb)</dt> - <dd>The adb tool lets you install your application's .apk files on an - emulator or device and access the emulator or device from a command line. - You can also use it to link a standard debugger to application code running + <dd>The adb tool lets you install your application's .apk files on an + emulator or device and access the emulator or device from a command line. + You can also use it to link a standard debugger to application code running on an Android emulator or device.</dd> - <dt><a href="aapt.html">Android Asset + <dt><a href="aapt.html">Android Asset Packaging Tool</a> (aapt)</dt> - <dd>The aapt tool lets you create .apk files containing the binaries and + <dd>The aapt tool lets you create .apk files containing the binaries and resources of Android applications.</dd> - <dt><a href="aidl.html" >Android Interface + <dt><a href="aidl.html" >Android Interface Description Language</a> (aidl)</dt> <dd>Lets you generate code for an interprocess interface, such as what a service might use.</dd> <dt><a href="adb.html#sqlite">sqlite3</a></dt> - <dd>Included as a convenience, this tool lets you access the SQLite data + <dd>Included as a convenience, this tool lets you access the SQLite data files created and used by Android applications.</dd> <dt><a href="traceview.html" >Traceview</a></dt> - <dd> This tool produces graphical analysis views of trace log data that you + <dd> This tool produces graphical analysis views of trace log data that you can generate from your Android application. </dd> <dt><a href="othertools.html#mksdcard">mksdcard</a></dt> - <dd>Helps you create a disk image that you can use with the emulator, + <dd>Helps you create a disk image that you can use with the emulator, to simulate the presence of an external storage card (such as an SD card).</dd> <dt><a href="othertools.html#dx">dx</a></dt> - <dd>The dx tool rewrites .class bytecode into Android bytecode + <dd>The dx tool rewrites .class bytecode into Android bytecode (stored in .dex files.)</dd> - <dt><a href="monkey.html">UI/Application + <dt><a href="monkey.html">UI/Application Exerciser Monkey</a></dt> <dd>The Monkey is a program that runs on your emulator or device and generates pseudo-random streams of user events such as clicks, touches, or gestures, as well as a number of system- @@ -92,9 +92,9 @@ efficiency. <dt><a href="othertools.html#android">android</a></dt> <dd>A script that lets you manage AVDs and generate <a - href="http://ant.apache.org/" title="Ant">Ant</a> build files that + href="http://ant.apache.org/" title="Ant">Ant</a> build files that you can use to compile your Android applications. </dd> - + <dt><a href="zipalign.html">zipalign</a></dt> <dd>An important .apk optimization tool. This tool ensures that all uncompressed data starts with a particular alignment relative to the start of the file. This should always be used diff --git a/docs/html/guide/developing/tools/monkeyrunner_concepts.jd b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd new file mode 100644 index 0000000..1838905 --- /dev/null +++ b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd @@ -0,0 +1,308 @@ +page.title=monkeyrunner +@jd:body + +<div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + <ol> + <li> + <a href="#SampleProgram">A Simple monkeyrunner Program</a> + </li> + <li> + <a href="#APIClasses">The monkeyrunner API</a> + </li> + <li> + <a href="#RunningMonkeyRunner">Running monkeyrunner</a> + </li> + <li> + <a href="#Help">monkeyrunner Built-in Help</a> + </li> + <li> + <a href="#Plugins">Extending monkeyrunner with Plugins</a> + </li> + </ol> + <h2>See Also</h2> + <ol> + <li> + <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a> + </li> + </ol> + </div> +</div> +<p> + The monkeyrunner tool provides an API for writing programs that control an Android device + or emulator from outside of Android code. With monkeyrunner, you can write a Python program + that installs an Android application or test package, runs it, sends keystrokes to it, + takes screenshots of its user interface, and stores screenshots on the workstation. The + monkeyrunner tool is primarily designed to test applications and devices at the + functional/framework level and for running unit test suites, but you are free to use it for + other purposes. +</p> +<p> + The monkeyrunner tool is not related to the + <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>, + also known as the <code>monkey</code> tool. The <code>monkey</code> tool runs in an + <code><a href="{@docRoot}guide/developing/tools/adb.html">adb</a></code> shell directly on the + device or emulator and generates pseudo-random streams of user and system events. In comparison, + the monkeyrunner tool controls devices and emulators from a workstation by sending specific + commands and events from an API. +</p> +<p> + The monkeyrunner tool provides these unique features for Android testing: +</p> +<ul> + <li> + Multiple device control: The monkeyrunner API can apply one or more + test suites across multiple devices or emulators. You can physically attach all the devices + or start up all the emulators (or both) at once, connect to each one in turn + programmatically, and then run one or more tests. You can also start up an emulator + configuration programmatically, run one or more tests, and then shut down the emulator. + </li> + <li> + Functional testing: monkeyrunner can run an automated start-to-finish test of an Android + application. You provide input values with keystrokes or touch events, and view the results + as screenshots. + </li> + <li> + Regression testing - monkeyrunner can test application stability by running an application + and comparing its output screenshots to a set of screenshots that are known to be correct. + </li> + <li> + Extensible automation - Since monkeyrunner is an API toolkit, you can develop an entire + system of Python-based modules and programs for controlling Android devices. Besides using + the monkeyrunner API itself, you can use the standard Python + <code><a href="http://docs.python.org/library/os.html">os</a></code> and + <code><a href="http://docs.python.org/library/subprocess.html">subprocess</a></code> + modules to call Android tools such as + <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>. + <p> + You can also add your own classes to the monkeyrunner API. This is described + in more detail in the section + <a href="#Plugins">Extending monkeyrunner with plugins</a>. + </p> + </li> +</ul> +<p> + The monkeyrunner tool uses <a href="http://www.jython.org/">Jython</a>, a + implementation of Python that uses the Java programming language. Jython allows the + monkeyrunner API to interact easily with the Android framework. With Jython you can + use Python syntax to access the constants, classes, and methods of the API. +</p> + +<h2 id="SampleProgram">A Simple monkeyrunner Program</h2> +<p> + Here is a simple monkeyrunner program that connects to a device, creating a + <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code> + object. Using the <code>MonkeyDevice</code> object, the program installs an Android application + package, runs one of its activities, and sends key events to the activity. + The program then takes a screenshot of the result, creating a + <code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code> object. + From this object, the program writes out a <code>.png</code> file containing the screenshot. +</p> +<pre> +# Imports the monkeyrunner modules used by this program +from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice + +# Connects to the current device, returning a MonkeyDevice object +device = MonkeyRunner.waitForConnection() + +# Installs the Android package. Notice that this method returns a boolean, so you can test +# to see if the installation worked. +device.installPackage('myproject/bin/MyApplication.apk') + +# Runs an activity in the application +device.startActivity(component='com.example.android.myapplication.MainActivity') + +# Presses the Menu button +device.press('KEYCODE_MENU','DOWN_AND_UP') + +# Takes a screenshot +result = device.takeSnapShot + +# Writes the screenshot to a file +result.writeToFile('myproject/shot1.png','png') +</pre> + +<h2 id="APIClasses">The monkeyrunner API</h2> +<p> + The monkeyrunner API is contained in three modules in the package + <code>com.android.monkeyrunner</code>: +</p> +<ul> + <li> + <code><a href="{@docRoot}guide/developing/tools/MonkeyRunner.html">MonkeyRunner</a></code>: + A class of utility methods for monkeyrunner programs. This class provides a method for + connecting monkeyrunner to a device or emulator. It also provides methods for + creating UIs for a monkeyrunner program and for displaying the built-in help. + </li> + <li> + <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code>: + Represents a device or emulator. This class provides methods for installing and + uninstalling packages, starting an Activity, and sending keyboard or touch events to an + application. You also use this class to run test packages. + </li> + <li> + <code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code>: + Represents a screen capture image. This class provides methods for capturing screens, + converting bitmap images to various formats, comparing two MonkeyImage objects, and + writing an image to a file. + </li> +</ul> +<p> + In a Python program, you access each class as a Python module. The monkeyrunner tool + does not import these modules automatically. To import a module, use the + Python <code>from</code> statement: +</p> +<pre> +from com.android.monkeyrunner import <module> +</pre> +<p> + where <code><module></code> is the class name you want to import. You can import more + than one module in the same <code>from</code> statement by separating the module names with + commas. +</p> +<h2 id="RunningMonkeyRunner">Running monkeyrunner</h2> +<p> + You can either run monkeyrunner programs from a file, or enter monkeyrunner statements in + an interactive session. You do both by invoking the <code>monkeyrunner</code> command + which is found in the <code>tools/</code> subdirectory of your SDK directory. + If you provide a filename as an argument, the <code>monkeyrunner</code> command + runs the file's contents as a Python program; otherwise, it starts an interactive session. +</p> +<p> + The syntax of the <code>monkeyrunner</code> command is +</p> +<pre> +monkeyrunner -plugin <plugin_jar> <program_filename> <program_options> +</pre> +<p> +Table 1 explains the flags and arguments. +</p> +<p class="table-caption" id="table1"> + <strong>Table 1.</strong> <code>monkeyrunner</code> flags and arguments.</p> + +<table> + <tr> + <th>Argument</th> + <th>Description</th> + </tr> + <tr> + <td> + <nobr> + <code>-plugin <plugin_jar></code> + </nobr> + </td> + <td> + (Optional) Specifies a <code>.jar</code> file containing a plugin for monkeyrunner. + To learn more about monkeyrunner plugins, see + <a href="#Plugins">Extending monkeyrunner with plugins</a>. To specify more than one + file, include the argument multiple times. + </td> + </tr> + <tr> + <td> + <nobr> + <code><program_filename></code> + </nobr> + </td> + <td> + If you provide this argument, the <code>monkeyrunner</code> command runs the contents + of the file as a Python program. If the argument is not provided, the command starts an + interactive session. + </td> + </tr> + <tr> + <td> + <code><program_options></code> + </td> + <td> + (Optional) Flags and arguments for the program in <program_file>. + </td> + </tr> +</table> +<h2 id="Help">monkeyrunner Built-in Help</h2> +<p> + You can generate an API reference for monkeyrunner by running: +</p> +<pre> +monkeyrunner <format> help.py <outfile> +</pre> +<p> +The arguments are: +</p> + <ul> + <li> + <code><format></code> is either <code>text</code> for plain text output + or <code>html</code> for HTML output. + </li> + <li> + <code><outfile></code> is a path-qualified name for the output file. + </li> + </ul> +<h2 id="Plugins">Extending monkeyrunner with Plugins</h2> +<p> + You can extend the monkeyrunner API with classes you write in the Java programming language + and build into one or more <code>.jar</code> files. You can use this feature to extend the + monkeyrunner API with your own classes or to extend the existing classes. You can also use this + feature to initialize the monkeyrunner environment. +</p> +<p> + To provide a plugin to monkeyrunner, invoke the <code>monkeyrunner</code> command with the + <code>-plugin <plugin_jar></code> argument described in + <a href="#table1">table 1</a>. +</p> +<p> + In your plugin code, you can import and extend the the main monkeyrunner classes + <code>MonkeyDevice</code>, <code>MonkeyImage</code>, and <code>MonkeyRunner</code> in + <code>com.android.monkeyrunner</code> (see <a href="#APIClasses">The monkeyrunner API</a>). +</p> +<p> + Note that plugins do not give you access to the Android SDK. You can't import packages + such as <code>com.android.app</code>. This is because monkeyrunner interacts with the + device or emulator below the level of the framework APIs. +</p> +<h3>The plugin startup class</h3> +<p> + The <code>.jar</code> file for a plugin can specify a class that is instantiated before + script processing starts. To specify this class, add the key + <code>MonkeyRunnerStartupRunner</code> to the <code>.jar</code> file's + manifest. The value should be the name of the class to run at startup. The following + snippet shows how you would do this within an <code>ant</code> build script: +</p> +<pre> +<jar jarfile="myplugin" basedir="${build.dir}"> +<manifest> +<attribute name="MonkeyRunnerStartupRunner" value="com.myapp.myplugin"/> +</manifest> +</jar> + + +</pre> +<p> + To get access to monkeyrunner's runtime environment, the startup class can implement + <code>com.google.common.base.Predicate<PythonInterpreter></code>. For example, this + class sets up some variables in the default namespace: +</p> +<pre> +package com.android.example; + +import com.google.common.base.Predicate; +import org.python.util.PythonInterpreter; + +public class Main implements Predicate<PythonInterpreter> { + @Override + public boolean apply(PythonInterpreter anInterpreter) { + + /* + * Examples of creating and initializing variables in the monkeyrunner environment's + * namespace. During execution, the monkeyrunner program can refer to the variables "newtest" + * and "use_emulator" + * + */ + anInterpreter.set("newtest", "enabled"); + anInterpreter.set("use_emulator", 1); + + return true; + } +} +</pre> diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 2b80342..a43e334 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -254,34 +254,45 @@ <li><a href="<?cs var:toroot?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li> </ul> </li> + <li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html"> + <span class="en">Device Administration</span> + </a> <span class="new">new!</span> + </li> <li class="toggle-list"> <div> - <a href="<?cs var:toroot ?>guide/topics/testing/index.html"> + <a href="<?cs var:toroot?>guide/topics/testing/index.html"> <span class="en">Testing</span> - </a> <span class="new">new!</span> + </a> </div> <ul> - <li><a href="<?cs var:toroot?>guide/topics/testing/testing_android.html"> + <li> + <a href="<?cs var:toroot?>guide/topics/testing/testing_android.html"> <span class="en">Testing Fundamentals</span></a> + <span class="new">new!</span> </li> - <li><a href="<?cs var:toroot?>guide/topics/testing/activity_testing.html"> + <li> + <a href="<?cs var:toroot?>guide/topics/testing/activity_testing.html"> <span class="en">Activity Testing</span></a> + <span class="new">new!</span> </li> - <li><a href="<?cs var:toroot ?>guide/topics/testing/contentprovider_testing.html"> + <li> + <a href="<?cs var:toroot?>guide/topics/testing/contentprovider_testing.html"> <span class="en">Content Provider Testing</span></a> + <span class="new">new!</span> </li> - <li><a href="<?cs var:toroot ?>guide/topics/testing/service_testing.html"> + <li> + <a href="<?cs var:toroot?>guide/topics/testing/service_testing.html"> <span class="en">Service Testing</span></a> + <span class="new">new!</span> </li> - <li><a href="<?cs var:toroot ?>guide/topics/testing/what_to_test.html"> + <li> + <a href="<?cs var:toroot ?>guide/topics/testing/what_to_test.html"> <span class="en">What To Test</span></a> + <span class="new">new!</span> </li> + </ul> </li> - <li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html"> - <span class="en">Device Administration</span> - </a> <span class="new">new!</span> - </li> </ul> </li> @@ -336,6 +347,7 @@ <span class="en">Testing in Eclipse, with ADT</span> </a> </li> + <li> <a href="<?cs var:toroot ?>guide/developing/testing/testing_otheride.html"> <span class="en">Testing in Other IDEs</span> @@ -363,6 +375,34 @@ <li><a href="<?cs var:toroot ?>guide/developing/tools/layoutopt.html">layoutopt</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/othertools.html#mksdcard">mksdcard</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/monkey.html">Monkey</a></li> + <li class="toggle-list"> + <div> + <a href="<?cs var:toroot?>guide/developing/tools/monkeyrunner_concepts.html"> + <span class="en">monkeyrunner</span> + </a> + <span class="new">new!</span> + </div> + <ul> + <li> + <a href="<?cs var:toroot?>guide/developing/tools/MonkeyDevice.html"> + <span class="en">MonkeyDevice</span> + </a> + <span class="new">new!</span> + </li> + <li> + <a href="<?cs var:toroot?>guide/developing/tools/MonkeyImage.html"> + <span class="en">MonkeyImage</span> + </a> + <span class="new">new!</span> + </li> + <li> + <a href="<?cs var:toroot?>guide/developing/tools/MonkeyRunner.html"> + <span class="en">MonkeyRunner</span> + </a> + <span class="new">new!</span> + </li> + </ul> + </li> <li><a href="<?cs var:toroot ?>guide/developing/tools/adb.html#sqlite">sqlite3</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/traceview.html" >Traceview</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html" >zipalign</a></li> diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index 1d6ab25..d868599 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -129,9 +129,8 @@ Menu. See <a href="menu-resource.html">Menu Resource</a>.</td> <tr> <td><code>raw/</code></td> - <td><p>Arbitrary files to save in their raw form. Files in here are not compressed by the -system. To open these resources with a raw {@link java.io.InputStream}, call {@link -android.content.res.Resources#openRawResource(int) + <td><p>Arbitrary files to save in their raw form. To open these resources with a raw +{@link java.io.InputStream}, call {@link android.content.res.Resources#openRawResource(int) Resources.openRawResource()} with the resource ID, which is {@code R.raw.<em>filename</em>}.</p> <p>However, if you need access to original file names and file hierarchy, you might consider saving some resources in the {@code diff --git a/docs/html/guide/topics/testing/index.jd b/docs/html/guide/topics/testing/index.jd index b75656f..762a897 100644 --- a/docs/html/guide/topics/testing/index.jd +++ b/docs/html/guide/topics/testing/index.jd @@ -59,6 +59,20 @@ page.title=Testing which guides you through a more complex testing scenario. </li> </ul> +<h4>Tools</h4> +<ul> + <li> + The + <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>, + usually called Monkey, is a command-line tool that sends pseudo-random + streams of keystrokes, touches, and gestures to a device. + </li> + <li> + The <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a> tool + is an API and execution environment. You use monkeyrunner with Python programs + to test applications and devices. + </li> +</ul> <h4>Samples</h4> <ul> <li> diff --git a/docs/html/guide/topics/testing/testing_android.jd b/docs/html/guide/topics/testing/testing_android.jd index 2a4c949..d4b0dcc 100755 --- a/docs/html/guide/topics/testing/testing_android.jd +++ b/docs/html/guide/topics/testing/testing_android.jd @@ -38,7 +38,7 @@ page.title=Testing Fundamentals <a href="#TestResults">Seeing Test Results</a> </li> <li> - <a href="#Monkeys">Monkey and MonkeyRunner</a> + <a href="#Monkeys">monkey and monkeyrunner</a> </li> <li> <a href="#PackageNames">Working With Package Names</a> @@ -77,6 +77,13 @@ page.title=Testing Fundamentals <a href="{@docRoot}guide/developing/testing/testing_otheride.html"> Testing in Other IDEs</a> </li> + <li> + <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html"> + monkeyrunner</a> + </li> + <li> + <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a> + </li> </ol> </div> </div> @@ -112,10 +119,10 @@ page.title=Testing Fundamentals </li> <li> The SDK also provides - <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a>, an API for - testing devices with Jython scripts, and <a - href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a>, a command-line tool for - stress-testing UIs by sending pseudo-random events to a device. + <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a>, an API + testing devices with Python programs, and <a + href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>, + a command-line tool for stress-testing UIs by sending pseudo-random events to a device. </li> </ul> <p> @@ -540,25 +547,28 @@ page.title=Testing Fundamentals <a href="{@docRoot}guide/developing/testing/testing_otheride.html#RunTestsCommand"> Testing in Other IDEs</a>. </p> -<h2 id="Monkeys">Monkey and MonkeyRunner</h2> +<h2 id="Monkeys">monkey and monkeyrunner</h2> <p> The SDK provides two tools for functional-level application testing: </p> <ul> <li> - <a href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a> is a command-line - tool that sends pseudo-random streams of keystrokes, touches, and gestures to a - device. You run it with the <a href="{@docRoot}guide/developing/tools/adb.html"> - Android Debug Bridge</a> (adb) tool. You use it to stress-test your application and - report back errors that are encountered. You can repeat a stream of events by - running the tool each time with the same random number seed. +The <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>, + usually called "monkey", is a command-line tool that sends pseudo-random streams of + keystrokes, touches, and gestures to a device. You run it with the + <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a> (adb) tool. + You use it to stress-test your application and report back errors that are encountered. + You can repeat a stream of events by running the tool each time with the same random + number seed. </li> <li> - <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a> is a - Jython API that you use in test programs written in Python. The API includes functions - for connecting to a device, installing and uninstalling packages, taking screenshots, - comparing two images, and running a test package against an application. Using the API - with Python, you can write a wide range of large, powerful, and complex tests. + The <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a> tool + is an API and execution environment for test programs written in Python. The API + includes functions for connecting to a device, installing and uninstalling packages, + taking screenshots, comparing two images, and running a test package against an + application. Using the API, you can write a wide range of large, powerful, and complex + tests. You run programs that use the API with the <code>monkeyrunner</code> command-line + tool. </li> </ul> <h2 id="PackageNames">Working With Package names</h2> diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd index 3b4ccb0..cef057e 100644 --- a/docs/html/resources/dashboard/platform-versions.jd +++ b/docs/html/resources/dashboard/platform-versions.jd @@ -52,7 +52,7 @@ Android Market within a 14-day period ending on the data collection date noted b <div class="dashboard-panel"> <img alt="" height="250" width="460" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:9.7,16.4,0.1,40.4,33.4&chl= +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:7.9,15.0,0.1,40.8,36.2&chl= Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, 6fad0c" /> @@ -62,13 +62,13 @@ Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, <th>API Level</th> <th>Distribution</th> </tr> -<tr><td>Android 1.5</td><td>3</td><td>9.7%</td></tr> -<tr><td>Android 1.6</td><td>4</td><td>16.4%</td></tr> -<tr><td>Android 2.1</td><td>7</td><td>40.4%</td></tr> -<tr><td>Android 2.2</td><td>8</td><td>33.4%</td></tr> +<tr><td>Android 1.5</td><td>3</td><td>7.9%</td></tr> +<tr><td>Android 1.6</td><td>4</td><td>15.0%</td></tr> +<tr><td>Android 2.1</td><td>7</td><td>40.8%</td></tr> +<tr><td>Android 2.2</td><td>8</td><td>36.2%</td></tr> </table> -<p><em>Data collected during two weeks ending on October 1, 2010</em></p> +<p><em>Data collected during two weeks ending on November 1, 2010</em></p> <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p> </div><!-- end dashboard-panel --> @@ -96,19 +96,18 @@ Android Market within a 14-day period ending on the date indicated on the x-axis <div class="dashboard-panel"> <img alt="" height="250" width="660" style="padding:5px;background:#fff" -src="http://chart.apis.google.com/chart?cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100& -chxl=0:|2010/04/01|04/15|05/01|05/15|06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|2010/10/01|1:|0 -%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12& -chxtc=0,5&chd=t:99.6,99.6,99.6,99.7,100.6,101.1,99.9,100.0,100.0,99.8,99.9,100.0,100.0|61.5,61.7,62. -3,63.5,73.0,76.4,78.6,81.1,84.5,86.6,88.0,89.3,90.3|29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1,68. -2,70.4,72.2,73.9|4.0,28.3,32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8|0.0,0.0,0.0,0.0,0.8 -,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4&chm=tAndroid+1.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid+1 -.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|tAndroid+2.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid+2 -.1,2f4708,3,1,15,,t:-30:-40|b,89cf19,3,4,0|tAndroid+2.2,131d02,4,9,15,,t::-5|B,6fad0c,4,5,0&chg=7,25 -&chdl=Android+1.5|Android+1.6|Android+2.0.1|Android+2.1|Android+2.2&chco=add274,9ad145,84c323,6ba213 -,507d08" /> - -<p><em>Last historical dataset collected during two weeks ending on October 1, 2010</em></p> +src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100& +chxl=0%3A%7C2010/05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/ +01%7C10/15%7C2010/11/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25 +%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.9,99.3,100.3,100.8,99.7,99. +8,99.8,99.7,99.8,99.9,99.9,99.9,99.9|61.6,63.1,72.7,76.1,78.4,80.9,84.3,86.5,87.9,89.2,90.2,91.1,92. +0|32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77.0|0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3, +27.8,32.1,33.4,34.5,36.2&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6, +5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202. +2,131d02,3,7,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1| +Android%202.2&chco=add274,94d134,73ad18,507d08" /> + +<p><em>Last historical dataset collected during two weeks ending on November 1, 2010</em></p> </div><!-- end dashboard-panel --> diff --git a/docs/html/resources/faq/framework.jd b/docs/html/resources/faq/framework.jd index f4b8db0..4a7a3fc 100644 --- a/docs/html/resources/faq/framework.jd +++ b/docs/html/resources/faq/framework.jd @@ -68,12 +68,17 @@ Preferences</a> storage mechanism.</p> <p>For sharing complex non-persistent user-defined objects for short duration, the following approaches are recommended: </p> - <h4>The android.app.Application class</h4> - <p>The android.app.Application is a base class for those who need to -maintain global application state. It can be accessed via -getApplication() from any Activity or Service. It has a couple of -life-cycle methods and will be instantiated by Android automatically if -your register it in AndroidManifest.xml.</p> + <h4>Singleton class</h4> + <p>You can take advantage of the fact that your application +components run in the same process through the use of a singleton. +This is a class that is designed to have only one instance. It +has a static method with a name such as <code>getInstance()</code> +that returns the instance; the first time this method is called, +it creates the global instance. Because all callers get the same +instance, they can use this as a point of interaction. For +example activity A may retrieve the instance and call setValue(3); +later activity B may retrieve the instance and call getValue() to +retrieve the last set value.</p> <h4>A public static field/method</h4> <p>An alternate way to make data accessible across Activities/Services is to use <em>public static</em> @@ -90,18 +95,6 @@ Long based on a counter or time stamp) to the recipient activity via intent extras. The recipient activity retrieves the object using this key.</p> - <h4>A Singleton class</h4> - <p>There are advantages to using a static Singleton, such as you can -refer to them without casting getApplication() to an -application-specific class, or going to the trouble of hanging an -interface on all your Application subclasses so that your various -modules can refer to that interface instead. </p> -<p>But, the life cycle of a static is not well under your control; so -to abide by the life-cycle model, the application class should initiate and -tear down these static objects in the onCreate() and onTerminate() methods -of the Application Class</p> -</p> - <h3>Persistent Objects</h3> <p>Even while an application appears to continue running, the system @@ -146,15 +139,11 @@ call.</p> <h2>If an Activity starts a remote service, is there any way for the Service to pass a message back to the Activity?</h2> -<p>The remote service can define a callback interface and register it with the -clients to callback into the clients. The -{@link android.os.RemoteCallbackList RemoteCallbackList} class provides methods to -register and unregister clients with the service, and send and receive -messages.</p> - -<p>The sample code for remote service callbacks is given in <a -href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">ApiDemos/RemoteService</a></p> - +<p>See the {@link android.app.Service} documentation's for examples of +how clients can interact with a service. You can take advantage of the +fact that your components run in the same process to greatly simplify +service interaction from the generic remote case, as shown by the "Local +Service Sample". In some cases techniques like singletons may also make sense. <a name="6" id="6"></a> diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index a25fad4..289348a 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -51,7 +51,7 @@ public class ColorDrawable extends Drawable { */ public ColorDrawable(int color) { this(null); - mState.mBaseColor = mState.mUseColor = color; + setColor(color); } private ColorDrawable(ColorState state) { @@ -72,6 +72,25 @@ public class ColorDrawable extends Drawable { } /** + * Gets the drawable's color value. + * + * @return int The color to draw. + */ + public int getColor() { + return mState.mUseColor; + } + + /** + * Sets the drawable's color value. This action will clobber the results of prior calls to + * {@link #setAlpha(int)} on this object, which side-affected the underlying color. + * + * @param color The color to draw. + */ + public void setColor(int color) { + mState.mBaseColor = mState.mUseColor = color; + } + + /** * Returns the alpha value of this drawable's color. * * @return A value between 0 and 255. @@ -131,7 +150,7 @@ public class ColorDrawable extends Drawable { } final static class ColorState extends ConstantState { - int mBaseColor; // initial color. never changes + int mBaseColor; // base color, independent of setAlpha() int mUseColor; // basecolor modulated by setAlpha() int mChangingConfigurations; diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index f8ad5cc..dfd6ac8 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -199,6 +199,7 @@ public class Allocation extends BaseObj { throw new IllegalStateException("Resize only support for 1D allocations at this time."); } mRS.nAllocationResize1D(mID, dimX); + mRS.finish(); // Necessary because resize is fifoed and update is async. int typeID = mRS.nAllocationGetType(mID); mType = new Type(typeID, mRS); diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 9fd905f..1081c35 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -262,11 +262,15 @@ public: DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, DEVICE_OUT_AUX_DIGITAL = 0x400, + DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, + DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, DEVICE_OUT_DEFAULT = 0x8000, DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET | DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET | DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT), + DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | + DEVICE_OUT_ANLG_DOCK_HEADSET | DEVICE_OUT_DGTL_DOCK_HEADSET | + DEVICE_OUT_DEFAULT), DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), @@ -309,6 +313,8 @@ public: FORCE_WIRED_ACCESSORY, FORCE_BT_CAR_DOCK, FORCE_BT_DESK_DOCK, + FORCE_ANALOG_DOCK, + FORCE_DIGITAL_DOCK, NUM_FORCE_CONFIG, FORCE_DEFAULT = FORCE_NONE }; diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h index b009e1b..4fd281b 100644 --- a/include/media/stagefright/HardwareAPI.h +++ b/include/media/stagefright/HardwareAPI.h @@ -37,11 +37,11 @@ namespace android { // buffers. This is the mode that will be used when CPU access to the buffer is // required. // -// When Android native buffer use has been enabled, the OMX node must support -// only color formats in the range [OMX_COLOR_FormatAndroidPrivateStart, -// OMX_COLOR_FormatAndroidPrivateEnd). The node should then expect to receive +// When Android native buffer use has been enabled for a given port, the video +// color format for the port is to be interpreted as an Android pixel format +// rather than an OMX color format. The node should then expect to receive // UseAndroidNativeBuffer calls (via OMX_SetParameter) rather than UseBuffer -// calls. +// calls for that port. struct EnableAndroidNativeBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -75,16 +75,6 @@ struct StoreMetaDataInBuffersParams { OMX_BOOL bStoreMetaData; }; -// Color formats in the range [OMX_COLOR_FormatAndroidPrivateStart, -// OMX_COLOR_FormatAndroidPrivateEnd) will be converted to a gralloc pixel -// format when used to allocate Android native buffers via gralloc. The -// conversion is done by subtracting OMX_COLOR_FormatAndroidPrivateStart from -// the color format reported by the codec. -enum { - OMX_COLOR_FormatAndroidPrivateStart = 0xA0000000, - OMX_COLOR_FormatAndroidPrivateEnd = 0xB0000000, -}; - // A pointer to this struct is passed to OMX_SetParameter when the extension // index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is // given. This call will only be performed if a prior call was made with the diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 1f5ed7c..c692bc1 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -78,10 +78,6 @@ void ScriptC::setupScript(Context *rsc) if (dest) { *dest = ptr; - } else { - if (rsc->props.mLogScripts) { - LOGV("ScriptC::setupScript, NULL var binding address."); - } } } } @@ -404,16 +400,14 @@ static BCCvoid* symbolLookup(BCCvoid* pContext, const BCCchar* name) const ScriptCState::SymbolTable_t *sym; ScriptC *s = (ScriptC *)pContext; sym = ScriptCState::lookupSymbol(name); - if (sym) { - return sym->mPtr; + if (!sym) { + sym = ScriptCState::lookupSymbolCL(name); } - sym = ScriptCState::lookupSymbolCL(name); - if (sym) { - return sym->mPtr; + if (!sym) { + sym = ScriptCState::lookupSymbolGL(name); } - s->mEnviroment.mIsThreadable = false; - sym = ScriptCState::lookupSymbolGL(name); if (sym) { + s->mEnviroment.mIsThreadable &= sym->threadable; return sym->mPtr; } LOGE("ScriptC sym lookup failed for %s", name); @@ -425,7 +419,6 @@ extern unsigned rs_runtime_lib_bc_size; void ScriptCState::runCompiler(Context *rsc, ScriptC *s) { - LOGV("%p ScriptCState::runCompiler ", rsc); { StopWatch compileTimer("RenderScript compile time"); s->mBccScript = bccCreateScript(); diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index e5b5ba9..86a7ad3 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -89,6 +89,7 @@ public: struct SymbolTable_t { const char * mName; void * mPtr; + bool threadable; }; //static SymbolTable_t gSyms[]; static const SymbolTable_t * lookupSymbol(const char *); diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp index 9fadee0..ecae306 100644 --- a/libs/rs/rsScriptC_Lib.cpp +++ b/libs/rs/rsScriptC_Lib.cpp @@ -436,120 +436,120 @@ void SC_ForEach2(RsScript vs, // ::= d # double static ScriptCState::SymbolTable_t gSyms[] = { - { "__divsi3", (void *)&SC_divsi3 }, + { "__divsi3", (void *)&SC_divsi3, true }, // allocation - { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX }, - { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY }, - { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ }, - { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD }, - { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces }, - { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation }, + { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX, true }, + { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY, true }, + { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ, true }, + { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD, true }, + { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces, true }, + { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation, true }, - { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX }, - { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY }, - { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ }, + { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX, true }, + { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY, true }, + { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ, true }, - { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject }, - { "_Z10rsIsObject10rs_element", (void *)&SC_isObject }, + { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject10rs_element", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject }, - { "_Z10rsIsObject7rs_type", (void *)&SC_isObject }, + { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_type", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject }, - { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject }, + { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject }, - { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject }, + { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject }, - { "_Z10rsIsObject9rs_script", (void *)&SC_isObject }, + { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject9rs_script", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject }, - { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject }, + { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject }, - { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject }, + { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject }, - { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject }, + { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject }, - { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject }, + { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject }, - { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject }, + { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject, true }, - { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject }, - { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject }, - { "_Z10rsIsObject7rs_font", (void *)&SC_isObject }, + { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_font", (void *)&SC_isObject, true }, - { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty }, + { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty, true }, // Debug - { "_Z7rsDebugPKcf", (void *)&SC_debugF }, - { "_Z7rsDebugPKcff", (void *)&SC_debugFv2 }, - { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3 }, - { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4 }, - { "_Z7rsDebugPKcd", (void *)&SC_debugD }, - { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4 }, - { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3 }, - { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2 }, - { "_Z7rsDebugPKci", (void *)&SC_debugI32 }, - { "_Z7rsDebugPKcj", (void *)&SC_debugU32 }, + { "_Z7rsDebugPKcf", (void *)&SC_debugF, true }, + { "_Z7rsDebugPKcff", (void *)&SC_debugFv2, true }, + { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3, true }, + { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4, true }, + { "_Z7rsDebugPKcd", (void *)&SC_debugD, true }, + { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4, true }, + { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3, true }, + { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2, true }, + { "_Z7rsDebugPKci", (void *)&SC_debugI32, true }, + { "_Z7rsDebugPKcj", (void *)&SC_debugU32, true }, // Both "long" and "unsigned long" need to be redirected to their // 64-bit counterparts, since we have hacked Slang to use 64-bit // for "long" on Arm (to be similar to Java). - { "_Z7rsDebugPKcl", (void *)&SC_debugLL64 }, - { "_Z7rsDebugPKcm", (void *)&SC_debugULL64 }, - { "_Z7rsDebugPKcx", (void *)&SC_debugLL64 }, - { "_Z7rsDebugPKcy", (void *)&SC_debugULL64 }, - { "_Z7rsDebugPKcPKv", (void *)&SC_debugP }, + { "_Z7rsDebugPKcl", (void *)&SC_debugLL64, true }, + { "_Z7rsDebugPKcm", (void *)&SC_debugULL64, true }, + { "_Z7rsDebugPKcx", (void *)&SC_debugLL64, true }, + { "_Z7rsDebugPKcy", (void *)&SC_debugULL64, true }, + { "_Z7rsDebugPKcPKv", (void *)&SC_debugP, true }, // RS Math - { "_Z6rsRandi", (void *)&SC_randi }, - { "_Z6rsRandii", (void *)&SC_randi2 }, - { "_Z6rsRandf", (void *)&SC_randf }, - { "_Z6rsRandff", (void *)&SC_randf2 }, - { "_Z6rsFracf", (void *)&SC_frac }, + { "_Z6rsRandi", (void *)&SC_randi, true }, + { "_Z6rsRandii", (void *)&SC_randi2, true }, + { "_Z6rsRandf", (void *)&SC_randf, true }, + { "_Z6rsRandff", (void *)&SC_randf2, true }, + { "_Z6rsFracf", (void *)&SC_frac, true }, // time - { "_Z8rsSecondv", (void *)&SC_second }, - { "_Z8rsMinutev", (void *)&SC_minute }, - { "_Z6rsHourv", (void *)&SC_hour }, - { "_Z5rsDayv", (void *)&SC_day }, - { "_Z7rsMonthv", (void *)&SC_month }, - { "_Z6rsYearv", (void *)&SC_year }, - { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis }, - { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos }, - { "_Z7rsGetDtv", (void*)&SC_getDt }, - - { "_Z14rsSendToClienti", (void *)&SC_toClient }, - { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2 }, - { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking }, - { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2 }, - - { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach }, - //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2 }, + { "_Z8rsSecondv", (void *)&SC_second, true }, + { "_Z8rsMinutev", (void *)&SC_minute, true }, + { "_Z6rsHourv", (void *)&SC_hour, true }, + { "_Z5rsDayv", (void *)&SC_day, true }, + { "_Z7rsMonthv", (void *)&SC_month, true }, + { "_Z6rsYearv", (void *)&SC_year, true }, + { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis, true }, + { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos, true }, + { "_Z7rsGetDtv", (void*)&SC_getDt, false }, + + { "_Z14rsSendToClienti", (void *)&SC_toClient, false }, + { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2, false }, + { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking, false }, + { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2, false }, + + { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach, false }, + //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2, true }, //////////////////////////////////////////////////////////////////// - //{ "sinf_fast", (void *)&SC_sinf_fast }, - //{ "cosf_fast", (void *)&SC_cosf_fast }, + //{ "sinf_fast", (void *)&SC_sinf_fast, true }, + //{ "cosf_fast", (void *)&SC_cosf_fast, true }, - { NULL, NULL } + { NULL, NULL, false } }; const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) diff --git a/libs/rs/rsScriptC_LibCL.cpp b/libs/rs/rsScriptC_LibCL.cpp index 1b1a752..7031f84 100644 --- a/libs/rs/rsScriptC_LibCL.cpp +++ b/libs/rs/rsScriptC_LibCL.cpp @@ -88,11 +88,6 @@ static float SC_tanpi(float v) { return tanf(v * M_PI); } - //{ "logb", (void *)& }, - //{ "mad", (void *)& }, - //{ "nan", (void *)& }, - //{ "tgamma", (void *)& }, - ////////////////////////////////////////////////////////////////////////////// // Integer ////////////////////////////////////////////////////////////////////////////// @@ -198,105 +193,105 @@ static float SC_sign_f32(float value) static ScriptCState::SymbolTable_t gSyms[] = { // OpenCL math - { "_Z4acosf", (void *)&acosf }, - { "_Z5acoshf", (void *)&acoshf }, - { "_Z6acospif", (void *)&SC_acospi }, - { "_Z4asinf", (void *)&asinf }, - { "_Z5asinhf", (void *)&asinhf }, - { "_Z6asinpif", (void *)&SC_asinpi }, - { "_Z4atanf", (void *)&atanf }, - { "_Z5atan2ff", (void *)&atan2f }, - { "_Z6atanpif", (void *)&SC_atanpi }, - { "_Z7atan2piff", (void *)&SC_atan2pi }, - { "_Z4cbrtf", (void *)&cbrtf }, - { "_Z4ceilf", (void *)&ceilf }, - { "_Z8copysignff", (void *)©signf }, - { "_Z3cosf", (void *)&cosf }, - { "_Z4coshf", (void *)&coshf }, - { "_Z5cospif", (void *)&SC_cospi }, - { "_Z4erfcf", (void *)&erfcf }, - { "_Z3erff", (void *)&erff }, - { "_Z3expf", (void *)&expf }, - { "_Z4exp2f", (void *)&exp2f }, - { "_Z5exp10f", (void *)&SC_exp10 }, - { "_Z5expm1f", (void *)&expm1f }, - { "_Z4fabsf", (void *)&fabsf }, - { "_Z4fdimff", (void *)&fdimf }, - { "_Z5floorf", (void *)&floorf }, - { "_Z3fmafff", (void *)&fmaf }, - { "_Z4fmaxff", (void *)&fmaxf }, - { "_Z4fminff", (void *)&fminf }, // float fmin(float, float) - { "_Z4fmodff", (void *)&fmodf }, - { "_Z5fractfPf", (void *)&SC_fract }, - { "_Z5frexpfPi", (void *)&frexpf }, - { "_Z5hypotff", (void *)&hypotf }, - { "_Z5ilogbf", (void *)&ilogbf }, - { "_Z5ldexpfi", (void *)&ldexpf }, - { "_Z6lgammaf", (void *)&lgammaf }, - { "_Z3logf", (void *)&logf }, - { "_Z4log2f", (void *)&SC_log2 }, - { "_Z5log10f", (void *)&log10f }, - { "_Z5log1pf", (void *)&log1pf }, - //{ "logb", (void *)& }, - //{ "mad", (void *)& }, - { "modf", (void *)&modff }, - //{ "nan", (void *)& }, - { "_Z9nextafterff", (void *)&nextafterf }, - { "_Z3powff", (void *)&powf }, - { "_Z4pownfi", (void *)&SC_pown }, - { "_Z4powrff", (void *)&SC_powr }, - { "_Z9remainderff", (void *)&remainderf }, - { "remquo", (void *)&remquof }, - { "_Z4rintf", (void *)&rintf }, - { "_Z5rootnfi", (void *)&SC_rootn }, - { "_Z5roundf", (void *)&roundf }, - { "_Z5rsqrtf", (void *)&SC_rsqrt }, - { "_Z3sinf", (void *)&sinf }, - { "sincos", (void *)&SC_sincos }, - { "_Z4sinhf", (void *)&sinhf }, - { "_Z5sinpif", (void *)&SC_sinpi }, - { "_Z4sqrtf", (void *)&sqrtf }, - { "_Z3tanf", (void *)&tanf }, - { "_Z4tanhf", (void *)&tanhf }, - { "_Z5tanpif", (void *)&SC_tanpi }, - //{ "tgamma", (void *)& }, - { "_Z5truncf", (void *)&truncf }, + { "_Z4acosf", (void *)&acosf, true }, + { "_Z5acoshf", (void *)&acoshf, true }, + { "_Z6acospif", (void *)&SC_acospi, true }, + { "_Z4asinf", (void *)&asinf, true }, + { "_Z5asinhf", (void *)&asinhf, true }, + { "_Z6asinpif", (void *)&SC_asinpi, true }, + { "_Z4atanf", (void *)&atanf, true }, + { "_Z5atan2ff", (void *)&atan2f, true }, + { "_Z6atanpif", (void *)&SC_atanpi, true }, + { "_Z7atan2piff", (void *)&SC_atan2pi, true }, + { "_Z4cbrtf", (void *)&cbrtf, true }, + { "_Z4ceilf", (void *)&ceilf, true }, + { "_Z8copysignff", (void *)©signf, true }, + { "_Z3cosf", (void *)&cosf, true }, + { "_Z4coshf", (void *)&coshf, true }, + { "_Z5cospif", (void *)&SC_cospi, true }, + { "_Z4erfcf", (void *)&erfcf, true }, + { "_Z3erff", (void *)&erff, true }, + { "_Z3expf", (void *)&expf, true }, + { "_Z4exp2f", (void *)&exp2f, true }, + { "_Z5exp10f", (void *)&SC_exp10, true }, + { "_Z5expm1f", (void *)&expm1f, true }, + { "_Z4fabsf", (void *)&fabsf, true }, + { "_Z4fdimff", (void *)&fdimf, true }, + { "_Z5floorf", (void *)&floorf, true }, + { "_Z3fmafff", (void *)&fmaf, true }, + { "_Z4fmaxff", (void *)&fmaxf, true }, + { "_Z4fminff", (void *)&fminf, true }, // float fmin(float, float) + { "_Z4fmodff", (void *)&fmodf, true }, + { "_Z5fractfPf", (void *)&SC_fract, true }, + { "_Z5frexpfPi", (void *)&frexpf, true }, + { "_Z5hypotff", (void *)&hypotf, true }, + { "_Z5ilogbf", (void *)&ilogbf, true }, + { "_Z5ldexpfi", (void *)&ldexpf, true }, + { "_Z6lgammaf", (void *)&lgammaf, true }, + { "_Z3logf", (void *)&logf, true }, + { "_Z4log2f", (void *)&SC_log2, true }, + { "_Z5log10f", (void *)&log10f, true }, + { "_Z5log1pf", (void *)&log1pf, true }, + //{ "logb", (void *)&, true }, + //{ "mad", (void *)&, true }, + { "modf", (void *)&modff, true }, + //{ "nan", (void *)&, true }, + { "_Z9nextafterff", (void *)&nextafterf, true }, + { "_Z3powff", (void *)&powf, true }, + { "_Z4pownfi", (void *)&SC_pown, true }, + { "_Z4powrff", (void *)&SC_powr, true }, + { "_Z9remainderff", (void *)&remainderf, true }, + { "remquo", (void *)&remquof, true }, + { "_Z4rintf", (void *)&rintf, true }, + { "_Z5rootnfi", (void *)&SC_rootn, true }, + { "_Z5roundf", (void *)&roundf, true }, + { "_Z5rsqrtf", (void *)&SC_rsqrt, true }, + { "_Z3sinf", (void *)&sinf, true }, + { "sincos", (void *)&SC_sincos, true }, + { "_Z4sinhf", (void *)&sinhf, true }, + { "_Z5sinpif", (void *)&SC_sinpi, true }, + { "_Z4sqrtf", (void *)&sqrtf, true }, + { "_Z3tanf", (void *)&tanf, true }, + { "_Z4tanhf", (void *)&tanhf, true }, + { "_Z5tanpif", (void *)&SC_tanpi, true }, + //{ "tgamma", (void *)&, true }, + { "_Z5truncf", (void *)&truncf, true }, // OpenCL Int - { "_Z3absi", (void *)&SC_abs_i32 }, - { "_Z3abss", (void *)&SC_abs_i16 }, - { "_Z3absc", (void *)&SC_abs_i8 }, - { "_Z3clzj", (void *)&SC_clz_u32 }, - { "_Z3clzt", (void *)&SC_clz_u16 }, - { "_Z3clzh", (void *)&SC_clz_u8 }, - { "_Z3clzi", (void *)&SC_clz_i32 }, - { "_Z3clzs", (void *)&SC_clz_i16 }, - { "_Z3clzc", (void *)&SC_clz_i8 }, - { "_Z3maxjj", (void *)&SC_max_u32 }, - { "_Z3maxtt", (void *)&SC_max_u16 }, - { "_Z3maxhh", (void *)&SC_max_u8 }, - { "_Z3maxii", (void *)&SC_max_i32 }, - { "_Z3maxss", (void *)&SC_max_i16 }, - { "_Z3maxcc", (void *)&SC_max_i8 }, - { "_Z3minjj", (void *)&SC_min_u32 }, - { "_Z3mintt", (void *)&SC_min_u16 }, - { "_Z3minhh", (void *)&SC_min_u8 }, - { "_Z3minii", (void *)&SC_min_i32 }, - { "_Z3minss", (void *)&SC_min_i16 }, - { "_Z3mincc", (void *)&SC_min_i8 }, + { "_Z3absi", (void *)&SC_abs_i32, true }, + { "_Z3abss", (void *)&SC_abs_i16, true }, + { "_Z3absc", (void *)&SC_abs_i8, true }, + { "_Z3clzj", (void *)&SC_clz_u32, true }, + { "_Z3clzt", (void *)&SC_clz_u16, true }, + { "_Z3clzh", (void *)&SC_clz_u8, true }, + { "_Z3clzi", (void *)&SC_clz_i32, true }, + { "_Z3clzs", (void *)&SC_clz_i16, true }, + { "_Z3clzc", (void *)&SC_clz_i8, true }, + { "_Z3maxjj", (void *)&SC_max_u32, true }, + { "_Z3maxtt", (void *)&SC_max_u16, true }, + { "_Z3maxhh", (void *)&SC_max_u8, true }, + { "_Z3maxii", (void *)&SC_max_i32, true }, + { "_Z3maxss", (void *)&SC_max_i16, true }, + { "_Z3maxcc", (void *)&SC_max_i8, true }, + { "_Z3minjj", (void *)&SC_min_u32, true }, + { "_Z3mintt", (void *)&SC_min_u16, true }, + { "_Z3minhh", (void *)&SC_min_u8, true }, + { "_Z3minii", (void *)&SC_min_i32, true }, + { "_Z3minss", (void *)&SC_min_i16, true }, + { "_Z3mincc", (void *)&SC_min_i8, true }, // OpenCL 6.11.4 - { "_Z5clampfff", (void *)&SC_clamp_f32 }, - { "_Z7degreesf", (void *)&SC_degrees }, - { "_Z3maxff", (void *)&SC_max_f32 }, - { "_Z3minff", (void *)&SC_min_f32 }, - { "_Z3mixfff", (void *)&SC_mix_f32 }, - { "_Z7radiansf", (void *)&SC_radians }, - { "_Z4stepff", (void *)&SC_step_f32 }, - //{ "smoothstep", (void *)& }, - { "_Z4signf", (void *)&SC_sign_f32 }, - - { NULL, NULL } + { "_Z5clampfff", (void *)&SC_clamp_f32, true }, + { "_Z7degreesf", (void *)&SC_degrees, true }, + { "_Z3maxff", (void *)&SC_max_f32, true }, + { "_Z3minff", (void *)&SC_min_f32, true }, + { "_Z3mixfff", (void *)&SC_mix_f32, true }, + { "_Z7radiansf", (void *)&SC_radians, true }, + { "_Z4stepff", (void *)&SC_step_f32, true }, + //{ "smoothstep", (void *)&, true }, + { "_Z4signf", (void *)&SC_sign_f32, true }, + + { NULL, NULL, false } }; const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolCL(const char *sym) diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index b991cab..5b07e7b 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -441,53 +441,53 @@ static void SC_FontColor(float r, float g, float b, float a) // ::= d # double static ScriptCState::SymbolTable_t gSyms[] = { - { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment }, - { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore }, - { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex }, - { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster }, - { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler }, - { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture }, + { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment, false }, + { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore, false }, + { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex, false }, + { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster, false }, + { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler, false }, + { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture, false }, - { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix }, - { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix }, - { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix }, + { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix, false }, + { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix, false }, + { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix, false }, - { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix }, + { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix, false }, - { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor }, + { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor, false }, - { "_Z11rsgGetWidthv", (void *)&SC_getWidth }, - { "_Z12rsgGetHeightv", (void *)&SC_getHeight }, + { "_Z11rsgGetWidthv", (void *)&SC_getWidth, false }, + { "_Z12rsgGetHeightv", (void *)&SC_getHeight, false }, - { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2 }, - { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture }, - { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject }, + { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2, false }, + { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture, false }, + { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject, false }, - { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect }, - { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad }, - { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords }, - { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace }, + { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect, false }, + { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad, false }, + { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords, false }, + { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace, false }, - { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh }, - { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive }, - { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange }, - { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox }, + { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh, false }, + { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive, false }, + { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange, false }, + { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox, false }, - { "_Z13rsgClearColorffff", (void *)&SC_ClearColor }, - { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth }, + { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false }, + { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false }, - { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText }, - { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc }, - { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText }, - { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc }, + { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText, false }, + { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc, false }, + { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText, false }, + { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc, false }, - { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont }, - { "_Z12rsgFontColorffff", (void *)&SC_FontColor }, + { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont, false }, + { "_Z12rsgFontColorffff", (void *)&SC_FontColor, false }, // misc - { "_Z5colorffff", (void *)&SC_color }, + { "_Z5colorffff", (void *)&SC_color, false }, - { NULL, NULL } + { NULL, NULL, false } }; const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolGL(const char *sym) diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index b936c4d..58d4c56 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -1408,8 +1408,13 @@ String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { int32_t eventType = POWER_MANAGER_BUTTON_EVENT; - if (eventEntry->type == EventEntry::TYPE_MOTION) { + switch (eventEntry->type) { + case EventEntry::TYPE_MOTION: { const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); + if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { + return; + } + if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { switch (motionEntry->action) { case AMOTION_EVENT_ACTION_DOWN: @@ -1427,6 +1432,15 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { break; } } + break; + } + case EventEntry::TYPE_KEY: { + const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); + if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { + return; + } + break; + } } CommandEntry* commandEntry = postCommandLocked( diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 23f34d2..a49bb37 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -307,10 +307,13 @@ public class AudioService extends IAudioService.Stub { // Register for device connection intent broadcasts. IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(Intent.ACTION_DOCK_EVENT); + intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); + intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); context.registerReceiver(mReceiver, intentFilter); // Register for media button intent broadcasts. @@ -1816,6 +1819,12 @@ public class AudioService extends IAudioService.Stub { case Intent.EXTRA_DOCK_STATE_CAR: config = AudioSystem.FORCE_BT_CAR_DOCK; break; + case Intent.EXTRA_DOCK_STATE_LE_DESK: + config = AudioSystem.FORCE_ANALOG_DOCK; + break; + case Intent.EXTRA_DOCK_STATE_HE_DESK: + config = AudioSystem.FORCE_DIGITAL_DOCK; + break; case Intent.EXTRA_DOCK_STATE_UNDOCKED: default: config = AudioSystem.FORCE_NONE; @@ -1927,6 +1936,32 @@ public class AudioService extends IAudioService.Stub { mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); } } + } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) { + int state = intent.getIntExtra("state", 0); + Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state); + boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); + if (state == 0 && isConnected) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, + AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); + mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); + } else if (state == 1 && !isConnected) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, + AudioSystem.DEVICE_STATE_AVAILABLE, ""); + mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), ""); + } + } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) { + int state = intent.getIntExtra("state", 0); + Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state); + boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); + if (state == 0 && isConnected) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, + AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); + mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); + } else if (state == 1 && !isConnected) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, + AudioSystem.DEVICE_STATE_AVAILABLE, ""); + mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); + } } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); synchronized (mScoClients) { diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index a4818ff..5442791 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -243,6 +243,8 @@ public class AudioSystem public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100; public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200; public static final int DEVICE_OUT_AUX_DIGITAL = 0x400; + public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800; + public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000; public static final int DEVICE_OUT_DEFAULT = 0x8000; // input devices public static final int DEVICE_IN_COMMUNICATION = 0x10000; @@ -273,6 +275,8 @@ public class AudioSystem public static final int FORCE_WIRED_ACCESSORY = 5; public static final int FORCE_BT_CAR_DOCK = 6; public static final int FORCE_BT_DESK_DOCK = 7; + public static final int FORCE_ANALOG_DOCK = 8; + public static final int FORCE_DIGITAL_DOCK = 9; public static final int FORCE_DEFAULT = FORCE_NONE; // usage for serForceUse diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 381b77a..532a2df 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -83,6 +83,11 @@ public class MediaFile { private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL; + // Drm file types + public static final int FILE_TYPE_FL = 51; + private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL; + private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL; + // Other popular file types public static final int FILE_TYPE_TEXT = 100; public static final int FILE_TYPE_HTML = 101; @@ -189,6 +194,8 @@ public class MediaFile { addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST); addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST); + addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl"); + addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT); addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML); addFileType("HTML", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML); @@ -222,6 +229,11 @@ public class MediaFile { fileType <= LAST_PLAYLIST_FILE_TYPE); } + public static boolean isDrmFileType(int fileType) { + return (fileType >= FIRST_DRM_FILE_TYPE && + fileType <= LAST_DRM_FILE_TYPE); + } + public static MediaFileType getFileType(String path) { int lastDot = path.lastIndexOf("."); if (lastDot < 0) diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index e5fa0f8..5aabddf 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.IContentProvider; import android.database.Cursor; import android.database.SQLException; +import android.drm.DrmManagerClient; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Process; @@ -360,6 +361,7 @@ public class MediaScanner private ArrayList<FileCacheEntry> mPlayLists; private HashMap<String, Uri> mGenreCache; + private DrmManagerClient mDrmManagerClient = null; public MediaScanner(Context c) { native_setup(); @@ -447,6 +449,11 @@ public class MediaScanner } } + if (System.getProperty("drm.service.enabled").equals("true") + && MediaFile.isDrmFileType(mFileType)) { + mFileType = getFileTypeFromDrm(path); + } + String key = path; if (mCaseInsensitivePaths) { key = path.toLowerCase(); @@ -874,6 +881,27 @@ public class MediaScanner } } + private int getFileTypeFromDrm(String path) { + if (!System.getProperty("drm.service.enabled").equals("true")) { + return 0; + } + + int resultFileType = 0; + + if (mDrmManagerClient == null) { + mDrmManagerClient = new DrmManagerClient(mContext); + } + + if (mDrmManagerClient.canHandle(path, null)) { + String drmMimetype = mDrmManagerClient.getOriginalMimeType(path); + if (drmMimetype != null) { + mMimeType = drmMimetype; + resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype); + } + } + return resultFileType; + } + }; // end of anonymous MediaScannerClient instance private void prescan(String filePath, boolean prescanFiles) throws RemoteException { diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index 51647434..57ab3a1 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; +import android.os.Environment; import android.os.RemoteException; import android.provider.MediaStore.Audio; import android.provider.MediaStore.Files; @@ -45,6 +46,7 @@ public class MtpDatabase { private final String mVolumeName; private final Uri mObjectsUri; private final String mMediaStoragePath; + private final String mExternalStoragePath; // true if the database has been modified in the current MTP session private boolean mDatabaseModified; @@ -77,7 +79,6 @@ public class MtpDatabase { Files.FileColumns.DATE_MODIFIED, // 5 }; private static final String ID_WHERE = Files.FileColumns._ID + "=?"; - private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?"; private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + Files.FileColumns.FORMAT + "=?"; @@ -98,6 +99,7 @@ public class MtpDatabase { mMediaProvider = context.getContentResolver().acquireProvider("media"); mVolumeName = volumeName; mMediaStoragePath = storagePath; + mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mObjectsUri = Files.getMtpObjectsUri(volumeName); mMediaScanner = new MediaScanner(context); openDevicePropertiesDatabase(context); @@ -112,6 +114,16 @@ public class MtpDatabase { } } + private String externalToMediaPath(String path) { + // convert external storage path to media path + if (path != null && mMediaStoragePath != null + && mExternalStoragePath != null + && path.startsWith(mExternalStoragePath)) { + path = mMediaStoragePath + path.substring(mExternalStoragePath.length()); + } + return path; + } + private void openDevicePropertiesDatabase(Context context) { mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null); int version = mDevicePropDb.getVersion(); @@ -482,7 +494,7 @@ public class MtpDatabase { try { c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null); if (c != null && c.moveToNext()) { - path = c.getString(1); + path = externalToMediaPath(c.getString(1)); } } catch (RemoteException e) { Log.e(TAG, "RemoteException in getObjectFilePath", e); @@ -763,7 +775,7 @@ public class MtpDatabase { return true; } } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getObjectProperty", e); + Log.e(TAG, "RemoteException in getObjectInfo", e); } finally { if (c != null) { c.close(); @@ -786,7 +798,7 @@ public class MtpDatabase { c = mMediaProvider.query(mObjectsUri, PATH_SIZE_PROJECTION, ID_WHERE, new String[] { Integer.toString(handle) }, null); if (c != null && c.moveToNext()) { - String path = c.getString(1); + String path = externalToMediaPath(c.getString(1)); path.getChars(0, path.length(), outFilePath, 0); outFilePath[path.length()] = 0; outFileLength[0] = c.getLong(2); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 0d8abe2..9e1b436 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1710,16 +1710,11 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { return err; } - // Check that the color format is in the correct range. - CHECK(OMX_COLOR_FormatAndroidPrivateStart <= def.format.video.eColorFormat); - CHECK(def.format.video.eColorFormat < OMX_COLOR_FormatAndroidPrivateEnd); - err = native_window_set_buffers_geometry( mNativeWindow.get(), def.format.video.nFrameWidth, def.format.video.nFrameHeight, - def.format.video.eColorFormat - - OMX_COLOR_FormatAndroidPrivateStart); + def.format.video.eColorFormat); if (err != 0) { LOGE("native_window_set_buffers_geometry failed: %s (%d)", @@ -2109,6 +2104,17 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { CODEC_LOGV( "output crop (%ld, %ld, %ld, %ld)", rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight); + + if (mNativeWindow != NULL) { + android_native_rect_t crop; + crop.left = rect.nLeft; + crop.top = rect.nTop; + crop.right = crop.left + rect.nWidth - 1; + crop.bottom = crop.top + rect.nHeight - 1; + + CHECK_EQ(0, native_window_set_crop( + mNativeWindow.get(), &crop)); + } } else { CODEC_LOGE("getConfig(OMX_IndexConfigCommonOutputCrop) " "returned error 0x%08x", err); diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 1629e9f..6c05e03 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -39,7 +39,7 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm", ".ts" + ".mkv", ".mka", ".webm", ".ts", ".fl" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java index 91dc2b2..0f1aa4e 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java @@ -145,7 +145,7 @@ public abstract class KeyguardViewBase extends FrameLayout { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAYPAUSE toggle when phone is ringing or + /* Suppress PLAY/PAUSE toggle when phone is ringing or * in-call to avoid music playback */ if (mTelephonyManager == null) { mTelephonyManager = (TelephonyManager) getContext().getSystemService( @@ -155,11 +155,13 @@ public abstract class KeyguardViewBase extends FrameLayout { mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { return true; // suppress key event } - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); @@ -191,12 +193,15 @@ public abstract class KeyguardViewBase extends FrameLayout { } else if (event.getAction() == KeyEvent.ACTION_UP) { switch (keyCode) { case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index c034ec9..c870503 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -795,6 +795,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_CAMERA: return false; diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index 1383354..4644a7c 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -209,7 +209,12 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label : R.string.lockscreen_sound_off_label); } - mCallback.pokeWakelock(); + // Don't poke the wake lock when returning to a state where the handle is + // not grabbed since that can happen when the system (instead of the user) + // cancels the grab. + if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) { + mCallback.pokeWakelock(); + } } } @@ -231,10 +236,11 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { + // Don't poke the wake lock when returning to a state where the handle is + // not grabbed since that can happen when the system (instead of the user) + // cancels the grab. if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) { mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); - } else { - mCallback.pokeWakelock(); } } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 3691d97..e944f9d 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -627,13 +627,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // should be executed... do it! mPanelChordingKey = 0; mPanelMayLongPress = false; - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } return false; @@ -1259,7 +1252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAYPAUSE toggle when phone is ringing or in-call + /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call * to avoid music playback */ if (mTelephonyManager == null) { mTelephonyManager = (TelephonyManager) getContext().getSystemService( @@ -1275,6 +1268,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); @@ -1455,6 +1449,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -1462,6 +1457,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 7f49da9..e950ae5 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -54,6 +54,7 @@ import com.android.internal.telephony.ITelephony; import com.android.internal.view.BaseInputHandler; import com.android.internal.widget.PointerLocationView; +import android.telephony.TelephonyManager; import android.util.Config; import android.util.EventLog; import android.util.Log; @@ -63,6 +64,7 @@ import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputQueue; import android.view.InputHandler; import android.view.KeyEvent; @@ -217,7 +219,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mKeyguard = null; KeyguardViewMediator mKeyguardMediator; GlobalActions mGlobalActions; - boolean mShouldTurnOffOnKeyUp; + volatile boolean mPowerKeyHandled; RecentApplicationsDialog mRecentAppsDialog; Handler mHandler; @@ -477,28 +479,47 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - Runnable mPowerLongPress = new Runnable() { + private void interceptPowerKeyDown(boolean handled) { + mPowerKeyHandled = handled; + if (!handled) { + mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); + } + } + + private boolean interceptPowerKeyUp(boolean canceled) { + if (!mPowerKeyHandled) { + mHandler.removeCallbacks(mPowerLongPress); + return !canceled; + } else { + mPowerKeyHandled = true; + return false; + } + } + + private final Runnable mPowerLongPress = new Runnable() { public void run() { - // The context isn't read - if (mLongPressOnPowerBehavior < 0) { - mLongPressOnPowerBehavior = mContext.getResources().getInteger( - com.android.internal.R.integer.config_longPressOnPowerBehavior); - } - switch (mLongPressOnPowerBehavior) { - case LONG_PRESS_POWER_NOTHING: - break; - case LONG_PRESS_POWER_GLOBAL_ACTIONS: - mShouldTurnOffOnKeyUp = false; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - showGlobalActionsDialog(); - break; - case LONG_PRESS_POWER_SHUT_OFF: - mShouldTurnOffOnKeyUp = false; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - ShutdownThread.shutdown(mContext, true); - break; + if (!mPowerKeyHandled) { + // The context isn't read + if (mLongPressOnPowerBehavior < 0) { + mLongPressOnPowerBehavior = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longPressOnPowerBehavior); + } + switch (mLongPressOnPowerBehavior) { + case LONG_PRESS_POWER_NOTHING: + break; + case LONG_PRESS_POWER_GLOBAL_ACTIONS: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + showGlobalActionsDialog(); + break; + case LONG_PRESS_POWER_SHUT_OFF: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + ShutdownThread.shutdown(mContext, true); + break; + } } } }; @@ -1111,12 +1132,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.anim.lock_screen_behind_enter); } - static ITelephony getPhoneInterface() { - return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + static ITelephony getTelephonyService() { + ITelephony telephonyService = ITelephony.Stub.asInterface( + ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + if (telephonyService == null) { + Log.w(TAG, "Unable to find ITelephony interface."); + } + return telephonyService; } - static IAudioService getAudioInterface() { - return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE)); + static IAudioService getAudioService() { + IAudioService audioService = IAudioService.Stub.asInterface( + ServiceManager.checkService(Context.AUDIO_SERVICE)); + if (audioService == null) { + Log.w(TAG, "Unable to find IAudioService interface."); + } + return audioService; } boolean keyguardOn() { @@ -1131,7 +1162,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int metaState, int repeatCount, int policyFlags) { + int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { final boolean keyguardOn = keyguardOn(); final boolean down = (action == KeyEvent.ACTION_DOWN); final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0); @@ -1164,11 +1195,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // and his ONLY options are to answer or reject the call.) boolean incomingRinging = false; try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - incomingRinging = phoneServ.isRinging(); - } else { - Log.w(TAG, "Unable to find ITelephony interface"); + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + incomingRinging = telephonyService.isRinging(); } } catch (RemoteException ex) { Log.w(TAG, "RemoteException from getPhoneInterface()", ex); @@ -1824,23 +1853,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * @return Whether a telephone call is in progress right now. - */ - boolean isInCall() { - final ITelephony phone = getPhoneInterface(); - if (phone == null) { - Log.w(TAG, "couldn't get ITelephony reference"); - return false; - } - try { - return phone.isOffhook(); - } catch (RemoteException e) { - Log.w(TAG, "ITelephony.isOffhhook threw RemoteException " + e); - return false; - } - } - - /** * @return Whether music is being played right now. */ boolean isMusicActive() { @@ -1857,9 +1869,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * @param keycode */ void handleVolumeKey(int stream, int keycode) { - final IAudioService audio = getAudioInterface(); - if (audio == null) { - Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference"); + IAudioService audioService = getAudioService(); + if (audioService == null) { return; } try { @@ -1867,7 +1878,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // during the call, but we do it as a precaution for the rare possibility // that the music stops right before we call this mBroadcastWakeLock.acquire(); - audio.adjustStreamVolume(stream, + audioService.adjustStreamVolume(stream, keycode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, @@ -1878,41 +1889,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mBroadcastWakeLock.release(); } } - - static boolean isMediaKey(int code) { - if (code == KeyEvent.KEYCODE_HEADSETHOOK || - code == KeyEvent.KEYCODE_MEDIA_PLAY || - code == KeyEvent.KEYCODE_MEDIA_PAUSE || - code == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || - code == KeyEvent.KEYCODE_MEDIA_STOP || - code == KeyEvent.KEYCODE_MEDIA_NEXT || - code == KeyEvent.KEYCODE_MEDIA_PREVIOUS || - code == KeyEvent.KEYCODE_MEDIA_REWIND || - code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - return true; - } - return false; - } - + /** {@inheritDoc} */ @Override - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { - int result = ACTION_PASS_TO_USER; + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { + final boolean down = action == KeyEvent.ACTION_DOWN; + final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0; - if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) { - performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); - } - - final boolean isWakeKey = (policyFlags - & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; - - // If the key is injected, pretend that the screen is on and don't let the - // device go to sleep. This feature is mainly used for testing purposes. final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; - if (isInjected) { - isScreenOn = true; - } // If screen is off then we treat the case where the keyguard is open but hidden // the same as if it were open and in front. @@ -1927,202 +1912,210 @@ public class PhoneWindowManager implements WindowManagerPolicy { + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive); } - if (keyguardActive) { - if (isScreenOn) { - // when the screen is on, always give the event to the keyguard - result |= ACTION_PASS_TO_USER; - } else { - // otherwise, don't pass it to the user - result &= ~ACTION_PASS_TO_USER; + if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) { + performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + } - if (isWakeKey && down) { - - // tell the mediator about a wake key, it may decide to - // turn on the screen depending on whether the key is - // appropriate. - if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode) - && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - // when keyguard is showing and screen off, we need - // to handle the volume key for calls and music here - if (isInCall()) { - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - } else if (isMusicActive()) { - handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + // Basic policy based on screen state and keyguard. + // FIXME: This policy isn't quite correct. We shouldn't care whether the screen + // is on or off, really. We should care about whether the device is in an + // interactive state or is in suspend pretending to be "off". + // The primary screen might be turned off due to proximity sensor or + // because we are presenting media on an auxiliary screen or remotely controlling + // the device some other way (which is why we have an exemption here for injected + // events). + int result; + if (isScreenOn || isInjected) { + // When the screen is on or if the key is injected pass the key to the application. + result = ACTION_PASS_TO_USER; + } else { + // When the screen is off and the key is not injected, determine whether + // to wake the device but don't pass the key to the application. + result = 0; + + final boolean isWakeKey = (policyFlags + & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; + if (down && isWakeKey) { + if (keyguardActive) { + // If the keyguard is showing, let it decide what to do with the wake key. + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); + } else { + // Otherwise, wake the device ourselves. + result |= ACTION_POKE_USER_ACTIVITY; + } + } + } + + // Handle special keys. + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: { + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { + // If an incoming call is ringing, either VOLUME key means + // "silence ringer". We handle these keys here, rather than + // in the InCallScreen, to make sure we'll respond to them + // even if the InCallScreen hasn't come to the foreground yet. + // Look for the DOWN event here, to agree with the "fallback" + // behavior in the InCallScreen. + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " VOLUME key-down while ringing: Silence ringer!"); + + // Silence the ringer. (It's safe to call this + // even if the ringer has already been silenced.) + telephonyService.silenceRinger(); + + // And *don't* pass this key thru to the current activity + // (which is probably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + break; + } + if (telephonyService.isOffhook() + && (result & ACTION_PASS_TO_USER) == 0) { + // If we are in call but we decided not to pass the key to + // the application, handle the volume change here. + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); + break; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } } + + if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { + // If music is playing but we decided not to pass the key to the + // application, handle the volume change here. + handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + break; + } } + break; } - } else if (!isScreenOn) { - // If we are in-call with screen off and keyguard is not showing, - // then handle the volume key ourselves. - // This is necessary because the phone app will disable the keyguard - // when the proximity sensor is in use. - if (isInCall() && - (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - result &= ~ACTION_PASS_TO_USER; - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - } - if (isWakeKey) { - // a wake key has a sole purpose of waking the device; don't pass - // it to the user - result |= ACTION_POKE_USER_ACTIVITY; + + case KeyEvent.KEYCODE_ENDCALL: { result &= ~ACTION_PASS_TO_USER; + if (down) { + ITelephony telephonyService = getTelephonyService(); + boolean hungUp = false; + if (telephonyService != null) { + try { + hungUp = telephonyService.endCall(); + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); + } + } + interceptPowerKeyDown(!isScreenOn || hungUp); + } else { + if (interceptPowerKeyUp(canceled)) { + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { + if (goHome()) { + break; + } + } + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { + result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; + } + } + } + break; } - } - if (keyCode == KeyEvent.KEYCODE_ENDCALL - || keyCode == KeyEvent.KEYCODE_POWER) { - if (down) { - boolean handled = false; - boolean hungUp = false; - // key repeats are generated by the window manager, and we don't see them - // here, so unless the driver is doing something it shouldn't be, we know - // this is the real press event. - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - try { - if (keyCode == KeyEvent.KEYCODE_ENDCALL) { - handled = hungUp = phoneServ.endCall(); - } else if (keyCode == KeyEvent.KEYCODE_POWER) { - if (phoneServ.isRinging()) { + case KeyEvent.KEYCODE_POWER: { + result &= ~ACTION_PASS_TO_USER; + if (down) { + ITelephony telephonyService = getTelephonyService(); + boolean hungUp = false; + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { // Pressing Power while there's a ringing incoming // call should silence the ringer. - phoneServ.silenceRinger(); - handled = true; - } else if (phoneServ.isOffhook() && - ((mIncallPowerBehavior - & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) - != 0)) { + telephonyService.silenceRinger(); + } else if ((mIncallPowerBehavior + & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 + && telephonyService.isOffhook()) { // Otherwise, if "Power button ends call" is enabled, // the Power button will hang up any current active call. - handled = hungUp = phoneServ.endCall(); + hungUp = telephonyService.endCall(); } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException" + ex); } + interceptPowerKeyDown(!isScreenOn || hungUp); } else { - Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); - } - - if (!isScreenOn - || (handled && keyCode != KeyEvent.KEYCODE_POWER) - || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) { - mShouldTurnOffOnKeyUp = false; - } else { - // Only try to turn off the screen if we didn't already hang up. - mShouldTurnOffOnKeyUp = true; - mHandler.postDelayed(mPowerLongPress, - ViewConfiguration.getGlobalActionKeyTimeout()); - result &= ~ACTION_PASS_TO_USER; - } - } else { - mHandler.removeCallbacks(mPowerLongPress); - if (mShouldTurnOffOnKeyUp) { - mShouldTurnOffOnKeyUp = false; - boolean gohome, sleeps; - if (keyCode == KeyEvent.KEYCODE_ENDCALL) { - gohome = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0; - sleeps = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0; - } else { - gohome = false; - sleeps = true; - } - if (keyguardActive - || (sleeps && !gohome) - || (gohome && !goHome() && sleeps)) { - // They must already be on the keyguard or home screen, - // go to sleep instead unless the event was injected. - if (!isInjected) { - Log.d(TAG, "I'm tired mEndcallBehavior=0x" - + Integer.toHexString(mEndcallBehavior)); - result &= ~ACTION_POKE_USER_ACTIVITY; - result |= ACTION_GO_TO_SLEEP; - } + if (interceptPowerKeyUp(canceled)) { + result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; } - result &= ~ACTION_PASS_TO_USER; } + break; } - } else if (isMediaKey(keyCode)) { - // This key needs to be handled even if the screen is off. - // If others need to be handled while it's off, this is a reasonable - // pattern to follow. - if ((result & ACTION_PASS_TO_USER) == 0) { - // Only do this if we would otherwise not pass it to the user. In that - // case, the PhoneWindow class will do the same thing, except it will - // only do it if the showing app doesn't process the key on its own. - long when = whenNanos / 1000000; - KeyEvent keyEvent = new KeyEvent(when, when, - down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, - keyCode, 0); - mBroadcastWakeLock.acquire(); - mHandler.post(new PassHeadsetKey(keyEvent)); - } - } else if (keyCode == KeyEvent.KEYCODE_CALL) { - // If an incoming call is ringing, answer it! - // (We handle this key here, rather than in the InCallScreen, to make - // sure we'll respond to the key even if the InCallScreen hasn't come to - // the foreground yet.) - - // We answer the call on the DOWN event, to agree with - // the "fallback" behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " CALL key-down while ringing: Answer the call!"); - phoneServ.answerRingingCall(); - - // And *don't* pass this key thru to the current activity - // (which is presumably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; + + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (!telephonyService.isIdle()) { + // Suppress PLAY/PAUSE toggle when phone is ringing or in-call + // to avoid music playback. + break; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } else { - Log.w(TAG, "CALL button: Unable to find ITelephony interface"); } - } catch (RemoteException ex) { - Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex); } + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if ((result & ACTION_PASS_TO_USER) == 0) { + // Only do this if we would otherwise not pass it to the user. In that + // case, the PhoneWindow class will do the same thing, except it will + // only do it if the showing app doesn't process the key on its own. + long when = whenNanos / 1000000; + KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0, + 0, scanCode, flags, InputDevice.SOURCE_KEYBOARD); + mBroadcastWakeLock.acquire(); + mHandler.post(new PassHeadsetKey(keyEvent)); + } + break; } - } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) - || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - // If an incoming call is ringing, either VOLUME key means - // "silence ringer". We handle these keys here, rather than - // in the InCallScreen, to make sure we'll respond to them - // even if the InCallScreen hasn't come to the foreground yet. - - // Look for the DOWN event here, to agree with the "fallback" - // behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " VOLUME key-down while ringing: Silence ringer!"); - // Silence the ringer. (It's safe to call this - // even if the ringer has already been silenced.) - phoneServ.silenceRinger(); - - // And *don't* pass this key thru to the current activity - // (which is probably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; + + case KeyEvent.KEYCODE_CALL: { + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " CALL key-down while ringing: Answer the call!"); + telephonyService.answerRingingCall(); + + // And *don't* pass this key thru to the current activity + // (which is presumably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } else { - Log.w(TAG, "VOLUME button: Unable to find ITelephony interface"); } - } catch (RemoteException ex) { - Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex); } + break; } } - return result; } diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index 65d9ef7..86d4c9f 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -356,7 +356,9 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst break; case AudioSystem::FOR_MEDIA: if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP && - config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) { + config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_ANALOG_DOCK && + config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) { LOGW("setForceUse() invalid config %d for FOR_MEDIA", config); return; } @@ -372,7 +374,10 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst break; case AudioSystem::FOR_DOCK: if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK && - config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) { + config != AudioSystem::FORCE_BT_DESK_DOCK && + config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_ANALOG_DOCK && + config != AudioSystem::FORCE_DIGITAL_DOCK) { LOGW("setForceUse() invalid config %d for FOR_DOCK", config); } forceVolumeReeval = true; @@ -1366,6 +1371,7 @@ status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devi void AudioPolicyManagerBase::closeA2dpOutputs() { + LOGV("setDeviceConnectionState() closing A2DP and duplicated output!"); if (mDuplicatedOutput != 0) { @@ -1558,6 +1564,8 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (mPhoneState != AudioSystem::MODE_IN_CALL) { @@ -1617,6 +1625,12 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + } #ifdef WITH_A2DP if (mA2dpOutput != 0) { if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) { @@ -1797,7 +1811,9 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioSystem::DEVICE_OUT_WIRED_HEADSET | - AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && + AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | + AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET | + AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) && (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) && streamDesc.mCanBeMuted) { volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index bee8872..f993093 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -103,7 +103,6 @@ class DockObserver extends UEventObserver { FileReader file = new FileReader(DOCK_STATE_PATH); int len = file.read(buffer, 0, 1024); mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); - } catch (FileNotFoundException e) { Slog.w(TAG, "This kernel does not have dock station support"); } catch (Exception e) { @@ -158,13 +157,17 @@ class DockObserver extends UEventObserver { { String whichSound = null; if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { - if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) { + if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { whichSound = Settings.System.DESK_UNDOCK_SOUND; } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { whichSound = Settings.System.CAR_UNDOCK_SOUND; } } else { - if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { whichSound = Settings.System.DESK_DOCK_SOUND; } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { whichSound = Settings.System.CAR_DOCK_SOUND; diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java deleted file mode 100644 index 6f0a91d..0000000 --- a/services/java/com/android/server/HeadsetObserver.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.app.ActivityManagerNative; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.UEventObserver; -import android.util.Slog; -import android.media.AudioManager; - -import java.io.FileReader; -import java.io.FileNotFoundException; - -/** - * <p>HeadsetObserver monitors for a wired headset. - */ -class HeadsetObserver extends UEventObserver { - private static final String TAG = HeadsetObserver.class.getSimpleName(); - private static final boolean LOG = true; - - private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w"; - private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state"; - private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name"; - - private static final int BIT_HEADSET = (1 << 0); - private static final int BIT_HEADSET_NO_MIC = (1 << 1); - private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC); - private static final int HEADSETS_WITH_MIC = BIT_HEADSET; - - private int mHeadsetState; - private int mPrevHeadsetState; - private String mHeadsetName; - - private final Context mContext; - private final WakeLock mWakeLock; // held while there is a pending route change - - public HeadsetObserver(Context context) { - mContext = context; - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver"); - mWakeLock.setReferenceCounted(false); - - startObserving(HEADSET_UEVENT_MATCH); - - init(); // set initial status - } - - @Override - public void onUEvent(UEventObserver.UEvent event) { - if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); - - try { - update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE"))); - } catch (NumberFormatException e) { - Slog.e(TAG, "Could not parse switch state from event " + event); - } - } - - private synchronized final void init() { - char[] buffer = new char[1024]; - - String newName = mHeadsetName; - int newState = mHeadsetState; - mPrevHeadsetState = mHeadsetState; - try { - FileReader file = new FileReader(HEADSET_STATE_PATH); - int len = file.read(buffer, 0, 1024); - newState = Integer.valueOf((new String(buffer, 0, len)).trim()); - - file = new FileReader(HEADSET_NAME_PATH); - len = file.read(buffer, 0, 1024); - newName = new String(buffer, 0, len).trim(); - - } catch (FileNotFoundException e) { - Slog.w(TAG, "This kernel does not have wired headset support"); - } catch (Exception e) { - Slog.e(TAG, "" , e); - } - - update(newName, newState); - } - - private synchronized final void update(String newName, int newState) { - // Retain only relevant bits - int headsetState = newState & SUPPORTED_HEADSETS; - int newOrOld = headsetState | mHeadsetState; - int delay = 0; - // reject all suspect transitions: only accept state changes from: - // - a: 0 heaset to 1 headset - // - b: 1 headset to 0 headset - if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) { - return; - } - - mHeadsetName = newName; - mPrevHeadsetState = mHeadsetState; - mHeadsetState = headsetState; - - if (headsetState == 0) { - Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - mContext.sendBroadcast(intent); - // It can take hundreds of ms flush the audio pipeline after - // apps pause audio playback, but audio route changes are - // immediate, so delay the route change by 1000ms. - // This could be improved once the audio sub-system provides an - // interface to clear the audio pipeline. - delay = 1000; - } else { - // Insert the same delay for headset connection so that the connection event is not - // broadcast before the disconnection event in case of fast removal/insertion - if (mHandler.hasMessages(0)) { - delay = 1000; - } - } - mWakeLock.acquire(); - mHandler.sendMessageDelayed(mHandler.obtainMessage(0, - mHeadsetState, - mPrevHeadsetState, - mHeadsetName), - delay); - } - - private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { - int allHeadsets = SUPPORTED_HEADSETS; - for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { - if ((curHeadset & allHeadsets) != 0) { - sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); - allHeadsets &= ~curHeadset; - } - } - } - - private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { - if ((headsetState & headset) != (prevHeadsetState & headset)) { - // Pack up the values and broadcast them to everyone - Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - int state = 0; - int microphone = 0; - - if ((headset & HEADSETS_WITH_MIC) != 0) { - microphone = 1; - } - if ((headsetState & headset) != 0) { - state = 1; - } - intent.putExtra("state", state); - intent.putExtra("name", headsetName); - intent.putExtra("microphone", microphone); - - if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); - // TODO: Should we require a permission? - ActivityManagerNative.broadcastStickyIntent(intent, null); - } - } - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - sendIntents(msg.arg1, msg.arg2, (String)msg.obj); - mWakeLock.release(); - } - }; -} diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index cb4071a..e7eb129 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -404,17 +404,18 @@ public class InputManager { } @SuppressWarnings("unused") - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing( - whenNanos, keyCode, down, policyFlags, isScreenOn); + whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn); } @SuppressWarnings("unused") public boolean interceptKeyBeforeDispatching(InputChannel focus, int action, - int flags, int keyCode, int metaState, int repeatCount, int policyFlags) { + int flags, int keyCode, int scanCode, int metaState, int repeatCount, + int policyFlags) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus, - action, flags, keyCode, metaState, repeatCount, policyFlags); + action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @SuppressWarnings("unused") diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 07da0fa..20e8bbe 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1881,11 +1881,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub String id = pair.first; ArrayList<String> subtypes = pair.second; builder.append(id); - if (subtypes.size() > 0) { - builder.append(subtypes.get(0)); - for (int i = 1; i < subtypes.size(); ++i) { - builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypes.get(i)); - } + // Inputmethod and subtypes are saved in the settings as follows: + // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1 + for (String subtypeId: subtypes) { + builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 237ab80..54f7441 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -121,7 +121,7 @@ class ServerThread extends Thread { WindowManagerService wm = null; BluetoothService bluetooth = null; BluetoothA2dpService bluetoothA2dp = null; - HeadsetObserver headset = null; + WiredAccessoryObserver wiredAccessory = null; DockObserver dock = null; UsbObserver usb = null; UiModeManagerService uiMode = null; @@ -388,14 +388,6 @@ class ServerThread extends Thread { } try { - Slog.i(TAG, "Headset Observer"); - // Listen for wired headset changes - headset = new HeadsetObserver(context); - } catch (Throwable e) { - Slog.e(TAG, "Failure starting HeadsetObserver", e); - } - - try { Slog.i(TAG, "Dock Observer"); // Listen for dock station changes dock = new DockObserver(context, power); @@ -404,6 +396,14 @@ class ServerThread extends Thread { } try { + Slog.i(TAG, "Wired Accessory Observer"); + // Listen for wired headset changes + wiredAccessory = new WiredAccessoryObserver(context); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting WiredAccessoryObserver", e); + } + + try { Slog.i(TAG, "USB Observer"); // Listen for USB changes usb = new UsbObserver(context); diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index 747af26..a7a0c68 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -103,6 +103,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private int mDataConnectionNetworkType; + private int mOtaspMode; + static final int PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_CALL_STATE | @@ -225,6 +227,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { + try { + r.callback.onOtaspChanged(mOtaspMode); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -467,6 +476,25 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + public void notifyOtaspChanged(int otaspMode) { + if (!checkNotifyPermission("notifyOtaspChanged()" )) { + return; + } + synchronized (mRecords) { + mOtaspMode = otaspMode; + for (Record r : mRecords) { + if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { + try { + r.callback.onOtaspChanged(otaspMode); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index c29e4a9..55ebded 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5706,20 +5706,20 @@ public class WindowManagerService extends IWindowManager.Stub /* Provides an opportunity for the window manager policy to intercept early key * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { - return mPolicy.interceptKeyBeforeQueueing(whenNanos, - keyCode, down, policyFlags, isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { + return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags, + keyCode, scanCode, policyFlags, isScreenOn); } /* Provides an opportunity for the window manager policy to process a key before * ordinary dispatch. */ public boolean interceptKeyBeforeDispatching(InputChannel focus, - int action, int flags, int keyCode, int metaState, int repeatCount, + int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { WindowState windowState = getWindowStateForInputChannel(focus); return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags, - keyCode, metaState, repeatCount, policyFlags); + keyCode, scanCode, metaState, repeatCount, policyFlags); } /* Called when the current input focus changes. diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java new file mode 100644 index 0000000..ab92fdf --- /dev/null +++ b/services/java/com/android/server/WiredAccessoryObserver.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.ActivityManagerNative; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.UEventObserver; +import android.util.Slog; +import android.media.AudioManager; +import android.util.Log; + +import java.io.FileReader; +import java.io.FileNotFoundException; + +/** + * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock. + */ +class WiredAccessoryObserver extends UEventObserver { + private static final String TAG = WiredAccessoryObserver.class.getSimpleName(); + private static final boolean LOG = true; + private static final int MAX_AUDIO_PORTS = 2; /* h2w & USB Audio */ + private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w", + "/sys/class/switch/h2w/state", + "/sys/class/switch/h2w/name"}, + {"DEVPATH=/devices/virtual/switch/usb_audio", + "/sys/class/switch/usb_audio/state", + "/sys/class/switch/usb_audio/name"} }; + + private static final int BIT_HEADSET = (1 << 0); + private static final int BIT_HEADSET_NO_MIC = (1 << 1); + private static final int BIT_USB_HEADSET_ANLG = (1 << 2); + private static final int BIT_USB_HEADSET_DGTL = (1 << 3); + private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| + BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL); + private static final int HEADSETS_WITH_MIC = BIT_HEADSET; + + private int mHeadsetState; + private int mPrevHeadsetState; + private String mHeadsetName; + private int switchState; + + private final Context mContext; + private final WakeLock mWakeLock; // held while there is a pending route change + + public WiredAccessoryObserver(Context context) { + mContext = context; + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); + mWakeLock.setReferenceCounted(false); + + // At any given time both headsets could be inserted + // one on the board and one on the dock + // observe two UEVENTs + for (int i = 0; i <= MAX_AUDIO_PORTS; i++) { + startObserving(uEventInfo[i][0]); + } + init(); // set initial status + } + + @Override + public void onUEvent(UEventObserver.UEvent event) { + if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); + + try { + if ((event.get("SWITCH_NAME")).equals("usb_audio")) { + if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) { + switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| + BIT_USB_HEADSET_DGTL)) | + (Integer.parseInt(event.get("SWITCH_STATE")) << 2)); + } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) { + switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| + BIT_USB_HEADSET_ANLG)) | + (Integer.parseInt(event.get("SWITCH_STATE")) << 3)); + } + else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC)); + } + else { + switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) | + (Integer.parseInt(event.get("SWITCH_STATE")))); + } + update(event.get("SWITCH_NAME"), switchState); + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse switch state from event " + event); + } + } + + private synchronized final void init() { + char[] buffer = new char[1024]; + + String newName = mHeadsetName; + int newState = mHeadsetState; + mPrevHeadsetState = mHeadsetState; + + for (int i = 0; i <= MAX_AUDIO_PORTS; i++) { + try { + FileReader file = new FileReader(uEventInfo[i][1]); + int len = file.read(buffer, 0, 1024); + newState = Integer.valueOf((new String(buffer, 0, len)).trim()); + + file = new FileReader(uEventInfo[i][2]); + len = file.read(buffer, 0, 1024); + newName = new String(buffer, 0, len).trim(); + + } catch (FileNotFoundException e) { + Slog.w(TAG, "This kernel does not have wired headset support"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } + + update(newName, newState); + } + } + + private synchronized final void update(String newName, int newState) { + // Retain only relevant bits + int headsetState = newState & SUPPORTED_HEADSETS; + int newOrOld = headsetState | mHeadsetState; + int delay = 0; + int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; + int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; + int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); + boolean h2wStateChange = true; + boolean usbStateChange = true; + // reject all suspect transitions: only accept state changes from: + // - a: 0 heaset to 1 headset + // - b: 1 headset to 0 headset + Log.v(TAG, "newState = "+newState+", headsetState = "+headsetState+", mHeadsetState = "+mHeadsetState); + if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) { + Log.e(TAG, "unsetting h2w flag"); + h2wStateChange = false; + } + // - c: 0 usb headset to 1 usb headset + // - d: 1 usb headset to 0 usb headset + if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) { + Log.e(TAG, "unsetting usb flag"); + usbStateChange = false; + } + if (!h2wStateChange && !usbStateChange) { + Log.e(TAG, "invalid transition, returning ..."); + return; + } + + mHeadsetName = newName; + mPrevHeadsetState = mHeadsetState; + mHeadsetState = headsetState; + + if (headsetState == 0) { + Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + // It can take hundreds of ms flush the audio pipeline after + // apps pause audio playback, but audio route changes are + // immediate, so delay the route change by 1000ms. + // This could be improved once the audio sub-system provides an + // interface to clear the audio pipeline. + delay = 1000; + } else { + // Insert the same delay for headset connection so that the connection event is not + // broadcast before the disconnection event in case of fast removal/insertion + if (mHandler.hasMessages(0)) { + delay = 1000; + } + } + mWakeLock.acquire(); + mHandler.sendMessageDelayed(mHandler.obtainMessage(0, + mHeadsetState, + mPrevHeadsetState, + mHeadsetName), + delay); + } + + private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { + int allHeadsets = SUPPORTED_HEADSETS; + for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { + if ((curHeadset & allHeadsets) != 0) { + sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); + allHeadsets &= ~curHeadset; + } + } + } + + private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { + if ((headsetState & headset) != (prevHeadsetState & headset)) { + + int state = 0; + if ((headsetState & headset) != 0) { + state = 1; + } + if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL)) { + Intent intent; + + // Pack up the values and broadcast them to everyone + if (headset == BIT_USB_HEADSET_ANLG) { + intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra("state", state); + intent.putExtra("name", headsetName); + ActivityManagerNative.broadcastStickyIntent(intent, null); + } else if (headset == BIT_USB_HEADSET_DGTL) { + intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra("state", state); + intent.putExtra("name", headsetName); + ActivityManagerNative.broadcastStickyIntent(intent, null); + } + + if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName); + // TODO: Should we require a permission? + } + if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) { + + // Pack up the values and broadcast them to everyone + Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + //int state = 0; + int microphone = 0; + + if ((headset & HEADSETS_WITH_MIC) != 0) { + microphone = 1; + } + + intent.putExtra("state", state); + intent.putExtra("name", headsetName); + intent.putExtra("microphone", microphone); + + if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); + // TODO: Should we require a permission? + ActivityManagerNative.broadcastStickyIntent(intent, null); + } + } + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + sendIntents(msg.arg1, msg.arg2, (String)msg.obj); + mWakeLock.release(); + } + }; +} diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 599163b..d4c4ba4 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -857,7 +857,7 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeQueueing, - when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn); + when, action, flags, keyCode, scanCode, policyFlags, isScreenOn); if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { wmActions = 0; } @@ -926,7 +926,7 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i jboolean consumed = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeDispatching, inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), - keyEvent->getKeyCode(), keyEvent->getMetaState(), + keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), policyFlags); bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); @@ -1358,10 +1358,10 @@ int register_android_server_InputManager(JNIEnv* env) { "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, - "interceptKeyBeforeQueueing", "(JIZIZ)I"); + "interceptKeyBeforeQueueing", "(JIIIIIZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, - "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z"); + "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 38f44d8..eda9b71 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -148,6 +148,14 @@ public class PhoneStateListener { */ public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; + /** + * Listen for changes to OTASP mode. + * + * @see #onOtaspChanged + * @hide + */ + public static final int LISTEN_OTASP_CHANGED = 0x00000200; + public PhoneStateListener() { } @@ -252,6 +260,21 @@ public class PhoneStateListener { // default implementation empty } + + /** + * The Over The Air Service Provisioning (OTASP) has changed. Requires + * the READ_PHONE_STATE permission. + * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code> + * means the value is currently unknown and the system should wait until + * <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before + * making the decisision to perform OTASP or not. + * + * @hide + */ + public void onOtaspChanged(int otaspMode) { + // default implementation empty + } + /** * The callback methods need to be called on the handler thread where * this object was created. If the binder did that for us it'd be nice. @@ -292,9 +315,14 @@ public class PhoneStateListener { public void onDataActivity(int direction) { Message.obtain(mHandler, LISTEN_DATA_ACTIVITY, direction, 0, null).sendToTarget(); } + public void onSignalStrengthsChanged(SignalStrength signalStrength) { Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength).sendToTarget(); } + + public void onOtaspChanged(int otaspMode) { + Message.obtain(mHandler, LISTEN_OTASP_CHANGED, otaspMode, 0).sendToTarget(); + } }; Handler mHandler = new Handler() { @@ -329,6 +357,9 @@ public class PhoneStateListener { case LISTEN_SIGNAL_STRENGTHS: PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj); break; + case LISTEN_OTASP_CHANGED: + PhoneStateListener.this.onOtaspChanged(msg.arg1); + break; } } }; diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 6a163dd..14808b6 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -148,6 +148,14 @@ public class DefaultPhoneNotifier implements PhoneNotifier { } } + public void notifyOtaspChanged(Phone sender, int otaspMode) { + try { + mRegistry.notifyOtaspChanged(otaspMode); + } catch (RemoteException ex) { + // system process is dead + } + } + private void log(String s) { Log.d(LOG_TAG, "[PhoneNotifier] " + s); } diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 856d663..082c097 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -32,5 +32,6 @@ oneway interface IPhoneStateListener { void onDataConnectionStateChanged(int state, int networkType); void onDataActivity(int direction); void onSignalStrengthsChanged(in SignalStrength signalStrength); + void onOtaspChanged(in int otaspMode); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 7a1587b..3c4bb12 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -66,7 +66,7 @@ interface ITelephony { boolean showCallScreenWithDialpad(boolean showDialpad); /** - * End call or go to the Home screen + * End call if there is a call in progress, otherwise does nothing. * * @return whether it hung up */ diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 6407a4e..3c83e50 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -38,4 +38,5 @@ interface ITelephonyRegistry { in LinkCapabilities linkCapabilities, int networkType); void notifyDataConnectionFailed(String reason, String apnType); void notifyCellLocation(in Bundle cellLocation); + void notifyOtaspChanged(in int otaspMode); } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 74e8c1b..554a7ba 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -750,6 +750,10 @@ public abstract class PhoneBase extends Handler implements Phone { mNotifier.notifyDataConnection(this, reason, apnType); } + public void notifyOtaspChanged(int otaspMode) { + mNotifier.notifyOtaspChanged(this, otaspMode); + } + public abstract String getPhoneName(); public abstract int getPhoneType(); diff --git a/telephony/java/com/android/internal/telephony/PhoneNotifier.java b/telephony/java/com/android/internal/telephony/PhoneNotifier.java index 691271f..b1cf953 100644 --- a/telephony/java/com/android/internal/telephony/PhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/PhoneNotifier.java @@ -42,4 +42,5 @@ public interface PhoneNotifier { public void notifyDataActivity(Phone sender); + public void notifyOtaspChanged(Phone sender, int otaspMode); } diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java index e8bbe5e..3f9ffc3 100644 --- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java @@ -53,6 +53,12 @@ public abstract class ServiceStateTracker extends Handler { public SignalStrength mSignalStrength; + /* The otaspMode passed to PhoneStateListener#onOtaspChanged */ + static public final int OTASP_UNINITIALIZED = 0; + static public final int OTASP_UNKNOWN = 1; + static public final int OTASP_NEEDED = 2; + static public final int OTASP_NOT_NEEDED = 3; + /** * A unique identifier to track requests associated with a poll * and ignore stale responses. The value is a count-down of @@ -268,9 +274,11 @@ public abstract class ServiceStateTracker extends Handler { public abstract void handleMessage(Message msg); + protected abstract Phone getPhone(); protected abstract void handlePollStateResult(int what, AsyncResult ar); protected abstract void updateSpnDisplay(); protected abstract void setPowerStateToDesired(); + protected abstract void log(String s); /** * Clean up existing voice and data connection then turn off radio power. diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 6e53ec5..b9d5673 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -61,6 +61,7 @@ import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneNotifier; import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; +import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.UUSInfo; @@ -83,10 +84,6 @@ public class CDMAPhone extends PhoneBase { static final String LOG_TAG = "CDMA"; private static final boolean DBG = true; - // Min values used to by needsActivation - private static final String UNACTIVATED_MIN2_VALUE = "000000"; - private static final String UNACTIVATED_MIN_VALUE = "1111110111"; - // Default Emergency Callback Mode exit timer private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; @@ -1170,19 +1167,7 @@ public class CDMAPhone extends PhoneBase { */ @Override public boolean needsOtaServiceProvisioning() { - String cdmaMin = getCdmaMin(); - boolean needsProvisioning; - if (cdmaMin == null || (cdmaMin.length() < 6)) { - if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: illegal cdmaMin='" - + cdmaMin + "' assume provisioning needed."); - needsProvisioning = true; - } else { - needsProvisioning = (cdmaMin.equals(UNACTIVATED_MIN_VALUE) - || cdmaMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE)) - || SystemProperties.getBoolean("test_cdma_setup", false); - } - if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: ret=" + needsProvisioning); - return needsProvisioning; + return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED; } private static final String IS683A_FEATURE_CODE = "*228"; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index 3669e60..325c2e1 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -354,6 +354,24 @@ public final class CdmaCallTracker extends CallTracker { || (foregroundCall.getState() == CdmaCall.State.ACTIVE) || !backgroundCall.getState().isAlive()); + if (!ret) { + log(String.format("canDial is false\n" + + "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" + + "&& pendingMO == null::=%s\n" + + "&& !ringingCall.isRinging()::=%s\n" + + "&& !disableCall.equals(\"true\")::=%s\n" + + "&& (!foregroundCall.getState().isAlive()::=%s\n" + + " || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" + + " ||!backgroundCall.getState().isAlive())::=%s)", + serviceState, + serviceState != ServiceState.STATE_POWER_OFF, + pendingMO == null, + !ringingCall.isRinging(), + !disableCall.equals("true"), + !foregroundCall.getState().isAlive(), + foregroundCall.getState() == CdmaCall.State.ACTIVE, + !backgroundCall.getState().isAlive())); + } return ret; } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 7d2013b..11e04d4 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -22,6 +22,7 @@ import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.EventLogTags; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.MccTable; +import com.android.internal.telephony.Phone; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; @@ -65,6 +66,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { CdmaCellLocation cellLoc; CdmaCellLocation newCellLoc; + // Min values used to by getOtasp() + private static final String UNACTIVATED_MIN2_VALUE = "000000"; + private static final String UNACTIVATED_MIN_VALUE = "1111110111"; + + // Current Otasp value + int mCurrentOtaspMode = OTASP_UNINITIALIZED; + /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */ private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10; private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing", @@ -446,6 +454,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { if (!mIsMinInfoReady) { mIsMinInfoReady = true; } + int otaspMode = getOtasp(); + if (mCurrentOtaspMode != otaspMode) { + Log.d(LOG_TAG, "call phone.notifyOtaspChanged old otaspMode=" + + mCurrentOtaspMode + " new otaspMode=" + otaspMode); + mCurrentOtaspMode = otaspMode; + phone.notifyOtaspChanged(mCurrentOtaspMode); + } phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI, null); } else { @@ -642,6 +657,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { curPlmn = plmn; } + @Override + protected Phone getPhone() { + return phone; + } + /** * Handle the result of one of the pollState()-related requests */ @@ -1641,10 +1661,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return false; } - protected void log(String s) { - Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s); - } - public String getMdnNumber() { return mMdn; } @@ -1700,6 +1716,32 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } } + /** + * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED + */ + int getOtasp() { + int provisioningState; + if (mMin == null || (mMin.length() < 6)) { + if (DBG) Log.d(LOG_TAG, "getOtasp: bad mMin='" + mMin + "'"); + provisioningState = OTASP_UNKNOWN; + } else { + if ((mMin.equals(UNACTIVATED_MIN_VALUE) + || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE)) + || SystemProperties.getBoolean("test_cdma_setup", false)) { + provisioningState = OTASP_NEEDED; + } else { + provisioningState = OTASP_NOT_NEEDED; + } + } + if (DBG) Log.d(LOG_TAG, "getOtasp: state=" + provisioningState); + return provisioningState; + } + + @Override + protected void log(String s) { + Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s); + } + private void hangupAndPowerOff() { // hang up all active voice calls phone.mCT.ringingCall.hangupIfAlive(); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java index 6332415..bc41b01 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -22,6 +22,7 @@ import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.EventLogTags; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.MccTable; +import com.android.internal.telephony.Phone; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.TelephonyIntents; @@ -226,6 +227,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); phone.getContext().registerReceiver(mIntentReceiver, filter); + + // Gsm doesn't support OTASP so its not needed + phone.notifyOtaspChanged(OTASP_NOT_NEEDED); } public void dispose() { @@ -246,6 +250,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker { if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized"); } + @Override + public Phone getPhone() { + return phone; + } + /** * Registration point for transition into GPRS attached. * @param h handler to notify @@ -1676,7 +1685,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } } - private void log(String s) { + @Override + protected void log(String s) { Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s); } } diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java index 8cb05cc..7bbe696 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java @@ -56,4 +56,7 @@ public class TestPhoneNotifier implements PhoneNotifier { public void notifyDataActivity(Phone sender) { } + + public void notifyOtaspChanged(Phone sender, int otaspMode) { + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 24fba72..e97b1e6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -60,6 +60,20 @@ public class Bitmap_Delegate { // ---- Public Helper methods ---- /** + * Returns the native delegate associated to a given {@link Bitmap_Delegate} object. + */ + public static Bitmap_Delegate getDelegate(Bitmap bitmap) { + return sManager.getDelegate(bitmap.mNativeBitmap); + } + + /** + * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object. + */ + public static Bitmap_Delegate getDelegate(int native_bitmap) { + return sManager.getDelegate(native_bitmap); + } + + /** * Creates and returns a {@link Bitmap} initialized with the given file content. */ public static Bitmap createBitmap(File input, Density density) throws IOException { @@ -118,6 +132,13 @@ public class Bitmap_Delegate { return BufferedImage.TYPE_INT_ARGB; } + /** + * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. + */ + public BufferedImage getImage() { + return mImage; + } + // ---- native methods ---- /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, @@ -127,8 +148,7 @@ public class Bitmap_Delegate { // create the image BufferedImage image = new BufferedImage(width, height, imageType); - // fill it - //image.setRGB(x, y, rgb) + // FIXME fill the bitmap! // create a delegate with the content of the stream. Bitmap_Delegate delegate = new Bitmap_Delegate(image); diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java deleted file mode 100644 index 24da812..0000000 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java +++ /dev/null @@ -1,1247 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import com.android.layoutlib.api.ILayoutLog; - -import android.graphics.Paint.Align; -import android.graphics.Paint.FontInfo; -import android.graphics.Paint.Style; -import android.graphics.Region.Op; - -import java.awt.AlphaComposite; -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.util.List; -import java.util.Stack; - -/** - * Re-implementation of the Canvas, 100% in java on top of a BufferedImage. - */ -public class Canvas extends _Original_Canvas { - - private BufferedImage mBufferedImage; - private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>(); - private final ILayoutLog mLogger; - - public Canvas() { - mLogger = null; - // the mBufferedImage will be taken from a bitmap in #setBitmap() - } - - public Canvas(Bitmap bitmap) { - mLogger = null; - mBufferedImage = Bitmap_Delegate.getImage(bitmap); - mGraphicsStack.push(mBufferedImage.createGraphics()); - } - - public Canvas(int nativeCanvas) { - mLogger = null; - throw new UnsupportedOperationException("Can't create Canvas(int)"); - } - - // custom constructors for our use. - public Canvas(int width, int height, ILayoutLog logger) { - mLogger = logger; - mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - mGraphicsStack.push(mBufferedImage.createGraphics()); - } - - public Canvas(int width, int height) { - this(width, height, null /* logger*/); - } - - // custom mehtods - public BufferedImage getImage() { - return mBufferedImage; - } - - public Graphics2D getGraphics2d() { - return mGraphicsStack.peek(); - } - - public void dispose() { - while (mGraphicsStack.size() > 0) { - mGraphicsStack.pop().dispose(); - } - } - - /** - * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. - * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. - */ - private Graphics2D getCustomGraphics(Paint paint) { - // make new one - Graphics2D g = getGraphics2d(); - g = (Graphics2D)g.create(); - - // configure it - g.setColor(new Color(paint.getColor())); - int alpha = paint.getAlpha(); - float falpha = alpha / 255.f; - - Style style = paint.getStyle(); - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - PathEffect e = paint.getPathEffect(); - if (e instanceof DashPathEffect) { - DashPathEffect dpe = (DashPathEffect)e; - g.setStroke(new BasicStroke( - paint.getStrokeWidth(), - paint.getStrokeCap().getJavaCap(), - paint.getStrokeJoin().getJavaJoin(), - paint.getStrokeMiter(), - dpe.getIntervals(), - dpe.getPhase())); - } else { - g.setStroke(new BasicStroke( - paint.getStrokeWidth(), - paint.getStrokeCap().getJavaCap(), - paint.getStrokeJoin().getJavaJoin(), - paint.getStrokeMiter())); - } - } - - Xfermode xfermode = paint.getXfermode(); - if (xfermode instanceof PorterDuffXfermode) { - PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode(); - - setModeInGraphics(mode, g, falpha); - } else { - if (mLogger != null && xfermode != null) { - mLogger.warning(String.format( - "Xfermode '%1$s' is not supported in the Layout Editor.", - xfermode.getClass().getCanonicalName())); - } - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); - } - - Shader shader = paint.getShader(); - if (shader != null) { - java.awt.Paint shaderPaint = shader.getJavaPaint(); - if (shaderPaint != null) { - g.setPaint(shaderPaint); - } else { - if (mLogger != null) { - mLogger.warning(String.format( - "Shader '%1$s' is not supported in the Layout Editor.", - shader.getClass().getCanonicalName())); - } - } - } - - return g; - } - - private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) { - switch (mode) { - case CLEAR: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha)); - break; - case DARKEN: - break; - case DST: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha)); - break; - case DST_ATOP: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha)); - break; - case DST_IN: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha)); - break; - case DST_OUT: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha)); - break; - case DST_OVER: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha)); - break; - case LIGHTEN: - break; - case MULTIPLY: - break; - case SCREEN: - break; - case SRC: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha)); - break; - case SRC_ATOP: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha)); - break; - case SRC_IN: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha)); - break; - case SRC_OUT: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha)); - break; - case SRC_OVER: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); - break; - case XOR: - g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha)); - break; - } - } - - - // -------------------- - // OVERRIDEN ENUMS - // This is needed since we rename Canvas into _Original_Canvas - // -------------------- - - public enum EdgeType { - BW(0), //!< treat edges by just rounding to nearest pixel boundary - AA(1); //!< treat edges by rounding-out, since they may be antialiased - - EdgeType(int nativeInt) { - this.nativeInt = nativeInt; - } - final int nativeInt; - } - - - // -------------------- - // OVERRIDEN METHODS - // -------------------- - - /* (non-Javadoc) - * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap) - */ - @Override - public void setBitmap(Bitmap bitmap) { - mBufferedImage = Bitmap_Delegate.getImage(bitmap); - mGraphicsStack.push(mBufferedImage.createGraphics()); - } - - - /* (non-Javadoc) - * @see android.graphics.Canvas#translate(float, float) - */ - @Override - public void translate(float dx, float dy) { - getGraphics2d().translate(dx, dy); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#save() - */ - @Override - public int save() { - // get the current save count - int count = mGraphicsStack.size(); - - // create a new graphics and add it to the stack - Graphics2D g = (Graphics2D)getGraphics2d().create(); - mGraphicsStack.push(g); - - // return the old save count - return count; - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#save(int) - */ - @Override - public int save(int saveFlags) { - // For now we ignore saveFlags - return save(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#restore() - */ - @Override - public void restore() { - mGraphicsStack.pop(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#restoreToCount(int) - */ - @Override - public void restoreToCount(int saveCount) { - while (mGraphicsStack.size() > saveCount) { - mGraphicsStack.pop(); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#getSaveCount() - */ - @Override - public int getSaveCount() { - return mGraphicsStack.size(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op) - */ - @Override - public boolean clipRect(float left, float top, float right, float bottom, Op op) { - return clipRect(left, top, right, bottom); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(float, float, float, float) - */ - @Override - public boolean clipRect(float left, float top, float right, float bottom) { - getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); - return true; - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(int, int, int, int) - */ - @Override - public boolean clipRect(int left, int top, int right, int bottom) { - getGraphics2d().clipRect(left, top, right-left, bottom-top); - return true; - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(android.graphics.Rect, android.graphics.Region.Op) - */ - @Override - public boolean clipRect(Rect rect, Op op) { - return clipRect(rect.left, rect.top, rect.right, rect.bottom); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(android.graphics.Rect) - */ - @Override - public boolean clipRect(Rect rect) { - return clipRect(rect.left, rect.top, rect.right, rect.bottom); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(android.graphics.RectF, android.graphics.Region.Op) - */ - @Override - public boolean clipRect(RectF rect, Op op) { - return clipRect(rect.left, rect.top, rect.right, rect.bottom); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRect(android.graphics.RectF) - */ - @Override - public boolean clipRect(RectF rect) { - return clipRect(rect.left, rect.top, rect.right, rect.bottom); - } - - public boolean quickReject(RectF rect, EdgeType type) { - return false; - } - - @Override - public boolean quickReject(RectF rect, _Original_Canvas.EdgeType type) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public boolean quickReject(Path path, EdgeType type) { - return false; - } - - @Override - public boolean quickReject(Path path, _Original_Canvas.EdgeType type) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public boolean quickReject(float left, float top, float right, float bottom, - EdgeType type) { - return false; - } - - @Override - public boolean quickReject(float left, float top, float right, float bottom, - _Original_Canvas.EdgeType type) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - /** - * Retrieve the clip bounds, returning true if they are non-empty. - * - * @param bounds Return the clip bounds here. If it is null, ignore it but - * still return true if the current clip is non-empty. - * @return true if the current clip is non-empty. - */ - @Override - public boolean getClipBounds(Rect bounds) { - Rectangle rect = getGraphics2d().getClipBounds(); - if (rect != null) { - bounds.left = rect.x; - bounds.top = rect.y; - bounds.right = rect.x + rect.width; - bounds.bottom = rect.y + rect.height; - return true; - } - return false; - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode) - */ - @Override - public void drawColor(int color, PorterDuff.Mode mode) { - Graphics2D g = getGraphics2d(); - - // save old color - Color c = g.getColor(); - - Composite composite = g.getComposite(); - - // get the alpha from the color - int alpha = color >>> 24; - float falpha = alpha / 255.f; - - setModeInGraphics(mode, g, falpha); - - g.setColor(new Color(color)); - - g.fillRect(0, 0, getWidth(), getHeight()); - - g.setComposite(composite); - - // restore color - g.setColor(c); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawColor(int) - */ - @Override - public void drawColor(int color) { - drawColor(color, PorterDuff.Mode.SRC_OVER); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawARGB(int, int, int, int) - */ - @Override - public void drawARGB(int a, int r, int g, int b) { - drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawRGB(int, int, int) - */ - @Override - public void drawRGB(int r, int g, int b) { - drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER); - } - - - /* (non-Javadoc) - * @see android.graphics.Canvas#getWidth() - */ - @Override - public int getWidth() { - return mBufferedImage.getWidth(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#getHeight() - */ - @Override - public int getHeight() { - return mBufferedImage.getHeight(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPaint(android.graphics.Paint) - */ - @Override - public void drawPaint(Paint paint) { - drawColor(paint.getColor()); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint) - */ - @Override - public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { - drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), - (int)left, (int)top, - (int)left+bitmap.getWidth(), (int)top+bitmap.getHeight(), paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint) - */ - @Override - public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { - boolean needsRestore = false; - if (matrix.isIdentity() == false) { - // create a new graphics and apply the matrix to it - save(); // this creates a new Graphics2D, and stores it for children call to use - needsRestore = true; - Graphics2D g = getGraphics2d(); // get the newly create Graphics2D - - // get the Graphics2D current matrix - AffineTransform currentTx = g.getTransform(); - // get the AffineTransform from the matrix - AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix); - - // combine them so that the matrix is applied after. - currentTx.preConcatenate(matrixTx); - - // give it to the graphics as a new matrix replacing all previous transform - g.setTransform(currentTx); - } - - // draw the bitmap - drawBitmap(bitmap, 0, 0, paint); - - if (needsRestore) { - // remove the new graphics - restore(); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) - */ - @Override - public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { - if (src == null) { - drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), - dst.left, dst.top, dst.right, dst.bottom, paint); - } else { - drawBitmap(bitmap, src.left, src.top, src.width(), src.height(), - dst.left, dst.top, dst.right, dst.bottom, paint); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint) - */ - @Override - public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { - if (src == null) { - drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), - (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint); - } else { - drawBitmap(bitmap, src.left, src.top, src.width(), src.height(), - (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint) - */ - @Override - public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, - int height, boolean hasAlpha, Paint paint) { - throw new UnsupportedOperationException(); - } - - private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft, - int dtop, int dright, int dbottom, Paint paint) { - BufferedImage image = Bitmap_Delegate.getImage(bitmap); - - Graphics2D g = getGraphics2d(); - - Composite c = null; - - if (paint != null) { - if (paint.isFilterBitmap()) { - g = (Graphics2D)g.create(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - } - - if (paint.getAlpha() != 0xFF) { - c = g.getComposite(); - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, - paint.getAlpha()/255.f)); - } - } - - g.drawImage(image, dleft, dtop, dright, dbottom, - sleft, stop, sright, sbottom, null); - - if (paint != null) { - if (paint.isFilterBitmap()) { - g.dispose(); - } - if (c != null) { - g.setComposite(c); - } - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#rotate(float, float, float) - */ - @Override - public void rotate(float degrees, float px, float py) { - if (degrees != 0) { - Graphics2D g = getGraphics2d(); - g.translate(px, py); - g.rotate(Math.toRadians(degrees)); - g.translate(-px, -py); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#rotate(float) - */ - @Override - public void rotate(float degrees) { - getGraphics2d().rotate(Math.toRadians(degrees)); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#scale(float, float, float, float) - */ - @Override - public void scale(float sx, float sy, float px, float py) { - Graphics2D g = getGraphics2d(); - g.translate(px, py); - g.scale(sx, sy); - g.translate(-px, -py); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#scale(float, float) - */ - @Override - public void scale(float sx, float sy) { - getGraphics2d().scale(sx, sy); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint) - */ - @Override - public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { - // WARNING: the logic in this method is similar to Paint.measureText. - // Any change to this method should be reflected in Paint.measureText - Graphics2D g = getGraphics2d(); - - g = (Graphics2D)g.create(); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - // set the color. because this only handles RGB, the alpha channel is handled - // as a composite. - g.setColor(new Color(paint.getColor())); - int alpha = paint.getAlpha(); - float falpha = alpha / 255.f; - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); - - - // Paint.TextAlign indicates how the text is positioned relative to X. - // LEFT is the default and there's nothing to do. - if (paint.getTextAlign() != Align.LEFT) { - float m = paint.measureText(text, index, count); - if (paint.getTextAlign() == Align.CENTER) { - x -= m / 2; - } else if (paint.getTextAlign() == Align.RIGHT) { - x -= m; - } - } - - List<FontInfo> fonts = paint.getFonts(); - try { - if (fonts.size() > 0) { - FontInfo mainFont = fonts.get(0); - int i = index; - int lastIndex = index + count; - while (i < lastIndex) { - // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); - if (upTo == -1) { - // draw all the rest and exit. - g.setFont(mainFont.mFont); - g.drawChars(text, i, lastIndex - i, (int)x, (int)y); - return; - } else if (upTo > 0) { - // draw what's possible - g.setFont(mainFont.mFont); - g.drawChars(text, i, upTo - i, (int)x, (int)y); - - // compute the width that was drawn to increase x - x += mainFont.mMetrics.charsWidth(text, i, upTo - i); - - // move index to the first non displayed char. - i = upTo; - - // don't call continue at this point. Since it is certain the main font - // cannot display the font a index upTo (now ==i), we move on to the - // fallback fonts directly. - } - - // no char supported, attempt to read the next char(s) with the - // fallback font. In this case we only test the first character - // and then go back to test with the main font. - // Special test for 2-char characters. - boolean foundFont = false; - for (int f = 1 ; f < fonts.size() ; f++) { - FontInfo fontInfo = fonts.get(f); - - // need to check that the font can display the character. We test - // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); - if (upTo == -1) { - // draw that char - g.setFont(fontInfo.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); - - // update x - x += fontInfo.mMetrics.charsWidth(text, i, charCount); - - // update the index in the text, and move on - i += charCount; - foundFont = true; - break; - - } - } - - // in case no font can display the char, display it with the main font. - // (it'll put a square probably) - if (foundFont == false) { - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - - g.setFont(mainFont.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); - - // measure it to advance x - x += mainFont.mMetrics.charsWidth(text, i, charCount); - - // and move to the next chars. - i += charCount; - } - } - } - } finally { - g.dispose(); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) - */ - @Override - public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { - drawText(text.toString().toCharArray(), start, end - start, x, y, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawText(java.lang.String, float, float, android.graphics.Paint) - */ - @Override - public void drawText(String text, float x, float y, Paint paint) { - drawText(text.toCharArray(), 0, text.length(), x, y, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawText(java.lang.String, int, int, float, float, android.graphics.Paint) - */ - @Override - public void drawText(String text, int start, int end, float x, float y, Paint paint) { - drawText(text.toCharArray(), start, end - start, x, y, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint) - */ - @Override - public void drawRect(RectF rect, Paint paint) { - doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint) - */ - @Override - public void drawRect(float left, float top, float right, float bottom, Paint paint) { - doDrawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top), paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawRect(android.graphics.Rect, android.graphics.Paint) - */ - @Override - public void drawRect(Rect r, Paint paint) { - doDrawRect(r.left, r.top, r.width(), r.height(), paint); - } - - private final void doDrawRect(int left, int top, int width, int height, Paint paint) { - if (width > 0 && height > 0) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - Style style = paint.getStyle(); - - // draw - if (style == Style.FILL || style == Style.FILL_AND_STROKE) { - g.fillRect(left, top, width, height); - } - - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - g.drawRect(left, top, width, height); - } - - // dispose Graphics2D object - g.dispose(); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint) - */ - @Override - public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { - if (rect.width() > 0 && rect.height() > 0) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - Style style = paint.getStyle(); - - // draw - - int arcWidth = (int)(rx * 2); - int arcHeight = (int)(ry * 2); - - if (style == Style.FILL || style == Style.FILL_AND_STROKE) { - g.fillRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), - arcWidth, arcHeight); - } - - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), - arcWidth, arcHeight); - } - - // dispose Graphics2D object - g.dispose(); - } - } - - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawLine(float, float, float, float, android.graphics.Paint) - */ - @Override - public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); - - // dispose Graphics2D object - g.dispose(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint) - */ - @Override - public void drawLines(float[] pts, int offset, int count, Paint paint) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - for (int i = 0 ; i < count ; i += 4) { - g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1], - (int)pts[i + offset + 2], (int)pts[i + offset + 3]); - } - - // dispose Graphics2D object - g.dispose(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawLines(float[], android.graphics.Paint) - */ - @Override - public void drawLines(float[] pts, Paint paint) { - drawLines(pts, 0, pts.length, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint) - */ - @Override - public void drawCircle(float cx, float cy, float radius, Paint paint) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - Style style = paint.getStyle(); - - int size = (int)(radius * 2); - - // draw - if (style == Style.FILL || style == Style.FILL_AND_STROKE) { - g.fillOval((int)(cx - radius), (int)(cy - radius), size, size); - } - - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - g.drawOval((int)(cx - radius), (int)(cy - radius), size, size); - } - - // dispose Graphics2D object - g.dispose(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawOval(android.graphics.RectF, android.graphics.Paint) - */ - @Override - public void drawOval(RectF oval, Paint paint) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - Style style = paint.getStyle(); - - // draw - if (style == Style.FILL || style == Style.FILL_AND_STROKE) { - g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); - } - - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); - } - - // dispose Graphics2D object - g.dispose(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint) - */ - @Override - public void drawPath(Path path, Paint paint) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = getCustomGraphics(paint); - - Style style = paint.getStyle(); - - // draw - if (style == Style.FILL || style == Style.FILL_AND_STROKE) { - g.fill(path.getAwtShape()); - } - - if (style == Style.STROKE || style == Style.FILL_AND_STROKE) { - g.draw(path.getAwtShape()); - } - - // dispose Graphics2D object - g.dispose(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix) - */ - @Override - public void setMatrix(Matrix matrix) { - // get the new current graphics - Graphics2D g = getGraphics2d(); - - // and apply the matrix - g.setTransform(Matrix_Delegate.getAffineTransform(matrix)); - - if (mLogger != null && Matrix_Delegate.hasPerspective(matrix)) { - mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor."); - } - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#concat(android.graphics.Matrix) - */ - @Override - public void concat(Matrix matrix) { - // get the current top graphics2D object. - Graphics2D g = getGraphics2d(); - - // get its current matrix - AffineTransform currentTx = g.getTransform(); - // get the AffineTransform of the given matrix - AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix); - - // combine them so that the given matrix is applied after. - currentTx.preConcatenate(matrixTx); - - // give it to the graphics2D as a new matrix replacing all previous transform - g.setTransform(currentTx); - } - - - // -------------------- - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipPath(android.graphics.Path, android.graphics.Region.Op) - */ - @Override - public boolean clipPath(Path path, Op op) { - // TODO Auto-generated method stub - return super.clipPath(path, op); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipPath(android.graphics.Path) - */ - @Override - public boolean clipPath(Path path) { - // TODO Auto-generated method stub - return super.clipPath(path); - } - - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRegion(android.graphics.Region, android.graphics.Region.Op) - */ - @Override - public boolean clipRegion(Region region, Op op) { - // TODO Auto-generated method stub - return super.clipRegion(region, op); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#clipRegion(android.graphics.Region) - */ - @Override - public boolean clipRegion(Region region) { - // TODO Auto-generated method stub - return super.clipRegion(region); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint) - */ - @Override - public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, - Paint paint) { - // TODO Auto-generated method stub - super.drawArc(oval, startAngle, sweepAngle, useCenter, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint) - */ - @Override - public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, - int vertOffset, int[] colors, int colorOffset, Paint paint) { - // TODO Auto-generated method stub - super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.Rect) - */ - @Override - public void drawPicture(Picture picture, Rect dst) { - // TODO Auto-generated method stub - super.drawPicture(picture, dst); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.RectF) - */ - @Override - public void drawPicture(Picture picture, RectF dst) { - // TODO Auto-generated method stub - super.drawPicture(picture, dst); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPicture(android.graphics.Picture) - */ - @Override - public void drawPicture(Picture picture) { - // TODO Auto-generated method stub - super.drawPicture(picture); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPoint(float, float, android.graphics.Paint) - */ - @Override - public void drawPoint(float x, float y, Paint paint) { - // TODO Auto-generated method stub - super.drawPoint(x, y, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPoints(float[], int, int, android.graphics.Paint) - */ - @Override - public void drawPoints(float[] pts, int offset, int count, Paint paint) { - // TODO Auto-generated method stub - super.drawPoints(pts, offset, count, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPoints(float[], android.graphics.Paint) - */ - @Override - public void drawPoints(float[] pts, Paint paint) { - // TODO Auto-generated method stub - super.drawPoints(pts, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPosText(char[], int, int, float[], android.graphics.Paint) - */ - @Override - public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) { - // TODO Auto-generated method stub - super.drawPosText(text, index, count, pos, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawPosText(java.lang.String, float[], android.graphics.Paint) - */ - @Override - public void drawPosText(String text, float[] pos, Paint paint) { - // TODO Auto-generated method stub - super.drawPosText(text, pos, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint) - */ - @Override - public void drawTextOnPath(char[] text, int index, int count, Path path, float offset, - float offset2, Paint paint) { - // TODO Auto-generated method stub - super.drawTextOnPath(text, index, count, path, offset, offset2, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint) - */ - @Override - public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) { - // TODO Auto-generated method stub - super.drawTextOnPath(text, path, offset, offset2, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint) - */ - @Override - public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, - float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, - int indexOffset, int indexCount, Paint paint) { - // TODO Auto-generated method stub - super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset, - indices, indexOffset, indexCount, paint); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#getDrawFilter() - */ - @Override - public DrawFilter getDrawFilter() { - // TODO Auto-generated method stub - return super.getDrawFilter(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#getMatrix() - */ - @Override - public Matrix getMatrix() { - // TODO Auto-generated method stub - return super.getMatrix(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#getMatrix(android.graphics.Matrix) - */ - @Override - public void getMatrix(Matrix ctm) { - // TODO Auto-generated method stub - super.getMatrix(ctm); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#isOpaque() - */ - @Override - public boolean isOpaque() { - // TODO Auto-generated method stub - return super.isOpaque(); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#saveLayer(float, float, float, float, android.graphics.Paint, int) - */ - @Override - public int saveLayer(float left, float top, float right, float bottom, Paint paint, - int saveFlags) { - // TODO Auto-generated method stub - return super.saveLayer(left, top, right, bottom, paint, saveFlags); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint, int) - */ - @Override - public int saveLayer(RectF bounds, Paint paint, int saveFlags) { - // TODO Auto-generated method stub - return super.saveLayer(bounds, paint, saveFlags); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int) - */ - @Override - public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, - int saveFlags) { - // TODO Auto-generated method stub - return super.saveLayerAlpha(left, top, right, bottom, alpha, saveFlags); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#saveLayerAlpha(android.graphics.RectF, int, int) - */ - @Override - public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) { - // TODO Auto-generated method stub - return super.saveLayerAlpha(bounds, alpha, saveFlags); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter) - */ - @Override - public void setDrawFilter(DrawFilter filter) { - // TODO Auto-generated method stub - super.setDrawFilter(filter); - } - - /* (non-Javadoc) - * @see android.graphics.Canvas#skew(float, float) - */ - @Override - public void skew(float sx, float sy) { - // TODO Auto-generated method stub - super.skew(sx, sy); - } - - - -} diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java new file mode 100644 index 0000000..6627d37 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import com.android.layoutlib.api.ILayoutLog; +import com.android.layoutlib.bridge.DelegateManager; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.Stack; + +/** + * Delegate implementing the native methods of android.graphics.Canvas + * + * Through the layoutlib_create tool, the original native methods of Canvas have been replaced + * by calls to methods of the same name in this delegate class. + * + * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between + * it and the original Canvas class. + * + * @see DelegateManager + * + */ +public class Canvas_Delegate { + + // ---- delegate manager ---- + private static final DelegateManager<Canvas_Delegate> sManager = + new DelegateManager<Canvas_Delegate>(); + + // ---- delegate helper data ---- + + // ---- delegate data ---- + private BufferedImage mBufferedImage; + private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>(); + private ILayoutLog mLogger; + + // ---- Public Helper methods ---- + + /** + * Returns the native delegate associated to a given {@link Canvas} object. + */ + public static Canvas_Delegate getDelegate(Canvas canvas) { + return sManager.getDelegate(canvas.mNativeCanvas); + } + + /** + * Returns the native delegate associated to a given an int referencing a {@link Canvas} object. + */ + public static Canvas_Delegate getDelegate(int native_canvas) { + return sManager.getDelegate(native_canvas); + } + + /** + * Sets the layoutlib logger into the canvas. + * @param logger + */ + public void setLogger(ILayoutLog logger) { + mLogger = logger; + } + + /** + * Returns the current {@link Graphics2D} used to draw. + */ + public Graphics2D getGraphics2d() { + return mGraphicsStack.peek(); + } + + /** + * Disposes of the {@link Graphics2D} stack. + */ + public void dispose() { + + } + + // ---- native methods ---- + + /*package*/ static boolean isOpaque(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int getWidth(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int getHeight(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void rotate(Canvas thisCanvas, float degrees) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void skew(Canvas thisCanvas, float sx, float sy) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, + float bottom) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, + int bottom) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int save(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int save(Canvas thisCanvas, int saveFlags) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void restore(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int getSaveCount(Canvas thisCanvas) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, + Paint paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count, + Paint paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void freeCaches() { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int initRaster(int nativeBitmapOrZero) { + if (nativeBitmapOrZero > 0) { + // get the Bitmap from the int + Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); + + // create a new Canvas_Delegate with the given bitmap and return its new native int. + Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage()); + + return sManager.addDelegate(newDelegate); + } else { + // create a new Canvas_Delegate and return its new native int. + Canvas_Delegate newDelegate = new Canvas_Delegate(); + + return sManager.addDelegate(newDelegate); + } + } + + /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) { + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return; + } + + // get the delegate from the native int. + Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); + if (bitmapDelegate == null) { + assert false; + return; + } + + canvasDelegate.setBitmap(bitmapDelegate.getImage()); + } + + /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, + int paint, int layerFlags) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_saveLayer(int nativeCanvas, float l, + float t, float r, float b, + int paint, int layerFlags) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_saveLayerAlpha(int nativeCanvas, + RectF bounds, int alpha, + int layerFlags) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l, + float t, float r, float b, + int alpha, int layerFlags) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static void native_concat(int nCanvas, int nMatrix) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_clipRect(int nCanvas, + float left, float top, + float right, float bottom, + int regionOp) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_clipPath(int nativeCanvas, + int nativePath, + int regionOp) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_clipRegion(int nativeCanvas, + int nativeRegion, + int regionOp) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeSetDrawFilter(int nativeCanvas, + int nativeFilter) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_getClipBounds(int nativeCanvas, + Rect bounds) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_getCTM(int canvas, int matrix) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_quickReject(int nativeCanvas, + RectF rect, + int native_edgeType) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_quickReject(int nativeCanvas, + int path, + int native_edgeType) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static boolean native_quickReject(int nativeCanvas, + float left, float top, + float right, float bottom, + int native_edgeType) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, + int b) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, + int g, int b) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawColor(int nativeCanvas, int color) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawColor(int nativeCanvas, int color, + int mode) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawLine(int nativeCanvas, float startX, + float startY, float stopX, + float stopY, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawRect(int nativeCanvas, float left, + float top, float right, + float bottom, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawOval(int nativeCanvas, RectF oval, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawCircle(int nativeCanvas, float cx, + float cy, float radius, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, + float startAngle, float sweep, + boolean useCenter, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawRoundRect(int nativeCanvas, + RectF rect, float rx, + float ry, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawPath(int nativeCanvas, int path, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, + float left, float top, + int nativePaintOrZero, + int canvasDensity, + int screenDensity, + int bitmapDensity) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, + Rect src, RectF dst, + int nativePaintOrZero, + int screenDensity, + int bitmapDensity) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, + Rect src, Rect dst, + int nativePaintOrZero, + int screenDensity, + int bitmapDensity) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, + int offset, int stride, float x, + float y, int width, int height, + boolean hasAlpha, + int nativePaintOrZero) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, + int nMatrix, int nPaint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, + int meshWidth, int meshHeight, + float[] verts, int vertOffset, + int[] colors, int colorOffset, int nPaint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, + float[] verts, int vertOffset, float[] texs, int texOffset, + int[] colors, int colorOffset, short[] indices, + int indexOffset, int indexCount, int nPaint) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static void native_drawText(int nativeCanvas, char[] text, + int index, int count, float x, + float y, int flags, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawText(int nativeCanvas, String text, + int start, int end, float x, + float y, int flags, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static void native_drawTextRun(int nativeCanvas, String text, + int start, int end, int contextStart, int contextEnd, + float x, float y, int flags, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, + int start, int count, int contextStart, int contextCount, + float x, float y, int flags, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static void native_drawPosText(int nativeCanvas, + char[] text, int index, + int count, float[] pos, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawPosText(int nativeCanvas, + String text, float[] pos, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawTextOnPath(int nativeCanvas, + char[] text, int index, + int count, int path, + float hOffset, + float vOffset, int bidiFlags, + int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawTextOnPath(int nativeCanvas, + String text, int path, + float hOffset, + float vOffset, + int flags, int paint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_drawPicture(int nativeCanvas, + int nativePicture) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void finalizer(int nativeCanvas) { + sManager.removeDelegate(nativeCanvas); + } + + // ---- Private delegate/helper methods ---- + + private Canvas_Delegate(BufferedImage image) { + setBitmap(image); + } + + private Canvas_Delegate() { + } + + private void setBitmap(BufferedImage image) { + mBufferedImage = image; + mGraphicsStack.push(mBufferedImage.createGraphics()); + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java deleted file mode 100644 index 2de21c1..0000000 --- a/tools/layoutlib/bridge/src/android/graphics/Paint.java +++ /dev/null @@ -1,1211 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.SpannedString; -import android.text.TextUtils; - -import java.awt.BasicStroke; -import java.awt.Font; -import java.awt.Toolkit; -import java.awt.font.FontRenderContext; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A paint implementation overridden by the LayoutLib bridge. - */ -public class Paint extends _Original_Paint { - - private int mColor = 0xFFFFFFFF; - private float mStrokeWidth = 1.f; - private float mTextSize = 20; - private float mScaleX = 1; - private float mSkewX = 0; - private Align mAlign = Align.LEFT; - private Style mStyle = Style.FILL; - private float mStrokeMiter = 4.0f; - private Cap mCap = Cap.BUTT; - private Join mJoin = Join.MITER; - private int mFlags = 0; - - /** - * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. - */ - public static final class FontInfo { - Font mFont; - java.awt.FontMetrics mMetrics; - } - - private List<FontInfo> mFonts; - private final FontRenderContext mFontContext = new FontRenderContext( - new AffineTransform(), true, true); - - public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; - public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG; - public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG; - public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG; - public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG; - public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG; - public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG; - public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG; - public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG; - - public static class FontMetrics extends _Original_Paint.FontMetrics { - } - - public static class FontMetricsInt extends _Original_Paint.FontMetricsInt { - } - - /** - * The Style specifies if the primitive being drawn is filled, - * stroked, or both (in the same color). The default is FILL. - */ - public enum Style { - /** - * Geometry and text drawn with this style will be filled, ignoring all - * stroke-related settings in the paint. - */ - FILL (0), - /** - * Geometry and text drawn with this style will be stroked, respecting - * the stroke-related fields on the paint. - */ - STROKE (1), - /** - * Geometry and text drawn with this style will be both filled and - * stroked at the same time, respecting the stroke-related fields on - * the paint. - */ - FILL_AND_STROKE (2); - - Style(int nativeInt) { - this.nativeInt = nativeInt; - } - final int nativeInt; - } - - /** - * The Cap specifies the treatment for the beginning and ending of - * stroked lines and paths. The default is BUTT. - */ - public enum Cap { - /** - * The stroke ends with the path, and does not project beyond it. - */ - BUTT (0), - /** - * The stroke projects out as a square, with the center at the end - * of the path. - */ - ROUND (1), - /** - * The stroke projects out as a semicircle, with the center at the - * end of the path. - */ - SQUARE (2); - - private Cap(int nativeInt) { - this.nativeInt = nativeInt; - } - final int nativeInt; - - /** custom for layoutlib */ - public int getJavaCap() { - switch (this) { - case BUTT: - return BasicStroke.CAP_BUTT; - case ROUND: - return BasicStroke.CAP_ROUND; - default: - case SQUARE: - return BasicStroke.CAP_SQUARE; - } - } - } - - /** - * The Join specifies the treatment where lines and curve segments - * join on a stroked path. The default is MITER. - */ - public enum Join { - /** - * The outer edges of a join meet at a sharp angle - */ - MITER (0), - /** - * The outer edges of a join meet in a circular arc. - */ - ROUND (1), - /** - * The outer edges of a join meet with a straight line - */ - BEVEL (2); - - private Join(int nativeInt) { - this.nativeInt = nativeInt; - } - final int nativeInt; - - /** custom for layoutlib */ - public int getJavaJoin() { - switch (this) { - default: - case MITER: - return BasicStroke.JOIN_MITER; - case ROUND: - return BasicStroke.JOIN_ROUND; - case BEVEL: - return BasicStroke.JOIN_BEVEL; - } - } - } - - /** - * Align specifies how drawText aligns its text relative to the - * [x,y] coordinates. The default is LEFT. - */ - public enum Align { - /** - * The text is drawn to the right of the x,y origin - */ - LEFT (0), - /** - * The text is drawn centered horizontally on the x,y origin - */ - CENTER (1), - /** - * The text is drawn to the left of the x,y origin - */ - RIGHT (2); - - private Align(int nativeInt) { - this.nativeInt = nativeInt; - } - final int nativeInt; - } - - public Paint() { - this(0); - } - - /* - * Do not remove or com.android.layoutlib.bridge.TestClassReplacement fails. - */ - @Override - public void finalize() { } - - public Paint(int flags) { - setFlags(flags | DEFAULT_PAINT_FLAGS); - initFont(); - } - - public Paint(Paint paint) { - set(paint); - initFont(); - } - - @Override - public void reset() { - super.reset(); - } - - /** - * Returns the list of {@link Font} objects. The first item is the main font, the rest - * are fall backs for characters not present in the main font. - */ - public List<FontInfo> getFonts() { - return mFonts; - } - - private void initFont() { - mTypeface = Typeface.DEFAULT; - updateFontObject(); - } - - /** - * Update the {@link Font} object from the typeface, text size and scaling - */ - @SuppressWarnings("deprecation") - private void updateFontObject() { - if (mTypeface != null) { - // Get the fonts from the TypeFace object. - List<Font> fonts = Typeface_Delegate.getFonts(mTypeface); - - // create new font objects as well as FontMetrics, based on the current text size - // and skew info. - ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); - for (Font font : fonts) { - FontInfo info = new FontInfo(); - info.mFont = font.deriveFont(mTextSize); - if (mScaleX != 1.0 || mSkewX != 0) { - // TODO: support skew - info.mFont = info.mFont.deriveFont(new AffineTransform( - mScaleX, mSkewX, 0, 0, 1, 0)); - } - info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); - - infoList.add(info); - } - - mFonts = Collections.unmodifiableList(infoList); - } - } - - //---------------------------------------- - - public void set(Paint src) { - if (this != src) { - mColor = src.mColor; - mTextSize = src.mTextSize; - mScaleX = src.mScaleX; - mSkewX = src.mSkewX; - mAlign = src.mAlign; - mStyle = src.mStyle; - mFlags = src.mFlags; - - updateFontObject(); - - super.set(src); - } - } - - @Override - public void setCompatibilityScaling(float factor) { - super.setCompatibilityScaling(factor); - } - - @Override - public int getFlags() { - return mFlags; - } - - @Override - public void setFlags(int flags) { - mFlags = flags; - } - - @Override - public boolean isAntiAlias() { - return super.isAntiAlias(); - } - - @Override - public boolean isDither() { - return super.isDither(); - } - - @Override - public boolean isLinearText() { - return super.isLinearText(); - } - - @Override - public boolean isStrikeThruText() { - return super.isStrikeThruText(); - } - - @Override - public boolean isUnderlineText() { - return super.isUnderlineText(); - } - - @Override - public boolean isFakeBoldText() { - return super.isFakeBoldText(); - } - - @Override - public boolean isSubpixelText() { - return super.isSubpixelText(); - } - - @Override - public boolean isFilterBitmap() { - return super.isFilterBitmap(); - } - - /** - * Return the font's recommended interline spacing, given the Paint's - * settings for typeface, textSize, etc. If metrics is not null, return the - * fontmetric values in it. - * - * @param metrics If this object is not null, its fields are filled with - * the appropriate values given the paint's text attributes. - * @return the font's recommended interline spacing. - */ - public float getFontMetrics(FontMetrics metrics) { - if (mFonts.size() > 0) { - java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; - if (metrics != null) { - // Android expects negative ascent so we invert the value from Java. - metrics.top = - javaMetrics.getMaxAscent(); - metrics.ascent = - javaMetrics.getAscent(); - metrics.descent = javaMetrics.getDescent(); - metrics.bottom = javaMetrics.getMaxDescent(); - metrics.leading = javaMetrics.getLeading(); - } - - return javaMetrics.getHeight(); - } - - return 0; - } - - public int getFontMetricsInt(FontMetricsInt metrics) { - if (mFonts.size() > 0) { - java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; - if (metrics != null) { - // Android expects negative ascent so we invert the value from Java. - metrics.top = - javaMetrics.getMaxAscent(); - metrics.ascent = - javaMetrics.getAscent(); - metrics.descent = javaMetrics.getDescent(); - metrics.bottom = javaMetrics.getMaxDescent(); - metrics.leading = javaMetrics.getLeading(); - } - - return javaMetrics.getHeight(); - } - - return 0; - } - - /** - * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics - */ - public FontMetrics getFontMetrics() { - FontMetrics fm = new FontMetrics(); - getFontMetrics(fm); - return fm; - } - - /** - * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt - */ - public FontMetricsInt getFontMetricsInt() { - FontMetricsInt fm = new FontMetricsInt(); - getFontMetricsInt(fm); - return fm; - } - - - - @Override - public float getFontMetrics(_Original_Paint.FontMetrics metrics) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - @Override - public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - @Override - public Typeface setTypeface(Typeface typeface) { - if (typeface != null) { - mTypeface = typeface; - } else { - mTypeface = Typeface.DEFAULT; - } - - updateFontObject(); - - return typeface; - } - - @Override - public Typeface getTypeface() { - return super.getTypeface(); - } - - @Override - public int getColor() { - return mColor; - } - - @Override - public void setColor(int color) { - mColor = color; - } - - @Override - public void setARGB(int a, int r, int g, int b) { - super.setARGB(a, r, g, b); - } - - @Override - public void setAlpha(int alpha) { - mColor = (alpha << 24) | (mColor & 0x00FFFFFF); - } - - @Override - public int getAlpha() { - return mColor >>> 24; - } - - /** - * Set or clear the shader object. - * <p /> - * Pass null to clear any previous shader. - * As a convenience, the parameter passed is also returned. - * - * @param shader May be null. the new shader to be installed in the paint - * @return shader - */ - @Override - public Shader setShader(Shader shader) { - return mShader = shader; - } - - @Override - public Shader getShader() { - return super.getShader(); - } - - /** - * Set or clear the paint's colorfilter, returning the parameter. - * - * @param filter May be null. The new filter to be installed in the paint - * @return filter - */ - @Override - public ColorFilter setColorFilter(ColorFilter filter) { - mColorFilter = filter; - return filter; - } - - @Override - public ColorFilter getColorFilter() { - return super.getColorFilter(); - } - - /** - * Set or clear the xfermode object. - * <p /> - * Pass null to clear any previous xfermode. - * As a convenience, the parameter passed is also returned. - * - * @param xfermode May be null. The xfermode to be installed in the paint - * @return xfermode - */ - @Override - public Xfermode setXfermode(Xfermode xfermode) { - return mXfermode = xfermode; - } - - @Override - public Xfermode getXfermode() { - return super.getXfermode(); - } - - @Override - public Rasterizer setRasterizer(Rasterizer rasterizer) { - mRasterizer = rasterizer; - return rasterizer; - } - - @Override - public Rasterizer getRasterizer() { - return super.getRasterizer(); - } - - @Override - public void setShadowLayer(float radius, float dx, float dy, int color) { - // TODO Auto-generated method stub - } - - @Override - public void clearShadowLayer() { - super.clearShadowLayer(); - } - - public void setTextAlign(Align align) { - mAlign = align; - } - - @Override - public void setTextAlign(android.graphics._Original_Paint.Align align) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public Align getTextAlign() { - return mAlign; - } - - public void setStyle(Style style) { - mStyle = style; - } - - @Override - public void setStyle(android.graphics._Original_Paint.Style style) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public Style getStyle() { - return mStyle; - } - - @Override - public void setDither(boolean dither) { - mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG; - } - - @Override - public void setAntiAlias(boolean aa) { - mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG; - } - - @Override - public void setFakeBoldText(boolean flag) { - mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG; - } - - @Override - public void setLinearText(boolean flag) { - mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG; - } - - @Override - public void setSubpixelText(boolean flag) { - mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG; - } - - @Override - public void setUnderlineText(boolean flag) { - mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG; - } - - @Override - public void setStrikeThruText(boolean flag) { - mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG; - } - - @Override - public void setFilterBitmap(boolean flag) { - mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG; - } - - @Override - public float getStrokeWidth() { - return mStrokeWidth; - } - - @Override - public void setStrokeWidth(float width) { - mStrokeWidth = width; - } - - @Override - public float getStrokeMiter() { - return mStrokeMiter; - } - - @Override - public void setStrokeMiter(float miter) { - mStrokeMiter = miter; - } - - @Override - public void setStrokeCap(android.graphics._Original_Paint.Cap cap) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public void setStrokeCap(Cap cap) { - mCap = cap; - } - - public Cap getStrokeCap() { - return mCap; - } - - @Override - public void setStrokeJoin(android.graphics._Original_Paint.Join join) { - throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); - } - - public void setStrokeJoin(Join join) { - mJoin = join; - } - - public Join getStrokeJoin() { - return mJoin; - } - - @Override - public boolean getFillPath(Path src, Path dst) { - return super.getFillPath(src, dst); - } - - @Override - public PathEffect setPathEffect(PathEffect effect) { - mPathEffect = effect; - return effect; - } - - @Override - public PathEffect getPathEffect() { - return super.getPathEffect(); - } - - @Override - public MaskFilter setMaskFilter(MaskFilter maskfilter) { - mMaskFilter = maskfilter; - return maskfilter; - } - - @Override - public MaskFilter getMaskFilter() { - return super.getMaskFilter(); - } - - /** - * Return the paint's text size. - * - * @return the paint's text size. - */ - @Override - public float getTextSize() { - return mTextSize; - } - - /** - * Set the paint's text size. This value must be > 0 - * - * @param textSize set the paint's text size. - */ - @Override - public void setTextSize(float textSize) { - mTextSize = textSize; - - updateFontObject(); - } - - /** - * Return the paint's horizontal scale factor for text. The default value - * is 1.0. - * - * @return the paint's scale factor in X for drawing/measuring text - */ - @Override - public float getTextScaleX() { - return mScaleX; - } - - /** - * Set the paint's horizontal scale factor for text. The default value - * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will - * stretch the text narrower. - * - * @param scaleX set the paint's scale in X for drawing/measuring text. - */ - @Override - public void setTextScaleX(float scaleX) { - mScaleX = scaleX; - - updateFontObject(); - } - - /** - * Return the paint's horizontal skew factor for text. The default value - * is 0. - * - * @return the paint's skew factor in X for drawing text. - */ - @Override - public float getTextSkewX() { - return mSkewX; - } - - /** - * Set the paint's horizontal skew factor for text. The default value - * is 0. For approximating oblique text, use values around -0.25. - * - * @param skewX set the paint's skew factor in X for drawing text. - */ - @Override - public void setTextSkewX(float skewX) { - mSkewX = skewX; - - updateFontObject(); - } - - @Override - public float getFontSpacing() { - return super.getFontSpacing(); - } - - /** - * Return the distance above (negative) the baseline (ascent) based on the - * current typeface and text size. - * - * @return the distance above (negative) the baseline (ascent) based on the - * current typeface and text size. - */ - @Override - public float ascent() { - if (mFonts.size() > 0) { - java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; - // Android expects negative ascent so we invert the value from Java. - return - javaMetrics.getAscent(); - } - - return 0; - } - - /** - * Return the distance below (positive) the baseline (descent) based on the - * current typeface and text size. - * - * @return the distance below (positive) the baseline (descent) based on - * the current typeface and text size. - */ - @Override - public float descent() { - if (mFonts.size() > 0) { - java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; - return javaMetrics.getDescent(); - } - - return 0; - } - - /** - * Return the width of the text. - * - * @param text The text to measure - * @param index The index of the first character to start measuring - * @param count THe number of characters to measure, beginning with start - * @return The width of the text - */ - @Override - public float measureText(char[] text, int index, int count) { - // WARNING: the logic in this method is similar to Canvas.drawText. - // Any change to this method should be reflected in Canvas.drawText - if (mFonts.size() > 0) { - FontInfo mainFont = mFonts.get(0); - int i = index; - int lastIndex = index + count; - float total = 0f; - while (i < lastIndex) { - // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); - if (upTo == -1) { - // shortcut to exit - return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); - } else if (upTo > 0) { - total += mainFont.mMetrics.charsWidth(text, i, upTo - i); - i = upTo; - // don't call continue at this point. Since it is certain the main font - // cannot display the font a index upTo (now ==i), we move on to the - // fallback fonts directly. - } - - // no char supported, attempt to read the next char(s) with the - // fallback font. In this case we only test the first character - // and then go back to test with the main font. - // Special test for 2-char characters. - boolean foundFont = false; - for (int f = 1 ; f < mFonts.size() ; f++) { - FontInfo fontInfo = mFonts.get(f); - - // need to check that the font can display the character. We test - // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); - if (upTo == -1) { - total += fontInfo.mMetrics.charsWidth(text, i, charCount); - i += charCount; - foundFont = true; - break; - - } - } - - // in case no font can display the char, measure it with the main font. - if (foundFont == false) { - int size = Character.isHighSurrogate(text[i]) ? 2 : 1; - total += mainFont.mMetrics.charsWidth(text, i, size); - i += size; - } - } - } - - return 0; - } - - /** - * Return the width of the text. - * - * @param text The text to measure - * @param start The index of the first character to start measuring - * @param end 1 beyond the index of the last character to measure - * @return The width of the text - */ - @Override - public float measureText(String text, int start, int end) { - return measureText(text.toCharArray(), start, end - start); - } - - /** - * Return the width of the text. - * - * @param text The text to measure - * @return The width of the text - */ - @Override - public float measureText(String text) { - return measureText(text.toCharArray(), 0, text.length()); - } - - /* - * re-implement to call SpannableStringBuilder.measureText with a Paint object - * instead of an _Original_Paint - */ - @Override - public float measureText(CharSequence text, int start, int end) { - if (text instanceof String) { - return measureText((String)text, start, end); - } - if (text instanceof SpannedString || - text instanceof SpannableString) { - return measureText(text.toString(), start, end); - } - if (text instanceof SpannableStringBuilder) { - return ((SpannableStringBuilder)text).measureText(start, end, this); - } - - char[] buf = TemporaryBuffer.obtain(end - start); - TextUtils.getChars(text, start, end, buf, 0); - float result = measureText(buf, 0, end - start); - TemporaryBuffer.recycle(buf); - return result; - } - - /** - * Measure the text, stopping early if the measured width exceeds maxWidth. - * Return the number of chars that were measured, and if measuredWidth is - * not null, return in it the actual width measured. - * - * @param text The text to measure - * @param index The offset into text to begin measuring at - * @param count The number of maximum number of entries to measure. If count - * is negative, then the characters before index are measured - * in reverse order. This allows for measuring the end of - * string. - * @param maxWidth The maximum width to accumulate. - * @param measuredWidth Optional. If not null, returns the actual width - * measured. - * @return The number of chars that were measured. Will always be <= - * abs(count). - */ - @Override - public int breakText(char[] text, int index, int count, - float maxWidth, float[] measuredWidth) { - int inc = count > 0 ? 1 : -1; - - int measureIndex = 0; - float measureAcc = 0; - for (int i = index ; i != index + count ; i += inc, measureIndex++) { - int start, end; - if (i < index) { - start = i; - end = index; - } else { - start = index; - end = i; - } - - // measure from start to end - float res = measureText(text, start, end - start + 1); - - if (measuredWidth != null) { - measuredWidth[measureIndex] = res; - } - - measureAcc += res; - if (res > maxWidth) { - // we should not return this char index, but since it's 0-based and we need - // to return a count, we simply return measureIndex; - return measureIndex; - } - - } - - return measureIndex; - } - - /** - * Measure the text, stopping early if the measured width exceeds maxWidth. - * Return the number of chars that were measured, and if measuredWidth is - * not null, return in it the actual width measured. - * - * @param text The text to measure - * @param measureForwards If true, measure forwards, starting at index. - * Otherwise, measure backwards, starting with the - * last character in the string. - * @param maxWidth The maximum width to accumulate. - * @param measuredWidth Optional. If not null, returns the actual width - * measured. - * @return The number of chars that were measured. Will always be <= - * abs(count). - */ - @Override - public int breakText(String text, boolean measureForwards, - float maxWidth, float[] measuredWidth) { - return breakText(text, - 0 /* start */, text.length() /* end */, - measureForwards, maxWidth, measuredWidth); - } - - /** - * Measure the text, stopping early if the measured width exceeds maxWidth. - * Return the number of chars that were measured, and if measuredWidth is - * not null, return in it the actual width measured. - * - * @param text The text to measure - * @param start The offset into text to begin measuring at - * @param end The end of the text slice to measure. - * @param measureForwards If true, measure forwards, starting at start. - * Otherwise, measure backwards, starting with end. - * @param maxWidth The maximum width to accumulate. - * @param measuredWidth Optional. If not null, returns the actual width - * measured. - * @return The number of chars that were measured. Will always be <= - * abs(end - start). - */ - @Override - public int breakText(CharSequence text, int start, int end, boolean measureForwards, - float maxWidth, float[] measuredWidth) { - char[] buf = new char[end - start]; - int result; - - TextUtils.getChars(text, start, end, buf, 0); - - if (measureForwards) { - result = breakText(buf, 0, end - start, maxWidth, measuredWidth); - } else { - result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth); - } - - return result; - } - - /** - * Return the advance widths for the characters in the string. - * - * @param text The text to measure - * @param index The index of the first char to to measure - * @param count The number of chars starting with index to measure - * @param widths array to receive the advance widths of the characters. - * Must be at least a large as count. - * @return the actual number of widths returned. - */ - @Override - public int getTextWidths(char[] text, int index, int count, - float[] widths) { - if (mFonts.size() > 0) { - if ((index | count) < 0 || index + count > text.length - || count > widths.length) { - throw new ArrayIndexOutOfBoundsException(); - } - - // FIXME: handle multi-char characters. - // Need to figure out if the lengths of the width array takes into account - // multi-char characters. - for (int i = 0; i < count; i++) { - char c = text[i + index]; - boolean found = false; - for (FontInfo info : mFonts) { - if (info.mFont.canDisplay(c)) { - widths[i] = info.mMetrics.charWidth(c); - found = true; - break; - } - } - - if (found == false) { - // we stop there. - return i; - } - } - - return count; - } - - return 0; - } - - /** - * Return the advance widths for the characters in the string. - * - * @param text The text to measure - * @param start The index of the first char to to measure - * @param end The end of the text slice to measure - * @param widths array to receive the advance widths of the characters. - * Must be at least a large as the text. - * @return the number of unichars in the specified text. - */ - @Override - public int getTextWidths(String text, int start, int end, float[] widths) { - if ((start | end | (end - start) | (text.length() - end)) < 0) { - throw new IndexOutOfBoundsException(); - } - if (end - start > widths.length) { - throw new ArrayIndexOutOfBoundsException(); - } - - return getTextWidths(text.toCharArray(), start, end - start, widths); - } - - /* - * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object - * instead of an _Original_Paint - */ - @Override - public int getTextWidths(CharSequence text, int start, int end, float[] widths) { - if (text instanceof String) { - return getTextWidths((String)text, start, end, widths); - } - if (text instanceof SpannedString || text instanceof SpannableString) { - return getTextWidths(text.toString(), start, end, widths); - } - if (text instanceof SpannableStringBuilder) { - return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this); - } - - char[] buf = TemporaryBuffer.obtain(end - start); - TextUtils.getChars(text, start, end, buf, 0); - int result = getTextWidths(buf, 0, end - start, widths); - TemporaryBuffer.recycle(buf); - return result; - } - - @Override - public int getTextWidths(String text, float[] widths) { - return super.getTextWidths(text, widths); - } - - /** - * Return the path (outline) for the specified text. - * Note: just like Canvas.drawText, this will respect the Align setting in - * the paint. - * - * @param text The text to retrieve the path from - * @param index The index of the first character in text - * @param count The number of characterss starting with index - * @param x The x coordinate of the text's origin - * @param y The y coordinate of the text's origin - * @param path The path to receive the data describing the text. Must - * be allocated by the caller. - */ - @Override - public void getTextPath(char[] text, int index, int count, - float x, float y, Path path) { - - // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE - - if ((index | count) < 0 || index + count > text.length) { - throw new ArrayIndexOutOfBoundsException(); - } - - // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); - - throw new UnsupportedOperationException("IMPLEMENT AS NEEDED"); - } - - /** - * Return the path (outline) for the specified text. - * Note: just like Canvas.drawText, this will respect the Align setting - * in the paint. - * - * @param text The text to retrieve the path from - * @param start The first character in the text - * @param end 1 past the last charcter in the text - * @param x The x coordinate of the text's origin - * @param y The y coordinate of the text's origin - * @param path The path to receive the data describing the text. Must - * be allocated by the caller. - */ - @Override - public void getTextPath(String text, int start, int end, - float x, float y, Path path) { - if ((start | end | (end - start) | (text.length() - end)) < 0) { - throw new IndexOutOfBoundsException(); - } - - getTextPath(text.toCharArray(), start, end - start, x, y, path); - } - - /** - * Return in bounds (allocated by the caller) the smallest rectangle that - * encloses all of the characters, with an implied origin at (0,0). - * - * @param text String to measure and return its bounds - * @param start Index of the first char in the string to measure - * @param end 1 past the last char in the string measure - * @param bounds Returns the unioned bounds of all the text. Must be - * allocated by the caller. - */ - @Override - public void getTextBounds(String text, int start, int end, Rect bounds) { - if ((start | end | (end - start) | (text.length() - end)) < 0) { - throw new IndexOutOfBoundsException(); - } - if (bounds == null) { - throw new NullPointerException("need bounds Rect"); - } - - getTextBounds(text.toCharArray(), start, end - start, bounds); - } - - /** - * Return in bounds (allocated by the caller) the smallest rectangle that - * encloses all of the characters, with an implied origin at (0,0). - * - * @param text Array of chars to measure and return their unioned bounds - * @param index Index of the first char in the array to measure - * @param count The number of chars, beginning at index, to measure - * @param bounds Returns the unioned bounds of all the text. Must be - * allocated by the caller. - */ - @Override - public void getTextBounds(char[] text, int index, int count, Rect bounds) { - // FIXME - if (mFonts.size() > 0) { - if ((index | count) < 0 || index + count > text.length) { - throw new ArrayIndexOutOfBoundsException(); - } - if (bounds == null) { - throw new NullPointerException("need bounds Rect"); - } - - FontInfo mainInfo = mFonts.get(0); - - Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext); - bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight()); - } - } - - public static void finalizer(int foo) { - // pass - } -} diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java new file mode 100644 index 0000000..e8079ed --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import com.android.layoutlib.bridge.DelegateManager; + +import android.graphics.Paint.FontMetrics; +import android.graphics.Paint.FontMetricsInt; + +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Delegate implementing the native methods of android.graphics.Paint + * + * Through the layoutlib_create tool, the original native methods of Paint have been replaced + * by calls to methods of the same name in this delegate class. + * + * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between + * it and the original Paint class. + * + * @see DelegateManager + * + */ +public class Paint_Delegate { + + /** + * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. + */ + public static final class FontInfo { + Font mFont; + java.awt.FontMetrics mMetrics; + } + + // ---- delegate manager ---- + private static final DelegateManager<Paint_Delegate> sManager = + new DelegateManager<Paint_Delegate>(); + + // ---- delegate helper data ---- + private List<FontInfo> mFonts; + private final FontRenderContext mFontContext = new FontRenderContext( + new AffineTransform(), true, true); + + // ---- delegate data ---- + private int mFlags; + private int mColor; + private int mStyle; + private int mCap; + private int mJoin; + private int mAlign; + private int mTypeface; + private float mStrokeWidth; + private float mStrokeMiter; + private float mTextSize; + private float mTextScaleX; + private float mTextSkewX; + + + // ---- Public Helper methods ---- + + /** + * Returns the list of {@link Font} objects. The first item is the main font, the rest + * are fall backs for characters not present in the main font. + */ + public List<FontInfo> getFonts() { + return mFonts; + } + + + // ---- native methods ---- + + /*package*/ static int getFlags(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mFlags; + } + + /*package*/ static void setFlags(Paint thisPaint, int flags) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mFlags = flags; + } + + /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) { + setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa); + } + + /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) { + setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText); + } + + /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) { + setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText); + } + + /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) { + setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText); + } + + /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) { + setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText); + } + + /*package*/ static void setDither(Paint thisPaint, boolean dither) { + setFlag(thisPaint, Paint.DITHER_FLAG, dither); + } + + /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) { + setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText); + } + + /*package*/ static int getColor(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mColor; + } + + /*package*/ static void setColor(Paint thisPaint, int color) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mColor = color; + } + + /*package*/ static int getAlpha(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mColor >>> 24; + } + + /*package*/ static void setAlpha(Paint thisPaint, int a) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF); + } + + /*package*/ static float getStrokeWidth(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 1.f; + } + + return delegate.mStrokeWidth; + } + + /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mStrokeWidth = width; + } + + /*package*/ static float getStrokeMiter(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 1.f; + } + + return delegate.mStrokeMiter; + } + + /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mStrokeMiter = miter; + } + + /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, + int color) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float getTextSize(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 1.f; + } + + return delegate.mTextSize; + } + + /*package*/ static void setTextSize(Paint thisPaint, float textSize) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mTextSize = textSize; + } + + /*package*/ static float getTextScaleX(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 1.f; + } + + return delegate.mTextScaleX; + } + + /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mTextScaleX = scaleX; + } + + /*package*/ static float getTextSkewX(Paint thisPaint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 1.f; + } + + return delegate.mTextSkewX; + } + + /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + delegate.mTextSkewX = skewX; + } + + /*package*/ static float ascent(Paint thisPaint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float descent(Paint thisPaint) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, + int count) { + // WARNING: the logic in this method is similar to Canvas.drawText. + // Any change to this method should be reflected in Canvas.drawText + + // get the delegate + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return 0; + } + + if (delegate.mFonts.size() > 0) { + FontInfo mainFont = delegate.mFonts.get(0); + int i = index; + int lastIndex = index + count; + float total = 0f; + while (i < lastIndex) { + // always start with the main font. + int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); + if (upTo == -1) { + // shortcut to exit + return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); + } else if (upTo > 0) { + total += mainFont.mMetrics.charsWidth(text, i, upTo - i); + i = upTo; + // don't call continue at this point. Since it is certain the main font + // cannot display the font a index upTo (now ==i), we move on to the + // fallback fonts directly. + } + + // no char supported, attempt to read the next char(s) with the + // fallback font. In this case we only test the first character + // and then go back to test with the main font. + // Special test for 2-char characters. + boolean foundFont = false; + for (int f = 1 ; f < delegate.mFonts.size() ; f++) { + FontInfo fontInfo = delegate.mFonts.get(f); + + // need to check that the font can display the character. We test + // differently if the char is a high surrogate. + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + if (upTo == -1) { + total += fontInfo.mMetrics.charsWidth(text, i, charCount); + i += charCount; + foundFont = true; + break; + + } + } + + // in case no font can display the char, measure it with the main font. + if (foundFont == false) { + int size = Character.isHighSurrogate(text[i]) ? 2 : 1; + total += mainFont.mMetrics.charsWidth(text, i, size); + i += size; + } + } + } + + return 0; + } + + /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) { + return native_measureText(thisPaint, text.toCharArray(), start, end - start); + } + + /*package*/ static float native_measureText(Paint thisPaint, String text) { + return native_measureText(thisPaint, text.toCharArray(), 0, text.length()); + } + + /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, + float maxWidth, float[] measuredWidth) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, + float maxWidth, float[] measuredWidth) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static int native_init() { + Paint_Delegate newDelegate = new Paint_Delegate(); + return sManager.addDelegate(newDelegate); + } + + /*package*/ static int native_initWithPaint(int paint) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(paint); + if (delegate == null) { + assert false; + return 0; + } + + Paint_Delegate newDelegate = new Paint_Delegate(delegate); + return sManager.addDelegate(newDelegate); + } + + /*package*/ static void native_reset(int native_object) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return; + } + + delegate.reset(); + } + + /*package*/ static void native_set(int native_dst, int native_src) { + // get the delegate from the native int. + Paint_Delegate delegate_dst = sManager.getDelegate(native_dst); + if (delegate_dst == null) { + assert false; + return; + } + + // get the delegate from the native int. + Paint_Delegate delegate_src = sManager.getDelegate(native_src); + if (delegate_src == null) { + assert false; + return; + } + + delegate_dst.set(delegate_src); + } + + /*package*/ static int native_getStyle(int native_object) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mStyle; + } + + /*package*/ static void native_setStyle(int native_object, int style) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return; + } + + delegate.mStyle = style; + } + + /*package*/ static int native_getStrokeCap(int native_object) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mCap; + } + + /*package*/ static void native_setStrokeCap(int native_object, int cap) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return; + } + + delegate.mCap = cap; + } + + /*package*/ static int native_getStrokeJoin(int native_object) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mJoin; + } + + /*package*/ static void native_setStrokeJoin(int native_object, int join) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return; + } + + delegate.mJoin = join; + } + + /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setShader(int native_object, int shader) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setColorFilter(int native_object, int filter) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setXfermode(int native_object, int xfermode) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setPathEffect(int native_object, int effect) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_setTypeface(int native_object, int typeface) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mTypeface = typeface; + } + + /*package*/ static int native_setRasterizer(int native_object, int rasterizer) { + // FIXME + throw new UnsupportedOperationException(); + } + + + /*package*/ static int native_getTextAlign(int native_object) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return 0; + } + + return delegate.mAlign; + } + + /*package*/ static void native_setTextAlign(int native_object, int align) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + assert false; + return; + } + + delegate.mAlign = align; + } + + /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, + int count, float[] widths) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_getTextWidths(int native_object, String text, int start, + int end, float[] widths) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float native_getTextRunAdvances(int native_object, + char[] text, int index, int count, int contextIndex, int contextCount, + int flags, float[] advances, int advancesIndex) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static float native_getTextRunAdvances(int native_object, + String text, int start, int end, int contextStart, int contextEnd, + int flags, float[] advances, int advancesIndex) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, + int contextStart, int contextLength, int flags, int offset, int cursorOpt) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, + int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_getTextPath(int native_object, int bidiFlags, + char[] text, int index, int count, float x, float y, int path) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_getTextPath(int native_object, int bidiFlags, + String text, int start, int end, float x, float y, int path) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, + int end, Rect bounds) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, + int count, Rect bounds) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void finalizer(int nativePaint) { + sManager.removeDelegate(nativePaint); + } + + // ---- Private delegate/helper methods ---- + + private Paint_Delegate() { + reset(); + + mTypeface = Typeface.sDefaults[0].native_instance; + updateFontObject(); + } + + private Paint_Delegate(Paint_Delegate paint) { + set(paint); + updateFontObject(); + } + + private void set(Paint_Delegate paint) { + mFlags = paint.mFlags; + mColor = paint.mColor; + mStyle = paint.mStyle; + mCap = paint.mCap; + mJoin = paint.mJoin; + mAlign = paint.mAlign; + mTypeface = paint.mTypeface; + mStrokeWidth = paint.mStrokeWidth; + mStrokeMiter = paint.mStrokeMiter; + mTextSize = paint.mTextSize; + mTextScaleX = paint.mTextScaleX; + mTextSkewX = paint.mTextSkewX; + } + + private void reset() { + mFlags = Paint.DEFAULT_PAINT_FLAGS; + mColor = 0; + mStyle = 0; + mCap = 0; + mJoin = 0; + mAlign = 0; + mTypeface = 0; + mStrokeWidth = 1.f; + mStrokeMiter = 2.f; + mTextSize = 20.f; + mTextScaleX = 1.f; + mTextSkewX = 0.f; + } + + /** + * Update the {@link Font} object from the typeface, text size and scaling + */ + private void updateFontObject() { + if (mTypeface != 0) { + // Get the fonts from the TypeFace object. + List<Font> fonts = Typeface_Delegate.getFonts(mTypeface); + + // create new font objects as well as FontMetrics, based on the current text size + // and skew info. + ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); + for (Font font : fonts) { + FontInfo info = new FontInfo(); + info.mFont = font.deriveFont(mTextSize); + if (mTextScaleX != 1.0 || mTextSkewX != 0) { + // TODO: support skew + info.mFont = info.mFont.deriveFont(new AffineTransform( + mTextScaleX, mTextSkewX, 0, 0, 1, 0)); + } + info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); + + infoList.add(info); + } + + mFonts = Collections.unmodifiableList(infoList); + } + } + + private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + assert false; + return; + } + + if (flagValue) { + delegate.mFlags |= flagMask; + } else { + delegate.mFlags &= ~flagMask; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 248bdab..7e90e7d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -72,7 +72,11 @@ public final class Typeface_Delegate { } public static List<Font> getFonts(Typeface typeface) { - Typeface_Delegate delegate = sManager.getDelegate(typeface.native_instance); + return getFonts(typeface.native_instance); + } + + public static List<Font> getFonts(int native_int) { + Typeface_Delegate delegate = sManager.getDelegate(native_int); if (delegate == null) { assert false; return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 9eb83c8..cdb4148 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -24,6 +24,7 @@ import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; import com.android.layoutlib.api.IXmlPullParser; +import com.android.layoutlib.api.IDensityBasedResourceValue.Density; import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo; import com.android.ninepatch.NinePatch; @@ -33,7 +34,9 @@ import com.android.tools.layoutlib.create.OverrideMethod; import android.content.ClipData; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; +import android.graphics.Canvas_Delegate; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Typeface_Delegate; @@ -64,6 +67,7 @@ import android.widget.FrameLayout; import android.widget.TabHost; import android.widget.TabWidget; +import java.awt.image.BufferedImage; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -450,13 +454,28 @@ public final class Bridge implements ILayoutBridge { view.layout(0, screenOffset, screenWidth, screenHeight); // draw the views - Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger); + // create the BufferedImage into which the layout will be rendered. + BufferedImage image = new BufferedImage(screenWidth, screenHeight - screenOffset, + BufferedImage.TYPE_INT_ARGB); + + // create an Android bitmap around the BufferedImage + Bitmap bitmap = Bitmap_Delegate.createBitmap(image, Density.getEnum(density)); + + // create a Canvas around the Android bitmap + Canvas canvas = new Canvas(bitmap); + + // to set the logger, get the native delegate + Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); + canvasDelegate.setLogger(logger); + root.draw(canvas); - canvas.dispose(); + canvasDelegate.dispose(); + + return new LayoutResult( + visit(((ViewGroup)view).getChildAt(0), context), + image); - return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context), - canvas.getImage()); } catch (PostInflateException e) { return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n" + e.getMessage()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java index abbf2f0..2c92567 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java @@ -19,6 +19,7 @@ package com.android.layoutlib.bridge; import com.android.ninepatch.NinePatch; import android.graphics.Canvas; +import android.graphics.Canvas_Delegate; import android.graphics.ColorFilter; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -80,7 +81,8 @@ public class NinePatchDrawable extends Drawable { @Override public void draw(Canvas canvas) { Rect r = getBounds(); - m9Patch.draw(canvas.getGraphics2d(), r.left, r.top, r.width(), r.height()); + Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); + m9Patch.draw(canvasDelegate.getGraphics2d(), r.left, r.top, r.width(), r.height()); return; } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java index 6e14e82..ba3c51a 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java @@ -17,8 +17,6 @@ package com.android.layoutlib.bridge; import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics._Original_Paint; import android.text.TextPaint; import junit.framework.TestCase; @@ -58,14 +56,6 @@ public class AndroidGraphicsTests extends TestCase { } } - public void testPaint() { - _Original_Paint o = new _Original_Paint(); - assertNotNull(o); - - Paint p = new Paint(); - assertNotNull(p); - } - public void textTextPaint() { TextPaint p = new TextPaint(); assertNotNull(p); diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java index 7c1eecd..a86b5c9 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java @@ -87,7 +87,11 @@ public class TestNativeDelegate extends TestCase { try { // try to load the method with the given parameter types. - delegateClass.getDeclaredMethod(originalMethod.getName(), parameters); + Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(), + parameters); + + // check that the method is static + assertTrue((delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC); } catch (NoSuchMethodException e) { // compute a full class name that's long but not too long. StringBuilder sb = new StringBuilder(originalMethod.getName() + "("); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 0ecb474..c845cc4 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -104,7 +104,9 @@ public final class CreateInfo implements ICreateInfo { */ private final static String[] DELEGATE_CLASS_NATIVES = new String[] { "android.graphics.Bitmap", + "android.graphics.Canvas", "android.graphics.Matrix", + "android.graphics.Paint", "android.graphics.Typeface", }; @@ -126,11 +128,9 @@ public final class CreateInfo implements ICreateInfo { new String[] { "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", - "android.graphics.Canvas", "android.graphics._Original_Canvas", "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", "android.graphics.DashPathEffect", "android.graphics._Original_DashPathEffect", "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", - "android.graphics.Paint", "android.graphics._Original_Paint", "android.graphics.Path", "android.graphics._Original_Path", "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", @@ -150,13 +150,6 @@ public final class CreateInfo implements ICreateInfo { */ private final static String[] DELETE_RETURNS = new String[] { - "android.graphics.Paint", // class to delete methods from - "android.graphics.Paint$Align", // list of type identifying methods to delete - "android.graphics.Paint$Style", - "android.graphics.Paint$Join", - "android.graphics.Paint$Cap", - "android.graphics.Paint$FontMetrics", - "android.graphics.Paint$FontMetricsInt", null }; // separator, for next class/methods list. } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java index 21d6682..c7968a4 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java @@ -169,7 +169,7 @@ class DelegateMethodAdapter implements MethodVisitor { // Construct the descriptor of the delegate. For a static method, it's the same // however for an instance method we need to pass the 'this' reference first String desc = mDesc; - if (!mIsStatic && argTypes.length > 0) { + if (!mIsStatic) { Type[] argTypes2 = new Type[argTypes.length + 1]; argTypes2[0] = Type.getObjectType(mClassName); |