diff options
111 files changed, 4285 insertions, 1476 deletions
@@ -158,6 +158,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ + keystore/java/android/security/IKeyChainService.aidl \ location/java/android/location/ICountryDetector.aidl \ location/java/android/location/ICountryListener.aidl \ location/java/android/location/IGeocodeProvider.aidl \ @@ -21945,49 +21945,24 @@ package android.view { package android.view.accessibility { - public final class AccessibilityEvent implements android.os.Parcelable { + public final class AccessibilityEvent extends android.view.accessibility.AccessibilityRecord implements android.os.Parcelable { + method public void appendRecord(android.view.accessibility.AccessibilityRecord); method public int describeContents(); - method public int getAddedCount(); - method public java.lang.CharSequence getBeforeText(); - method public java.lang.CharSequence getClassName(); - method public java.lang.CharSequence getContentDescription(); - method public int getCurrentItemIndex(); method public long getEventTime(); method public int getEventType(); - method public int getFromIndex(); - method public int getItemCount(); method public java.lang.CharSequence getPackageName(); - method public android.os.Parcelable getParcelableData(); - method public int getRemovedCount(); - method public java.util.List<java.lang.CharSequence> getText(); + method public android.view.accessibility.AccessibilityRecord getRecord(int); + method public int getRecordCount(); method public void initFromParcel(android.os.Parcel); - method public boolean isChecked(); - method public boolean isEnabled(); - method public boolean isFullScreen(); - method public boolean isPassword(); method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(); - method public void recycle(); - method public void setAddedCount(int); - method public void setBeforeText(java.lang.CharSequence); - method public void setChecked(boolean); - method public void setClassName(java.lang.CharSequence); - method public void setContentDescription(java.lang.CharSequence); - method public void setCurrentItemIndex(int); - method public void setEnabled(boolean); method public void setEventTime(long); method public void setEventType(int); - method public void setFromIndex(int); - method public void setFullScreen(boolean); - method public void setItemCount(int); method public void setPackageName(java.lang.CharSequence); - method public void setParcelableData(android.os.Parcelable); - method public void setPassword(boolean); - method public void setRemovedCount(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int INVALID_POSITION = -1; // 0xffffffff - field public static final int MAX_TEXT_LENGTH = 500; // 0x1f4 + field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4 field public static final int TYPES_ALL_MASK = -1; // 0xffffffff field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40 field public static final int TYPE_VIEW_CLICKED = 1; // 0x1 @@ -22010,6 +21985,51 @@ package android.view.accessibility { method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent); } + public class AccessibilityRecord { + ctor protected AccessibilityRecord(); + method protected void clear(); + method public int getAddedCount(); + method public java.lang.CharSequence getBeforeText(); + method public boolean getBooleanProperty(int); + method public java.lang.CharSequence getClassName(); + method public java.lang.CharSequence getContentDescription(); + method public int getCurrentItemIndex(); + method public int getFromIndex(); + method public int getItemCount(); + method public android.os.Parcelable getParcelableData(); + method public int getRemovedCount(); + method public java.util.List<java.lang.CharSequence> getText(); + method public boolean isChecked(); + method public boolean isEnabled(); + method public boolean isFullScreen(); + method public boolean isPassword(); + method protected static android.view.accessibility.AccessibilityRecord obtain(); + method public void recycle(); + method public void setAddedCount(int); + method public void setBeforeText(java.lang.CharSequence); + method public void setChecked(boolean); + method public void setClassName(java.lang.CharSequence); + method public void setContentDescription(java.lang.CharSequence); + method public void setCurrentItemIndex(int); + method public void setEnabled(boolean); + method public void setFromIndex(int); + method public void setFullScreen(boolean); + method public void setItemCount(int); + method public void setParcelableData(android.os.Parcelable); + method public void setPassword(boolean); + method public void setRemovedCount(int); + field protected int mAddedCount; + field protected java.lang.CharSequence mBeforeText; + field protected int mBooleanProperties; + field protected java.lang.CharSequence mClassName; + field protected java.lang.CharSequence mContentDescription; + field protected int mCurrentItemIndex; + field protected int mFromIndex; + field protected int mItemCount; + field protected android.os.Parcelable mParcelableData; + field protected int mRemovedCount; + field protected final java.util.List mText; + } } package android.view.animation { diff --git a/api/current.txt b/api/current.txt index c7e6ee6..8ec7732 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10778,6 +10778,9 @@ package android.net { field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity"; field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; field public static final java.lang.String EXTRA_REASON = "reason"; + field public static final int TYPE_BLUETOOTH = 7; // 0x7 + field public static final int TYPE_DUMMY = 8; // 0x8 + field public static final int TYPE_ETHERNET = 9; // 0x9 field public static final int TYPE_MOBILE = 0; // 0x0 field public static final int TYPE_MOBILE_DUN = 4; // 0x4 field public static final int TYPE_MOBILE_HIPRI = 5; // 0x5 @@ -21174,6 +21177,7 @@ package android.view { method protected void onLayout(boolean, int, int, int, int); method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); + method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method protected void onRestoreInstanceState(android.os.Parcelable); method protected android.os.Parcelable onSaveInstanceState(); method protected void onScrollChanged(int, int, int, int); @@ -21585,6 +21589,7 @@ package android.view { method public boolean onInterceptTouchEvent(android.view.MotionEvent); method protected abstract void onLayout(boolean, int, int, int, int); method protected boolean onRequestFocusInDescendants(int, android.graphics.Rect); + method public boolean onRequestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public void recomputeViewAttributes(android.view.View); method public void removeAllViews(); method public void removeAllViewsInLayout(); @@ -21597,6 +21602,7 @@ package android.view { method public void requestChildFocus(android.view.View, android.view.View); method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean); method public void requestDisallowInterceptTouchEvent(boolean); + method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public void requestTransparentRegion(android.view.View); method public void scheduleLayoutAnimation(); method public void setAddStatesFromChildren(boolean); @@ -21683,6 +21689,7 @@ package android.view { method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean); method public abstract void requestDisallowInterceptTouchEvent(boolean); method public abstract void requestLayout(); + method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public abstract void requestTransparentRegion(android.view.View); method public abstract boolean showContextMenuForChild(android.view.View); method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback); @@ -22028,53 +22035,32 @@ package android.view { package android.view.accessibility { - public final class AccessibilityEvent implements android.os.Parcelable { + public final class AccessibilityEvent extends android.view.accessibility.AccessibilityRecord implements android.os.Parcelable { + method public void appendRecord(android.view.accessibility.AccessibilityRecord); method public int describeContents(); - method public int getAddedCount(); - method public java.lang.CharSequence getBeforeText(); - method public java.lang.CharSequence getClassName(); - method public java.lang.CharSequence getContentDescription(); - method public int getCurrentItemIndex(); method public long getEventTime(); method public int getEventType(); - method public int getFromIndex(); - method public int getItemCount(); method public java.lang.CharSequence getPackageName(); - method public android.os.Parcelable getParcelableData(); - method public int getRemovedCount(); - method public java.util.List<java.lang.CharSequence> getText(); + method public android.view.accessibility.AccessibilityRecord getRecord(int); + method public int getRecordCount(); method public void initFromParcel(android.os.Parcel); - method public boolean isChecked(); - method public boolean isEnabled(); - method public boolean isFullScreen(); - method public boolean isPassword(); method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(); - method public void recycle(); - method public void setAddedCount(int); - method public void setBeforeText(java.lang.CharSequence); - method public void setChecked(boolean); - method public void setClassName(java.lang.CharSequence); - method public void setContentDescription(java.lang.CharSequence); - method public void setCurrentItemIndex(int); - method public void setEnabled(boolean); method public void setEventTime(long); method public void setEventType(int); - method public void setFromIndex(int); - method public void setFullScreen(boolean); - method public void setItemCount(int); method public void setPackageName(java.lang.CharSequence); - method public void setParcelableData(android.os.Parcelable); - method public void setPassword(boolean); - method public void setRemovedCount(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int INVALID_POSITION = -1; // 0xffffffff field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4 field public static final int TYPES_ALL_MASK = -1; // 0xffffffff field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40 + field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400 + field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200 field public static final int TYPE_VIEW_CLICKED = 1; // 0x1 field public static final int TYPE_VIEW_FOCUSED = 8; // 0x8 + field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80 + field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100 field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2 field public static final int TYPE_VIEW_SELECTED = 4; // 0x4 field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10 @@ -22088,11 +22074,58 @@ package android.view.accessibility { public final class AccessibilityManager { method public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList(); + method public java.util.List<android.content.pm.ServiceInfo> getEnabledAccessibilityServiceList(int); method public void interrupt(); method public boolean isEnabled(); method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent); } + public class AccessibilityRecord { + ctor protected AccessibilityRecord(); + method protected void clear(); + method public int getAddedCount(); + method public java.lang.CharSequence getBeforeText(); + method public boolean getBooleanProperty(int); + method public java.lang.CharSequence getClassName(); + method public java.lang.CharSequence getContentDescription(); + method public int getCurrentItemIndex(); + method public int getFromIndex(); + method public int getItemCount(); + method public android.os.Parcelable getParcelableData(); + method public int getRemovedCount(); + method public java.util.List<java.lang.CharSequence> getText(); + method public boolean isChecked(); + method public boolean isEnabled(); + method public boolean isFullScreen(); + method public boolean isPassword(); + method protected static android.view.accessibility.AccessibilityRecord obtain(); + method public void recycle(); + method public void setAddedCount(int); + method public void setBeforeText(java.lang.CharSequence); + method public void setChecked(boolean); + method public void setClassName(java.lang.CharSequence); + method public void setContentDescription(java.lang.CharSequence); + method public void setCurrentItemIndex(int); + method public void setEnabled(boolean); + method public void setFromIndex(int); + method public void setFullScreen(boolean); + method public void setItemCount(int); + method public void setParcelableData(android.os.Parcelable); + method public void setPassword(boolean); + method public void setRemovedCount(int); + field protected int mAddedCount; + field protected java.lang.CharSequence mBeforeText; + field protected int mBooleanProperties; + field protected java.lang.CharSequence mClassName; + field protected java.lang.CharSequence mContentDescription; + field protected int mCurrentItemIndex; + field protected int mFromIndex; + field protected int mItemCount; + field protected android.os.Parcelable mParcelableData; + field protected int mRemovedCount; + field protected final java.util.List mText; + } + } package android.view.animation { diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index bcab66e..ed4036d 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -31,11 +31,11 @@ import java.io.IOException; import java.util.ArrayList; /** - * This class is used to instantiate menu XML files into Animator objects. + * This class is used to instantiate animator XML files into Animator objects. * <p> - * For performance reasons, menu inflation relies heavily on pre-processing of + * For performance reasons, inflation relies heavily on pre-processing of * XML files that is done at build time. Therefore, it is not currently possible - * to use MenuInflater with an XmlPullParser over a plain XML file at runtime; + * to use this inflater with an XmlPullParser over a plain XML file at runtime; * it only works with an XmlPullParser returned from a compiled resource (R. * <em>something</em> file.) */ diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 328a55b..77c2d1b 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -2600,10 +2600,12 @@ public class Camera { * (1000, 1000) is the lower right point. The length and width of focus * areas cannot be 0 or negative. * - * The weight ranges from 1 to 1000. The sum of the weights of all focus - * areas must be 1000. Focus areas can partially overlap and the driver - * will add the weights in the overlap region. But apps should not set - * two focus areas that have identical coordinates. + * The weight must range from 1 to 1000. The weight should be + * interpreted as a per-pixel weight - all pixels in the area have the + * specified weight. This means a small area with the same weight as a + * larger area will have less influence on the focusing than the larger + * area. Focus areas can partially overlap and the driver will add the + * weights in the overlap region. * * A special case of all-zero single focus area means driver to decide * the focus area. For example, the driver may use more signals to @@ -2668,10 +2670,11 @@ public class Camera { * point. (1000, 1000) is the lower right point. The length and width of * metering areas cannot be 0 or negative. * - * The weight ranges from 1 to 1000. The sum of the weights of all - * metering areas must be 1000. Metering areas can partially overlap and - * the driver will add the weights in the overlap region. But apps - * should not set two metering areas that have identical coordinates. + * The weight must range from 1 to 1000, and represents a weight for + * every pixel in the area. This means that a large metering area with + * the same weight as a smaller area will have more effect in the + * metering result. Metering areas can partially overlap and the driver + * will add the weights in the overlap region. * * A special case of all-zero single metering area means driver to * decide the metering area. For example, the driver may use more diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index b541ec3..419288b 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -215,15 +215,20 @@ public class ConnectivityManager /** * Bluetooth data connection. This is used for Bluetooth reverse tethering. - * @hide */ public static final int TYPE_BLUETOOTH = 7; - /** {@hide} */ + /** + * Dummy data connection. This should not be used on shipping devices. + */ public static final int TYPE_DUMMY = 8; - /** {@hide} */ + /** + * Ethernet data connection. This may be via USB dongle or more + * traditional means. + */ public static final int TYPE_ETHERNET = 9; + /** * Over the air Adminstration. * {@hide} diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java index 42d555c..2e8d551 100644 --- a/core/java/android/preference/MultiSelectListPreference.java +++ b/core/java/android/preference/MultiSelectListPreference.java @@ -169,9 +169,9 @@ public class MultiSelectListPreference extends DialogPreference { new DialogInterface.OnMultiChoiceClickListener() { public void onClick(DialogInterface dialog, int which, boolean isChecked) { if (isChecked) { - mPreferenceChanged |= mNewValues.add(mEntries[which].toString()); + mPreferenceChanged |= mNewValues.add(mEntryValues[which].toString()); } else { - mPreferenceChanged |= mNewValues.remove(mEntries[which].toString()); + mPreferenceChanged |= mNewValues.remove(mEntryValues[which].toString()); } } }); @@ -180,7 +180,7 @@ public class MultiSelectListPreference extends DialogPreference { } private boolean[] getSelectedItems() { - final CharSequence[] entries = mEntries; + final CharSequence[] entries = mEntryValues; final int entryCount = entries.length; final Set<String> values = mValues; boolean[] result = new boolean[entryCount]; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2f4a4bb..570b801 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3721,6 +3721,22 @@ public final class Settings { "setup_prepaid_data_service_url"; /** + * URL to attempt a GET on to see if this is a prepay device + * @hide + */ + public static final String SETUP_PREPAID_DETECTION_TARGET_URL = + "setup_prepaid_detection_target_url"; + + /** + * Host to check for a redirect to after an attempt to GET + * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there, + * this is a prepaid device with zero balance.) + * @hide + */ + public static final String SETUP_PREPAID_DETECTION_REDIR_HOST = + "setup_prepaid_detection_redir_host"; + + /** * @hide */ public static final String[] SETTINGS_TO_BACKUP = { diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java index 6e2168b..831ccc5 100644 --- a/core/java/android/text/GraphicsOperations.java +++ b/core/java/android/text/GraphicsOperations.java @@ -61,8 +61,8 @@ extends CharSequence * Just like {@link Paint#getTextRunAdvances}. * @hide */ - float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex, Paint paint); + float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, + int flags, float[] advances, int advancesIndex, Paint paint, int reserved); /** * Just like {@link Paint#getTextRunCursor}. diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index ff6a4cd..6b2d8e4 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1173,8 +1173,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, * Don't call this yourself -- exists for Paint to use internally. * {@hide} */ - public float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, int flags, - float[] advances, int advancesPos, Paint p) { + public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags, + float[] advances, int advancesPos, Paint p, int reserved) { float ret; @@ -1182,16 +1182,16 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, int len = end - start; if (end <= mGapStart) { - ret = p.getTextRunAdvancesICU(mText, start, len, contextStart, contextLen, - flags, advances, advancesPos); + ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen, + flags, advances, advancesPos, reserved); } else if (start >= mGapStart) { - ret = p.getTextRunAdvancesICU(mText, start + mGapLength, len, - contextStart + mGapLength, contextLen, flags, advances, advancesPos); + ret = p.getTextRunAdvances(mText, start + mGapLength, len, + contextStart + mGapLength, contextLen, flags, advances, advancesPos, reserved); } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); - ret = p.getTextRunAdvancesICU(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesPos); + ret = p.getTextRunAdvances(buf, start - contextStart, len, + 0, contextLen, flags, advances, advancesPos, reserved); TextUtils.recycle(buf); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 980239b..8e839c0 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -200,8 +200,27 @@ public class Display { * @param outMetrics */ public void getMetrics(DisplayMetrics outMetrics) { - outMetrics.widthPixels = getWidth(); - outMetrics.heightPixels = getHeight(); + synchronized (mTmpPoint) { + getSize(mTmpPoint); + outMetrics.widthPixels = mTmpPoint.x; + outMetrics.heightPixels = mTmpPoint.y; + } + getNonSizeMetrics(outMetrics); + } + + /** + * Initialize a DisplayMetrics object from this display's data. + * + * @param outMetrics + * @hide + */ + public void getRealMetrics(DisplayMetrics outMetrics) { + outMetrics.widthPixels = getRealWidth(); + outMetrics.heightPixels = getRealHeight(); + getNonSizeMetrics(outMetrics); + } + + private void getNonSizeMetrics(DisplayMetrics outMetrics) { outMetrics.density = mDensity; outMetrics.densityDpi = (int)((mDensity*DisplayMetrics.DENSITY_DEFAULT)+.5f); outMetrics.scaledDensity= outMetrics.density; diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index b5ca2c2..e14b975 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -30,7 +30,6 @@ import android.util.Log; * @hide */ public final class InputEventConsistencyVerifier { - private static final String TAG = "InputEventConsistencyVerifier"; private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE); // The number of recent events to log when a problem is detected. @@ -44,6 +43,11 @@ public final class InputEventConsistencyVerifier { // Consistency verifier flags. private final int mFlags; + // Tag for logging which a client can set to help distinguish the output + // from different verifiers since several can be active at the same time. + // If not provided defaults to the simple class name. + private final String mLogTag; + // The most recently checked event and the nesting level at which it was checked. // This is only set when the verifier is called from a nesting level greater than 0 // so that the verifier can detect when it has been asked to verify the same event twice. @@ -103,8 +107,19 @@ public final class InputEventConsistencyVerifier { * @param flags Flags to the verifier, or 0 if none. */ public InputEventConsistencyVerifier(Object caller, int flags) { + this(caller, flags, InputEventConsistencyVerifier.class.getSimpleName()); + } + + /** + * Creates an input consistency verifier. + * @param caller The object to which the verifier is attached. + * @param flags Flags to the verifier, or 0 if none. + * @param logTag Tag for logging. If null defaults to the short class name. + */ + public InputEventConsistencyVerifier(Object caller, int flags, String logTag) { this.mCaller = caller; this.mFlags = flags; + this.mLogTag = (logTag != null) ? logTag : "InputEventConsistencyVerifier"; } /** @@ -596,7 +611,7 @@ public final class InputEventConsistencyVerifier { } } - Log.d(TAG, mViolationMessage.toString()); + Log.d(mLogTag, mViolationMessage.toString()); mViolationMessage.setLength(0); tainted = true; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4a62892..4bc7f39 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3455,6 +3455,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (!isShown()) { return; } + + // Populate these here since they are related to the View that + // sends the event and should not be modified while dispatching + // to descendants. event.setClassName(getClass().getName()); event.setPackageName(getContext().getPackageName()); event.setEnabled(isEnabled()); @@ -3470,22 +3474,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility dispatchPopulateAccessibilityEvent(event); - AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event); + // In the beginning we called #isShown(), so we know that getParent() is not null. + getParent().requestSendAccessibilityEvent(this, event); } /** - * Dispatches an {@link AccessibilityEvent} to the {@link View} children - * to be populated. + * Dispatches an {@link AccessibilityEvent} to the {@link View} children to be populated. + * This method first calls {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} + * on this view allowing it to populate information about itself and also decide + * whether to intercept the population i.e. to prevent its children from populating + * the event. * * @param event The event. * * @return True if the event population was completed. */ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + onPopulateAccessibilityEvent(event); return false; } /** + * Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} + * giving a chance to this View to populate the accessibility evnet with + * information about itself. + * + * @param event The accessibility event which to populate. + */ + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + + } + + /** * Gets the {@link View} description. It briefly describes the view and is * primarily used for accessibility support. Set this property to enable * better accessibility support for your application. This is especially @@ -5390,20 +5410,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * to receive the hover event. */ public boolean onHoverEvent(MotionEvent event) { - final int viewFlags = mViewFlags; - - if (((viewFlags & CLICKABLE) != CLICKABLE && - (viewFlags & LONG_CLICKABLE) != LONG_CLICKABLE)) { - // Nothing to do if the view is not clickable. - return false; - } - - if ((viewFlags & ENABLED_MASK) == DISABLED) { - // A disabled view that is clickable still consumes the hover events, it just doesn't - // respond to them. - return true; - } - switch (event.getAction()) { case MotionEvent.ACTION_HOVER_ENTER: setHovered(true); @@ -5414,7 +5420,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility break; } - return true; + return false; } /** @@ -5436,11 +5442,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if ((mPrivateFlags & HOVERED) == 0) { mPrivateFlags |= HOVERED; refreshDrawableState(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); } } else { if ((mPrivateFlags & HOVERED) != 0) { mPrivateFlags &= ~HOVERED; refreshDrawableState(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); } } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 739758c..94eb429 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -19,7 +19,6 @@ package android.view; import android.app.AppGlobals; import android.content.Context; import android.content.res.Configuration; -import android.os.Bundle; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.SparseArray; @@ -156,6 +155,13 @@ public class ViewConfiguration { private static final int MAXIMUM_FLING_VELOCITY = 8000; /** + * Distance between a touch up event denoting the end of a touch exploration + * gesture and the touch up event of a subsequent tap for the latter tap to be + * considered as a tap i.e. to perform a click. + */ + private static final int TOUCH_EXPLORATION_TAP_SLOP = 80; + + /** * The maximum size of View's drawing cache, expressed in bytes. This size * should be at least equal to the size of the screen in ARGB888 format. */ @@ -185,6 +191,7 @@ public class ViewConfiguration { private final int mTouchSlop; private final int mPagingTouchSlop; private final int mDoubleTapSlop; + private final int mScaledTouchExplorationTapSlop; private final int mWindowTouchSlop; private final int mMaximumDrawingCacheSize; private final int mOverscrollDistance; @@ -206,6 +213,7 @@ public class ViewConfiguration { mTouchSlop = TOUCH_SLOP; mPagingTouchSlop = PAGING_TOUCH_SLOP; mDoubleTapSlop = DOUBLE_TAP_SLOP; + mScaledTouchExplorationTapSlop = TOUCH_EXPLORATION_TAP_SLOP; mWindowTouchSlop = WINDOW_TOUCH_SLOP; //noinspection deprecation mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE; @@ -242,6 +250,7 @@ public class ViewConfiguration { mTouchSlop = (int) (sizeAndDensity * TOUCH_SLOP + 0.5f); mPagingTouchSlop = (int) (sizeAndDensity * PAGING_TOUCH_SLOP + 0.5f); mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f); + mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f); mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f); // Size of the screen in bytes, in ARGB_8888 format @@ -444,6 +453,17 @@ public class ViewConfiguration { } /** + * @return Distance between a touch up event denoting the end of a touch exploration + * gesture and the touch up event of a subsequent tap for the latter tap to be + * considered as a tap i.e. to perform a click. + * + * @hide + */ + public int getScaledTouchExplorationTapSlop() { + return mScaledTouchExplorationTapSlop; + } + + /** * @return Distance a touch must be outside the bounds of a window for it * to be counted as outside the window for purposes of dismissing that * window. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 08daa28..7b404b4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -586,6 +586,35 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * {@inheritDoc} */ + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { + ViewParent parent = getParent(); + if (parent == null) { + return false; + } + final boolean propagate = onRequestSendAccessibilityEvent(child, event); + if (!propagate) { + return false; + } + return parent.requestSendAccessibilityEvent(this, event); + } + + /** + * Called when a child has requested sending an {@link AccessibilityEvent} and + * gives an opportunity to its parent to augment the event. + * + * @param child The child which requests sending the event. + * @param event The event to be sent. + * @return True if the event should be sent. + * + * @see #requestSendAccessibilityEvent(View, AccessibilityEvent) + */ + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + return true; + } + + /** + * {@inheritDoc} + */ @Override public boolean dispatchUnhandledMove(View focused, int direction) { return mFocused != null && @@ -1216,9 +1245,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT); handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild); eventNoHistory.setAction(action); - mHoveredChild = null; - } else if (action == MotionEvent.ACTION_HOVER_MOVE) { + } else { // Pointer is still within the child. handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild); } @@ -1278,6 +1306,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return handled; } + @Override + public boolean onHoverEvent(MotionEvent event) { + // Handle the event only if leaf. This guarantees that + // the leafs (or any custom class that returns true from + // this method) will get a change to process the hover. + if (getChildCount() == 0) { + return super.onHoverEvent(event); + } + return false; + } + private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) { if (event.getHistorySize() == 0) { return event; @@ -2091,11 +2130,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - boolean populated = false; + // We first get a chance to populate the event. + onPopulateAccessibilityEvent(event); + // Let our children have a shot in populating the event. for (int i = 0, count = getChildCount(); i < count; i++) { - populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); + boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event); + if (handled) { + return handled; + } } - return populated; + return false; } /** diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index d7d4c3f..655df39 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -17,6 +17,7 @@ package android.view; import android.graphics.Rect; +import android.view.accessibility.AccessibilityEvent; /** * Defines the responsibilities for a class that will be a parent of a View. @@ -222,4 +223,22 @@ public interface ViewParent { */ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate); + + /** + * Called by a child to request from its parent to send an {@link AccessibilityEvent}. + * The child has already populated a record for itself in the event and is delegating + * to its parent to send the event. The parent can optionally add a record for itself. + * <p> + * Note: An accessibility event is fired by an individual view which populates the + * event with a record for its state and requests from its parent to perform + * the sending. The parent can optionally add a record for itself before + * dispatching the request to its parent. A parent can also choose not to + * respect the request for sending the event. The accessibility event is sent + * by the topmost view in the view tree. + * + * @param child The child which requests sending the event. + * @param event The event to be sent. + * @return True if the event was sent. + */ + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 4104b07..f02daba 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -3530,6 +3530,14 @@ public final class ViewRoot extends Handler implements ViewParent, public void childDrawableStateChanged(View child) { } + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (mView == null) { + return false; + } + AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event); + return true; + } + void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 8f7bb8c..3d19380 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -400,7 +400,7 @@ public interface WindowManagerPolicy { * display dimensions. */ public void setInitialDisplaySize(int width, int height); - + /** * Check permissions when adding a window. * @@ -816,6 +816,13 @@ public interface WindowManagerPolicy { boolean displayEnabled); /** + * Return the currently locked screen rotation, if any. Return + * Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, or + * Surface.ROTATION_270 if locked; return -1 if not locked. + */ + public int getLockedRotationLw(); + + /** * Called when the system is mostly done booting to determine whether * the system should go into safe mode. */ diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 9af19b8..11c9392 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -21,13 +21,26 @@ import android.os.Parcelable; import android.text.TextUtils; import java.util.ArrayList; -import java.util.List; /** * This class represents accessibility events that are sent by the system when * something notable happens in the user interface. For example, when a * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. * <p> + * An accessibility event is fired by an individual view which populates the event with + * a record for its state and requests from its parent to send the event to interested + * parties. The parent can optionally add a record for itself before dispatching a similar + * request to its parent. A parent can also choose not to respect the request for sending + * an event. The accessibility event is sent by the topmost view in the view tree. + * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore + * all records in an accessibility event to obtain more information about the context + * in which the event was fired. + * <p> + * A client can add, remove, and modify records. The getters and setters for individual + * properties operate on the current record which can be explicitly set by the client. By + * default current is the first record. Thus, querying a record would require setting + * it as the current one and interacting with the property getters and setters. + * <p> * This class represents various semantically different accessibility event * types. Each event type has associated a set of related properties. In other * words, each event type is characterized via a subset of the properties exposed @@ -145,7 +158,7 @@ import java.util.List; * @see android.view.accessibility.AccessibilityManager * @see android.accessibilityservice.AccessibilityService */ -public final class AccessibilityEvent implements Parcelable { +public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { /** * Invalid selection/focus position. @@ -207,6 +220,26 @@ public final class AccessibilityEvent implements Parcelable { public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; /** + * Represents the event of a hover enter over a {@link android.view.View}. + */ + public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080; + + /** + * Represents the event of a hover exit over a {@link android.view.View}. + */ + public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100; + + /** + * Represents the event of starting a touch exploration gesture. + */ + public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200; + + /** + * Represents the event of ending a touch exploration gesture. + */ + public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400; + + /** * Mask for {@link AccessibilityEvent} all types. * * @see #TYPE_VIEW_CLICKED @@ -219,116 +252,53 @@ public final class AccessibilityEvent implements Parcelable { */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; - private static final int MAX_POOL_SIZE = 2; + private static final int MAX_POOL_SIZE = 10; private static final Object mPoolLock = new Object(); private static AccessibilityEvent sPool; private static int sPoolSize; - private static final int CHECKED = 0x00000001; - private static final int ENABLED = 0x00000002; - private static final int PASSWORD = 0x00000004; - private static final int FULL_SCREEN = 0x00000080; - private AccessibilityEvent mNext; + private boolean mIsInPool; private int mEventType; - private int mBooleanProperties; - private int mCurrentItemIndex; - private int mItemCount; - private int mFromIndex; - private int mAddedCount; - private int mRemovedCount; - - private long mEventTime; - - private CharSequence mClassName; private CharSequence mPackageName; - private CharSequence mContentDescription; - private CharSequence mBeforeText; - - private Parcelable mParcelableData; - - private final List<CharSequence> mText = new ArrayList<CharSequence>(); + private long mEventTime; - private boolean mIsInPool; + private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); /* * Hide constructor from clients. */ private AccessibilityEvent() { - mCurrentItemIndex = INVALID_POSITION; - } - - /** - * Gets if the source is checked. - * - * @return True if the view is checked, false otherwise. - */ - public boolean isChecked() { - return getBooleanProperty(CHECKED); - } - - /** - * Sets if the source is checked. - * - * @param isChecked True if the view is checked, false otherwise. - */ - public void setChecked(boolean isChecked) { - setBooleanProperty(CHECKED, isChecked); - } - /** - * Gets if the source is enabled. - * - * @return True if the view is enabled, false otherwise. - */ - public boolean isEnabled() { - return getBooleanProperty(ENABLED); - } - - /** - * Sets if the source is enabled. - * - * @param isEnabled True if the view is enabled, false otherwise. - */ - public void setEnabled(boolean isEnabled) { - setBooleanProperty(ENABLED, isEnabled); - } - - /** - * Gets if the source is a password field. - * - * @return True if the view is a password field, false otherwise. - */ - public boolean isPassword() { - return getBooleanProperty(PASSWORD); } /** - * Sets if the source is a password field. + * Gets the number of records contained in the event. * - * @param isPassword True if the view is a password field, false otherwise. + * @return The number of records. */ - public void setPassword(boolean isPassword) { - setBooleanProperty(PASSWORD, isPassword); + public int getRecordCount() { + return mRecords.size(); } /** - * Sets if the source is taking the entire screen. + * Appends an {@link AccessibilityRecord} to the end of event records. * - * @param isFullScreen True if the source is full screen, false otherwise. + * @param record The record to append. */ - public void setFullScreen(boolean isFullScreen) { - setBooleanProperty(FULL_SCREEN, isFullScreen); + public void appendRecord(AccessibilityRecord record) { + mRecords.add(record); } /** - * Gets if the source is taking the entire screen. + * Gets the records at a given index. * - * @return True if the source is full screen, false otherwise. + * @param index The index. + * @return The records at the specified index. */ - public boolean isFullScreen() { - return getBooleanProperty(FULL_SCREEN); + public AccessibilityRecord getRecord(int index) { + return mRecords.get(index); } /** @@ -350,96 +320,6 @@ public final class AccessibilityEvent implements Parcelable { } /** - * Gets the number of items that can be visited. - * - * @return The number of items. - */ - public int getItemCount() { - return mItemCount; - } - - /** - * Sets the number of items that can be visited. - * - * @param itemCount The number of items. - */ - public void setItemCount(int itemCount) { - mItemCount = itemCount; - } - - /** - * Gets the index of the source in the list of items the can be visited. - * - * @return The current item index. - */ - public int getCurrentItemIndex() { - return mCurrentItemIndex; - } - - /** - * Sets the index of the source in the list of items that can be visited. - * - * @param currentItemIndex The current item index. - */ - public void setCurrentItemIndex(int currentItemIndex) { - mCurrentItemIndex = currentItemIndex; - } - - /** - * Gets the index of the first character of the changed sequence. - * - * @return The index of the first character. - */ - public int getFromIndex() { - return mFromIndex; - } - - /** - * Sets the index of the first character of the changed sequence. - * - * @param fromIndex The index of the first character. - */ - public void setFromIndex(int fromIndex) { - mFromIndex = fromIndex; - } - - /** - * Gets the number of added characters. - * - * @return The number of added characters. - */ - public int getAddedCount() { - return mAddedCount; - } - - /** - * Sets the number of added characters. - * - * @param addedCount The number of added characters. - */ - public void setAddedCount(int addedCount) { - mAddedCount = addedCount; - } - - /** - * Gets the number of removed characters. - * - * @return The number of removed characters. - */ - public int getRemovedCount() { - return mRemovedCount; - } - - /** - * Sets the number of removed characters. - * - * @param removedCount The number of removed characters. - */ - public void setRemovedCount(int removedCount) { - mRemovedCount = removedCount; - } - - /** * Gets the time in which this event was sent. * * @return The event time. @@ -458,24 +338,6 @@ public final class AccessibilityEvent implements Parcelable { } /** - * Gets the class name of the source. - * - * @return The class name. - */ - public CharSequence getClassName() { - return mClassName; - } - - /** - * Sets the class name of the source. - * - * @param className The lass name. - */ - public void setClassName(CharSequence className) { - mClassName = className; - } - - /** * Gets the package name of the source. * * @return The package name. @@ -494,70 +356,6 @@ public final class AccessibilityEvent implements Parcelable { } /** - * Gets the text of the event. The index in the list represents the priority - * of the text. Specifically, the lower the index the higher the priority. - * - * @return The text. - */ - public List<CharSequence> getText() { - return mText; - } - - /** - * Sets the text before a change. - * - * @return The text before the change. - */ - public CharSequence getBeforeText() { - return mBeforeText; - } - - /** - * Sets the text before a change. - * - * @param beforeText The text before the change. - */ - public void setBeforeText(CharSequence beforeText) { - mBeforeText = beforeText; - } - - /** - * Gets the description of the source. - * - * @return The description. - */ - public CharSequence getContentDescription() { - return mContentDescription; - } - - /** - * Sets the description of the source. - * - * @param contentDescription The description. - */ - public void setContentDescription(CharSequence contentDescription) { - mContentDescription = contentDescription; - } - - /** - * Gets the {@link Parcelable} data. - * - * @return The parcelable data. - */ - public Parcelable getParcelableData() { - return mParcelableData; - } - - /** - * Sets the {@link Parcelable} data of the event. - * - * @param parcelableData The parcelable data. - */ - public void setParcelableData(Parcelable parcelableData) { - mParcelableData = parcelableData; - } - - /** * Returns a cached instance if such is available or a new one is * instantiated with type property set. * @@ -595,11 +393,11 @@ public final class AccessibilityEvent implements Parcelable { * <p> * <b>Note: You must not touch the object after calling this function.</b> */ + @Override public void recycle() { if (mIsInPool) { return; } - clear(); synchronized (mPoolLock) { if (sPoolSize <= MAX_POOL_SIZE) { @@ -614,44 +412,15 @@ public final class AccessibilityEvent implements Parcelable { /** * Clears the state of this instance. */ - private void clear() { + @Override + protected void clear() { + super.clear(); mEventType = 0; - mBooleanProperties = 0; - mCurrentItemIndex = INVALID_POSITION; - mItemCount = 0; - mFromIndex = 0; - mAddedCount = 0; - mRemovedCount = 0; - mEventTime = 0; - mClassName = null; mPackageName = null; - mContentDescription = null; - mBeforeText = null; - mParcelableData = null; - mText.clear(); - } - - /** - * Gets the value of a boolean property. - * - * @param property The property. - * @return The value. - */ - private boolean getBooleanProperty(int property) { - return (mBooleanProperties & property) == property; - } - - /** - * Sets a boolean property. - * - * @param property The property. - * @param value The value. - */ - private void setBooleanProperty(int property, boolean value) { - if (value) { - mBooleanProperties |= property; - } else { - mBooleanProperties &= ~property; + mEventTime = 0; + while (!mRecords.isEmpty()) { + AccessibilityRecord record = mRecords.remove(0); + record.recycle(); } } @@ -662,38 +431,82 @@ public final class AccessibilityEvent implements Parcelable { */ public void initFromParcel(Parcel parcel) { mEventType = parcel.readInt(); - mBooleanProperties = parcel.readInt(); - mCurrentItemIndex = parcel.readInt(); - mItemCount = parcel.readInt(); - mFromIndex = parcel.readInt(); - mAddedCount = parcel.readInt(); - mRemovedCount = parcel.readInt(); - mEventTime = parcel.readLong(); - mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); - mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); - mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); - mParcelableData = parcel.readParcelable(null); - parcel.readList(mText, null); + mEventTime = parcel.readLong(); + readAccessibilityRecordFromParcel(this, parcel); + + // Read the records. + final int recordCount = parcel.readInt(); + for (int i = 0; i < recordCount; i++) { + AccessibilityRecord record = AccessibilityRecord.obtain(); + readAccessibilityRecordFromParcel(record, parcel); + mRecords.add(record); + } } + /** + * Reads an {@link AccessibilityRecord} from a parcel. + * + * @param record The record to initialize. + * @param parcel The parcel to read from. + */ + private void readAccessibilityRecordFromParcel(AccessibilityRecord record, + Parcel parcel) { + record.mBooleanProperties = parcel.readInt(); + record.mCurrentItemIndex = parcel.readInt(); + record.mItemCount = parcel.readInt(); + record.mFromIndex = parcel.readInt(); + record.mAddedCount = parcel.readInt(); + record.mRemovedCount = parcel.readInt(); + record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + record.mParcelableData = parcel.readParcelable(null); + parcel.readList(record.mText, null); + } + + /** + * {@inheritDoc} + */ public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mEventType); - parcel.writeInt(mBooleanProperties); - parcel.writeInt(mCurrentItemIndex); - parcel.writeInt(mItemCount); - parcel.writeInt(mFromIndex); - parcel.writeInt(mAddedCount); - parcel.writeInt(mRemovedCount); - parcel.writeLong(mEventTime); - TextUtils.writeToParcel(mClassName, parcel, 0); TextUtils.writeToParcel(mPackageName, parcel, 0); - TextUtils.writeToParcel(mContentDescription, parcel, 0); - TextUtils.writeToParcel(mBeforeText, parcel, 0); - parcel.writeParcelable(mParcelableData, flags); - parcel.writeList(mText); + parcel.writeLong(mEventTime); + writeAccessibilityRecordToParcel(this, parcel, flags); + + // Write the records. + final int recordCount = getRecordCount(); + parcel.writeInt(recordCount); + for (int i = 0; i < recordCount; i++) { + AccessibilityRecord record = mRecords.get(i); + writeAccessibilityRecordToParcel(record, parcel, flags); + } + } + + /** + * Writes an {@link AccessibilityRecord} to a parcel. + * + * @param record The record to write. + * @param parcel The parcel to which to write. + */ + private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel, + int flags) { + parcel.writeInt(record.mBooleanProperties); + parcel.writeInt(record.mCurrentItemIndex); + parcel.writeInt(record.mItemCount); + parcel.writeInt(record.mFromIndex); + parcel.writeInt(record.mAddedCount); + parcel.writeInt(record.mRemovedCount); + TextUtils.writeToParcel(record.mClassName, parcel, flags); + TextUtils.writeToParcel(record.mContentDescription, parcel, flags); + TextUtils.writeToParcel(record.mBeforeText, parcel, flags); + parcel.writeParcelable(record.mParcelableData, flags); + parcel.writeList(record.mText); } + /** + * {@inheritDoc} + */ public int describeContents() { return 0; } @@ -701,24 +514,21 @@ public final class AccessibilityEvent implements Parcelable { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append(super.toString()); builder.append("; EventType: " + mEventType); builder.append("; EventTime: " + mEventTime); - builder.append("; ClassName: " + mClassName); builder.append("; PackageName: " + mPackageName); - builder.append("; Text: " + mText); - builder.append("; ContentDescription: " + mContentDescription); - builder.append("; ItemCount: " + mItemCount); - builder.append("; CurrentItemIndex: " + mCurrentItemIndex); - builder.append("; IsEnabled: " + isEnabled()); - builder.append("; IsPassword: " + isPassword()); - builder.append("; IsChecked: " + isChecked()); - builder.append("; IsFullScreen: " + isFullScreen()); - builder.append("; BeforeText: " + mBeforeText); - builder.append("; FromIndex: " + mFromIndex); - builder.append("; AddedCount: " + mAddedCount); - builder.append("; RemovedCount: " + mRemovedCount); - builder.append("; ParcelableData: " + mParcelableData); + builder.append(" \n{\n"); + builder.append(super.toString()); + builder.append("\n"); + for (int i = 0; i < mRecords.size(); i++) { + AccessibilityRecord record = mRecords.get(i); + builder.append(" Record "); + builder.append(i); + builder.append(":"); + builder.append(record.toString()); + builder.append("\n"); + } + builder.append("}\n"); return builder.toString(); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 22cb0d4..dd77193 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -16,6 +16,8 @@ package android.view.accessibility; +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.content.pm.ServiceInfo; import android.os.Binder; @@ -44,6 +46,8 @@ import java.util.List; * @see android.content.Context#getSystemService */ public final class AccessibilityManager { + private static final boolean DEBUG = false; + private static final String LOG_TAG = "AccessibilityManager"; static final Object sInstanceSync = new Object(); @@ -164,7 +168,7 @@ public final class AccessibilityManager { long identityToken = Binder.clearCallingIdentity(); doRecycle = mService.sendAccessibilityEvent(event); Binder.restoreCallingIdentity(identityToken); - if (false) { + if (DEBUG) { Log.i(LOG_TAG, event + " sent"); } } catch (RemoteException re) { @@ -185,7 +189,7 @@ public final class AccessibilityManager { } try { mService.interrupt(); - if (false) { + if (DEBUG) { Log.i(LOG_TAG, "Requested interrupt from all services"); } } catch (RemoteException re) { @@ -202,7 +206,33 @@ public final class AccessibilityManager { List<ServiceInfo> services = null; try { services = mService.getAccessibilityServiceList(); - if (false) { + if (DEBUG) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); + } + return Collections.unmodifiableList(services); + } + + /** + * Returns the {@link ServiceInfo}s of the enabled accessibility services + * for a given feedback type. + * + * @param feedbackType The type of feedback. + * @return An unmodifiable list with {@link ServiceInfo}s. + * + * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE + * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC + * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN + * @see AccessibilityServiceInfo#FEEDBACK_VISUAL + * @see AccessibilityServiceInfo#FEEDBACK_GENERIC + */ + public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { + List<ServiceInfo> services = null; + try { + services = mService.getEnabledAccessibilityServiceList(feedbackType); + if (DEBUG) { Log.i(LOG_TAG, "Installed AccessibilityServices " + services); } } catch (RemoteException re) { diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java new file mode 100644 index 0000000..e095f43 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a record in an accessibility event. This class encapsulates + * the information for a {@link android.view.View}. Note that not all properties + * are applicable to all view types. For detailed information please refer to + * {@link AccessibilityEvent}. + * + * @see AccessibilityEvent + */ +public class AccessibilityRecord { + + private static final int INVALID_POSITION = -1; + + private static final int PROPERTY_CHECKED = 0x00000001; + private static final int PROPERTY_ENABLED = 0x00000002; + private static final int PROPERTY_PASSWORD = 0x00000004; + private static final int PROPERTY_FULL_SCREEN = 0x00000080; + + private static final int MAX_POOL_SIZE = 10; + private static final Object mPoolLock = new Object(); + private static AccessibilityRecord sPool; + private static int sPoolSize; + + private AccessibilityRecord mNext; + private boolean mIsInPool; + + protected int mBooleanProperties; + protected int mCurrentItemIndex; + protected int mItemCount; + protected int mFromIndex; + protected int mAddedCount; + protected int mRemovedCount; + + protected CharSequence mClassName; + protected CharSequence mContentDescription; + protected CharSequence mBeforeText; + protected Parcelable mParcelableData; + + protected final List<CharSequence> mText = new ArrayList<CharSequence>(); + + /* + * Hide constructor. + */ + protected AccessibilityRecord() { + + } + + /** + * Gets if the source is checked. + * + * @return True if the view is checked, false otherwise. + */ + public boolean isChecked() { + return getBooleanProperty(PROPERTY_CHECKED); + } + + /** + * Sets if the source is checked. + * + * @param isChecked True if the view is checked, false otherwise. + */ + public void setChecked(boolean isChecked) { + setBooleanProperty(PROPERTY_CHECKED, isChecked); + } + + /** + * Gets if the source is enabled. + * + * @return True if the view is enabled, false otherwise. + */ + public boolean isEnabled() { + return getBooleanProperty(PROPERTY_ENABLED); + } + + /** + * Sets if the source is enabled. + * + * @param isEnabled True if the view is enabled, false otherwise. + */ + public void setEnabled(boolean isEnabled) { + setBooleanProperty(PROPERTY_ENABLED, isEnabled); + } + + /** + * Gets if the source is a password field. + * + * @return True if the view is a password field, false otherwise. + */ + public boolean isPassword() { + return getBooleanProperty(PROPERTY_PASSWORD); + } + + /** + * Sets if the source is a password field. + * + * @param isPassword True if the view is a password field, false otherwise. + */ + public void setPassword(boolean isPassword) { + setBooleanProperty(PROPERTY_PASSWORD, isPassword); + } + + /** + * Sets if the source is taking the entire screen. + * + * @param isFullScreen True if the source is full screen, false otherwise. + */ + public void setFullScreen(boolean isFullScreen) { + setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); + } + + /** + * Gets if the source is taking the entire screen. + * + * @return True if the source is full screen, false otherwise. + */ + public boolean isFullScreen() { + return getBooleanProperty(PROPERTY_FULL_SCREEN); + } + + /** + * Gets the number of items that can be visited. + * + * @return The number of items. + */ + public int getItemCount() { + return mItemCount; + } + + /** + * Sets the number of items that can be visited. + * + * @param itemCount The number of items. + */ + public void setItemCount(int itemCount) { + mItemCount = itemCount; + } + + /** + * Gets the index of the source in the list of items the can be visited. + * + * @return The current item index. + */ + public int getCurrentItemIndex() { + return mCurrentItemIndex; + } + + /** + * Sets the index of the source in the list of items that can be visited. + * + * @param currentItemIndex The current item index. + */ + public void setCurrentItemIndex(int currentItemIndex) { + mCurrentItemIndex = currentItemIndex; + } + + /** + * Gets the index of the first character of the changed sequence. + * + * @return The index of the first character. + */ + public int getFromIndex() { + return mFromIndex; + } + + /** + * Sets the index of the first character of the changed sequence. + * + * @param fromIndex The index of the first character. + */ + public void setFromIndex(int fromIndex) { + mFromIndex = fromIndex; + } + + /** + * Gets the number of added characters. + * + * @return The number of added characters. + */ + public int getAddedCount() { + return mAddedCount; + } + + /** + * Sets the number of added characters. + * + * @param addedCount The number of added characters. + */ + public void setAddedCount(int addedCount) { + mAddedCount = addedCount; + } + + /** + * Gets the number of removed characters. + * + * @return The number of removed characters. + */ + public int getRemovedCount() { + return mRemovedCount; + } + + /** + * Sets the number of removed characters. + * + * @param removedCount The number of removed characters. + */ + public void setRemovedCount(int removedCount) { + mRemovedCount = removedCount; + } + + /** + * Gets the class name of the source. + * + * @return The class name. + */ + public CharSequence getClassName() { + return mClassName; + } + + /** + * Sets the class name of the source. + * + * @param className The lass name. + */ + public void setClassName(CharSequence className) { + mClassName = className; + } + + /** + * Gets the text of the event. The index in the list represents the priority + * of the text. Specifically, the lower the index the higher the priority. + * + * @return The text. + */ + public List<CharSequence> getText() { + return mText; + } + + /** + * Sets the text before a change. + * + * @return The text before the change. + */ + public CharSequence getBeforeText() { + return mBeforeText; + } + + /** + * Sets the text before a change. + * + * @param beforeText The text before the change. + */ + public void setBeforeText(CharSequence beforeText) { + mBeforeText = beforeText; + } + + /** + * Gets the description of the source. + * + * @return The description. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the description of the source. + * + * @param contentDescription The description. + */ + public void setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + } + + /** + * Gets the {@link Parcelable} data. + * + * @return The parcelable data. + */ + public Parcelable getParcelableData() { + return mParcelableData; + } + + /** + * Sets the {@link Parcelable} data of the event. + * + * @param parcelableData The parcelable data. + */ + public void setParcelableData(Parcelable parcelableData) { + mParcelableData = parcelableData; + } + + /** + * Gets the value of a boolean property. + * + * @param property The property. + * @return The value. + */ + public boolean getBooleanProperty(int property) { + return (mBooleanProperties & property) == property; + } + + /** + * Sets a boolean property. + * + * @param property The property. + * @param value The value. + */ + private void setBooleanProperty(int property, boolean value) { + if (value) { + mBooleanProperties |= property; + } else { + mBooleanProperties &= ~property; + } + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated. + * + * @return An instance. + */ + protected static AccessibilityRecord obtain() { + synchronized (mPoolLock) { + if (sPool != null) { + AccessibilityRecord record = sPool; + sPool = sPool.mNext; + sPoolSize--; + record.mNext = null; + record.mIsInPool = false; + return record; + } + return new AccessibilityRecord(); + } + } + + /** + * Return an instance back to be reused. + * <p> + * <b>Note: You must not touch the object after calling this function.</b> + */ + public void recycle() { + if (mIsInPool) { + return; + } + clear(); + synchronized (mPoolLock) { + if (sPoolSize <= MAX_POOL_SIZE) { + mNext = sPool; + sPool = this; + mIsInPool = true; + sPoolSize++; + } + } + } + + /** + * Clears the state of this instance. + */ + protected void clear() { + mBooleanProperties = 0; + mCurrentItemIndex = INVALID_POSITION; + mItemCount = 0; + mFromIndex = 0; + mAddedCount = 0; + mRemovedCount = 0; + mClassName = null; + mContentDescription = null; + mBeforeText = null; + mParcelableData = null; + mText.clear(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(" [ ClassName: " + mClassName); + builder.append("; Text: " + mText); + builder.append("; ContentDescription: " + mContentDescription); + builder.append("; ItemCount: " + mItemCount); + builder.append("; CurrentItemIndex: " + mCurrentItemIndex); + builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED)); + builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD)); + builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED)); + builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN)); + builder.append("; BeforeText: " + mBeforeText); + builder.append("; FromIndex: " + mFromIndex); + builder.append("; AddedCount: " + mAddedCount); + builder.append("; RemovedCount: " + mRemovedCount); + builder.append("; ParcelableData: " + mParcelableData); + builder.append(" ]"); + return builder.toString(); + } +} diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 7633569..aaaae32 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -35,5 +35,7 @@ interface IAccessibilityManager { List<ServiceInfo> getAccessibilityServiceList(); + List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType); + void interrupt(); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 6cb5c35..d63d421 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -55,6 +55,7 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -2556,6 +2557,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + // Add a record for ourselves as well. + AccessibilityEvent record = AccessibilityEvent.obtain(); + // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent + record.setClassName(getClass().getName()); + child.dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return false; } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index f16efbd..060f1a9 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -876,7 +876,6 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - boolean populated = false; // This is an exceptional case which occurs when a window gets the // focus and sends a focus event via its focused child to announce // current focus/selection. AdapterView fires selection but not focus @@ -885,22 +884,27 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); } - // we send selection events only from AdapterView to avoid - // generation of such event for each child + // We first get a chance to populate the event. + onPopulateAccessibilityEvent(event); + + // We send selection events only from AdapterView to avoid + // generation of such event for each child. View selectedView = getSelectedView(); if (selectedView != null) { - populated = selectedView.dispatchPopulateAccessibilityEvent(event); + return selectedView.dispatchPopulateAccessibilityEvent(event); } - if (!populated) { - if (selectedView != null) { - event.setEnabled(selectedView.isEnabled()); - } - event.setItemCount(getCount()); - event.setCurrentItemIndex(getSelectedItemPosition()); - } + return false; + } - return populated; + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + View selectedView = getSelectedView(); + if (selectedView != null) { + event.setEnabled(selectedView.isEnabled()); + } + event.setItemCount(getCount()); + event.setCurrentItemIndex(getSelectedItemPosition()); } @Override diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index bf63607..bd595a5 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -199,11 +199,8 @@ public class CheckedTextView extends TextView implements Checkable { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - boolean populated = super.dispatchPopulateAccessibilityEvent(event); - if (!populated) { - event.setChecked(mChecked); - } - return populated; + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + event.setChecked(mChecked); } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 0df45cc..f050d41 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -208,22 +208,18 @@ public abstract class CompoundButton extends Button implements Checkable { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - boolean populated = super.dispatchPopulateAccessibilityEvent(event); - - if (!populated) { - int resourceId = 0; - if (mChecked) { - resourceId = R.string.accessibility_compound_button_selected; - } else { - resourceId = R.string.accessibility_compound_button_unselected; - } - String state = getResources().getString(resourceId); - event.getText().add(state); - event.setChecked(mChecked); + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + + int resourceId = 0; + if (mChecked) { + resourceId = R.string.accessibility_compound_button_selected; + } else { + resourceId = R.string.accessibility_compound_button_unselected; } - - return populated; + String state = getResources().getString(resourceId); + event.getText().add(state); + event.setChecked(mChecked); } @Override diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 7210e21..30fb927 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -353,13 +353,14 @@ public class DatePicker extends FrameLayout { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + + final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR; String selectedDateUtterance = DateUtils.formatDateTime(mContext, mCurrentDate.getTimeInMillis(), flags); event.getText().add(selectedDateUtterance); - return true; } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index d76a956..5618dbe 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1998,36 +1998,32 @@ public class ListView extends AbsListView { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - boolean populated = super.dispatchPopulateAccessibilityEvent(event); + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); // If the item count is less than 15 then subtract disabled items from the count and // position. Otherwise ignore disabled items. - if (!populated) { - int itemCount = 0; - int currentItemIndex = getSelectedItemPosition(); - - ListAdapter adapter = getAdapter(); - if (adapter != null) { - final int count = adapter.getCount(); - if (count < 15) { - for (int i = 0; i < count; i++) { - if (adapter.isEnabled(i)) { - itemCount++; - } else if (i <= currentItemIndex) { - currentItemIndex--; - } + int itemCount = 0; + int currentItemIndex = getSelectedItemPosition(); + + ListAdapter adapter = getAdapter(); + if (adapter != null) { + final int count = adapter.getCount(); + if (count < 15) { + for (int i = 0; i < count; i++) { + if (adapter.isEnabled(i)) { + itemCount++; + } else if (i <= currentItemIndex) { + currentItemIndex--; } - } else { - itemCount = count; } + } else { + itemCount = count; } - - event.setItemCount(itemCount); - event.setCurrentItemIndex(currentItemIndex); } - return populated; + event.setItemCount(itemCount); + event.setCurrentItemIndex(currentItemIndex); } /** diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 8db34d9..96d41a0 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1027,12 +1027,10 @@ public class ProgressBar extends View { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - if (!super.dispatchPopulateAccessibilityEvent(event)) { - event.setItemCount(mMax); - event.setCurrentItemIndex(mProgress); - } - return true; + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + event.setItemCount(mMax); + event.setCurrentItemIndex(mProgress); } /** diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 6f76dd0..31ec785 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -427,12 +427,19 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - event.setItemCount(getTabCount()); - event.setCurrentItemIndex(mSelectedTab); + onPopulateAccessibilityEvent(event); + // Dispatch only to the selected tab. if (mSelectedTab != -1) { - getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event); + return getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event); } - return true; + return false; + } + + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + event.setItemCount(getTabCount()); + event.setCurrentItemIndex(mSelectedTab); } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 537709d..4d3aa68 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -81,8 +81,8 @@ import android.text.method.TextKeyListener; import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.style.ClickableSpan; -import android.text.style.SuggestionSpan; import android.text.style.ParagraphStyle; +import android.text.style.SuggestionSpan; import android.text.style.URLSpan; import android.text.style.UpdateAppearance; import android.text.util.Linkify; @@ -2967,14 +2967,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener advancesIndex); } - public float getTextRunAdvancesICU(int start, int end, int contextStart, + public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex, - Paint p) { + Paint p, int reserved) { int count = end - start; int contextCount = contextEnd - contextStart; - return p.getTextRunAdvancesICU(mChars, start + mStart, count, + return p.getTextRunAdvances(mChars, start + mStart, count, contextStart + mStart, contextCount, flags, advances, - advancesIndex); + advancesIndex, reserved); } public int getTextRunCursor(int contextStart, int contextEnd, int flags, @@ -7896,9 +7896,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { if (!isShown()) { - return false; + return; } final boolean isPassword = hasPasswordTransformationMethod(); @@ -7914,7 +7914,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { event.setPassword(isPassword); } - return false; } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, @@ -8038,7 +8037,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case ID_SELECTION_MODE: if (mSelectionActionMode != null) { // Selection mode is already started, simply change selected part. - updateSelectedRegion(); + selectCurrentWord(); } else { startSelectionActionMode(); } @@ -8188,8 +8187,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0); stopSelectionActionMode(); } else { - // New selection at touch position - updateSelectedRegion(); + selectCurrentWord(); } handled = true; } @@ -8205,17 +8203,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return handled; } - /** - * When selection mode is already started, this method simply updates the selected part of text - * to the text under the finger. - */ - private void updateSelectedRegion() { - // Start a new selection at current position, keep selectionAction mode on - selectCurrentWord(); - // Updates handles' positions - getSelectionController().show(); - } - private boolean touchPositionIsInSelection() { int selectionStart = getSelectionStart(); int selectionEnd = getSelectionEnd(); @@ -8783,7 +8770,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mTouchOffsetY; // Where the touch position should be on the handle to ensure a maximum cursor visibility private float mIdealVerticalOffset; - // Parent's (TextView) position in window + // Parent's (TextView) previous position in window private int mLastParentX, mLastParentY; // PopupWindow container absolute position with respect to the enclosing window private int mContainerPositionX, mContainerPositionY; @@ -8857,12 +8844,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void show() { - updateContainerPosition(); if (isShowing()) { mContainer.update(mContainerPositionX, mContainerPositionY, mRight - mLeft, mBottom - mTop); - - hideAssociatedPopupWindow(); } else { mContainer.showAtLocation(TextView.this, 0, mContainerPositionX, mContainerPositionY); @@ -8877,7 +8861,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void dismiss() { mIsDragging = false; mContainer.dismiss(); - hideAssociatedPopupWindow(); } public void hide() { @@ -8908,22 +8891,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int compoundPaddingLeft = getCompoundPaddingLeft(); final int compoundPaddingRight = getCompoundPaddingRight(); - final TextView hostView = TextView.this; + final TextView textView = TextView.this; if (mTempRect == null) mTempRect = new Rect(); final Rect clip = mTempRect; clip.left = compoundPaddingLeft; clip.top = extendedPaddingTop; - clip.right = hostView.getWidth() - compoundPaddingRight; - clip.bottom = hostView.getHeight() - extendedPaddingBottom; + clip.right = textView.getWidth() - compoundPaddingRight; + clip.bottom = textView.getHeight() - extendedPaddingBottom; - final ViewParent parent = hostView.getParent(); - if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) { + final ViewParent parent = textView.getParent(); + if (parent == null || !parent.getChildVisibleRect(textView, clip, null)) { return false; } final int[] coords = mTempCoords; - hostView.getLocationInWindow(coords); + textView.getLocationInWindow(coords); final int posX = coords[0] + mPositionX + (int) mHotspotX; final int posY = coords[1] + mPositionY; @@ -8932,23 +8915,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener posY >= clip.top && posY <= clip.bottom; } - private void moveTo(int x, int y) { - mPositionX = x - TextView.this.mScrollX; - mPositionY = y - TextView.this.mScrollY; - - if (mIsDragging) { - TextView.this.getLocationInWindow(mTempCoords); - if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) { - mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX; - mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY; - mLastParentX = mTempCoords[0]; - mLastParentY = mTempCoords[1]; - } - - hideAssociatedPopupWindow(); - } - } - public abstract int getCurrentCursorOffset(); public abstract void updateOffset(int offset); @@ -8957,44 +8923,44 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void positionAtCursorOffset(int offset) { addPositionToTouchUpFilter(offset); - final int width = mDrawable.getIntrinsicWidth(); - final int height = mDrawable.getIntrinsicHeight(); final int line = mLayout.getLineForOffset(offset); final int lineBottom = mLayout.getLineBottom(line); - final Rect bounds = sCursorControllerTempRect; - bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) + - TextView.this.mScrollX; - bounds.top = lineBottom + TextView.this.mScrollY; - - bounds.right = bounds.left + width; - bounds.bottom = bounds.top + height; + mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX); + mPositionY = lineBottom; - convertFromViewportToContentCoordinates(bounds); - moveTo(bounds.left, bounds.top); + // Take TextView's padding into account. + mPositionX += viewportToContentHorizontalOffset(); + mPositionY += viewportToContentVerticalOffset(); } - /** - * Updates the global container's position. - * @return whether or not the position has actually changed - */ - private boolean updateContainerPosition() { + protected boolean updateContainerPosition() { positionAtCursorOffset(getCurrentCursorOffset()); + + final int previousContainerPositionX = mContainerPositionX; + final int previousContainerPositionY = mContainerPositionY; + TextView.this.getLocationInWindow(mTempCoords); - final int containerPositionX = mTempCoords[0] + mPositionX; - final int containerPositionY = mTempCoords[1] + mPositionY; + mContainerPositionX = mTempCoords[0] + mPositionX; + mContainerPositionY = mTempCoords[1] + mPositionY; - if (containerPositionX != mContainerPositionX || - containerPositionY != mContainerPositionY) { - mContainerPositionX = containerPositionX; - mContainerPositionY = containerPositionY; - return true; - } - return false; + return (previousContainerPositionX != mContainerPositionX || + previousContainerPositionY != mContainerPositionY); } public boolean onPreDraw() { if (updateContainerPosition()) { + if (mIsDragging) { + if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) { + mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX; + mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY; + mLastParentX = mTempCoords[0]; + mLastParentY = mTempCoords[1]; + } + } + + onHandleMoved(); + if (isPositionVisible()) { mContainer.update(mContainerPositionX, mContainerPositionY, mRight - mLeft, mBottom - mTop); @@ -9007,9 +8973,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener dismiss(); } } - - // Hide paste popup as soon as the view is scrolled or moved - hideAssociatedPopupWindow(); } return true; } @@ -9076,8 +9039,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mIsDragging; } - void hideAssociatedPopupWindow() { - // No associated popup window by default + void onHandleMoved() { + // Does nothing by default } public void onDetached() { @@ -9096,15 +9059,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Runnable mHider; private Runnable mPastePopupShower; - public InsertionHandleView() { - super(); - } - @Override public void show() { super.show(); hideDelayed(); - removePastePopupCallback(); + hidePastePopupWindow(); } public void show(int delayBeforePaste) { @@ -9118,11 +9077,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mPastePopupShower == null) { mPastePopupShower = new Runnable() { public void run() { - showAssociatedPopupWindow(); + showPastePopupWindow(); } }; } - postDelayed(mPastePopupShower, delayBeforePaste); + TextView.this.postDelayed(mPastePopupShower, delayBeforePaste); } } @@ -9132,11 +9091,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener onDetached(); } - @Override - public void hide() { - super.hide(); - } - private void hideDelayed() { removeHiderCallback(); if (mHider == null) { @@ -9146,18 +9100,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } }; } - postDelayed(mHider, DELAY_BEFORE_FADE_OUT); - } - - private void removePastePopupCallback() { - if (mPastePopupShower != null) { - removeCallbacks(mPastePopupShower); - } + TextView.this.postDelayed(mHider, DELAY_BEFORE_FADE_OUT); } private void removeHiderCallback() { if (mHider != null) { - removeCallbacks(mHider); + TextView.this.removeCallbacks(mHider); } } @@ -9197,6 +9145,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } + hideDelayed(); + break; + + case MotionEvent.ACTION_CANCEL: + hideDelayed(); break; default: @@ -9214,31 +9167,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void updateOffset(int offset) { Selection.setSelection((Spannable) mText, offset); - positionAtCursorOffset(offset); } @Override public void updatePosition(int x, int y) { - final int previousOffset = getCurrentCursorOffset(); - final int newOffset = getOffset(x, y); - - if (newOffset != previousOffset) { - updateOffset(newOffset); - removePastePopupCallback(); - } - hideDelayed(); + updateOffset(getOffset(x, y)); } - void showAssociatedPopupWindow() { + void showPastePopupWindow() { if (mPastePopupWindow == null) { - // Lazy initialisation: create when actually shown only. mPastePopupWindow = new PastePopupWindow(); } mPastePopupWindow.show(); } @Override - void hideAssociatedPopupWindow() { + void onHandleMoved() { + removeHiderCallback(); + hidePastePopupWindow(); + } + + void hidePastePopupWindow() { + if (mPastePopupShower != null) { + TextView.this.removeCallbacks(mPastePopupShower); + } if (mPastePopupWindow != null) { mPastePopupWindow.hide(); } @@ -9247,15 +9199,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onDetached() { removeHiderCallback(); - removePastePopupCallback(); + hidePastePopupWindow(); } } private class SelectionStartHandleView extends HandleView { - public SelectionStartHandleView() { - super(); - } - @Override protected void initDrawable() { if (mSelectHandleLeft == null) { @@ -9274,7 +9222,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void updateOffset(int offset) { Selection.setSelection((Spannable) mText, offset, getSelectionEnd()); - positionAtCursorOffset(offset); } @Override @@ -9290,15 +9237,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (offset >= selectionEnd) offset = selectionEnd - 1; Selection.setSelection((Spannable) mText, offset, selectionEnd); - positionAtCursorOffset(offset); } } private class SelectionEndHandleView extends HandleView { - public SelectionEndHandleView() { - super(); - } - @Override protected void initDrawable() { if (mSelectHandleRight == null) { @@ -9317,7 +9259,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void updateOffset(int offset) { Selection.setSelection((Spannable) mText, getSelectionStart(), offset); - positionAtCursorOffset(offset); } @Override @@ -9333,7 +9274,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (offset <= selectionStart) offset = selectionStart + 1; Selection.setSelection((Spannable) mText, selectionStart, offset); - positionAtCursorOffset(offset); } } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 029d690..423e735 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -409,7 +409,9 @@ public class TimePicker extends FrameLayout { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + int flags = DateUtils.FORMAT_SHOW_TIME; if (mIs24HourView) { flags |= DateUtils.FORMAT_24HOUR; @@ -421,7 +423,6 @@ public class TimePicker extends FrameLayout { String selectedDateUtterance = DateUtils.formatDateTime(mContext, mTempCalendar.getTimeInMillis(), flags); event.getText().add(selectedDateUtterance); - return true; } private void updateHourControl() { diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 16d5539..9652085 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -861,7 +861,7 @@ public class ActionBarImpl extends ActionBar { @Override public void setIcon(int resId) { - mActionView.setIcon(mContext.getResources().getDrawable(resId)); + mActionView.setIcon(resId); } @Override @@ -871,7 +871,7 @@ public class ActionBarImpl extends ActionBar { @Override public void setLogo(int resId) { - mActionView.setLogo(mContext.getResources().getDrawable(resId)); + mActionView.setLogo(resId); } @Override diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index 71511c6..16f51fd 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -104,6 +104,10 @@ public abstract class BaseMenuPresenter implements MenuPresenter { * @param childIndex Index within the parent to insert at */ protected void addItemView(View itemView, int childIndex) { + final ViewGroup currentParent = (ViewGroup) itemView.getParent(); + if (currentParent != null) { + currentParent.removeView(itemView); + } ((ViewGroup) mMenuView).addView(itemView, childIndex); } diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index 42ef916..c6d386d 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -523,7 +523,9 @@ public final class MenuItemImpl implements MenuItem { } public boolean showsTextAsAction() { - return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT; + return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT && + mMenu.getContext().getResources().getBoolean( + com.android.internal.R.bool.allow_action_menu_item_text_with_icon); } public void setShowAsAction(int actionEnum) { diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 74a6ae7..fa8eb51 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -30,11 +30,13 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Log; import android.view.ActionMode; import android.view.Gravity; @@ -85,7 +87,6 @@ public class ActionBarView extends ViewGroup { private CharSequence mSubtitle; private Drawable mIcon; private Drawable mLogo; - private Drawable mDivider; private View mHomeLayout; private View mHomeAsUpView; @@ -211,8 +212,6 @@ public class ActionBarView extends ViewGroup { mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); - mDivider = a.getDrawable(R.styleable.ActionBar_divider); - a.recycle(); mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); @@ -434,6 +433,10 @@ public class ActionBarView extends ViewGroup { } } + public void setIcon(int resId) { + setIcon(mContext.getResources().getDrawableForDensity(resId, getPreferredIconDensity())); + } + public void setLogo(Drawable logo) { mLogo = logo; if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { @@ -441,6 +444,29 @@ public class ActionBarView extends ViewGroup { } } + public void setLogo(int resId) { + mContext.getResources().getDrawable(resId); + } + + /** + * @return Drawable density to load that will best fit the available height. + */ + private int getPreferredIconDensity() { + final Resources res = mContext.getResources(); + final int availableHeight = getLayoutParams().height - + mIconView.getPaddingTop() - mIconView.getPaddingBottom(); + int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size); + + if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) { + return DisplayMetrics.DENSITY_LOW; + } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM >= availableHeight) { + return DisplayMetrics.DENSITY_MEDIUM; + } else if (iconSize * DisplayMetrics.DENSITY_HIGH >= availableHeight) { + return DisplayMetrics.DENSITY_HIGH; + } + return DisplayMetrics.DENSITY_XHIGH; + } + public void setNavigationMode(int mode) { final int oldMode = mNavigationMode; if (mode != oldMode) { diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 0a54e17..768b836 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -482,44 +482,28 @@ public: return totalAdvance; } - static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + static float getTextRunAdvances___CIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, - jint flags, jfloatArray advances, jint advancesIndex) { + jint flags, jfloatArray advances, jint advancesIndex, jint reserved) { jchar* textArray = env->GetCharArrayElements(text, NULL); - jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex, - index - contextIndex, count, contextCount, flags, advances, advancesIndex); + jfloat result = (reserved == 0) ? + doTextRunAdvances(env, paint, textArray + contextIndex, index - contextIndex, + count, contextCount, flags, advances, advancesIndex) : + doTextRunAdvancesICU(env, paint, textArray + contextIndex, index - contextIndex, + count, contextCount, flags, advances, advancesIndex); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } - static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + static float getTextRunAdvances__StringIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, - jfloatArray advances, jint advancesIndex) { + jfloatArray advances, jint advancesIndex, jint reserved) { const jchar* textArray = env->GetStringChars(text, NULL); - jfloat result = doTextRunAdvances(env, paint, textArray + contextStart, - start - contextStart, end - start, contextEnd - contextStart, flags, advances, - advancesIndex); - env->ReleaseStringChars(text, textArray); - return result; - } - - static float getTextRunAdvancesICU___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, - jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, - jint flags, jfloatArray advances, jint advancesIndex) { - jchar* textArray = env->GetCharArrayElements(text, NULL); - jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextIndex, - index - contextIndex, count, contextCount, flags, advances, advancesIndex); - env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); - return result; - } - - static float getTextRunAdvancesICU__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, - jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, - jfloatArray advances, jint advancesIndex) { - const jchar* textArray = env->GetStringChars(text, NULL); - jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextStart, - start - contextStart, end - start, contextEnd - contextStart, flags, advances, - advancesIndex); + jfloat result = (reserved == 0) ? + doTextRunAdvances(env, paint, textArray + contextStart, start - contextStart, + end - start, contextEnd - contextStart, flags, advances, advancesIndex) : + doTextRunAdvancesICU(env, paint, textArray + contextStart, start - contextStart, + end - start, contextEnd - contextStart, flags, advances, advancesIndex); env->ReleaseStringChars(text, textArray); return result; } @@ -816,14 +800,12 @@ static JNINativeMethod methods[] = { {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, - {"native_getTextRunAdvances","(I[CIIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI}, - {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI}, - {"native_getTextRunAdvancesICU","(I[CIIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvancesICU___CIIIII_FI}, - {"native_getTextRunAdvancesICU","(ILjava/lang/String;IIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvancesICU__StringIIIII_FI}, + {"native_getTextRunAdvances","(I[CIIIII[FII)F", + (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FII}, + {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FII)F", + (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FII}, + + {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I", (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C}, {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7f18121..2ed39e4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -550,6 +550,14 @@ android:description="@string/permdesc_sdcardWrite" android:protectionLevel="dangerous" /> + <!-- Allows an application to write to internal media storage + @hide --> + <permission android:name="android.permission.WRITE_MEDIA_STORAGE" + android:permissionGroup="android.permission-group.STORAGE" + android:label="@string/permlab_mediaStorageWrite" + android:description="@string/permdesc_mediaStorageWrite" + android:protectionLevel="signatureOrSystem" /> + <!-- ============================================ --> <!-- Permissions for low-level system interaction --> <!-- ============================================ --> diff --git a/core/res/res/layout-large/action_bar_home.xml b/core/res/res/layout-large/action_bar_home.xml new file mode 100644 index 0000000..86580bc --- /dev/null +++ b/core/res/res/layout-large/action_bar_home.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<view xmlns:android="http://schemas.android.com/apk/res/android" + class="com.android.internal.widget.ActionBarView$HomeView" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="?android:attr/selectableItemBackground" > + <ImageView android:id="@android:id/up" + android:src="?android:attr/homeAsUpIndicator" + android:layout_gravity="center_vertical|left" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="-12dip" /> + <ImageView android:id="@android:id/home" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:paddingTop="4dip" + android:paddingBottom="4dip" + android:adjustViewBounds="true" + android:layout_gravity="center" + android:scaleType="fitCenter" /> +</view> diff --git a/core/res/res/layout-large/action_mode_close_item.xml b/core/res/res/layout-large/action_mode_close_item.xml new file mode 100644 index 0000000..321622e --- /dev/null +++ b/core/res/res/layout-large/action_mode_close_item.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/action_mode_close_button" + android:background="@drawable/btn_cab_done" + android:focusable="true" + android:clickable="true" + android:paddingLeft="16dip" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + <ImageView android:layout_width="48dip" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:scaleType="center" + android:src="@drawable/ic_cab_close_holo" /> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginLeft="8dip" + android:layout_marginRight="16dip" + android:textAppearance="@android:style/TextAppearance.Holo.Medium" + android:textColor="@android:color/white" + android:text="@string/action_mode_done" /> +</LinearLayout> diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml index c82f91d..7f7c55c 100644 --- a/core/res/res/layout/action_bar_home.xml +++ b/core/res/res/layout/action_bar_home.xml @@ -25,12 +25,15 @@ android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="-12dip" /> + android:layout_marginRight="-4dip" /> <ImageView android:id="@android:id/home" android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingLeft="16dip" - android:paddingRight="16dip" + android:layout_height="wrap_content" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:paddingTop="@dimen/action_bar_icon_vertical_padding" + android:paddingBottom="@dimen/action_bar_icon_vertical_padding" android:layout_gravity="center" - android:scaleType="center" /> + android:adjustViewBounds="true" + android:scaleType="fitCenter" /> </view> diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml index 4a73368..5e828fa 100644 --- a/core/res/res/layout/action_menu_item_layout.xml +++ b/core/res/res/layout/action_menu_item_layout.xml @@ -31,10 +31,9 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" - android:paddingLeft="4dip" - android:paddingRight="4dip" - android:minHeight="56dip" - android:scaleType="center" + android:padding="@dimen/action_bar_icon_vertical_padding" + android:scaleType="fitCenter" + android:adjustViewBounds="true" android:background="@null" android:focusable="false" /> <Button android:id="@+id/textButton" @@ -46,7 +45,6 @@ style="?attr/buttonStyleSmall" android:textColor="?attr/actionMenuTextColor" android:background="@null" - android:paddingLeft="4dip" - android:paddingRight="4dip" + android:padding="4dip" android:focusable="false" /> </com.android.internal.view.menu.ActionMenuItemView> diff --git a/core/res/res/layout/action_mode_close_item.xml b/core/res/res/layout/action_mode_close_item.xml index 7badbac..2a4d8e0 100644 --- a/core/res/res/layout/action_mode_close_item.xml +++ b/core/res/res/layout/action_mode_close_item.xml @@ -19,20 +19,12 @@ android:background="@drawable/btn_cab_done" android:focusable="true" android:clickable="true" - android:paddingLeft="16dip" + android:paddingLeft="8dip" android:layout_width="wrap_content" android:layout_height="match_parent"> - <ImageView android:layout_width="48dip" + <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:scaleType="center" + android:scaleType="fitCenter" android:src="@drawable/ic_cab_close_holo" /> - <TextView android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginLeft="8dip" - android:layout_marginRight="16dip" - android:textAppearance="@android:style/TextAppearance.Holo.Medium" - android:textColor="@android:color/white" - android:text="@string/action_mode_done" /> </LinearLayout> diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 058daa8..8def578e 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -28,4 +28,8 @@ <dimen name="preference_screen_side_margin">96dp</dimen> <dimen name="preference_screen_side_margin_negative">-100dp</dimen> <dimen name="preference_widget_width">72dp</dimen> + + <!-- Default height of an action bar. --> + <dimen name="action_bar_default_height">40dip</dimen> + </resources> diff --git a/core/res/res/values-large/dimens.xml b/core/res/res/values-large/dimens.xml index cd1847f..5355847 100644 --- a/core/res/res/values-large/dimens.xml +++ b/core/res/res/values-large/dimens.xml @@ -22,4 +22,10 @@ <!-- Preference UI dimensions for larger screens. --> <dimen name="preference_widget_width">56dp</dimen> + <!-- The maximum number of action buttons that should be permitted within + an action bar/action mode. This will be used to determine how many + showAsAction="ifRoom" items can fit. "always" items can override this. --> + <integer name="max_action_buttons">5</integer> + <!-- Default height of an action bar. --> + <dimen name="action_bar_default_height">56dip</dimen> </resources> diff --git a/core/res/res/values-port/dimens.xml b/core/res/res/values-port/dimens.xml new file mode 100644 index 0000000..bf0a342 --- /dev/null +++ b/core/res/res/values-port/dimens.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- The maximum number of action buttons that should be permitted within + an action bar/action mode. This will be used to determine how many + showAsAction="ifRoom" items can fit. "always" items can override this. --> + <integer name="max_action_buttons">2</integer> +</resources> diff --git a/core/res/res/values-w480dp/bools.xml b/core/res/res/values-w480dp/bools.xml new file mode 100644 index 0000000..ea7eeb5 --- /dev/null +++ b/core/res/res/values-w480dp/bools.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <bool name="allow_action_menu_item_text_with_icon">true</bool> +</resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 33825c7..05a4810 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -68,7 +68,7 @@ <string name="serviceNotProvisioned" msgid="8614830180508686666">"無法提供此服務。"</string> <string name="CLIRPermanent" msgid="5460892159398802465">"本機號碼顯示設定無法變更。"</string> <string name="RestrictedChangedTitle" msgid="5592189398956187498">"受限存取已變更"</string> - <string name="RestrictedOnData" msgid="8653794784690065540">"已封鎖資料傳輸服務。"</string> + <string name="RestrictedOnData" msgid="8653794784690065540">"已封鎖數據傳輸服務。"</string> <string name="RestrictedOnEmergency" msgid="6581163779072833665">"已封鎖緊急服務。"</string> <string name="RestrictedOnNormal" msgid="4953867011389750673">"已封鎖語音服務。"</string> <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"已封鎖所有語音服務。"</string> @@ -700,15 +700,15 @@ <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"1 個月前"</string> <plurals name="num_seconds_ago"> <item quantity="one" msgid="4869870056547896011">"1 秒以前"</item> - <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g> 秒以前"</item> + <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g> 秒前"</item> </plurals> <plurals name="num_minutes_ago"> <item quantity="one" msgid="3306787433088810191">"1 分鐘以前"</item> - <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> 分鐘以前"</item> + <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> 分鐘前"</item> </plurals> <plurals name="num_hours_ago"> <item quantity="one" msgid="9150797944610821849">"1 小時以前"</item> - <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時以前"</item> + <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item> </plurals> <plurals name="last_num_days"> <item quantity="other" msgid="3069992808164318268">"最近 <xliff:g id="COUNT">%d</xliff:g> 天"</item> @@ -717,7 +717,7 @@ <string name="older" msgid="5211975022815554840">"較舊"</string> <plurals name="num_days_ago"> <item quantity="one" msgid="861358534398115820">"昨天"</item> - <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天以前"</item> + <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item> </plurals> <plurals name="in_num_seconds"> <item quantity="one" msgid="2729745560954905102">"1 秒內"</item> @@ -745,11 +745,11 @@ </plurals> <plurals name="abbrev_num_hours_ago"> <item quantity="one" msgid="4796212039724722116">"1 小時以前"</item> - <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g> 小時以前"</item> + <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item> </plurals> <plurals name="abbrev_num_days_ago"> <item quantity="one" msgid="8463161711492680309">"昨天"</item> - <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g> 天以前"</item> + <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item> </plurals> <plurals name="abbrev_in_num_seconds"> <item quantity="one" msgid="5842225370795066299">"1 秒內"</item> @@ -922,14 +922,14 @@ <string name="ext_media_unmountable_notification_title" product="nosdcard" msgid="2090046769532713563">"USB 儲存裝置已毀損"</string> <string name="ext_media_unmountable_notification_title" product="default" msgid="6410723906019100189">"SD 卡已損壞"</string> <string name="ext_media_unmountable_notification_message" product="nosdcard" msgid="529021299294450667">"USB 儲存裝置已損壞,您可能必須重新格式化。"</string> - <string name="ext_media_unmountable_notification_message" product="default" msgid="6902531775948238989">"SD 卡已毀損,您可能必須予以重新格式化。"</string> - <string name="ext_media_badremoval_notification_title" product="nosdcard" msgid="1661683031330951073">"USB 儲存裝置已意外移除"</string> + <string name="ext_media_unmountable_notification_message" product="default" msgid="6902531775948238989">"SD 卡已毀損,您可能必須重新格式化。"</string> + <string name="ext_media_badremoval_notification_title" product="nosdcard" msgid="1661683031330951073">"USB 儲存裝置未正常移除"</string> <string name="ext_media_badremoval_notification_title" product="default" msgid="6872152882604407837">"SD 卡未正常移除"</string> <string name="ext_media_badremoval_notification_message" product="nosdcard" msgid="4329848819865594241">"請先卸載 USB 儲存裝置,再將其移除,以免資料遺失。"</string> <string name="ext_media_badremoval_notification_message" product="default" msgid="7260183293747448241">"請先卸載 SD 卡,再將其移除,以免資料遺失。"</string> <string name="ext_media_safe_unmount_notification_title" product="nosdcard" msgid="3967973893270360230">"USB 儲存裝置已可安全移除"</string> <string name="ext_media_safe_unmount_notification_title" product="default" msgid="6729801130790616200">"可安全移除 SD 卡"</string> - <string name="ext_media_safe_unmount_notification_message" product="nosdcard" msgid="6142195361606493530">"您可安全移除 USB 儲存裝置了。"</string> + <string name="ext_media_safe_unmount_notification_message" product="nosdcard" msgid="6142195361606493530">"您現在可以安全地移除 USB 儲存裝置。"</string> <string name="ext_media_safe_unmount_notification_message" product="default" msgid="568841278138377604">"您現在可以安全地移除 SD 卡。"</string> <string name="ext_media_nomedia_notification_title" product="nosdcard" msgid="4486377230140227651">"USB 儲存裝置已移除"</string> <string name="ext_media_nomedia_notification_title" product="default" msgid="8902518030404381318">"已移除 SD 卡"</string> diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml new file mode 100644 index 0000000..c7dcb51 --- /dev/null +++ b/core/res/res/values/bools.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <bool name="allow_action_menu_item_text_with_icon">false</bool> +</resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index da1c157..a1511b3 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -28,7 +28,7 @@ <!-- The maximum number of action buttons that should be permitted within an action bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit. "always" items can override this. --> - <integer name="max_action_buttons">5</integer> + <integer name="max_action_buttons">3</integer> <dimen name="toast_y_offset">64dip</dimen> <!-- Height of the status bar --> <dimen name="status_bar_height">25dip</dimen> @@ -79,4 +79,9 @@ <!-- Minimum width of the search view text entry area. --> <dimen name="search_view_text_min_width">160dip</dimen> + + <!-- Default height of an action bar. --> + <dimen name="action_bar_default_height">48dip</dimen> + <!-- Vertical padding around action bar icons. --> + <dimen name="action_bar_icon_vertical_padding">4dip</dimen> </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index b9fd6a5..be7b42f 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -276,7 +276,7 @@ <item name="actionModeStyle">@style/Widget.ActionMode</item> <item name="actionModeCloseButtonStyle">@style/Widget.ActionButton.CloseMode</item> <item name="actionBarStyle">@android:style/Widget.ActionBar</item> - <item name="actionBarSize">56dip</item> + <item name="actionBarSize">@dimen/action_bar_default_height</item> <item name="actionModePopupWindowStyle">?android:attr/popupWindowStyle</item> <item name="actionMenuTextAppearance">?android:attr/textAppearanceMedium</item> <item name="actionMenuTextColor">?android:attr/textColorPrimary</item> @@ -1009,7 +1009,7 @@ <item name="actionModeStyle">@style/Widget.Holo.ActionMode</item> <item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item> <item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item> - <item name="actionBarSize">56dip</item> + <item name="actionBarSize">@dimen/action_bar_default_height</item> <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.PopupWindow.ActionMode</item> <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item> @@ -1294,7 +1294,7 @@ <item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item> <item name="actionModeCloseButtonStyle">@style/Widget.Holo.Light.ActionButton.CloseMode</item> <item name="actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item> - <item name="actionBarSize">56dip</item> + <item name="actionBarSize">@dimen/action_bar_default_height</item> <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow.ActionMode</item> <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item> diff --git a/data/etc/platform.xml b/data/etc/platform.xml index df80546..1870a4a 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -58,6 +58,10 @@ <group gid="sdcard_rw" /> </permission> + <permission name="android.permission.WRITE_MEDIA_STORAGE" > + <group gid="media_rw" /> + </permission> + <permission name="android.permission.ACCESS_MTP" > <group gid="mtp" /> </permission> diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd index d8898ae..7e13569 100644 --- a/docs/html/guide/topics/ui/actionbar.jd +++ b/docs/html/guide/topics/ui/actionbar.jd @@ -429,21 +429,18 @@ private class MyTabListener implements ActionBar.TabListener { private TabContentFragment mFragment; // Called to create an instance of the listener when adding a new tab - public TabListener(TabContentFragment fragment) { + public MyTabListener(TabContentFragment fragment) { mFragment = fragment; } - @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { ft.add(R.id.fragment_content, mFragment, null); } - @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { ft.remove(mFragment); } - @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { // do nothing } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 0949beb..962f22c 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1501,48 +1501,20 @@ public class Paint { public float getTextRunAdvances(char[] chars, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { - - if ((index | count | contextIndex | contextCount | advancesIndex - | (index - contextIndex) - | ((contextIndex + contextCount) - (index + count)) - | (chars.length - (contextIndex + contextCount)) - | (advances == null ? 0 : - (advances.length - (advancesIndex + count)))) < 0) { - throw new IndexOutOfBoundsException(); - } - if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { - throw new IllegalArgumentException("unknown flags value: " + flags); - } - - if (!mHasCompatScaling) { - return native_getTextRunAdvances(mNativePaint, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); - } - - final float oldSize = getTextSize(); - setTextSize(oldSize * mCompatScaling); - float res = native_getTextRunAdvances(mNativePaint, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); - setTextSize(oldSize); - - if (advances != null) { - for (int i = advancesIndex, e = i + count; i < e; i++) { - advances[i] *= mInvCompatScaling; - } - } - return res * mInvCompatScaling; // assume errors are not significant + return getTextRunAdvances(chars, index, count, contextIndex, contextCount, flags, + advances, advancesIndex, 0 /* use Harfbuzz*/); } /** * Convenience overload that takes a char array instead of a * String. * - * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int) + * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int, int) * @hide */ - public float getTextRunAdvancesICU(char[] chars, int index, int count, + public float getTextRunAdvances(char[] chars, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, - int advancesIndex) { + int advancesIndex, int reserved) { if ((index | count | contextIndex | contextCount | advancesIndex | (index - contextIndex) @@ -1557,14 +1529,14 @@ public class Paint { } if (!mHasCompatScaling) { - return native_getTextRunAdvancesICU(mNativePaint, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); + return native_getTextRunAdvances(mNativePaint, chars, index, count, + contextIndex, contextCount, flags, advances, advancesIndex, reserved); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float res = native_getTextRunAdvancesICU(mNativePaint, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); + float res = native_getTextRunAdvances(mNativePaint, chars, index, count, + contextIndex, contextCount, flags, advances, advancesIndex, reserved); setTextSize(oldSize); if (advances != null) { @@ -1585,29 +1557,8 @@ public class Paint { public float getTextRunAdvances(CharSequence text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { - - if (text instanceof String) { - return getTextRunAdvances((String) text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); - } - if (text instanceof SpannedString || - text instanceof SpannableString) { - return getTextRunAdvances(text.toString(), start, end, - contextStart, contextEnd, flags, advances, advancesIndex); - } - if (text instanceof GraphicsOperations) { - return ((GraphicsOperations) text).getTextRunAdvances(start, end, - contextStart, contextEnd, flags, advances, advancesIndex, this); - } - - int contextLen = contextEnd - contextStart; - int len = end - start; - char[] buf = TemporaryBuffer.obtain(contextLen); - TextUtils.getChars(text, start, end, buf, 0); - float result = getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesIndex); - TemporaryBuffer.recycle(buf); - return result; + return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags, + advances, advancesIndex, 0 /* use Harfbuzz */); } /** @@ -1617,21 +1568,21 @@ public class Paint { * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int) * @hide */ - public float getTextRunAdvancesICU(CharSequence text, int start, int end, + public float getTextRunAdvances(CharSequence text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, - int advancesIndex) { + int advancesIndex, int reserved) { if (text instanceof String) { - return getTextRunAdvancesICU((String) text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + return getTextRunAdvances((String) text, start, end, + contextStart, contextEnd, flags, advances, advancesIndex, reserved); } if (text instanceof SpannedString || text instanceof SpannableString) { - return getTextRunAdvancesICU(text.toString(), start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + return getTextRunAdvances(text.toString(), start, end, + contextStart, contextEnd, flags, advances, advancesIndex, reserved); } if (text instanceof GraphicsOperations) { - return ((GraphicsOperations) text).getTextRunAdvancesICU(start, end, + return ((GraphicsOperations) text).getTextRunAdvances(start, end, contextStart, contextEnd, flags, advances, advancesIndex, this); } @@ -1639,8 +1590,8 @@ public class Paint { int len = end - start; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, start, end, buf, 0); - float result = getTextRunAdvancesICU(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesIndex); + float result = getTextRunAdvances(buf, start - contextStart, len, + 0, contextLen, flags, advances, advancesIndex, reserved); TemporaryBuffer.recycle(buf); return result; } @@ -1689,44 +1640,55 @@ public class Paint { */ public float getTextRunAdvances(String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { - - if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) - | (start - contextStart) | (contextEnd - end) - | (text.length() - contextEnd) - | (advances == null ? 0 : - (advances.length - advancesIndex - (end - start)))) < 0) { - throw new IndexOutOfBoundsException(); - } - if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { - throw new IllegalArgumentException("unknown flags value: " + flags); - } - - if (!mHasCompatScaling) { - return native_getTextRunAdvances(mNativePaint, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); - } - - final float oldSize = getTextSize(); - setTextSize(oldSize * mCompatScaling); - float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); - setTextSize(oldSize); - - if (advances != null) { - for (int i = advancesIndex, e = i + (end - start); i < e; i++) { - advances[i] *= mInvCompatScaling; - } - } - return totalAdvance * mInvCompatScaling; // assume errors are insignificant + return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags, + advances, advancesIndex, 0 /* use Harfbuzz*/); } /** - * Temporary - DO NOT USE + * Returns the total advance width for the characters in the run + * between start and end, and if advances is not null, the advance + * assigned to each of these characters (java chars). + * + * <p>The trailing surrogate in a valid surrogate pair is assigned + * an advance of 0. Thus the number of returned advances is + * always equal to count, not to the number of unicode codepoints + * represented by the run. + * + * <p>In the case of conjuncts or combining marks, the total + * advance is assigned to the first logical character, and the + * following characters are assigned an advance of 0. + * + * <p>This generates the sum of the advances of glyphs for + * characters in a reordered cluster as the width of the first + * logical character in the cluster, and 0 for the widths of all + * other characters in the cluster. In effect, such clusters are + * treated like conjuncts. + * + * <p>The shaping bounds limit the amount of context available + * outside start and end that can be used for shaping analysis. + * These bounds typically reflect changes in bidi level or font + * metrics across which shaping does not occur. + * + * @param text the text to measure + * @param start the index of the first character to measure + * @param end the index past the last character to measure + * @param contextStart the index of the first character to use for shaping context, + * must be <= start + * @param contextEnd the index past the last character to use for shaping context, + * must be >= end + * @param flags the flags to control the advances, either {@link #DIRECTION_LTR} + * or {@link #DIRECTION_RTL} + * @param advances array to receive the advances, must have room for all advances, + * can be null if only total advance is needed + * @param advancesIndex the position in advances at which to put the + * advance corresponding to the character at start + * @param reserved int reserved value + * @return the total advance * * @hide */ - public float getTextRunAdvancesICU(String text, int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex) { + public float getTextRunAdvances(String text, int start, int end, int contextStart, + int contextEnd, int flags, float[] advances, int advancesIndex, int reserved) { if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) | (start - contextStart) | (contextEnd - end) @@ -1740,14 +1702,14 @@ public class Paint { } if (!mHasCompatScaling) { - return native_getTextRunAdvancesICU(mNativePaint, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + return native_getTextRunAdvances(mNativePaint, text, start, end, + contextStart, contextEnd, flags, advances, advancesIndex, reserved); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + contextStart, contextEnd, flags, advances, advancesIndex, reserved); setTextSize(oldSize); if (advances != null) { @@ -2017,17 +1979,10 @@ public class Paint { private static native float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, - int flags, float[] advances, int advancesIndex); + int flags, float[] advances, int advancesIndex, int reserved); private static native float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex); - - private static native float native_getTextRunAdvancesICU(int native_object, - char[] text, int index, int count, int contextIndex, int contextCount, - int flags, float[] advances, int advancesIndex); - private static native float native_getTextRunAdvancesICU(int native_object, - String text, int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex); + int flags, float[] advances, int advancesIndex, int reserved); private native int native_getTextRunCursor(int native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt); diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index c6c86b2..b752850 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -197,7 +197,7 @@ nContextSetSurface(JNIEnv *_env, jobject _this, RsContext con, jint width, jint window = (Surface*) android_Surface_getNativeWindow(_env, wnd).get(); } - rsContextSetSurface(con, width, height, window); + rsContextSetSurface(con, width, height, window, 1); } static void @@ -309,7 +309,11 @@ nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobj nameArray[ct] = _env->GetStringUTFChars(s, NULL); sizeArray[ct] = _env->GetStringUTFLength(s); } - jint id = (jint)rsElementCreate2(con, fieldCount, (RsElement *)ids, nameArray, sizeArray, (const uint32_t *)arraySizes); + jint id = (jint)rsElementCreate2(con, + (RsElement *)ids, fieldCount, + nameArray, fieldCount, + sizeArray, fieldCount, + (const uint32_t *)arraySizes, fieldCount); for (int ct=0; ct < fieldCount; ct++) { jstring s = (jstring)_env->GetObjectArrayElement(_names, ct); _env->ReleaseStringUTFChars(s, nameArray[ct]); @@ -579,7 +583,8 @@ nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintAr jint len = _env->GetArrayLength(data); LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); jint *ptr = _env->GetIntArrayElements(data, NULL); - rsAllocationRead(con, (RsAllocation)alloc, ptr); + jsize length = _env->GetArrayLength(data); + rsAllocationRead(con, (RsAllocation)alloc, ptr, length); _env->ReleaseIntArrayElements(data, ptr, 0); } @@ -589,7 +594,8 @@ nAllocationRead_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jshort jint len = _env->GetArrayLength(data); LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); jshort *ptr = _env->GetShortArrayElements(data, NULL); - rsAllocationRead(con, (RsAllocation)alloc, ptr); + jsize length = _env->GetArrayLength(data); + rsAllocationRead(con, (RsAllocation)alloc, ptr, length); _env->ReleaseShortArrayElements(data, ptr, 0); } @@ -599,7 +605,8 @@ nAllocationRead_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jbyteA jint len = _env->GetArrayLength(data); LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); jbyte *ptr = _env->GetByteArrayElements(data, NULL); - rsAllocationRead(con, (RsAllocation)alloc, ptr); + jsize length = _env->GetArrayLength(data); + rsAllocationRead(con, (RsAllocation)alloc, ptr, length); _env->ReleaseByteArrayElements(data, ptr, 0); } @@ -609,7 +616,8 @@ nAllocationRead_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jfloat jint len = _env->GetArrayLength(data); LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); jfloat *ptr = _env->GetFloatArrayElements(data, NULL); - rsAllocationRead(con, (RsAllocation)alloc, ptr); + jsize length = _env->GetArrayLength(data); + rsAllocationRead(con, (RsAllocation)alloc, ptr, length); _env->ReleaseFloatArrayElements(data, ptr, 0); } @@ -713,7 +721,9 @@ nFontCreateFromFile(JNIEnv *_env, jobject _this, RsContext con, jstring fileName, jfloat fontSize, jint dpi) { AutoJavaStringToUTF8 fileNameUTF(_env, fileName); - jint id = (jint)rsFontCreateFromFile(con, fileNameUTF.c_str(), fontSize, dpi); + jint id = (jint)rsFontCreateFromFile(con, + fileNameUTF.c_str(), fileNameUTF.length(), + fontSize, dpi); return id; } @@ -725,7 +735,9 @@ nFontCreateFromAssetStream(JNIEnv *_env, jobject _this, RsContext con, Asset* asset = reinterpret_cast<Asset*>(native_asset); AutoJavaStringToUTF8 nameUTF(_env, name); - jint id = (jint)rsFontCreateFromMemory(con, nameUTF.c_str(), fontSize, dpi, + jint id = (jint)rsFontCreateFromMemory(con, + nameUTF.c_str(), nameUTF.length(), + fontSize, dpi, asset->getBuffer(false), asset->getLength()); return id; } @@ -745,7 +757,9 @@ nFontCreateFromAsset(JNIEnv *_env, jobject _this, RsContext con, jobject _assetM return 0; } - jint id = (jint)rsFontCreateFromMemory(con, str.c_str(), fontSize, dpi, + jint id = (jint)rsFontCreateFromMemory(con, + str.c_str(), str.length(), + fontSize, dpi, asset->getBuffer(false), asset->getLength()); delete asset; return id; @@ -877,7 +891,9 @@ nScriptCCreate(JNIEnv *_env, jobject _this, RsContext con, //rsScriptCSetText(con, (const char *)script_ptr, length); - ret = (jint)rsScriptCCreate(con, resNameUTF.c_str(), cacheDirUTF.c_str(), + ret = (jint)rsScriptCCreate(con, + resNameUTF.c_str(), resNameUTF.length(), + cacheDirUTF.c_str(), cacheDirUTF.length(), (const char *)script_ptr, length); exit: diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index e272839..db81721 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -265,11 +265,12 @@ public: // (-1000,-1000) is the upper left point. (1000, 1000) is the lower right // point. The length and width of focus areas cannot be 0 or negative. // - // The fifth element is the weight. The weight ranges from 1 to 1000. - // The sum of the weights of all focus areas must be 1000. Focus areas - // can partially overlap and the driver will add the weights in the - // overlap region. But apps should not set two focus areas that have - // identical coordinates. + // The fifth element is the weight. Values for weight must range from 1 to + // 1000. The weight should be interpreted as a per-pixel weight - all + // pixels in the area have the specified weight. This means a small area + // with the same weight as a larger area will have less influence on the + // focusing than the larger area. Focus areas can partially overlap and the + // driver will add the weights in the overlap region. // // A special case of single focus area (0,0,0,0,0) means driver to decide // the focus area. For example, the driver may use more signals to decide @@ -327,10 +328,12 @@ public: // is the lower right point. The length and width of metering areas cannot // be 0 or negative. // - // The weight ranges from 1 to 1000. The sum of the weights of all metering - // areas must be 1000. Metering areas can partially overlap and the driver - // will add the weights in the overlap region. But apps should not set two - // metering areas that have identical coordinates. + // The fifth element is the weight. Values for weight must range from 1 to + // 1000. The weight should be interpreted as a per-pixel weight - all + // pixels in the area have the specified weight. This means a small area + // with the same weight as a larger area will have less influence on the + // metering than the larger area. Metering areas can partially overlap and + // the driver will add the weights in the overlap region. // // A special case of all-zero single metering area means driver to decide // the metering area. For example, the driver may use more signals to decide diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h index df5be32..765c039 100644 --- a/include/media/mediascanner.h +++ b/include/media/mediascanner.h @@ -55,7 +55,7 @@ private: status_t doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, - ExceptionCheck exceptionCheck, void *exceptionEnv); + bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv); MediaScanner(const MediaScanner &); MediaScanner &operator=(const MediaScanner &); @@ -72,10 +72,9 @@ public: void endFile(); virtual bool scanFile(const char* path, long long lastModified, - long long fileSize, bool isDirectory) = 0; + long long fileSize, bool isDirectory, bool noMedia) = 0; virtual bool handleStringTag(const char* name, const char* value) = 0; virtual bool setMimeType(const char* mimeType) = 0; - virtual bool addNoMediaFolder(const char* path) = 0; protected: void convertValues(uint32_t encoding); diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index c650141..6b69b8a 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -31,6 +31,8 @@ public class Credentials { public static final String INSTALL_ACTION = "android.credentials.INSTALL"; + public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK"; + /** Key prefix for CA certificates. */ public static final String CA_CERTIFICATE = "CACERT_"; @@ -69,7 +71,7 @@ public class Credentials { public void unlock(Context context) { try { - Intent intent = new Intent("com.android.credentials.UNLOCK"); + Intent intent = new Intent(UNLOCK_ACTION); context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOGTAG, e.toString()); diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl new file mode 100644 index 0000000..64f5a48 --- /dev/null +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security; + +import android.os.Bundle; + +/** + * Caller is required to ensure that {@link KeyStore#unlock + * KeyStore.unlock} was successful. + * + * @hide + */ +interface IKeyChainService { + byte[] getPrivate(String alias, String authToken); + byte[] getCertificate(String alias, String authToken); + byte[] getCaCertificate(String alias, String authToken); + String findIssuer(in Bundle cert); +} diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java new file mode 100644 index 0000000..69847bf --- /dev/null +++ b/keystore/java/android/security/KeyChain.java @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import dalvik.system.CloseGuard; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters; +import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; + +/** + * @hide + */ +public final class KeyChain { + + private static final String TAG = "KeyChain"; + + /** + * @hide Also used by KeyChainService implementation + */ + public static final String ACCOUNT_TYPE = "com.android.keychain"; + + /** + * @hide Also used by KeyChainService implementation + */ + // TODO This non-localized CA string to be removed when CAs moved out of keystore + public static final String CA_SUFFIX = " CA"; + + public static final String KEY_INTENT = "intent"; + + /** + * Intentionally not public to leave open the future possibility + * of hardware based keys. Callers should use {@link #toPrivateKey + * toPrivateKey} in order to convert a bundle to a {@code + * PrivateKey} + */ + private static final String KEY_PKCS8 = "pkcs8"; + + /** + * Intentionally not public to leave open the future possibility + * of hardware based certs. Callers should use {@link + * #toCertificate toCertificate} in order to convert a bundle to a + * {@code PrivateKey} + */ + private static final String KEY_X509 = "x509"; + + /** + * Returns an {@code Intent} for use with {@link + * android.app.Activity#startActivityForResult + * startActivityForResult}. The result will be returned via {@link + * android.app.Activity#onActivityResult onActivityResult} with + * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias + * in the returned {@code Intent}'s extra data with key {@link + * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}. + */ + public static Intent chooseAlias() { + return new Intent("com.android.keychain.CHOOSER"); + } + + /** + * Returns a new {@code KeyChain} instance. When the caller is + * done using the {@code KeyChain}, it must be closed with {@link + * #close()} or resource leaks will occur. + */ + public static KeyChain getInstance(Context context) throws InterruptedException { + return new KeyChain(context); + } + + private final AccountManager mAccountManager; + + private final Object mServiceLock = new Object(); + private IKeyChainService mService; + private boolean mIsBound; + + private Account mAccount; + + private ServiceConnection mServiceConnection = new ServiceConnection() { + @Override public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mServiceLock) { + mService = IKeyChainService.Stub.asInterface(service); + mServiceLock.notifyAll(); + + // Account is created if necessary during binding of the IKeyChainService + mAccount = mAccountManager.getAccountsByType(ACCOUNT_TYPE)[0]; + } + } + + @Override public void onServiceDisconnected(ComponentName name) { + synchronized (mServiceLock) { + mService = null; + } + } + }; + + private final Context mContext; + + private final CloseGuard mGuard = CloseGuard.get(); + + private KeyChain(Context context) throws InterruptedException { + if (context == null) { + throw new NullPointerException("context == null"); + } + mContext = context; + ensureNotOnMainThread(); + mAccountManager = AccountManager.get(mContext); + mIsBound = mContext.bindService(new Intent(IKeyChainService.class.getName()), + mServiceConnection, + Context.BIND_AUTO_CREATE); + if (!mIsBound) { + throw new AssertionError(); + } + synchronized (mServiceLock) { + // there is a race between binding on this thread and the + // callback on the main thread. wait until binding is done + // to be sure we have the mAccount initialized. + if (mService == null) { + mServiceLock.wait(); + } + } + mGuard.open("close"); + } + + /** + * {@code Bundle} will contain {@link #KEY_INTENT} if user needs + * to confirm application access to requested key. In the alias + * does not exist or there is an error, null is + * returned. Otherwise the {@code Bundle} contains information + * representing the private key which can be interpreted with + * {@link #toPrivateKey toPrivateKey}. + * + * non-null alias + */ + public Bundle getPrivate(String alias) { + return get(alias, Credentials.USER_PRIVATE_KEY); + } + + public Bundle getCertificate(String alias) { + return get(alias, Credentials.USER_CERTIFICATE); + } + + public Bundle getCaCertificate(String alias) { + return get(alias, Credentials.CA_CERTIFICATE); + } + + private Bundle get(String alias, String type) { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + ensureNotOnMainThread(); + + String authAlias = (type.equals(Credentials.CA_CERTIFICATE)) ? (alias + CA_SUFFIX) : alias; + AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount, + authAlias, + false, + null, + null); + Bundle bundle; + try { + bundle = future.getResult(); + } catch (OperationCanceledException e) { + throw new AssertionError(e); + } catch (IOException e) { + throw new AssertionError(e); + } catch (AuthenticatorException e) { + throw new AssertionError(e); + } + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + if (intent != null) { + Bundle result = new Bundle(); + // we don't want this Eclair compatability flag, + // it will prevent onActivityResult from being called + intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + result.putParcelable(KEY_INTENT, intent); + return result; + } + String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); + if (authToken == null) { + throw new AssertionError("Invalid authtoken"); + } + + byte[] bytes; + try { + if (type.equals(Credentials.USER_PRIVATE_KEY)) { + bytes = mService.getPrivate(alias, authToken); + } else if (type.equals(Credentials.USER_CERTIFICATE)) { + bytes = mService.getCertificate(alias, authToken); + } else if (type.equals(Credentials.CA_CERTIFICATE)) { + bytes = mService.getCaCertificate(alias, authToken); + } else { + throw new AssertionError(); + } + } catch (RemoteException e) { + throw new AssertionError(e); + } + if (bytes == null) { + throw new AssertionError(); + } + Bundle result = new Bundle(); + if (type.equals(Credentials.USER_PRIVATE_KEY)) { + result.putByteArray(KEY_PKCS8, bytes); + } else if (type.equals(Credentials.USER_CERTIFICATE)) { + result.putByteArray(KEY_X509, bytes); + } else if (type.equals(Credentials.CA_CERTIFICATE)) { + result.putByteArray(KEY_X509, bytes); + } else { + throw new AssertionError(); + } + return result; + } + + public static PrivateKey toPrivateKey(Bundle bundle) { + byte[] bytes = bundle.getByteArray(KEY_PKCS8); + if (bytes == null) { + throw new IllegalArgumentException("not a private key bundle"); + } + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeySpecException e) { + throw new AssertionError(e); + } + } + + public static Bundle fromPrivateKey(PrivateKey privateKey) { + Bundle bundle = new Bundle(); + String format = privateKey.getFormat(); + if (!format.equals("PKCS#8")) { + throw new IllegalArgumentException("Unsupported private key format " + format); + } + bundle.putByteArray(KEY_PKCS8, privateKey.getEncoded()); + return bundle; + } + + public static X509Certificate toCertificate(Bundle bundle) { + byte[] bytes = bundle.getByteArray(KEY_X509); + if (bytes == null) { + throw new IllegalArgumentException("not a certificate bundle"); + } + try { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes)); + return (X509Certificate) cert; + } catch (CertificateException e) { + throw new AssertionError(e); + } + } + + public static Bundle fromCertificate(Certificate cert) { + Bundle bundle = new Bundle(); + String type = cert.getType(); + if (!type.equals("X.509")) { + throw new IllegalArgumentException("Unsupported certificate type " + type); + } + try { + bundle.putByteArray(KEY_X509, cert.getEncoded()); + } catch (CertificateEncodingException e) { + throw new AssertionError(e); + } + return bundle; + } + + private void ensureNotOnMainThread() { + Looper looper = Looper.myLooper(); + if (looper != null && looper == mContext.getMainLooper()) { + throw new IllegalStateException( + "calling this from your main thread can lead to deadlock"); + } + } + + public Bundle findIssuer(X509Certificate cert) { + if (cert == null) { + throw new NullPointerException("cert == null"); + } + ensureNotOnMainThread(); + + // check and see if the issuer is already known to the default IndexedPKIXParameters + IndexedPKIXParameters index = SSLParametersImpl.getDefaultIndexedPKIXParameters(); + try { + TrustAnchor anchor = index.findTrustAnchor(cert); + if (anchor != null && anchor.getTrustedCert() != null) { + X509Certificate ca = anchor.getTrustedCert(); + return fromCertificate(ca); + } + } catch (CertPathValidatorException ignored) { + } + + // otherwise, it might be a user installed CA in the keystore + String alias; + try { + alias = mService.findIssuer(fromCertificate(cert)); + } catch (RemoteException e) { + throw new AssertionError(e); + } + if (alias == null) { + Log.w(TAG, "Lookup failed for issuer"); + return null; + } + + Bundle bundle = get(alias, Credentials.CA_CERTIFICATE); + Intent intent = bundle.getParcelable(KEY_INTENT); + if (intent != null) { + // permission still required + return bundle; + } + // add the found CA to the index for next time + X509Certificate ca = toCertificate(bundle); + index.index(new TrustAnchor(ca, null)); + return bundle; + } + + public void close() { + if (mIsBound) { + mContext.unbindService(mServiceConnection); + mIsBound = false; + mGuard.close(); + } + } + + protected void finalize() throws Throwable { + // note we don't close, we just warn. + // shouldn't be doing I/O in a finalizer, + // which the unbind would cause. + try { + if (mGuard != null) { + mGuard.warnIfOpen(); + } + } finally { + super.finalize(); + } + } +} diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 7f28959..75f5a5f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -897,7 +897,8 @@ void OpenGLRenderer::setupDrawColor(int color) { void OpenGLRenderer::setupDrawColor(int color, int alpha) { mColorA = alpha / 255.0f; - // BUG on this next line? a is alpha divided by 255 *twice* + // Second divide of a by 255 is an optimization, allowing us to simply multiply + // the rgb values by a instead of also dividing by 255 const float a = mColorA / 255.0f; mColorR = a * ((color >> 16) & 0xFF); mColorG = a * ((color >> 8) & 0xFF); @@ -908,6 +909,8 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) { void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { mColorA = alpha / 255.0f; + // Double-divide of a by 255 is an optimization, allowing us to simply multiply + // the rgb values by a instead of also dividing by 255 const float a = mColorA / 255.0f; mColorR = a * ((color >> 16) & 0xFF); mColorG = a * ((color >> 8) & 0xFF); diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index cead75b..18d98cb 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -57,7 +57,6 @@ namespace uirenderer { #define PROGRAM_KEY_COLOR_BLEND 0x80 #define PROGRAM_KEY_BITMAP_NPOT 0x100 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000 -#define PROGRAM_KEY_VERTEX_WIDTH 0x4000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 @@ -76,6 +75,8 @@ namespace uirenderer { #define PROGRAM_IS_POINT_SHIFT 36 +#define PROGRAM_HAS_WIDTH_SHIFT 37 + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// @@ -205,7 +206,6 @@ struct ProgramDescription { programid key() const { programid key = 0; if (hasTexture) key |= PROGRAM_KEY_TEXTURE; - if (hasWidth) key |= PROGRAM_KEY_VERTEX_WIDTH; if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; if (hasBitmap) { key |= PROGRAM_KEY_BITMAP; @@ -239,6 +239,7 @@ struct ProgramDescription { if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT; + if (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT; return key; } diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp index bd1fd0e..093e311 100644 --- a/libs/rs/driver/rsdRuntimeMath.cpp +++ b/libs/rs/driver/rsdRuntimeMath.cpp @@ -243,13 +243,15 @@ static void SC_MatrixTranspose_2x2(Matrix2x2 *m) { static float SC_randf(float max) { float r = (float)rand(); r *= max; - return r / RAND_MAX; + r /= RAND_MAX; + return r; } static float SC_randf2(float min, float max) { float r = (float)rand(); + r /= RAND_MAX; r = r * (max - min) + min; - return r / RAND_MAX; + return r; } static int SC_randi(int max) { diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index a7f473c..dac5cec 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -51,9 +51,8 @@ ContextDestroyWorker { } AssignName { - param void *obj + param RsObjectBase obj param const char *name - param size_t len } ObjDestroy { @@ -69,7 +68,6 @@ ElementCreate { } ElementCreate2 { - param size_t count param const RsElement * elements param const char ** names param const size_t * nameLengths @@ -80,7 +78,6 @@ ElementCreate2 { AllocationCopyToBitmap { param RsAllocation alloc param void * data - param size_t dataLen } @@ -90,7 +87,6 @@ Allocation1DData { param uint32_t lod param uint32_t count param const void *data - param uint32_t bytes handcodeApi togglePlay } @@ -101,7 +97,6 @@ Allocation1DElementData { param uint32_t lod param const void *data param uint32_t comp_offset - param uint32_t bytes handcodeApi togglePlay } @@ -115,7 +110,6 @@ Allocation2DData { param uint32_t w param uint32_t h param const void *data - param uint32_t bytes } Allocation2DElementData { @@ -126,7 +120,6 @@ Allocation2DElementData { param RsAllocationCubemapFace face param const void *data param uint32_t element_offset - param uint32_t bytes } AllocationGenerateMipmaps { @@ -184,7 +177,6 @@ ScriptBindAllocation { ScriptSetTimeZone { param RsScript s param const char * timeZone - param uint32_t length } @@ -197,7 +189,6 @@ ScriptInvokeV { param RsScript s param uint32_t slot param const void * data - param uint32_t dataLen handcodeApi togglePlay } @@ -236,7 +227,6 @@ ScriptSetVarV { param RsScript s param uint32_t slot param const void * data - param uint32_t dataLen handcodeApi togglePlay } @@ -246,7 +236,6 @@ ScriptCCreate { param const char * resName param const char * cacheDir param const char * text - param uint32_t length ret RsScript } @@ -294,17 +283,13 @@ ProgramBindSampler { ProgramFragmentCreate { param const char * shaderText - param uint32_t shaderLength param const uint32_t * params - param uint32_t paramLength ret RsProgramFragment } ProgramVertexCreate { param const char * shaderText - param uint32_t shaderLength param const uint32_t * params - param uint32_t paramLength ret RsProgramVertex } @@ -319,8 +304,7 @@ FontCreateFromMemory { param const char *name param float fontSize param uint32_t dpi - param const void *data - param uint32_t dataLen + param const void *data ret RsFont } @@ -346,14 +330,3 @@ MeshBindVertex { MeshInitVertexAttribs { param RsMesh mesh } - -AnimationCreate { - param const float *inValues - param const float *outValues - param uint32_t valueCount - param RsAnimationInterpolation interp - param RsAnimationEdge pre - param RsAnimationEdge post - ret RsAnimation - } - diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 6b37e03..a759004 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -780,7 +780,7 @@ void rsi_Allocation2DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t a->data(rsc, xoff, yoff, lod, face, w, h, data, sizeBytes); } -void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data) { +void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data, size_t data_length) { Allocation *a = static_cast<Allocation *>(va); a->read(data); } diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 20fa367..0ca892d 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -705,9 +705,9 @@ void rsi_ContextBindFont(Context *rsc, RsFont vfont) { rsc->setFont(font); } -void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len) { +void rsi_AssignName(Context *rsc, RsObjectBase obj, const char *name, uint32_t name_length) { ObjectBase *ob = static_cast<ObjectBase *>(obj); - rsc->assignName(ob, name, len); + rsc->assignName(ob, name, name_length); } void rsi_ObjDestroy(Context *rsc, void *optr) { @@ -724,7 +724,7 @@ void rsi_ContextResume(Context *rsc) { rsc->resume(); } -void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur) { +void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur, size_t sur_length) { rsc->setSurface(w, h, sur); } diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 477cb61..d5d5ca5 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -348,12 +348,15 @@ RsElement rsi_ElementCreate(Context *rsc, } RsElement rsi_ElementCreate2(Context *rsc, - size_t count, const RsElement * ein, + size_t ein_length, const char ** names, + size_t names_length, const size_t * nameLengths, - const uint32_t * arraySizes) { - const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes); + size_t nameLengths_length, + const uint32_t * arraySizes, + size_t arraySizes_length) { + const Element *e = Element::create(rsc, ein_length, (const Element **)ein, names, nameLengths, arraySizes); e->incUserRef(); return (RsElement)e; } diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index c30b857..b7b85b6 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -827,7 +827,9 @@ bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOri namespace android { namespace renderscript { -RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, float fontSize, uint32_t dpi) { +RsFont rsi_FontCreateFromFile(Context *rsc, + char const *name, size_t name_length, + float fontSize, uint32_t dpi) { Font *newFont = Font::create(rsc, name, fontSize, dpi); if (newFont) { newFont->incUserRef(); @@ -835,8 +837,11 @@ RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, float fontSize, ui return newFont; } -RsFont rsi_FontCreateFromMemory(Context *rsc, char const *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { - Font *newFont = Font::create(rsc, name, fontSize, dpi, data, dataLen); +RsFont rsi_FontCreateFromMemory(Context *rsc, + char const *name, size_t name_length, + float fontSize, uint32_t dpi, + const void *data, size_t data_length) { + Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length); if (newFont) { newFont->incUserRef(); } diff --git a/libs/rs/rsHandcode.h b/libs/rs/rsHandcode.h index 57da10a..da51d95 100644 --- a/libs/rs/rsHandcode.h +++ b/libs/rs/rsHandcode.h @@ -7,7 +7,7 @@ static inline void rsHCAPI_ContextFinish (RsContext rsc) { io->mToCore.commitSync(RS_CMD_ID_ContextFinish, size); } -static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes) { +static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t slot, const void * data, size_t sizeBytes) { ThreadIO *io = &((Context *)rsc)->mIO; uint32_t size = sizeof(RS_CMD_ScriptInvokeV); if (sizeBytes < DATA_SYNC_SIZE) { @@ -16,7 +16,7 @@ static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t s RS_CMD_ScriptInvokeV *cmd = static_cast<RS_CMD_ScriptInvokeV *>(io->mToCore.reserve(size)); cmd->s = va; cmd->slot = slot; - cmd->dataLen = sizeBytes; + cmd->data_length = sizeBytes; cmd->data = data; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); @@ -28,7 +28,7 @@ static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t s } -static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes) { +static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t slot, const void * data, size_t sizeBytes) { ThreadIO *io = &((Context *)rsc)->mIO; uint32_t size = sizeof(RS_CMD_ScriptSetVarV); if (sizeBytes < DATA_SYNC_SIZE) { @@ -37,7 +37,7 @@ static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t s RS_CMD_ScriptSetVarV *cmd = static_cast<RS_CMD_ScriptSetVarV *>(io->mToCore.reserve(size)); cmd->s = va; cmd->slot = slot; - cmd->dataLen = sizeBytes; + cmd->data_length = sizeBytes; cmd->data = data; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); @@ -49,7 +49,7 @@ static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t s } static inline void rsHCAPI_Allocation1DData (RsContext rsc, RsAllocation va, uint32_t xoff, uint32_t lod, - uint32_t count, const void * data, uint32_t sizeBytes) { + uint32_t count, const void * data, size_t sizeBytes) { ThreadIO *io = &((Context *)rsc)->mIO; uint32_t size = sizeof(RS_CMD_Allocation1DData); if (sizeBytes < DATA_SYNC_SIZE) { @@ -61,7 +61,7 @@ static inline void rsHCAPI_Allocation1DData (RsContext rsc, RsAllocation va, uin cmd->lod = lod; cmd->count = count; cmd->data = data; - cmd->bytes = sizeBytes; + cmd->data_length = sizeBytes; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); memcpy(cmd+1, data, sizeBytes); @@ -72,7 +72,7 @@ static inline void rsHCAPI_Allocation1DData (RsContext rsc, RsAllocation va, uin } static inline void rsHCAPI_Allocation1DElementData (RsContext rsc, RsAllocation va, uint32_t x, uint32_t lod, - const void * data, uint32_t comp_offset, uint32_t sizeBytes) { + const void * data, size_t sizeBytes, uint32_t comp_offset) { ThreadIO *io = &((Context *)rsc)->mIO; uint32_t size = sizeof(RS_CMD_Allocation1DElementData); if (sizeBytes < DATA_SYNC_SIZE) { @@ -84,7 +84,7 @@ static inline void rsHCAPI_Allocation1DElementData (RsContext rsc, RsAllocation cmd->lod = lod; cmd->data = data; cmd->comp_offset = comp_offset; - cmd->bytes = sizeBytes; + cmd->data_length = sizeBytes; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); memcpy(cmd+1, data, sizeBytes); diff --git a/libs/rs/rsMatrix4x4.cpp b/libs/rs/rsMatrix4x4.cpp index 2d90a98..f34af47 100644 --- a/libs/rs/rsMatrix4x4.cpp +++ b/libs/rs/rsMatrix4x4.cpp @@ -305,3 +305,10 @@ void Matrix4x4::vectorMultiply(float *out, const float *in) const { out[2] = (m[2] * in[0]) + (m[6] * in[1]) + (m[10] * in[2]) + m[14]; out[3] = (m[3] * in[0]) + (m[7] * in[1]) + (m[11] * in[2]) + m[15]; } + +void Matrix4x4::logv(const char *s) const { + LOGV("%s {%f, %f, %f, %f", s, m[0], m[4], m[8], m[12]); + LOGV("%s %f, %f, %f, %f", s, m[1], m[5], m[9], m[13]); + LOGV("%s %f, %f, %f, %f", s, m[2], m[6], m[10], m[14]); + LOGV("%s %f, %f, %f, %f}", s, m[3], m[7], m[11], m[15]); +} diff --git a/libs/rs/rsMatrix4x4.h b/libs/rs/rsMatrix4x4.h index abf34a3..d30184f 100644 --- a/libs/rs/rsMatrix4x4.h +++ b/libs/rs/rsMatrix4x4.h @@ -54,6 +54,7 @@ struct Matrix4x4 : public rs_matrix4x4 { bool inverseTranspose(); void transpose(); + void logv(const char *s) const; void multiply(const rs_matrix4x4 *rhs) { diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index c379b8b..6d0701d 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -267,12 +267,13 @@ namespace android { namespace renderscript { RsScript rsi_ScriptCCreate(Context *rsc, - const char *resName, const char *cacheDir, - const char *text, uint32_t len) + const char *resName, size_t resName_length, + const char *cacheDir, size_t cacheDir_length, + const char *text, uint32_t text_length) { ScriptC *s = new ScriptC(rsc); - if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, len)) { + if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, text_length)) { // Error during compile, destroy s and return null. delete s; return NULL; diff --git a/libs/rs/spec.l b/libs/rs/spec.l index 6a9010fe..c8af891 100644 --- a/libs/rs/spec.l +++ b/libs/rs/spec.l @@ -20,6 +20,19 @@ ID [a-zA-Z_][a-zA-Z0-9_]* int typeNextState; + void checkPointerType() { + VarType *lastType = currType; + if (lastType->ptrLevel) { + currType = &apis[apiCount].params[apis[apiCount].paramCount]; + currType->type = 4; + sprintf(currType->typeName, "%s", "size_t"); + if (lastType->name[0]) { + sprintf(currType->name, "%s_length", lastType->name); + } + apis[apiCount].paramCount++; + } + } + extern "C" int yylex(); %% @@ -145,6 +158,7 @@ ID [a-zA-Z_][a-zA-Z0-9_]* <api_entry_param>{ID} { memcpy(currType->name, yytext, yyleng); + checkPointerType(); BEGIN(api_entry2); } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index a7ac9ed..80cc94e 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -422,9 +422,10 @@ public class MediaScanner private long mFileSize; private String mWriter; private int mCompilation; + private boolean mNoMedia; // flag to suppress file from appearing in media tables public FileCacheEntry beginFile(String path, String mimeType, long lastModified, - long fileSize, boolean isDirectory) { + long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; mFileType = 0; mFileSize = fileSize; @@ -435,28 +436,31 @@ public class MediaScanner // to avoid memory allocation int lastSlash = path.lastIndexOf('/'); if (lastSlash >= 0 && lastSlash + 2 < path.length()) { - // ignore those ._* files created by MacOS - if (path.regionMatches(lastSlash + 1, "._", 0, 2)) { - return null; - } - - // ignore album art files created by Windows Media Player: - // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg - // and AlbumArt_{...}_Small.jpg - if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) { - if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) || - path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) { - return null; + if (!noMedia) { + // ignore those ._* files created by MacOS + if (path.regionMatches(lastSlash + 1, "._", 0, 2)) { + noMedia = true; } - int length = path.length() - lastSlash - 1; - if ((length == 17 && path.regionMatches( - true, lastSlash + 1, "AlbumArtSmall", 0, 13)) || - (length == 10 - && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) { - return null; + + // ignore album art files created by Windows Media Player: + // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg + // and AlbumArt_{...}_Small.jpg + if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) { + if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) || + path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) { + noMedia = true; + } + int length = path.length() - lastSlash - 1; + if ((length == 17 && path.regionMatches( + true, lastSlash + 1, "AlbumArtSmall", 0, 13)) || + (length == 10 + && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) { + noMedia = true; + } } } } + mNoMedia = noMedia; // try mimeType first, if it is specified if (mimeType != null) { @@ -523,36 +527,41 @@ public class MediaScanner return entry; } - public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory) { + public void scanFile(String path, long lastModified, long fileSize, + boolean isDirectory, boolean noMedia) { // This is the callback funtion from native codes. // Log.v(TAG, "scanFile: "+path); - doScanFile(path, null, lastModified, fileSize, isDirectory, false); + doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia); } public Uri doScanFile(String path, String mimeType, long lastModified, - long fileSize, boolean isDirectory, boolean scanAlways) { + long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) { Uri result = null; // long t1 = System.currentTimeMillis(); try { FileCacheEntry entry = beginFile(path, mimeType, lastModified, - fileSize, isDirectory); + fileSize, isDirectory, noMedia); // rescan for metadata if file was modified since last scan if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { - String lowpath = path.toLowerCase(); - boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0); - boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0); - boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0); - boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0); - boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) || - (!ringtones && !notifications && !alarms && !podcasts); - - // we only extract metadata for audio and video files - if (MediaFile.isAudioFileType(mFileType) - || MediaFile.isVideoFileType(mFileType)) { - processFile(path, mimeType, this); - } + if (noMedia) { + result = endFile(entry, false, false, false, false, false); + } else { + String lowpath = path.toLowerCase(); + boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0); + boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0); + boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0); + boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0); + boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) || + (!ringtones && !notifications && !alarms && !podcasts); + + // we only extract metadata for audio and video files + if (MediaFile.isAudioFileType(mFileType) + || MediaFile.isVideoFileType(mFileType)) { + processFile(path, mimeType, this); + } - result = endFile(entry, ringtones, notifications, alarms, music, podcasts); + result = endFile(entry, ringtones, notifications, alarms, music, podcasts); + } } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); @@ -689,27 +698,31 @@ public class MediaScanner map.put(MediaStore.MediaColumns.SIZE, mFileSize); map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType); - if (MediaFile.isVideoFileType(mFileType)) { - map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); - map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING)); - map.put(Video.Media.DURATION, mDuration); - // FIXME - add RESOLUTION - } else if (MediaFile.isImageFileType(mFileType)) { - // FIXME - add DESCRIPTION - } else if (MediaFile.isAudioFileType(mFileType)) { - map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? - mArtist : MediaStore.UNKNOWN_STRING); - map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && - mAlbumArtist.length() > 0) ? mAlbumArtist : null); - map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ? - mAlbum : MediaStore.UNKNOWN_STRING); - map.put(Audio.Media.COMPOSER, mComposer); - if (mYear != 0) { - map.put(Audio.Media.YEAR, mYear); + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 + ? mArtist : MediaStore.UNKNOWN_STRING)); + map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 + ? mAlbum : MediaStore.UNKNOWN_STRING)); + map.put(Video.Media.DURATION, mDuration); + // FIXME - add RESOLUTION + } else if (MediaFile.isImageFileType(mFileType)) { + // FIXME - add DESCRIPTION + } else if (MediaFile.isAudioFileType(mFileType)) { + map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? + mArtist : MediaStore.UNKNOWN_STRING); + map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && + mAlbumArtist.length() > 0) ? mAlbumArtist : null); + map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ? + mAlbum : MediaStore.UNKNOWN_STRING); + map.put(Audio.Media.COMPOSER, mComposer); + if (mYear != 0) { + map.put(Audio.Media.YEAR, mYear); + } + map.put(Audio.Media.TRACK, mTrack); + map.put(Audio.Media.DURATION, mDuration); + map.put(Audio.Media.COMPILATION, mCompilation); } - map.put(Audio.Media.TRACK, mTrack); - map.put(Audio.Media.DURATION, mDuration); - map.put(Audio.Media.COMPILATION, mCompilation); } return map; } @@ -719,7 +732,7 @@ public class MediaScanner throws RemoteException { // update database - // use album artist if artist is missing + // use album artist if artist is missing if (mArtist == null || mArtist.length() == 0) { mArtist = mAlbumArtist; } @@ -761,7 +774,7 @@ public class MediaScanner values.put(Audio.Media.IS_ALARM, alarms); values.put(Audio.Media.IS_MUSIC, music); values.put(Audio.Media.IS_PODCAST, podcasts); - } else if (mFileType == MediaFile.FILE_TYPE_JPEG) { + } else if (mFileType == MediaFile.FILE_TYPE_JPEG && !mNoMedia) { ExifInterface exif = null; try { exif = new ExifInterface(entry.mPath); @@ -814,12 +827,14 @@ public class MediaScanner } Uri tableUri = mFilesUri; - if (MediaFile.isVideoFileType(mFileType)) { - tableUri = mVideoUri; - } else if (MediaFile.isImageFileType(mFileType)) { - tableUri = mImagesUri; - } else if (MediaFile.isAudioFileType(mFileType)) { - tableUri = mAudioUri; + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + tableUri = mVideoUri; + } else if (MediaFile.isImageFileType(mFileType)) { + tableUri = mImagesUri; + } else if (MediaFile.isAudioFileType(mFileType)) { + tableUri = mAudioUri; + } } Uri result = null; if (rowId == 0) { @@ -930,25 +945,6 @@ public class MediaScanner } } - public void addNoMediaFolder(String path) { - ContentValues values = new ContentValues(); - values.put(MediaStore.Images.ImageColumns.DATA, ""); - String [] pathSpec = new String[] {path + '%'}; - try { - // These tables have DELETE_FILE triggers that delete the file from the - // sd card when deleting the database entry. We don't want to do this in - // this case, since it would cause those files to be removed if a .nomedia - // file was added after the fact, when in that case we only want the database - // entries to be removed. - mMediaProvider.update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values, - MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec); - mMediaProvider.update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values, - MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec); - } catch (RemoteException e) { - throw new RuntimeException(); - } - } - private int getFileTypeFromDrm(String path) { if (!isDrmEnabled()) { return 0; @@ -1228,13 +1224,37 @@ public class MediaScanner // always scan the file, so we can return the content://media Uri for existing files return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), - false, true); + false, true, false); } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); return null; } } + public static boolean isNoMediaPath(String path) { + if (path == null) return false; + + // return true if file or any parent directory has name starting with a dot + if (path.indexOf("/.") >= 0) return true; + + // now check to see if any parent directories have a ".nomedia" file + // start from 1 so we don't bother checking in the root directory + int offset = 1; + while (offset >= 0) { + int slashIndex = path.indexOf('/', offset); + if (slashIndex > offset) { + slashIndex++; // move past slash + File file = new File(path.substring(0, slashIndex) + ".nomedia"); + if (file.exists()) { + // we have a .nomedia in one of the parent directories + return true; + } + } + offset = slashIndex; + } + return false; + } + public void scanMtpFile(String path, String volumeName, int objectHandle, int format) { initialize(volumeName); MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); @@ -1279,7 +1299,7 @@ public class MediaScanner // always scan the file, so we can return the content://media Uri for existing files mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), - (format == MtpConstants.FORMAT_ASSOCIATION), true); + (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path)); } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java index ac326ef..b326671 100644 --- a/media/java/android/media/MediaScannerClient.java +++ b/media/java/android/media/MediaScannerClient.java @@ -21,9 +21,8 @@ package android.media; */ public interface MediaScannerClient { - public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory); - - public void addNoMediaFolder(String path); + public void scanFile(String path, long lastModified, long fileSize, + boolean isDirectory, boolean noMedia); /** * Called by native code to return metadata extracted from media files. diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index a3dd136..9151799 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -67,7 +67,7 @@ public: mScanFileMethodID = env->GetMethodID( mediaScannerClientInterface, "scanFile", - "(Ljava/lang/String;JJZ)V"); + "(Ljava/lang/String;JJZZ)V"); mHandleStringTagMethodID = env->GetMethodID( mediaScannerClientInterface, @@ -78,11 +78,6 @@ public: mediaScannerClientInterface, "setMimeType", "(Ljava/lang/String;)V"); - - mAddNoMediaFolderMethodID = env->GetMethodID( - mediaScannerClientInterface, - "addNoMediaFolder", - "(Ljava/lang/String;)V"); } } @@ -95,7 +90,7 @@ public: // Returns true if it succeeded, false if an exception occured // in the Java code virtual bool scanFile(const char* path, long long lastModified, - long long fileSize, bool isDirectory) + long long fileSize, bool isDirectory, bool noMedia) { LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)", path, lastModified, fileSize, isDirectory); @@ -106,7 +101,7 @@ public: } mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, - fileSize, isDirectory); + fileSize, isDirectory, noMedia); mEnv->DeleteLocalRef(pathStr); return (!mEnv->ExceptionCheck()); @@ -149,30 +144,12 @@ public: return (!mEnv->ExceptionCheck()); } - // Returns true if it succeeded, false if an exception occured - // in the Java code - virtual bool addNoMediaFolder(const char* path) - { - LOGV("addNoMediaFolder: path(%s)", path); - jstring pathStr; - if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { - return false; - } - - mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr); - - mEnv->DeleteLocalRef(pathStr); - return (!mEnv->ExceptionCheck()); - } - - private: JNIEnv *mEnv; jobject mClient; jmethodID mScanFileMethodID; jmethodID mHandleStringTagMethodID; jmethodID mSetMimeTypeMethodID; - jmethodID mAddNoMediaFolderMethodID; }; diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 06708da..585cd30 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -29,6 +29,7 @@ #include "MtpDatabase.h" #include "MtpDataPacket.h" +#include "MtpObjectInfo.h" #include "MtpProperty.h" #include "MtpStringBuffer.h" #include "MtpUtils.h" @@ -138,7 +139,7 @@ public: MtpDataPacket& packet); virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, - MtpDataPacket& packet); + MtpObjectInfo& info); virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, @@ -744,7 +745,7 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, } MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, - MtpDataPacket& packet) { + MtpObjectInfo& info) { char date[20]; JNIEnv* env = AndroidRuntime::getJNIEnv(); @@ -754,46 +755,27 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, return MTP_RESPONSE_INVALID_OBJECT_HANDLE; jint* intValues = env->GetIntArrayElements(mIntBuffer, 0); - MtpStorageID storageID = intValues[0]; - MtpObjectFormat format = intValues[1]; - MtpObjectHandle parent = intValues[2]; + info.mStorageID = intValues[0]; + info.mFormat = intValues[1]; + info.mParent = intValues[2]; env->ReleaseIntArrayElements(mIntBuffer, intValues, 0); jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); uint64_t size = longValues[0]; - uint64_t modified = longValues[1]; + info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size); + info.mDateModified = longValues[1]; env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); -// int associationType = (format == MTP_FORMAT_ASSOCIATION ? +// info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ? // MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : // MTP_ASSOCIATION_TYPE_UNDEFINED); - int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED; - - packet.putUInt32(storageID); - packet.putUInt16(format); - packet.putUInt16(0); // protection status - packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size)); - packet.putUInt16(0); // thumb format - packet.putUInt32(0); // thumb compressed size - packet.putUInt32(0); // thumb pix width - packet.putUInt32(0); // thumb pix height - packet.putUInt32(0); // image pix width - packet.putUInt32(0); // image pix height - packet.putUInt32(0); // image bit depth - packet.putUInt32(parent); - packet.putUInt16(associationType); - packet.putUInt32(0); // association desc - packet.putUInt32(0); // sequence number + info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED; jchar* str = env->GetCharArrayElements(mStringBuffer, 0); - packet.putString(str); // file name + MtpString temp(str); + info.mName = strdup((const char *)temp); env->ReleaseCharArrayElements(mStringBuffer, str, 0); - packet.putEmptyString(); - formatDateTime(modified, date, sizeof(date)); - packet.putString(date); // date modified - packet.putEmptyString(); // keywords - checkAndClearExceptionFromCallback(env, __FUNCTION__); return MTP_RESPONSE_OK; } diff --git a/media/jni/mediaeditor/VideoBrowserInternal.h b/media/jni/mediaeditor/VideoBrowserInternal.h index ed63129..3cfb6b9 100755 --- a/media/jni/mediaeditor/VideoBrowserInternal.h +++ b/media/jni/mediaeditor/VideoBrowserInternal.h @@ -67,7 +67,7 @@ { \ if (M4OSA_NULL != p) \ { \ - M4OSA_free((M4OSA_MemAddr32)p) ; \ + free(p) ; \ p = M4OSA_NULL ; \ } \ } diff --git a/media/jni/mediaeditor/VideoBrowserMain.c b/media/jni/mediaeditor/VideoBrowserMain.c index cddab60..6ef688d 100755 --- a/media/jni/mediaeditor/VideoBrowserMain.c +++ b/media/jni/mediaeditor/VideoBrowserMain.c @@ -73,7 +73,7 @@ M4OSA_ERR videoBrowserSetWindow( if (pC->m_frameColorType == VideoBrowser_kGB565) { pC->m_outputPlane[0].u_stride = pC->m_outputPlane[0].u_width << 1; - pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)M4OSA_malloc( + pC->m_outputPlane[0].pac_data = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc( pC->m_outputPlane[0].u_stride * pC->m_outputPlane[0].u_height, VIDEOBROWSER, (M4OSA_Char *)"output plane"); @@ -154,7 +154,7 @@ M4OSA_ERR videoBrowserCreate( CHECK_PTR(videoBrowserCreate, pURL, err, M4ERR_PARAMETER); /*--- Create context ---*/ - pContext = (VideoBrowserContext*)M4OSA_malloc( + pContext = (VideoBrowserContext*)M4OSA_32bitAlignedMalloc( sizeof(VideoBrowserContext), VIDEOBROWSER, (M4OSA_Char*)"Video browser context"); diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp index ea73e11..5696433 100755 --- a/media/jni/mediaeditor/VideoEditorClasses.cpp +++ b/media/jni/mediaeditor/VideoEditorClasses.cpp @@ -28,7 +28,6 @@ extern "C" { #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> #include <M4OSA_Debug.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4VSS3GPP_API.h> #include <M4xVSS_API.h> @@ -2465,7 +2464,7 @@ videoEditClasses_getEffectSettings( if (pSettings->xVSS.pFramingFilePath != M4OSA_NULL) { pSettings->xVSS.pFramingBuffer = - (M4VIFI_ImagePlane *)M4OSA_malloc(sizeof(M4VIFI_ImagePlane), + (M4VIFI_ImagePlane *)M4OSA_32bitAlignedMalloc(sizeof(M4VIFI_ImagePlane), 0x00,(M4OSA_Char *)"framing buffer"); } diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index b792295..c95a0c2 100755 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -41,15 +41,12 @@ extern "C" { #include <M4OSA_FileCommon.h> #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> -#include <M4OSA_FileExtra.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4xVSS_API.h> #include <M4VSS3GPP_ErrorCodes.h> #include <M4MCS_API.h> #include <M4MCS_ErrorCodes.h> -#include <M4MDP_API.h> #include <M4READER_Common.h> #include <M4WRITER_common.h> }; @@ -416,7 +413,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, LOGV("MSG_TYPE_OVERLAY_UPDATE"); if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -424,7 +421,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, strlen((const char*)pContext->pEditSettings->Effects[overlayEffectIndex].xVSS.pFramingFilePath); pContext->mOverlayFileName = - (char*)M4OSA_malloc(overlayFileNameLen+1, + (char*)M4OSA_32bitAlignedMalloc(overlayFileNameLen+1, M4VS, (M4OSA_Char*)"videoEdito JNI overlayFile"); if (pContext->mOverlayFileName != NULL) { strncpy (pContext->mOverlayFileName, @@ -454,7 +451,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, case MSG_TYPE_OVERLAY_CLEAR: isSendProgress = false; if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -504,7 +501,7 @@ static int videoEditor_stopPreview(JNIEnv* pEnv, lastProgressTimeMs = pContext->mPreviewController->stopPreview(); if (pContext->mOverlayFileName != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); + free(pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } @@ -750,7 +747,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, framesizeYuv = width * height * 1.5; - pixelArray = (M4VIFI_UInt8 *)M4OSA_malloc(framesizeYuv, M4VS, + pixelArray = (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(framesizeYuv, M4VS, (M4OSA_Char*)"videoEditor pixelArray"); if (pixelArray == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", @@ -768,7 +765,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, ClipProperties.uiVideoHeight, &tnTimeMs); if (result != M4NO_ERROR) { - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); ThumbnailClose(tnContext); return -1; } @@ -792,12 +789,12 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, /** * Allocate output YUV planes */ - yuvPlane = (M4VIFI_ImagePlane*)M4OSA_malloc(3*sizeof(M4VIFI_ImagePlane), M4VS, + yuvPlane = (M4VIFI_ImagePlane*)M4OSA_32bitAlignedMalloc(3*sizeof(M4VIFI_ImagePlane), M4VS, (M4OSA_Char*)"videoEditor_renderPreviewFrame Output plane YUV"); if (yuvPlane == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_renderPreviewFrame() malloc error for yuv plane"); - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); pMessage = videoEditJava_getErrorName(M4ERR_ALLOC); jniThrowException(pEnv, "java/lang/RuntimeException", pMessage); return -1; @@ -902,10 +899,10 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType ==\ /*M4VIDEOEDITING_kFileType_JPG */ M4VIDEOEDITING_kFileType_ARGB8888) { - M4OSA_free((M4OSA_MemAddr32)frameStr.pBuffer); + free(frameStr.pBuffer); } else { - M4OSA_free((M4OSA_MemAddr32)yuvPlane[0].pac_data); - M4OSA_free((M4OSA_MemAddr32)yuvPlane); + free(yuvPlane[0].pac_data); + free(yuvPlane); } return tnTimeMs; } @@ -981,7 +978,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, framesizeYuv = ((frameWidth)*(frameHeight)*1.5); - pixelArray = (M4VIFI_UInt8 *)M4OSA_malloc(framesizeYuv, M4VS,\ + pixelArray = (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(framesizeYuv, M4VS,\ (M4OSA_Char*)"videoEditor pixelArray"); if (pixelArray == M4OSA_NULL) { VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", @@ -996,7 +993,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, frameWidth, frameHeight, &timeMs); if (result != M4NO_ERROR) { - M4OSA_free((M4OSA_MemAddr32)pixelArray); + free(pixelArray); ThumbnailClose(tnContext); return fromMs; } @@ -1066,7 +1063,7 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, (M4NO_ERROR != result), result); /* free the pixelArray and yuvPlane[0].pac_data */ - M4OSA_free((M4OSA_MemAddr32)yuvPlane[0].pac_data); + free(yuvPlane[0].pac_data); ThumbnailClose(tnContext); @@ -1148,7 +1145,7 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "M4MCS_init()"); - pOutputParams = (M4MCS_OutputParams *)M4OSA_malloc( + pOutputParams = (M4MCS_OutputParams *)M4OSA_32bitAlignedMalloc( sizeof(M4MCS_OutputParams),0x00, (M4OSA_Char *)"M4MCS_OutputParams"); videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv, @@ -1158,14 +1155,14 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, return M4ERR_ALLOC; } - pEncodingParams = (M4MCS_EncodingParams *)M4OSA_malloc( + pEncodingParams = (M4MCS_EncodingParams *)M4OSA_32bitAlignedMalloc( sizeof(M4MCS_EncodingParams),0x00, (M4OSA_Char *)"M4MCS_EncodingParams"); videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv, (M4OSA_NULL == pEncodingParams), "not initialized"); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return M4ERR_ALLOC; } @@ -1179,15 +1176,15 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, (M4OSA_NULL == mcsContext), "not initialized"); if(needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } // generate the path for temp 3gp output file - pTemp3gpFilePath = (M4OSA_Char*) M4OSA_malloc ( + pTemp3gpFilePath = (M4OSA_Char*) M4OSA_32bitAlignedMalloc ( (strlen((const char*)pContext->initParams.pTempPath) + strlen((const char*)TEMP_MCS_OUT_FILE_PATH)) + 1 /* for null termination */ , 0x0, (M4OSA_Char*)"Malloc for temp 3gp file"); @@ -1204,9 +1201,9 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, } else { M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return M4ERR_ALLOC; } @@ -1231,12 +1228,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if(needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1281,12 +1278,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1311,12 +1308,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1327,12 +1324,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4NO_ERROR != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1379,12 +1376,12 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, (M4MCS_WAR_TRANSCODING_DONE != result), result); if (needToBeLoaded == false) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); pTemp3gpFilePath = M4OSA_NULL; M4MCS_abort(mcsContext); - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); pOutputParams = M4OSA_NULL; - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); pEncodingParams = M4OSA_NULL; return result; } @@ -1399,13 +1396,13 @@ M4OSA_ERR videoEditor_generateAudio(JNIEnv* pEnv,ManualEditContext* pContext, VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_generateAudio() EXIT "); if (pTemp3gpFilePath != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pTemp3gpFilePath); + free(pTemp3gpFilePath); } if (pOutputParams != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pOutputParams); + free(pOutputParams); } if(pEncodingParams != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pEncodingParams); + free(pEncodingParams); } return result; } @@ -1420,7 +1417,7 @@ static int removeAlphafromRGB8888 ( LOGV("removeAlphafromRGB8888: width %d", pFramingCtx->width); - M4OSA_UInt8 *pTmpData = (M4OSA_UInt8*) M4OSA_malloc(frameSize_argb, M4VS, (M4OSA_Char*)"Image argb data"); + M4OSA_UInt8 *pTmpData = (M4OSA_UInt8*) M4OSA_32bitAlignedMalloc(frameSize_argb, M4VS, (M4OSA_Char*)"Image argb data"); if (pTmpData == M4OSA_NULL) { LOGE("Failed to allocate memory for Image clip"); return M4ERR_ALLOC; @@ -1433,7 +1430,7 @@ static int removeAlphafromRGB8888 ( if ((lerr != M4NO_ERROR) || (lImageFileFp == M4OSA_NULL)) { LOGE("removeAlphafromRGB8888: Can not open the file "); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4ERR_FILE_NOT_FOUND; } @@ -1443,22 +1440,22 @@ static int removeAlphafromRGB8888 ( { LOGE("removeAlphafromRGB8888: can not read the data "); M4OSA_fileReadClose(lImageFileFp); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return lerr; } M4OSA_fileReadClose(lImageFileFp); M4OSA_UInt32 frameSize = (pFramingCtx->width * pFramingCtx->height * 3); //Size of RGB 888 data. - pFramingCtx->FramingRgb = (M4VIFI_ImagePlane*)M4OSA_malloc( + pFramingCtx->FramingRgb = (M4VIFI_ImagePlane*)M4OSA_32bitAlignedMalloc( sizeof(M4VIFI_ImagePlane), M4VS, (M4OSA_Char*)"Image clip RGB888 data"); - pFramingCtx->FramingRgb->pac_data = (M4VIFI_UInt8*)M4OSA_malloc( + pFramingCtx->FramingRgb->pac_data = (M4VIFI_UInt8*)M4OSA_32bitAlignedMalloc( frameSize, M4VS, (M4OSA_Char*)"Image clip RGB888 data"); if (pFramingCtx->FramingRgb == M4OSA_NULL) { LOGE("Failed to allocate memory for Image clip"); - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4ERR_ALLOC; } @@ -1468,7 +1465,7 @@ static int removeAlphafromRGB8888 ( pFramingCtx->FramingRgb->pac_data[j] = pTmpData[i]; j++; } - M4OSA_free((M4OSA_MemAddr32)pTmpData); + free(pTmpData); return M4NO_ERROR; } @@ -1566,7 +1563,7 @@ videoEditor_populateSettings( { if (pContext->pEditSettings->Effects[j].xVSS.pFramingFilePath != M4OSA_NULL) { if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[j].xVSS.pFramingBuffer); pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer = M4OSA_NULL; } @@ -1613,7 +1610,7 @@ videoEditor_populateSettings( if (pContext->pEditSettings->nbEffects > 0) { pOverlayIndex - = (int*) M4OSA_malloc(pContext->pEditSettings->nbEffects * sizeof(int), 0, + = (int*) M4OSA_32bitAlignedMalloc(pContext->pEditSettings->nbEffects * sizeof(int), 0, (M4OSA_Char*)"pOverlayIndex"); if (pOverlayIndex == M4OSA_NULL) { videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, @@ -1633,7 +1630,7 @@ videoEditor_populateSettings( M4xVSS_FramingStruct *aFramingCtx = M4OSA_NULL; aFramingCtx - = (M4xVSS_FramingStruct*)M4OSA_malloc(sizeof(M4xVSS_FramingStruct), M4VS, + = (M4xVSS_FramingStruct*)M4OSA_32bitAlignedMalloc(sizeof(M4xVSS_FramingStruct), M4VS, (M4OSA_Char*)"M4xVSS_internalDecodeGIF: Context of the framing effect"); if (aFramingCtx == M4OSA_NULL) { @@ -1671,7 +1668,7 @@ videoEditor_populateSettings( if (needToBeLoaded == false) { M4OSA_TRACE1_1("M4xVSS_internalConvertARGB888toYUV420_FrammingEffect returned 0x%x", result); if (aFramingCtx != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx); + free(aFramingCtx); aFramingCtx = M4OSA_NULL; } goto videoEditor_populateSettings_cleanup; @@ -1699,7 +1696,7 @@ videoEditor_populateSettings( //for RGB565 pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->u_topleft = 0; pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->pac_data = - (M4VIFI_UInt8 *)M4OSA_malloc(width*height*2, + (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(width*height*2, 0x00,(M4OSA_Char *)"pac_data buffer"); if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer->pac_data == M4OSA_NULL) { @@ -1720,31 +1717,31 @@ videoEditor_populateSettings( if (aFramingCtx->FramingYuv != M4OSA_NULL ) { if (aFramingCtx->FramingYuv[0].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[0].pac_data); + free(aFramingCtx->FramingYuv[0].pac_data); aFramingCtx->FramingYuv[0].pac_data = M4OSA_NULL; } if (aFramingCtx->FramingYuv[1].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[1].pac_data); + free(aFramingCtx->FramingYuv[1].pac_data); aFramingCtx->FramingYuv[1].pac_data = M4OSA_NULL; } if (aFramingCtx->FramingYuv[2].pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv[2].pac_data); + free(aFramingCtx->FramingYuv[2].pac_data); aFramingCtx->FramingYuv[2].pac_data = M4OSA_NULL; } - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingYuv); + free(aFramingCtx->FramingYuv); aFramingCtx->FramingYuv = M4OSA_NULL; } if (aFramingCtx->FramingRgb->pac_data != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingRgb->pac_data); + free(aFramingCtx->FramingRgb->pac_data); aFramingCtx->FramingRgb->pac_data = M4OSA_NULL; } if (aFramingCtx->FramingRgb != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx->FramingRgb); + free(aFramingCtx->FramingRgb); aFramingCtx->FramingRgb = M4OSA_NULL; } if (aFramingCtx != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)aFramingCtx); + free(aFramingCtx); aFramingCtx = M4OSA_NULL; } nbOverlays++; @@ -1775,11 +1772,11 @@ videoEditor_populateSettings( /* free previous allocations , if any */ if (pContext->mAudioSettings != M4OSA_NULL) { if (pContext->mAudioSettings->pFile != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + free(pContext->mAudioSettings->pFile); pContext->mAudioSettings->pFile = M4OSA_NULL; } if (pContext->mAudioSettings->pPCMFilePath != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + free(pContext->mAudioSettings->pPCMFilePath); pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; } } @@ -1850,7 +1847,7 @@ videoEditor_populateSettings( strPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPath, M4OSA_NULL); if (pTempChar != NULL) { - pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_malloc( + pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_32bitAlignedMalloc( (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, (M4OSA_Char*)"strPath allocation " ); if (pContext->mAudioSettings->pFile != M4OSA_NULL) { @@ -1875,7 +1872,7 @@ videoEditor_populateSettings( strPCMPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPCMPath, M4OSA_NULL); if (pTempChar != NULL) { - pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_malloc( + pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_32bitAlignedMalloc( (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, (M4OSA_Char*)"strPCMPath allocation " ); if (pContext->mAudioSettings->pPCMFilePath != M4OSA_NULL) { @@ -1986,7 +1983,7 @@ videoEditor_populateSettings_cleanup: { if (pContext->pEditSettings->Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data != \ M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data); pContext->pEditSettings->\ Effects[pOverlayIndex[j]].xVSS.pFramingBuffer->pac_data = M4OSA_NULL; @@ -1999,7 +1996,7 @@ videoEditor_populateSettings_cleanup: { if (pContext->pEditSettings->Effects[j].xVSS.pFramingFilePath != M4OSA_NULL) { if (pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->pEditSettings->\ + free(pContext->pEditSettings->\ Effects[j].xVSS.pFramingBuffer); pContext->pEditSettings->Effects[j].xVSS.pFramingBuffer = M4OSA_NULL; } @@ -2009,7 +2006,7 @@ videoEditor_populateSettings_cleanup: if (pOverlayIndex != M4OSA_NULL) { - M4OSA_free((M4OSA_MemAddr32)pOverlayIndex); + free(pOverlayIndex); pOverlayIndex = M4OSA_NULL; } return; @@ -2498,7 +2495,7 @@ videoEditor_init( (M4OSA_Char *)videoEditJava_getString(&initialized, pEnv, tempPath, NULL, M4OSA_NULL); pContext->initParams.pTempPath = (M4OSA_Char *) - M4OSA_malloc(strlen((const char *)tmpString) + 1, 0x0, + M4OSA_32bitAlignedMalloc(strlen((const char *)tmpString) + 1, 0x0, (M4OSA_Char *)"tempPath"); //initialize the first char. so that strcat works. M4OSA_Char *ptmpChar = (M4OSA_Char*)pContext->initParams.pTempPath; @@ -2506,7 +2503,7 @@ videoEditor_init( strncat((char *)pContext->initParams.pTempPath, (const char *)tmpString, (size_t)strlen((const char *)tmpString)); strncat((char *)pContext->initParams.pTempPath, (const char *)"/", (size_t)1); - M4OSA_free((M4OSA_MemAddr32)tmpString); + free(tmpString); pContext->mIsUpdateOverlay = false; pContext->mOverlayFileName = NULL; } @@ -2564,7 +2561,7 @@ videoEditor_init( "not initialized"); pContext->mAudioSettings = (M4xVSS_AudioMixingSettings *) - M4OSA_malloc(sizeof(M4xVSS_AudioMixingSettings),0x0, + M4OSA_32bitAlignedMalloc(sizeof(M4xVSS_AudioMixingSettings),0x0, (M4OSA_Char *)"mAudioSettings"); videoEditJava_checkAndThrowIllegalStateException(&initialized, pEnv, (M4OSA_NULL == pContext->mAudioSettings), @@ -3066,15 +3063,15 @@ videoEditor_release( if(pContext->mAudioSettings != M4OSA_NULL) { if (pContext->mAudioSettings->pFile != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + free(pContext->mAudioSettings->pFile); pContext->mAudioSettings->pFile = M4OSA_NULL; } if (pContext->mAudioSettings->pPCMFilePath != NULL) { - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + free(pContext->mAudioSettings->pPCMFilePath); pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; } - M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings); + free(pContext->mAudioSettings); pContext->mAudioSettings = M4OSA_NULL; } videoEditor_freeContext(pEnv, &pContext); @@ -3252,7 +3249,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, *******************************************************************************/ samplesCountInBytes = (samplesPerValue * sizeof(M4OSA_UInt16) * channels); - bufferIn.m_dataAddress = (M4OSA_UInt8*)M4OSA_malloc(samplesCountInBytes*sizeof(M4OSA_UInt16), 0, + bufferIn.m_dataAddress = (M4OSA_UInt8*)M4OSA_32bitAlignedMalloc(samplesCountInBytes*sizeof(M4OSA_UInt16), 0, (M4OSA_Char*)"AudioGraph" ); if ( bufferIn.m_dataAddress != M4OSA_NULL) { bufferIn.m_bufferSize = samplesCountInBytes*sizeof(M4OSA_UInt16); @@ -3380,7 +3377,7 @@ M4OSA_ERR M4MA_generateAudioGraphFile(JNIEnv* pEnv, M4OSA_Char* pInputFileURL, /****************************************************************************** CLOSE AND FREE ALLOCATIONS *******************************************************************************/ - M4OSA_free((M4OSA_MemAddr32)bufferIn.m_dataAddress); + free(bufferIn.m_dataAddress); M4OSA_fileReadClose(inputFileHandle); M4OSA_fileWriteClose(outFileHandle); /* final finish callback */ diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp index 339c0d1..53e7de1 100755 --- a/media/jni/mediaeditor/VideoEditorOsal.cpp +++ b/media/jni/mediaeditor/VideoEditorOsal.cpp @@ -25,7 +25,6 @@ extern "C" { #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4xVSS_API.h> #include <M4VSS3GPP_ErrorCodes.h> @@ -82,14 +81,6 @@ static const VideoEdit_Osal_Result gkRESULTS[] = VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_BAD_MODE_ACCESS ), VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_FILE_INVALID_POSITION ), - // M4OSA_String.h - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_STRING ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_CONV_FAILED ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_OVERFLOW ), - VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_STR_BAD_ARGS ), - VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_OVERFLOW ), - VIDEOEDIT_OSAL_RESULT_INIT(M4WAR_STR_NOT_FOUND ), - // M4OSA_Thread.h VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_THREAD_NOT_STARTED ), @@ -276,7 +267,7 @@ videoEditOsal_alloc( if (*pResult) { // Allocate memory for the settings. - pData = (M4VSS3GPP_EditSettings*)M4OSA_malloc(size, 0, (M4OSA_Char*)pDescription); + pData = (M4VSS3GPP_EditSettings*)M4OSA_32bitAlignedMalloc(size, 0, (M4OSA_Char*)pDescription); if (M4OSA_NULL != pData) { // Reset the allocated memory. @@ -314,10 +305,10 @@ videoEditOsal_free( VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "videoEditOsal_free()"); // Log the API call. - VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "M4OSA_free()"); + VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEO_EDITOR_OSAL", "free"); // Free the memory. - M4OSA_free((M4OSA_MemAddr32)pData); + free(pData); #ifdef OSAL_MEM_LEAK_DEBUG // Update the allocated block count. gAllocatedBlockCount--; diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp index 39221f3..9de7207 100755 --- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp +++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp @@ -34,13 +34,11 @@ extern "C" { #include <M4OSA_FileReader.h> #include <M4OSA_FileWriter.h> #include <M4OSA_Memory.h> -#include <M4OSA_String.h> #include <M4OSA_Thread.h> #include <M4VSS3GPP_API.h> #include <M4VSS3GPP_ErrorCodes.h> #include <M4MCS_API.h> #include <M4MCS_ErrorCodes.h> -#include <M4MDP_API.h> #include <M4READER_Common.h> #include <M4WRITER_common.h> #include <M4DECODER_Common.h> diff --git a/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp b/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp index 461bdd0..fe3734f 100755 --- a/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp +++ b/media/jni/mediaeditor/VideoEditorThumbnailMain.cpp @@ -165,7 +165,7 @@ M4OSA_ERR ThumbnailOpen(M4OSA_Context *pPContext, CHECK_PTR(ThumbnailOpen, pString, err, M4ERR_BAD_CONTEXT); /*--- Create context ---*/ - pContext = (ThumbnailContext*)M4OSA_malloc(sizeof(ThumbnailContext), VIDEOBROWSER, + pContext = (ThumbnailContext*)M4OSA_32bitAlignedMalloc(sizeof(ThumbnailContext), VIDEOBROWSER, (M4OSA_Char*)"Thumbnail context") ; M4OSA_TRACE3_1("context value is = %d",pContext); CHECK_PTR(ThumbnailOpen, pContext, err, M4ERR_ALLOC); @@ -211,7 +211,7 @@ ThumbnailOpen_cleanUp: { videoBrowserCleanUp(pContext->m_pVideoBrowser) ; } - M4OSA_free((M4OSA_MemAddr32)pContext) ; + free(pContext) ; } return err; } @@ -320,7 +320,7 @@ void ThumbnailClose(const M4OSA_Context pContext) { videoBrowserCleanUp(pC->m_pVideoBrowser); } - M4OSA_free((M4OSA_MemAddr32)pC); + free(pC); } ThumbnailClose_cleanUp: diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 2399216..8885bd5 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -192,8 +192,9 @@ public: } status_t invoke(const Parcel& request, Parcel *reply) - { // Avoid doing any extra copy. The interface descriptor should - // have been set by MediaPlayer.java. + { + // Avoid doing any extra copy. The interface descriptor should + // have been set by MediaPlayer.java. return remote()->transact(INVOKE, request, reply); } @@ -334,8 +335,8 @@ status_t BnMediaPlayer::onTransact( } break; case INVOKE: { CHECK_INTERFACE(IMediaPlayer, data, reply); - invoke(data, reply); - return NO_ERROR; + status_t result = invoke(data, reply); + return result; } break; case SET_METADATA_FILTER: { CHECK_INTERFACE(IMediaPlayer, data, reply); diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index 5ec573e..4e22175 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -70,8 +70,7 @@ status_t MediaScanner::processDirectory( client.setLocale(locale()); status_t result = - doProcessDirectory( - pathBuffer, pathRemaining, client, exceptionCheck, exceptionEnv); + doProcessDirectory(pathBuffer, pathRemaining, client, false, exceptionCheck, exceptionEnv); free(pathBuffer); @@ -80,20 +79,18 @@ status_t MediaScanner::processDirectory( status_t MediaScanner::doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, - ExceptionCheck exceptionCheck, void *exceptionEnv) { + bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv) { // place to copy file or directory name char* fileSpot = path + strlen(path); struct dirent* entry; struct stat statbuf; - // ignore directories that contain a ".nomedia" file + // Treat all files as non-media in directories that contain a ".nomedia" file if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { strcpy(fileSpot, ".nomedia"); if (access(path, F_OK) == 0) { - LOGD("found .nomedia, skipping directory\n"); - fileSpot[0] = 0; - client.addNoMediaFolder(path); - return OK; + LOGD("found .nomedia, setting noMedia flag\n"); + noMedia = true; } // restore path @@ -138,19 +135,20 @@ status_t MediaScanner::doProcessDirectory( } if (type == DT_REG || type == DT_DIR) { if (type == DT_DIR) { - // ignore directories with a name that starts with '.' + // set noMedia flag on directories with a name that starts with '.' // for example, the Mac ".Trashes" directory - if (name[0] == '.') continue; + if (name[0] == '.') + noMedia = true; // report the directory to the client if (stat(path, &statbuf) == 0) { - client.scanFile(path, statbuf.st_mtime, 0, true); + client.scanFile(path, statbuf.st_mtime, 0, true, noMedia); } // and now process its contents strcat(fileSpot, "/"); int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, - exceptionCheck, exceptionEnv); + noMedia, exceptionCheck, exceptionEnv); if (err) { // pass exceptions up - ignore other errors if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; @@ -159,7 +157,7 @@ status_t MediaScanner::doProcessDirectory( } } else { stat(path, &statbuf); - client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false); + client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false, noMedia); if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; } } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index f9db1a1..01d0a92 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -2028,14 +2028,18 @@ status_t MPEG4Source::read( size_t dstOffset = 0; while (srcOffset < size) { - CHECK(srcOffset + mNALLengthSize <= size); - size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]); - srcOffset += mNALLengthSize; + bool isMalFormed = (srcOffset + mNALLengthSize > size); + size_t nalLength = 0; + if (!isMalFormed) { + nalLength = parseNALSize(&mSrcBuffer[srcOffset]); + srcOffset += mNALLengthSize; + isMalFormed = srcOffset + nalLength > size; + } - if (srcOffset + nalLength > size) { + if (isMalFormed) { + LOGE("Video is malformed"); mBuffer->release(); mBuffer = NULL; - return ERROR_MALFORMED; } diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp index 616836c..0d0d6c2 100644 --- a/media/libstagefright/XINGSeeker.cpp +++ b/media/libstagefright/XINGSeeker.cpp @@ -41,8 +41,6 @@ sp<XINGSeeker> XINGSeeker::CreateFromSource( return NULL; } - LOGI("Found XING header."); - return seeker; } diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index 4d9a1ae..d7bde00 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -23,6 +23,7 @@ namespace android { class MtpDataPacket; class MtpProperty; +class MtpObjectInfo; class MtpDatabase { public: @@ -81,7 +82,7 @@ public: MtpDataPacket& packet) = 0; virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, - MtpDataPacket& packet) = 0; + MtpObjectInfo& info) = 0; virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp index 1668ecf..9f3037d 100644 --- a/media/mtp/MtpDebug.cpp +++ b/media/mtp/MtpDebug.cpp @@ -63,6 +63,12 @@ static const CodeEntry sOperationCodes[] = { { "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 }, { "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 }, { "MTP_OPERATION_SKIP", 0x9820 }, + // android extensions + { "MTP_OPERATION_GET_PARTIAL_OBJECT_64", 0x95C1 }, + { "MTP_OPERATION_SEND_PARTIAL_OBJECT", 0x95C2 }, + { "MTP_OPERATION_TRUNCATE_OBJECT", 0x95C3 }, + { "MTP_OPERATION_BEGIN_EDIT_OBJECT", 0x95C4 }, + { "MTP_OPERATION_END_EDIT_OBJECT", 0x95C5 }, { 0, 0 }, }; diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 37e02a3..b744b5b 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -30,6 +30,7 @@ #include "MtpDebug.h" #include "MtpDatabase.h" +#include "MtpObjectInfo.h" #include "MtpProperty.h" #include "MtpServer.h" #include "MtpStorage.h" @@ -79,6 +80,12 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_GET_OBJECT_REFERENCES, MTP_OPERATION_SET_OBJECT_REFERENCES, // MTP_OPERATION_SKIP, + // Android extension for direct file IO + MTP_OPERATION_GET_PARTIAL_OBJECT_64, + MTP_OPERATION_SEND_PARTIAL_OBJECT, + MTP_OPERATION_TRUNCATE_OBJECT, + MTP_OPERATION_BEGIN_EDIT_OBJECT, + MTP_OPERATION_END_EDIT_OBJECT, }; static const MtpEventCode kSupportedEventCodes[] = { @@ -218,6 +225,15 @@ void MtpServer::run() { } } + // commit any open edits + int count = mObjectEditList.size(); + for (int i = 0; i < count; i++) { + ObjectEdit* edit = mObjectEditList[i]; + commitEdit(edit); + delete edit; + } + mObjectEditList.clear(); + if (mSessionOpen) mDatabase->sessionEnded(); } @@ -252,6 +268,39 @@ void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) { } } +void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path, + uint64_t size, MtpObjectFormat format, int fd) { + ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd); + mObjectEditList.add(edit); +} + +MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) { + int count = mObjectEditList.size(); + for (int i = 0; i < count; i++) { + ObjectEdit* edit = mObjectEditList[i]; + if (edit->mHandle == handle) return edit; + } + return NULL; +} + +void MtpServer::removeEditObject(MtpObjectHandle handle) { + int count = mObjectEditList.size(); + for (int i = 0; i < count; i++) { + ObjectEdit* edit = mObjectEditList[i]; + if (edit->mHandle == handle) { + delete edit; + mObjectEditList.removeAt(i); + return; + } + } + LOGE("ObjectEdit not found in removeEditObject"); +} + +void MtpServer::commitEdit(ObjectEdit* edit) { + mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true); +} + + bool MtpServer::handleRequest() { Mutex::Autolock autoLock(mMutex); @@ -322,7 +371,8 @@ bool MtpServer::handleRequest() { response = doGetObject(); break; case MTP_OPERATION_GET_PARTIAL_OBJECT: - response = doGetPartialObject(); + case MTP_OPERATION_GET_PARTIAL_OBJECT_64: + response = doGetPartialObject(operation); break; case MTP_OPERATION_SEND_OBJECT_INFO: response = doSendObjectInfo(); @@ -339,6 +389,18 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_GET_DEVICE_PROP_DESC: response = doGetDevicePropDesc(); break; + case MTP_OPERATION_SEND_PARTIAL_OBJECT: + response = doSendPartialObject(); + break; + case MTP_OPERATION_TRUNCATE_OBJECT: + response = doTruncateObject(); + break; + case MTP_OPERATION_BEGIN_EDIT_OBJECT: + response = doBeginEditObject(); + break; + case MTP_OPERATION_END_EDIT_OBJECT: + response = doEndEditObject(); + break; default: LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; @@ -363,7 +425,7 @@ MtpResponseCode MtpServer::doGetDeviceInfo() { mData.putUInt16(MTP_STANDARD_VERSION); mData.putUInt32(6); // MTP Vendor Extension ID mData.putUInt16(MTP_STANDARD_VERSION); - string.set("microsoft.com: 1.0;"); + string.set("microsoft.com: 1.0; android.com: 1.0;"); mData.putString(string); // MTP Extensions mData.putUInt16(0); //Functional Mode mData.putAUInt16(kSupportedOperationCodes, @@ -601,7 +663,40 @@ MtpResponseCode MtpServer::doGetObjectInfo() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; MtpObjectHandle handle = mRequest.getParameter(1); - return mDatabase->getObjectInfo(handle, mData); + MtpObjectInfo info(handle); + MtpResponseCode result = mDatabase->getObjectInfo(handle, info); + if (result == MTP_RESPONSE_OK) { + char date[20]; + + mData.putUInt32(info.mStorageID); + mData.putUInt16(info.mFormat); + mData.putUInt16(info.mProtectionStatus); + + // if object is being edited the database size may be out of date + uint32_t size = info.mCompressedSize; + ObjectEdit* edit = getEditObject(handle); + if (edit) + size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize); + mData.putUInt32(size); + + mData.putUInt16(info.mThumbFormat); + mData.putUInt32(info.mThumbCompressedSize); + mData.putUInt32(info.mThumbPixWidth); + mData.putUInt32(info.mThumbPixHeight); + mData.putUInt32(info.mImagePixWidth); + mData.putUInt32(info.mImagePixHeight); + mData.putUInt32(info.mImagePixDepth); + mData.putUInt32(info.mParent); + mData.putUInt16(info.mAssociationType); + mData.putUInt32(info.mAssociationDesc); + mData.putUInt32(info.mSequenceNumber); + mData.putString(info.mName); + mData.putEmptyString(); // date created + formatDateTime(info.mDateModified, date, sizeof(date)); + mData.putString(date); // date modified + mData.putEmptyString(); // keywords + } + return result; } MtpResponseCode MtpServer::doGetObject() { @@ -641,12 +736,22 @@ MtpResponseCode MtpServer::doGetObject() { return MTP_RESPONSE_OK; } -MtpResponseCode MtpServer::doGetPartialObject() { +MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; MtpObjectHandle handle = mRequest.getParameter(1); - uint32_t offset = mRequest.getParameter(2); - uint32_t length = mRequest.getParameter(3); + uint64_t offset; + uint32_t length; + offset = mRequest.getParameter(2); + if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) { + // android extension with 64 bit offset + uint64_t offset2 = mRequest.getParameter(3); + offset = offset | (offset2 << 32); + length = mRequest.getParameter(4); + } else { + // standard GetPartialObject + length = mRequest.getParameter(3); + } MtpString pathBuf; int64_t fileLength; MtpObjectFormat format; @@ -933,4 +1038,113 @@ MtpResponseCode MtpServer::doGetDevicePropDesc() { return MTP_RESPONSE_OK; } +MtpResponseCode MtpServer::doSendPartialObject() { + if (!hasStorage()) + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + MtpObjectHandle handle = mRequest.getParameter(1); + uint64_t offset = mRequest.getParameter(2); + uint64_t offset2 = mRequest.getParameter(3); + offset = offset | (offset2 << 32); + uint32_t length = mRequest.getParameter(4); + + ObjectEdit* edit = getEditObject(handle); + if (!edit) { + LOGE("object not open for edit in doSendPartialObject"); + return MTP_RESPONSE_GENERAL_ERROR; + } + + // can't start writing past the end of the file + if (offset > edit->mSize) { + LOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize); + return MTP_RESPONSE_GENERAL_ERROR; + } + + // read the header + int ret = mData.readDataHeader(mFD); + // FIXME - check for errors here. + + // reset so we don't attempt to send this back + mData.reset(); + + const char* filePath = (const char *)edit->mPath; + LOGV("receiving partial %s %lld %ld\n", filePath, offset, length); + mtp_file_range mfr; + mfr.fd = edit->mFD; + mfr.offset = offset; + mfr.length = length; + + // transfer the file + ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); + LOGV("MTP_RECEIVE_FILE returned %d", ret); + if (ret < 0) { + mResponse.setParameter(1, 0); + if (errno == ECANCELED) + return MTP_RESPONSE_TRANSACTION_CANCELLED; + else + return MTP_RESPONSE_GENERAL_ERROR; + } + mResponse.setParameter(1, length); + uint64_t end = offset + length; + if (end > edit->mSize) { + edit->mSize = end; + } + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doTruncateObject() { + MtpObjectHandle handle = mRequest.getParameter(1); + ObjectEdit* edit = getEditObject(handle); + if (!edit) { + LOGE("object not open for edit in doTruncateObject"); + return MTP_RESPONSE_GENERAL_ERROR; + } + + uint64_t offset = mRequest.getParameter(2); + uint64_t offset2 = mRequest.getParameter(3); + offset |= (offset2 << 32); + if (ftruncate(edit->mFD, offset) != 0) { + return MTP_RESPONSE_GENERAL_ERROR; + } else { + edit->mSize = offset; + return MTP_RESPONSE_OK; + } +} + +MtpResponseCode MtpServer::doBeginEditObject() { + MtpObjectHandle handle = mRequest.getParameter(1); + if (getEditObject(handle)) { + LOGE("object already open for edit in doBeginEditObject"); + return MTP_RESPONSE_GENERAL_ERROR; + } + + MtpString path; + int64_t fileLength; + MtpObjectFormat format; + int result = mDatabase->getObjectFilePath(handle, path, fileLength, format); + if (result != MTP_RESPONSE_OK) + return result; + + int fd = open((const char *)path, O_RDWR | O_EXCL); + if (fd < 0) { + LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno); + return MTP_RESPONSE_GENERAL_ERROR; + } + + addEditObject(handle, path, fileLength, format, fd); + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doEndEditObject() { + MtpObjectHandle handle = mRequest.getParameter(1); + ObjectEdit* edit = getEditObject(handle); + if (!edit) { + LOGE("object not open for edit in doEndEditObject"); + return MTP_RESPONSE_GENERAL_ERROR; + } + + commitEdit(edit); + removeEditObject(handle); + return MTP_RESPONSE_OK; +} + } // namespace android diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index fa729a8..b06eb28 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -65,6 +65,27 @@ private: Mutex mMutex; + // represents an MTP object that is being edited using the android extensions + // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject) + class ObjectEdit { + public: + MtpObjectHandle mHandle; + MtpString mPath; + uint64_t mSize; + MtpObjectFormat mFormat; + int mFD; + + ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size, + MtpObjectFormat format, int fd) + : mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) { + } + + virtual ~ObjectEdit() { + close(mFD); + } + }; + Vector<ObjectEdit*> mObjectEditList; + public: MtpServer(int fd, MtpDatabase* database, int fileGroup, int filePerm, int directoryPerm); @@ -86,6 +107,12 @@ private: void sendStoreRemoved(MtpStorageID id); void sendEvent(MtpEventCode code, uint32_t param1); + void addEditObject(MtpObjectHandle handle, MtpString& path, + uint64_t size, MtpObjectFormat format, int fd); + ObjectEdit* getEditObject(MtpObjectHandle handle); + void removeEditObject(MtpObjectHandle handle); + void commitEdit(ObjectEdit* edit); + bool handleRequest(); MtpResponseCode doGetDeviceInfo(); @@ -106,12 +133,16 @@ private: MtpResponseCode doGetObjectPropList(); MtpResponseCode doGetObjectInfo(); MtpResponseCode doGetObject(); - MtpResponseCode doGetPartialObject(); + MtpResponseCode doGetPartialObject(MtpOperationCode operation); MtpResponseCode doSendObjectInfo(); MtpResponseCode doSendObject(); MtpResponseCode doDeleteObject(); MtpResponseCode doGetObjectPropDesc(); MtpResponseCode doGetDevicePropDesc(); + MtpResponseCode doSendPartialObject(); + MtpResponseCode doTruncateObject(); + MtpResponseCode doBeginEditObject(); + MtpResponseCode doEndEditObject(); }; }; // namespace android diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h index 8bc2e22..d270df5 100644 --- a/media/mtp/mtp.h +++ b/media/mtp/mtp.h @@ -391,6 +391,19 @@ #define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 #define MTP_OPERATION_SKIP 0x9820 +// Android extensions for direct file IO + +// Same as GetPartialObject, but with 64 bit offset +#define MTP_OPERATION_GET_PARTIAL_OBJECT_64 0x95C1 +// Same as GetPartialObject64, but copying host to device +#define MTP_OPERATION_SEND_PARTIAL_OBJECT 0x95C2 +// Truncates file to 64 bit length +#define MTP_OPERATION_TRUNCATE_OBJECT 0x95C3 +// Must be called before using SendPartialObject and TruncateObject +#define MTP_OPERATION_BEGIN_EDIT_OBJECT 0x95C4 +// Called to commit changes made by SendPartialObject and TruncateObject +#define MTP_OPERATION_END_EDIT_OBJECT 0x95C5 + // MTP Response Codes #define MTP_RESPONSE_UNDEFINED 0x2000 #define MTP_RESPONSE_OK 0x2001 diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py index aeba213..96cde57 100755 --- a/opengl/libs/GLES2_dbg/generate_api_cpp.py +++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py @@ -38,7 +38,7 @@ def generate_api(lines): "glShaderSource", "glTexImage2D", "glTexSubImage2D"] # these also needs to be forwarded to DbgContext - contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", + contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", "glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",] for line in lines: @@ -114,10 +114,10 @@ def generate_api(lines): else: getData += " msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(%s));" % (paramName, annotation, paramType) paramType += "*" - else: + else: if paramType == "GLfloat" or paramType == "GLclampf" or paramType.find("*") >= 0: setMsgParameters += " msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName) - else: + else: setMsgParameters += " msg.set_arg%d(%s);\n" % (paramIndex, paramName) if paramIndex < len(parameters) - 1: arguments += ', ' @@ -156,7 +156,7 @@ def generate_api(lines): } caller;""" print setCallerMembers print setMsgParameters - + if line.find("*") >= 0 or line.find(":") >= 0: print " // FIXME: check for pointer usage" if inout in ["in", "inout"]: diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py index 57e008c..535b13e 100755 --- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py +++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py @@ -25,7 +25,7 @@ def generate_egl_entries(output, lines, i): line = line.split(",")[1].strip() #extract EGL function name output.write(" %s = %d;\n" % (line, i)) i += 1 - return i + return i def generate_gl_entries(output,lines,i): @@ -118,7 +118,7 @@ message Message optional int32 arg4 = 16; optional int32 arg5 = 17; optional int32 arg6 = 18; - optional int32 arg7 = 19; + optional int32 arg7 = 19; // glDrawArrays/Elements sets this to active number of attributes optional int32 arg8 = 20; optional bytes data = 10; // variable length data used for GL call diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 879f1a8..73003c8 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -511,7 +511,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } // This will populate st.shownPanelView - if (!initializePanelContent(st) || (st.shownPanelView == null)) { + if (!initializePanelContent(st) || !st.hasPanelItems()) { return; } @@ -2976,6 +2976,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { refreshDecorView = false; } + public boolean hasPanelItems() { + if (shownPanelView == null) return false; + + if (isInExpandedMode) { + return expandedMenuPresenter.getAdapter().getCount() > 0; + } else { + return ((ViewGroup) shownPanelView).getChildCount() > 0; + } + } + /** * Unregister and free attached MenuPresenters. They will be recreated as needed. */ diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 65321b7..a37ccc7 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -2615,6 +2615,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + public int getLockedRotationLw() { + synchronized (mLock) { + if (false) { + // Not yet working. + if (mHdmiPlugged) { + return Surface.ROTATION_0; + } else if (mLidOpen == LID_OPEN) { + return mLidOpenRotation; + } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && mCarDockRotation >= 0) { + return mCarDockRotation; + } else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK && mDeskDockRotation >= 0) { + return mDeskDockRotation; + } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { + return mUserRotation; + } + } + return -1; + } + } + private int getCurrentLandscapeRotation(int lastRotation) { // if the user has locked rotation, we ignore the sensor if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java index ced8feb..8ba0a0b 100644 --- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -20,8 +20,8 @@ import com.android.server.wm.InputFilter; import android.content.Context; import android.util.Slog; +import android.view.InputDevice; import android.view.InputEvent; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManagerPolicy; @@ -32,10 +32,35 @@ import android.view.WindowManagerPolicy; */ public class AccessibilityInputFilter extends InputFilter { private static final String TAG = "AccessibilityInputFilter"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final Context mContext; + /** + * This is an interface for explorers that take a {@link MotionEvent} + * stream and perform touch exploration of the screen content. + */ + public interface Explorer { + /** + * Handles a {@link MotionEvent}. + * + * @param event The event to handle. + * @param policyFlags The policy flags associated with the event. + */ + public void onMotionEvent(MotionEvent event, int policyFlags); + + /** + * Requests that the explorer clears its internal state. + * + * @param event The last received event. + * @param policyFlags The policy flags associated with the event. + */ + public void clear(MotionEvent event, int policyFlags); + } + + private TouchExplorer mTouchExplorer; + private int mTouchscreenSourceDeviceId; + public AccessibilityInputFilter(Context context) { super(context.getMainLooper()); mContext = context; @@ -60,27 +85,27 @@ public class AccessibilityInputFilter extends InputFilter { @Override public void onInputEvent(InputEvent event, int policyFlags) { if (DEBUG) { - Slog.d(TAG, "Accessibility input filter received input event: " - + event + ", policyFlags=0x" + Integer.toHexString(policyFlags)); + Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" + + Integer.toHexString(policyFlags)); } - - // To prove that this is working as intended, we will silently transform - // Q key presses into non-repeating Z's as part of this stub implementation. - // TODO: Replace with the real thing. - if (event instanceof KeyEvent) { - final KeyEvent keyEvent = (KeyEvent)event; - if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_Q) { - if (keyEvent.getRepeatCount() == 0) { - sendInputEvent(new KeyEvent(keyEvent.getDownTime(), keyEvent.getEventTime(), - keyEvent.getAction(), KeyEvent.KEYCODE_Z, keyEvent.getRepeatCount(), - keyEvent.getMetaState(), keyEvent.getDeviceId(), keyEvent.getScanCode(), - keyEvent.getFlags(), keyEvent.getSource()), - policyFlags | WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT); + if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) { + MotionEvent motionEvent = (MotionEvent) event; + int deviceId = event.getDeviceId(); + if (mTouchscreenSourceDeviceId != deviceId) { + mTouchscreenSourceDeviceId = deviceId; + if (mTouchExplorer != null) { + mTouchExplorer.clear(motionEvent, policyFlags); + } else { + mTouchExplorer = new TouchExplorer(this, mContext); } - return; } + if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) { + mTouchExplorer.onMotionEvent(motionEvent, policyFlags); + } else { + mTouchExplorer.clear(motionEvent, policyFlags); + } + } else { + super.onInputEvent(event, policyFlags); } - - super.onInputEvent(event, policyFlags); } } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 7a483aa..1ad8047 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -56,6 +56,7 @@ import android.view.accessibility.IAccessibilityManagerClient; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -74,6 +75,8 @@ import java.util.Set; public class AccessibilityManagerService extends IAccessibilityManager.Stub implements HandlerCaller.Callback { + private static final boolean DEBUG = false; + private static final String LOG_TAG = "AccessibilityManagerService"; private static int sIdCounter = 0; @@ -102,6 +105,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); + private final SparseArray<List<ServiceInfo>> mFeedbackTypeToEnabledServicesMap = + new SparseArray<List<ServiceInfo>>(); + private PackageManager mPackageManager; private int mHandledFeedbackTypes = 0; @@ -211,7 +217,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } manageServicesLocked(); - updateInputFilterLocked(); } return; @@ -252,7 +257,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub unbindAllServicesLocked(); } updateClientsLocked(); - updateInputFilterLocked(); } } }); @@ -300,6 +304,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { + synchronized (mLock) { + List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType); + if (enabledServices == null) { + return Collections.emptyList(); + } + return enabledServices; + } + } + public void interrupt() { synchronized (mLock) { for (int i = 0, count = mServices.size(); i < count; i++) { @@ -339,6 +353,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } service.mNotificationTimeout = info.notificationTimeout; service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0; + + updateStateOnEnabledService(service); } return; default: @@ -449,7 +465,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub try { listener.onAccessibilityEvent(event); - if (false) { + if (DEBUG) { Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); } } catch (RemoteException re) { @@ -469,10 +485,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @return True if the service was removed, false otherwise. */ private boolean removeDeadServiceLocked(Service service) { - if (false) { + if (DEBUG) { Slog.i(LOG_TAG, "Dead service " + service.mService + " removed"); } mHandler.removeMessages(service.mId); + updateStateOnDisabledService(service); return mServices.remove(service); } @@ -593,7 +610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (isEnabled) { if (enabledServices.contains(componentName)) { if (service == null) { - service = new Service(componentName); + service = new Service(componentName, intalledService); } service.bind(); } else if (!enabledServices.contains(componentName)) { @@ -644,6 +661,47 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** + * Updates the set of enabled services for a given feedback type and + * if more than one of them provides spoken feedback enables touch + * exploration. + * + * @param service An enable service. + */ + private void updateStateOnEnabledService(Service service) { + int feedbackType = service.mFeedbackType; + List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType); + if (enabledServices == null) { + enabledServices = new ArrayList<ServiceInfo>(); + mFeedbackTypeToEnabledServicesMap.put(feedbackType, enabledServices); + } + enabledServices.add(service.mServiceInfo); + + // We enable touch exploration if at least one + // enabled service provides spoken feedback. + if (enabledServices.size() > 0 + && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { + updateClientsLocked(); + updateInputFilterLocked(); + } + } + + private void updateStateOnDisabledService(Service service) { + List<ServiceInfo> enabledServices = + mFeedbackTypeToEnabledServicesMap.get(service.mFeedbackType); + if (enabledServices == null) { + return; + } + enabledServices.remove(service.mServiceInfo); + // We disable touch exploration if no + // enabled service provides spoken feedback. + if (enabledServices.isEmpty() + && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { + updateClientsLocked(); + updateInputFilterLocked(); + } + } + + /** * This class represents an accessibility service. It stores all per service * data required for the service management, provides API for starting/stopping the * service and is responsible for adding/removing the service in the data structures @@ -654,6 +712,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection { int mId = 0; + ServiceInfo mServiceInfo; + IBinder mService; IEventListener mServiceInterface; @@ -678,9 +738,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<AccessibilityEvent>(); - Service(ComponentName componentName) { + Service(ComponentName componentName, ServiceInfo serviceInfo) { mId = sIdCounter++; mComponentName = componentName; + mServiceInfo = serviceInfo; mIntent = new Intent().setComponent(mComponentName); mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.accessibility_binding_label); @@ -712,6 +773,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.unbindService(this); mComponentNameToServiceMap.remove(mComponentName); mServices.remove(this); + updateStateOnDisabledService(this); return true; } return false; diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java new file mode 100644 index 0000000..4ba6060 --- /dev/null +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -0,0 +1,1540 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +package com.android.server.accessibility; + +import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END; +import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START; + +import com.android.server.accessibility.AccessibilityInputFilter.Explorer; +import com.android.server.wm.InputFilter; + +import android.content.Context; +import android.os.Handler; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.ViewConfiguration; +import android.view.WindowManagerPolicy; +import android.view.MotionEvent.PointerCoords; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.util.Arrays; + +/** + * This class is a strategy for performing touch exploration. It + * transforms the motion event stream by modifying, adding, replacing, + * and consuming certain events. The interaction model is: + * + * <ol> + * <li>1. One finger moving around performs touch exploration.</li> + * <li>2. Two close fingers moving in the same direction perform a drag.</li> + * <li>3. Multi-finger gestures are delivered to view hierarchy.</li> + * <li>4. Pointers that have not moved more than a specified distance after they + * went down are considered inactive.</li> + * <li>5. Two fingers moving too far from each other or in different directions + * are considered a multi-finger gesture.</li> + * <li>6. Tapping on the last touch explored location within given time and + * distance slop performs a click.</li> + * <li>7. Tapping and holding for a while on the last touch explored location within + * given time and distance slop performs a long press.</li> + * <ol> + * + * @hide + */ +public class TouchExplorer implements Explorer { + private static final boolean DEBUG = false; + + // Tag for logging received events. + private static final String LOG_TAG_RECEIVED = "TouchExplorer-RECEIVED"; + // Tag for logging injected events. + private static final String LOG_TAG_INJECTED = "TouchExplorer-INJECTED"; + // Tag for logging the current state. + private static final String LOG_TAG_STATE = "TouchExplorer-STATE"; + + // States this explorer can be in. + private static final int STATE_TOUCH_EXPLORING = 0x00000001; + private static final int STATE_DRAGGING = 0x00000002; + private static final int STATE_DELEGATING = 0x00000004; + + // Human readable symbolic names for the states of the explorer. + private static final SparseArray<String> sStateSymbolicNames = new SparseArray<String>(); + static { + SparseArray<String> symbolicNames = sStateSymbolicNames; + symbolicNames.append(STATE_TOUCH_EXPLORING, "STATE_TOUCH_EXPLORING"); + symbolicNames.append(STATE_DRAGGING, "STATE_DRAGING"); + symbolicNames.append(STATE_DELEGATING, "STATE_DELEGATING"); + } + + // Invalid pointer ID. + private static final int INVALID_POINTER_ID = -1; + + // The coefficient by which to multiply + // ViewConfiguration.#getScaledTouchExplorationTapSlop() + // to compute #mDraggingDistance. + private static final int COEFFICIENT_DRAGGING_DISTANCE = 2; + + // The time slop in milliseconds for activating an item after it has + // been touch explored. Tapping on an item within this slop will perform + // a click and tapping and holding down a long press. + private static final long ACTIVATION_TIME_SLOP = 2000; + + // This constant captures the current implementation detail that + // pointer IDs are between 0 and 31 inclusive (subject to change). + // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) + private static final int MAX_POINTER_COUNT = 32; + + // The minimum of the cosine between the vectors of two moving + // pointers so they can be considered moving in the same direction. + private static final float MIN_ANGLE_COS = 0.866025404f; // cos(pi/6) + + // The delay for sending a hover enter event. + private static final long DELAY_SEND_HOVER_MOVE = 200; + + // Temporary array for storing pointer IDs. + private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT]; + + // Temporary array for mapping new to old pointer IDs while filtering inactive pointers. + private final int [] mTempNewToOldPointerIndexMap = new int[MAX_POINTER_COUNT]; + + // Temporary array for storing PointerCoords + private final PointerCoords[] mTempPointerCoords= new PointerCoords[MAX_POINTER_COUNT]; + + // The maximal distance between two pointers so they are + // considered to be performing a drag operation. + private final float mDraggingDistance; + + // The distance from the last touch explored location tapping within + // which would perform a click and tapping and holding a long press. + private final int mTouchExplorationTapSlop; + + // Context handle for accessing resources. + private final Context mContext; + + // The InputFilter this tracker is associated with i.e. the filter + // which delegates event processing to this touch explorer. + private final InputFilter mInputFilter; + + // Helper class for tracking pointers on the screen, for example which + // pointers are down, which are active, etc. + private final PointerTracker mPointerTracker; + + // Handle to the accessibility manager for firing accessibility events + // announcing touch exploration gesture start and end. + private final AccessibilityManager mAccessibilityManager; + + // The last event that was received while performing touch exploration. + private MotionEvent mLastTouchExploreEvent; + + // The current state of the touch explorer. + private int mCurrentState = STATE_TOUCH_EXPLORING; + + // Flag whether a touch exploration gesture is in progress. + private boolean mTouchExploreGestureInProgress; + + // The ID of the pointer used for dragging. + private int mDraggingPointerId; + + // Handler for performing asynchronous operations. + private final Handler mHandler; + + // Command for delayed sending of a hover event. + private final SendHoverDelayed mSendHoverDelayed; + + /** + * Creates a new instance. + * + * @param inputFilter The input filter associated with this explorer. + * @param context A context handle for accessing resources. + */ + public TouchExplorer(InputFilter inputFilter, Context context) { + mInputFilter = inputFilter; + mTouchExplorationTapSlop = + ViewConfiguration.get(context).getScaledTouchExplorationTapSlop(); + mDraggingDistance = mTouchExplorationTapSlop * COEFFICIENT_DRAGGING_DISTANCE; + mPointerTracker = new PointerTracker(context); + mContext = context; + mHandler = new Handler(context.getMainLooper()); + mSendHoverDelayed = new SendHoverDelayed(); + mAccessibilityManager = AccessibilityManager.getInstance(context); + + // Populate the temporary array with PointerCorrds to be reused. + for (int i = 0, count = mTempPointerCoords.length; i < count; i++) { + mTempPointerCoords[i] = new PointerCoords(); + } + } + + public void clear(MotionEvent event, int policyFlags) { + sendUpForInjectedDownPointers(event, policyFlags); + clear(); + } + + /** + * {@inheritDoc} + */ + public void onMotionEvent(MotionEvent event, int policyFlags) { + if (DEBUG) { + Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x" + + Integer.toHexString(policyFlags)); + Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState)); + } + + // Keep track of the pointers's state. + mPointerTracker.onReceivedMotionEvent(event); + + switch(mCurrentState) { + case STATE_TOUCH_EXPLORING: { + handleMotionEventStateTouchExploring(event, policyFlags); + } break; + case STATE_DRAGGING: { + handleMotionEventStateDragging(event, policyFlags); + } break; + case STATE_DELEGATING: { + handleMotionEventStateDelegating(event, policyFlags); + } break; + default: { + throw new IllegalStateException("Illegal state: " + mCurrentState); + } + } + } + + /** + * Handles a motion event in touch exploring state. + * + * @param event The event to be handled. + * @param policyFlags The policy flags associated with the event. + */ + private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) { + PointerTracker pointerTracker = mPointerTracker; + final int activePointerCount = pointerTracker.getActivePointerCount(); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // Send a hover for every finger down so the user gets feedback + // where she is currently touching. + mSendHoverDelayed.forceSendAndRemove(); + mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 0, policyFlags, + DELAY_SEND_HOVER_MOVE); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + switch (activePointerCount) { + case 0: { + throw new IllegalStateException("The must always be one active pointer in" + + "touch exploring state!"); + } + case 1: { + // Schedule a hover event which will lead to firing an + // accessibility event from the hovered view. + mSendHoverDelayed.remove(); + final int pointerId = pointerTracker.getPrimaryActivePointerId(); + final int pointerIndex = event.findPointerIndex(pointerId); + final int lastAction = pointerTracker.getLastInjectedHoverAction(); + // If a schedules hover enter for another pointer is delivered we send move. + final int action = (lastAction == MotionEvent.ACTION_HOVER_ENTER) + ? MotionEvent.ACTION_HOVER_MOVE + : MotionEvent.ACTION_HOVER_ENTER; + mSendHoverDelayed.post(event, action, pointerIndex, policyFlags, + DELAY_SEND_HOVER_MOVE); + + if (mLastTouchExploreEvent == null) { + break; + } + + // If more pointers down on the screen since the last touch + // exploration we discard the last cached touch explore event. + if (event.getPointerCount() != mLastTouchExploreEvent.getPointerCount()) { + mLastTouchExploreEvent = null; + } + } break; + default: { + /* do nothing - let the code for ACTION_MOVE decide what to do */ + } break; + } + } break; + case MotionEvent.ACTION_MOVE: { + switch (activePointerCount) { + case 0: { + /* do nothing - no active pointers so we swallow the event */ + } break; + case 1: { + final int pointerId = pointerTracker.getPrimaryActivePointerId(); + final int pointerIndex = event.findPointerIndex(pointerId); + + // Detect touch exploration gesture start by having one active pointer + // that moved more than a given distance. + if (!mTouchExploreGestureInProgress) { + final float deltaX = pointerTracker.getReceivedPointerDownX(pointerId) + - event.getX(pointerIndex); + final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId) + - event.getY(pointerIndex); + final double moveDelta = Math.hypot(deltaX, deltaY); + + if (moveDelta > mTouchExplorationTapSlop) { + mTouchExploreGestureInProgress = true; + sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); + // Make sure the scheduled down/move event is sent. + mSendHoverDelayed.forceSendAndRemove(); + sendHoverEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIndex, + policyFlags); + } + } else { + // Touch exploration gesture in progress so send a hover event. + sendHoverEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIndex, + policyFlags); + } + + // Detect long press on the last touch explored position. + if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null) { + + // If the down was not in the time slop => nothing else to do. + final long pointerDownTime = + pointerTracker.getReceivedPointerDownTime(pointerId); + final long lastExploreTime = mLastTouchExploreEvent.getEventTime(); + final long deltaTimeExplore = pointerDownTime - lastExploreTime; + if (deltaTimeExplore > ACTIVATION_TIME_SLOP) { + mLastTouchExploreEvent = null; + break; + } + + // If the pointer moved more than the tap slop => nothing else to do. + final float deltaX = mLastTouchExploreEvent.getX(pointerIndex) + - event.getX(pointerIndex); + final float deltaY = mLastTouchExploreEvent.getY(pointerIndex) + - event.getY(pointerIndex); + final float moveDelta = (float) Math.hypot(deltaX, deltaY); + if (moveDelta > mTouchExplorationTapSlop) { + mLastTouchExploreEvent = null; + break; + } + + // If down for long enough we get a long press. + final long deltaTimeMove = event.getEventTime() - pointerDownTime; + if (deltaTimeMove > ViewConfiguration.getLongPressTimeout()) { + mCurrentState = STATE_DELEGATING; + // Make sure the scheduled hover exit is delivered. + mSendHoverDelayed.forceSendAndRemove(); + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + sendMotionEvent(event, policyFlags); + mTouchExploreGestureInProgress = false; + mLastTouchExploreEvent = null; + } + } + } break; + case 2: { + // Make sure the scheduled hover enter is delivered. + mSendHoverDelayed.forceSendAndRemove(); + // We want to no longer hover over the location so subsequent + // touch at the same spot will generate a hover enter. + final int pointerId = pointerTracker.getPrimaryActivePointerId(); + final int pointerIndex = event.findPointerIndex(pointerId); + sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex, + policyFlags); + + if (isDraggingGesture(event)) { + // Two pointers moving in the same direction within + // a given distance perform a drag. + mCurrentState = STATE_DRAGGING; + if (mTouchExploreGestureInProgress) { + sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + mTouchExploreGestureInProgress = false; + } + mDraggingPointerId = pointerTracker.getPrimaryActivePointerId(); + sendDragEvent(event, MotionEvent.ACTION_DOWN, policyFlags); + } else { + // Two pointers moving arbitrary are delegated to the view hierarchy. + mCurrentState = STATE_DELEGATING; + if (mTouchExploreGestureInProgress) { + sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + mTouchExploreGestureInProgress = false; + } + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } + } break; + default: { + // Make sure the scheduled hover enter is delivered. + mSendHoverDelayed.forceSendAndRemove(); + // We want to no longer hover over the location so subsequent + // touch at the same spot will generate a hover enter. + final int pointerId = pointerTracker.getPrimaryActivePointerId(); + final int pointerIndex = event.findPointerIndex(pointerId); + sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex, + policyFlags); + + // More than two pointers are delegated to the view hierarchy. + mCurrentState = STATE_DELEGATING; + mSendHoverDelayed.remove(); + if (mTouchExploreGestureInProgress) { + sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + mTouchExploreGestureInProgress = false; + } + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } + } + } break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: { + switch (activePointerCount) { + case 0: { + // If the pointer that went up was not active we have nothing to do. + if (!pointerTracker.wasLastReceivedUpPointerActive()) { + break; + } + + // If touch exploring announce the end of the gesture. + if (mTouchExploreGestureInProgress) { + sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); + mTouchExploreGestureInProgress = false; + } + + // Detect whether to activate i.e. click on the last explored location. + if (mLastTouchExploreEvent != null) { + final int pointerId = pointerTracker.getLastReceivedUpPointerId(); + + // If the down was not in the time slop => nothing else to do. + final long eventTime = + pointerTracker.getLastReceivedUpPointerDownTime(); + final long exploreTime = mLastTouchExploreEvent.getEventTime(); + final long deltaTime = eventTime - exploreTime; + if (deltaTime > ACTIVATION_TIME_SLOP) { + mSendHoverDelayed.forceSendAndRemove(); + scheduleHoverExit(event, policyFlags); + mLastTouchExploreEvent = MotionEvent.obtain(event); + break; + } + + // If the pointer moved more than the tap slop => nothing else to do. + final int pointerIndex = event.findPointerIndex(pointerId); + final float deltaX = pointerTracker.getLastReceivedUpPointerDownX() + - event.getX(pointerIndex); + final float deltaY = pointerTracker.getLastReceivedUpPointerDownY() + - event.getY(pointerIndex); + final float deltaMove = (float) Math.hypot(deltaX, deltaY); + if (deltaMove > mTouchExplorationTapSlop) { + mSendHoverDelayed.forceSendAndRemove(); + scheduleHoverExit(event, policyFlags); + mLastTouchExploreEvent = MotionEvent.obtain(event); + break; + } + + // All preconditions are met, so click the last explored location. + mSendHoverDelayed.forceSendAndRemove(); + sendActionDownAndUp(mLastTouchExploreEvent, policyFlags); + mLastTouchExploreEvent = null; + } else { + mSendHoverDelayed.forceSendAndRemove(); + scheduleHoverExit(event, policyFlags); + mLastTouchExploreEvent = MotionEvent.obtain(event); + } + } break; + } + } break; + case MotionEvent.ACTION_CANCEL: { + final int lastAction = pointerTracker.getLastInjectedHoverAction(); + if (lastAction != MotionEvent.ACTION_HOVER_EXIT) { + final int pointerId = pointerTracker.getPrimaryActivePointerId(); + final int pointerIndex = event.findPointerIndex(pointerId); + sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex, + policyFlags); + } + clear(); + } break; + } + } + + /** + * Handles a motion event in dragging state. + * + * @param event The event to be handled. + * @param policyFlags The policy flags associated with the event. + */ + private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + throw new IllegalStateException("Dragging state can be reached only if two " + + "pointers are already down"); + } + case MotionEvent.ACTION_POINTER_DOWN: { + // We are in dragging state so we have two pointers and another one + // goes down => delegate the three pointers to the view hierarchy + mCurrentState = STATE_DELEGATING; + sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags); + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } break; + case MotionEvent.ACTION_MOVE: { + final int activePointerCount = mPointerTracker.getActivePointerCount(); + switch (activePointerCount) { + case 2: { + if (isDraggingGesture(event)) { + // If still dragging send a drag event. + sendDragEvent(event, MotionEvent.ACTION_MOVE, policyFlags); + } else { + // The two pointers are moving either in different directions or + // no close enough => delegate the gesture to the view hierarchy. + mCurrentState = STATE_DELEGATING; + // Send an event to the end of the drag gesture. + sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags); + // Deliver all active pointers to the view hierarchy. + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } + } break; + default: { + mCurrentState = STATE_DELEGATING; + // Send an event to the end of the drag gesture. + sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags); + // Deliver all active pointers to the view hierarchy. + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } + } + } break; + case MotionEvent.ACTION_POINTER_UP: { + mCurrentState = STATE_TOUCH_EXPLORING; + // Send an event to the end of the drag gesture. + sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags); + } break; + case MotionEvent.ACTION_CANCEL: { + clear(); + } break; + } + } + + /** + * Handles a motion event in delegating state. + * + * @param event The event to be handled. + * @param policyFlags The policy flags associated with the event. + */ + public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + throw new IllegalStateException("Delegating state can only be reached if " + + "there is at least one pointer down!"); + } + case MotionEvent.ACTION_UP: { + mCurrentState = STATE_TOUCH_EXPLORING; + } break; + case MotionEvent.ACTION_MOVE: { + // Check whether some other pointer became active because they have moved + // a given distance and if such exist send them to the view hierarchy + final int notInjectedCount = mPointerTracker.getNotInjectedActivePointerCount(); + if (notInjectedCount > 0) { + sendDownForAllActiveNotInjectedPointers(event, policyFlags); + } + } break; + case MotionEvent.ACTION_POINTER_UP: { + // No active pointers => go to initial state. + if (mPointerTracker.getActivePointerCount() == 0) { + mCurrentState = STATE_TOUCH_EXPLORING; + } + } break; + case MotionEvent.ACTION_CANCEL: { + clear(); + } break; + } + // Deliver the event striping out inactive pointers. + sendMotionEventStripInactivePointers(event, policyFlags); + } + + /** + * Schedules a hover up event so subsequent poking on the same location after + * the scheduled delay will perform exploration. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + private void scheduleHoverExit(MotionEvent prototype, + int policyFlags) { + final int pointerId = mPointerTracker.getLastReceivedUpPointerId(); + final int pointerIndex = prototype.findPointerIndex(pointerId); + // We want to no longer hover over the location so subsequent + // touch at the same spot will generate a hover enter. + mSendHoverDelayed.post(prototype, MotionEvent.ACTION_HOVER_EXIT, + pointerIndex, policyFlags, ACTIVATION_TIME_SLOP); + } + + /** + * Sends down events to the view hierarchy for all active pointers which are + * not already being delivered i.e. pointers that are not yet injected. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) { + PointerCoords[] pointerCoords = mTempPointerCoords; + PointerTracker pointerTracker = mPointerTracker; + int[] pointerIds = mTempPointerIds; + int pointerDataIndex = 0; + + final int pinterCount = prototype.getPointerCount(); + for (int i = 0; i < pinterCount; i++) { + final int pointerId = prototype.getPointerId(i); + + // Skip inactive pointers. + if (!pointerTracker.isActivePointer(pointerId)) { + continue; + } + // Skip already delivered pointers. + if (pointerTracker.isInjectedPointerDown(pointerId)) { + continue; + } + + // Populate and inject an event for the current pointer. + pointerIds[pointerDataIndex] = pointerId; + prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]); + + final long downTime = pointerTracker.getLastInjectedDownEventTime(); + final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, pointerDataIndex); + final int pointerCount = pointerDataIndex + 1; + final long pointerDownTime = SystemClock.uptimeMillis(); + + MotionEvent event = MotionEvent.obtain(downTime, pointerDownTime, + action, pointerCount, pointerIds, pointerCoords, prototype.getMetaState(), + prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(), + prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags()); + sendMotionEvent(event, policyFlags); + event.recycle(); + + pointerDataIndex++; + } + } + + /** + * Sends up events to the view hierarchy for all active pointers which are + * already being delivered i.e. pointers that are injected. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { + PointerTracker pointerTracker = mPointerTracker; + PointerCoords[] pointerCoords = mTempPointerCoords; + int[] pointerIds = mTempPointerIds; + int pointerDataIndex = 0; + + final int pointerCount = prototype.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + final int pointerId = prototype.getPointerId(i); + + // Skip non injected down pointers. + if (!pointerTracker.isInjectedPointerDown(pointerId)) { + continue; + } + + // Populate and inject event. + pointerIds[pointerDataIndex] = pointerId; + prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]); + + final long downTime = pointerTracker.getLastInjectedDownEventTime(); + final int action = computeInjectionAction(MotionEvent.ACTION_UP, pointerDataIndex); + final int newPointerCount = pointerDataIndex + 1; + final long eventTime = SystemClock.uptimeMillis(); + + MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, + newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(), + prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(), + prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags()); + + sendMotionEvent(event, policyFlags); + event.recycle(); + + pointerDataIndex++; + } + } + + /** + * Sends a motion event by first stripping the inactive pointers. + * + * @param prototype The prototype from which to create the injected event. + * @param policyFlags The policy flags associated with the event. + */ + private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) { + PointerTracker pointerTracker = mPointerTracker; + + // All pointers active therefore we just inject the event as is. + if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) { + sendMotionEvent(prototype, policyFlags); + return; + } + + // No active pointers and the one that just went up was not + // active, therefore we have nothing to do. + if (pointerTracker.getActivePointerCount() == 0 + && !pointerTracker.wasLastReceivedUpPointerActive()) { + return; + } + + // Filter out inactive pointers from the event and inject it. + PointerCoords[] pointerCoords = mTempPointerCoords; + int[] pointerIds = mTempPointerIds; + int [] newToOldPointerIndexMap = mTempNewToOldPointerIndexMap; + int newPointerIndex = 0; + int actionIndex = prototype.getActionIndex(); + + final int oldPointerCount = prototype.getPointerCount(); + for (int oldPointerIndex = 0; oldPointerIndex < oldPointerCount; oldPointerIndex++) { + final int pointerId = prototype.getPointerId(oldPointerIndex); + + // If the pointer is inactive or the pointer that just went up + // was inactive we strip the pointer data from the event. + if (!pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) { + if (oldPointerIndex <= prototype.getActionIndex()) { + actionIndex--; + } + continue; + } + + newToOldPointerIndexMap[newPointerIndex] = oldPointerIndex; + pointerIds[newPointerIndex] = pointerId; + prototype.getPointerCoords(oldPointerIndex, pointerCoords[newPointerIndex]); + + newPointerIndex++; + } + + // If we skipped all pointers => nothing to do. + if (newPointerIndex == 0) { + return; + } + + // Populate and inject the event. + final long downTime = pointerTracker.getLastInjectedDownEventTime(); + final int action = computeInjectionAction(prototype.getActionMasked(), actionIndex); + final int newPointerCount = newPointerIndex; + MotionEvent prunedEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action, + newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(), + prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(), + prototype.getEdgeFlags(), prototype.getSource(),prototype.getFlags()); + + // Add the filtered history. + final int historySize = prototype.getHistorySize(); + for (int historyIndex = 0; historyIndex < historySize; historyIndex++) { + for (int pointerIndex = 0; pointerIndex < newPointerCount; pointerIndex++) { + final int oldPointerIndex = newToOldPointerIndexMap[pointerIndex]; + prototype.getPointerCoords(oldPointerIndex, pointerCoords[pointerIndex]); + } + final long historicalTime = prototype.getHistoricalEventTime(historyIndex); + prunedEvent.addBatch(historicalTime, pointerCoords, 0); + } + + sendMotionEvent(prunedEvent, policyFlags); + prunedEvent.recycle(); + } + + /** + * Sends a dragging event from a two pointer event. The two pointers are + * merged into one and delivered to the view hierarchy. Through the entire + * drag gesture the pointer id delivered to the view hierarchy is the same. + * + * @param prototype The prototype from which to create the injected event. + * @param action The dragging action that is to be injected. + * @param policyFlags The policy flags associated with the event. + */ + private void sendDragEvent(MotionEvent prototype, int action, int policyFlags) { + PointerCoords[] pointerCoords = mTempPointerCoords; + int[] pointerIds = mTempPointerIds; + final int pointerId = mDraggingPointerId; + final int pointerIndex = prototype.findPointerIndex(pointerId); + + // Populate the event with the date of the dragging pointer and inject it. + pointerIds[0] = pointerId; + prototype.getPointerCoords(pointerIndex, pointerCoords[0]); + + MotionEvent event = MotionEvent.obtain(prototype.getDownTime(), + prototype.getEventTime(), action, 1, pointerIds, pointerCoords, + prototype.getMetaState(), prototype.getXPrecision(), prototype.getYPrecision(), + prototype.getDeviceId(), prototype.getEdgeFlags(), prototype.getSource(), + prototype.getFlags()); + + sendMotionEvent(event, policyFlags); + event.recycle(); + } + + /** + * Sends an up and down events. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { + PointerCoords[] pointerCoords = mTempPointerCoords; + int[] pointerIds = mTempPointerIds; + final int pointerId = mPointerTracker.getLastReceivedUpPointerId(); + final int pointerIndex = prototype.findPointerIndex(pointerId); + + // Send down. + pointerIds[0] = pointerId; + prototype.getPointerCoords(pointerIndex, pointerCoords[0]); + + final long downTime = SystemClock.uptimeMillis(); + + MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, + 1, mTempPointerIds, mTempPointerCoords, prototype.getMetaState(), + prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(), + prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags()); + + // Clone the down event before recycling it. + MotionEvent upEvent = MotionEvent.obtain(downEvent); + + sendMotionEvent(downEvent, policyFlags); + downEvent.recycle(); + + // Send up. + upEvent.setAction(MotionEvent.ACTION_UP); + sendMotionEvent(upEvent, policyFlags); + upEvent.recycle(); + } + + /** + * Sends a hover event. + * + * @param prototype The prototype from which to create the injected event. + * @param action The hover action. + * @param pointerIndex The action pointer index. + * @param policyFlags The policy flags associated with the event. + */ + private void sendHoverEvent(MotionEvent prototype, int action, int pointerIndex, int + policyFlags) { + PointerCoords[] pointerCoords = mTempPointerCoords; + int[] pointerIds = mTempPointerIds; + + // Keep only data relevant to a hover event. + pointerIds[0] = prototype.getPointerId(pointerIndex); + pointerCoords[0].clear(); + pointerCoords[0].x = prototype.getX(pointerIndex); + pointerCoords[0].y = prototype.getY(pointerIndex); + + final long downTime = mPointerTracker.getLastInjectedDownEventTime(); + + // Populate and inject a hover event. + MotionEvent hoverEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action, + 1, pointerIds, pointerCoords, 0, 0, 0, prototype.getDeviceId(), 0, + prototype.getSource(), 0); + + sendMotionEvent(hoverEvent, policyFlags); + hoverEvent.recycle(); + } + + /** + * Computes the action for an injected event based on a masked action + * and a pointer index. + * + * @param actionMasked The masked action. + * @param pointerIndex The index of the pointer which has changed. + * @return The action to be used for injection. + */ + private int computeInjectionAction(int actionMasked, int pointerIndex) { + switch (actionMasked) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: { + PointerTracker pointerTracker = mPointerTracker; + // Compute the action based on how many down pointers are injected. + if (pointerTracker.getInjectedPointerDownCount() == 0) { + return MotionEvent.ACTION_DOWN; + } else { + return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + | MotionEvent.ACTION_POINTER_DOWN; + } + } + case MotionEvent.ACTION_POINTER_UP: { + PointerTracker pointerTracker = mPointerTracker; + // Compute the action based on how many down pointers are injected. + if (pointerTracker.getInjectedPointerDownCount() == 1) { + return MotionEvent.ACTION_UP; + } else { + return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + | MotionEvent.ACTION_POINTER_UP; + } + } + default: + return actionMasked; + } + } + + /** + * Determines whether a two pointer gesture is a dragging one. + * + * @param event The event with the pointer data. + * @return True if the gesture is a dragging one. + */ + private boolean isDraggingGesture(MotionEvent event) { + PointerTracker pointerTracker = mPointerTracker; + int[] pointerIds = mTempPointerIds; + pointerTracker.populateActivePointerIds(pointerIds); + + final int firstPtrIndex = event.findPointerIndex(pointerIds[0]); + final int secondPtrIndex = event.findPointerIndex(pointerIds[1]); + + final float firstPtrX = event.getX(firstPtrIndex); + final float firstPtrY = event.getY(firstPtrIndex); + final float secondPtrX = event.getX(secondPtrIndex); + final float secondPtrY = event.getY(secondPtrIndex); + + // Check if the pointers are close enough. + final float deltaX = firstPtrX - secondPtrX; + final float deltaY = firstPtrY - secondPtrY; + final float deltaMove = (float) Math.hypot(deltaX, deltaY); + if (deltaMove > mDraggingDistance) { + return false; + } + + // Check if the pointers are moving in the same direction. + final float firstDeltaX = + firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex); + final float firstDeltaY = + firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex); + final float firstMagnitude = + (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY); + final float firstXNormalized = + (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX; + final float firstYNormalized = + (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY; + + final float secondDeltaX = + secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex); + final float secondDeltaY = + secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex); + final float secondMagnitude = + (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY); + final float secondXNormalized = + (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX; + final float secondYNormalized = + (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY; + + final float angleCos = + firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized; + + if (angleCos < MIN_ANGLE_COS) { + return false; + } + + return true; + } + + /** + * Sends an event announcing the start/end of a touch exploration gesture. + * + * @param eventType The type of the event to send. + */ + private void sendAccessibilityEvent(int eventType) { + AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + event.setPackageName(mContext.getPackageName()); + event.setClassName(getClass().getName()); + mAccessibilityManager.sendAccessibilityEvent(event); + } + + /** + * Sends a motion event to the input filter for injection. + * + * @param event The event to send. + * @param policyFlags The policy flags associated with the event. + */ + private void sendMotionEvent(MotionEvent event, int policyFlags) { + if (DEBUG) { + Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x" + + Integer.toHexString(policyFlags)); + } + // Make sure that the user will see the event. + policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; + mPointerTracker.onInjectedMotionEvent(event); + mInputFilter.sendInputEvent(event, policyFlags); + } + + /** + * Clears the internal state of this explorer. + */ + private void clear() { + mSendHoverDelayed.remove(); + mPointerTracker.clear(); + mLastTouchExploreEvent = null; + mCurrentState = STATE_TOUCH_EXPLORING; + mTouchExploreGestureInProgress = false; + mDraggingPointerId = INVALID_POINTER_ID; + } + + /** + * Helper class for tracking pointers and more specifically which of + * them are currently down, which are active, and which are delivered + * to the view hierarchy. The enclosing {@link TouchExplorer} uses the + * pointer state reported by this class to perform touch exploration. + * <p> + * The main purpose of this class is to allow the touch explorer to + * disregard pointers put down by accident by the user and not being + * involved in the interaction. For example, a blind user grabs the + * device with her left hand such that she touches the screen and she + * uses her right hand's index finger to explore the screen content. + * In this scenario the touches generated by the left hand are to be + * ignored. + */ + class PointerTracker { + private static final String LOG_TAG = "PointerTracker"; + + // The coefficient by which to multiply + // ViewConfiguration.#getScaledTouchSlop() + // to compute #mThresholdActivePointer. + private static final int COEFFICIENT_ACTIVE_POINTER = 2; + + // Pointers that moved less than mThresholdActivePointer + // are considered active i.e. are ignored. + private final double mThresholdActivePointer; + + // Keep track of where and when a pointer went down. + private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; + private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; + private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; + + // Which pointers are down. + private int mReceivedPointersDown; + + // Which down pointers are active. + private int mActivePointers; + + // Primary active pointer which is either the first that went down + // or if it goes up the next active that most recently went down. + private int mPrimaryActivePointerId; + + // Flag indicating that there is at least one active pointer moving. + private boolean mHasMovingActivePointer; + + // Keep track of which pointers sent to the system are down. + private int mInjectedPointersDown; + + // Keep track of the last up pointer data. + private float mLastReceivedUpPointerDownX; + private float mLastReveivedUpPointerDownY; + private long mLastReceivedUpPointerDownTime; + private int mLastReceivedUpPointerId; + private boolean mLastReceivedUpPointerActive; + + // The time of the last injected down. + private long mLastInjectedDownEventTime; + + // The action of the last injected hover event. + private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT; + + /** + * Creates a new instance. + * + * @param context Context for looking up resources. + */ + public PointerTracker(Context context) { + mThresholdActivePointer = + ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER; + } + + /** + * Clears the internals state. + */ + public void clear() { + Arrays.fill(mReceivedPointerDownX, 0); + Arrays.fill(mReceivedPointerDownY, 0); + Arrays.fill(mReceivedPointerDownTime, 0); + mReceivedPointersDown = 0; + mActivePointers = 0; + mPrimaryActivePointerId = 0; + mHasMovingActivePointer = false; + mInjectedPointersDown = 0; + mLastReceivedUpPointerDownX = 0; + mLastReveivedUpPointerDownY = 0; + mLastReceivedUpPointerDownTime = 0; + mLastReceivedUpPointerId = 0; + mLastReceivedUpPointerActive = false; + } + + /** + * Processes a received {@link MotionEvent} event. + * + * @param event The event to process. + */ + public void onReceivedMotionEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: { + // New gesture so restart tracking injected down pointers. + mInjectedPointersDown = 0; + handleReceivedPointerDown(0, event); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + handleReceivedPointerDown(event.getActionIndex(), event); + } break; + case MotionEvent.ACTION_MOVE: { + handleReceivedPointerMove(event); + } break; + case MotionEvent.ACTION_UP: { + handleReceivedPointerUp(0, event); + } break; + case MotionEvent.ACTION_POINTER_UP: { + handleReceivedPointerUp(event.getActionIndex(), event); + } break; + } + if (DEBUG) { + Slog.i(LOG_TAG, "Received pointer: " + toString()); + } + } + + /** + * Processes an injected {@link MotionEvent} event. + * + * @param event The event to process. + */ + public void onInjectedMotionEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: { + handleInjectedPointerDown(0, event); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + handleInjectedPointerDown(event.getActionIndex(), event); + } break; + case MotionEvent.ACTION_UP: { + handleInjectedPointerUp(0, event); + } break; + case MotionEvent.ACTION_POINTER_UP: { + handleInjectedPointerUp(event.getActionIndex(), event); + } break; + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + case MotionEvent.ACTION_HOVER_EXIT: { + mLastInjectedHoverEventAction = event.getActionMasked(); + } break; + } + if (DEBUG) { + Slog.i(LOG_TAG, "Injected pointer: " + toString()); + } + } + + /** + * @return The number of received pointers that are down. + */ + public int getReceivedPointerDownCount() { + return Integer.bitCount(mReceivedPointersDown); + } + + /** + * @return The number of down input pointers that are active. + */ + public int getActivePointerCount() { + return Integer.bitCount(mActivePointers); + } + + /** + * Whether an received pointer is down. + * + * @param pointerId The unique pointer id. + * @return True if the pointer is down. + */ + public boolean isReceivedPointerDown(int pointerId) { + final int pointerFlag = (1 << pointerId); + return (mReceivedPointersDown & pointerFlag) != 0; + } + + /** + * Whether an injected pointer is down. + * + * @param pointerId The unique pointer id. + * @return True if the pointer is down. + */ + public boolean isInjectedPointerDown(int pointerId) { + final int pointerFlag = (1 << pointerId); + return (mInjectedPointersDown & pointerFlag) != 0; + } + + /** + * @return The number of down pointers injected to the view hierarchy. + */ + public int getInjectedPointerDownCount() { + return Integer.bitCount(mInjectedPointersDown); + } + + /** + * Whether an input pointer is active. + * + * @param pointerId The unique pointer id. + * @return True if the pointer is active. + */ + public boolean isActivePointer(int pointerId) { + final int pointerFlag = (1 << pointerId); + return (mActivePointers & pointerFlag) != 0; + } + + /** + * @param pointerId The unique pointer id. + * @return The X coordinate where the pointer went down. + */ + public float getReceivedPointerDownX(int pointerId) { + return mReceivedPointerDownX[pointerId]; + } + + /** + * @param pointerId The unique pointer id. + * @return The Y coordinate where the pointer went down. + */ + public float getReceivedPointerDownY(int pointerId) { + return mReceivedPointerDownY[pointerId]; + } + + /** + * @param pointerId The unique pointer id. + * @return The time when the pointer went down. + */ + public long getReceivedPointerDownTime(int pointerId) { + return mReceivedPointerDownTime[pointerId]; + } + + /** + * @return The id of the primary pointer. + */ + public int getPrimaryActivePointerId() { + if (mPrimaryActivePointerId == INVALID_POINTER_ID) { + mPrimaryActivePointerId = findPrimaryActivePointer(); + } + return mPrimaryActivePointerId; + } + + /** + * @return The X coordinate where the last up received pointer went down. + */ + public float getLastReceivedUpPointerDownX() { + return mLastReceivedUpPointerDownX; + } + + /** + * @return The Y coordinate where the last up received pointer went down. + */ + public float getLastReceivedUpPointerDownY() { + return mLastReveivedUpPointerDownY; + } + + /** + * @return The time when the last up received pointer went down. + */ + public long getLastReceivedUpPointerDownTime() { + return mLastReceivedUpPointerDownTime; + } + + /** + * @return The id of the last received pointer that went up. + */ + public int getLastReceivedUpPointerId() { + return mLastReceivedUpPointerId; + } + + /** + * @return Whether the last received pointer that went up was active. + */ + public boolean wasLastReceivedUpPointerActive() { + return mLastReceivedUpPointerActive; + } + + /** + * @return The time of the last injected down event. + */ + public long getLastInjectedDownEventTime() { + return mLastInjectedDownEventTime; + } + + /** + * @return The action of the last injected hover event. + */ + public int getLastInjectedHoverAction() { + return mLastInjectedHoverEventAction; + } + + /** + * Populates the active pointer IDs to the given array. + * <p> + * Note: The client is responsible for providing large enough array. + * + * @param outPointerIds The array to which to write the active pointers. + */ + public void populateActivePointerIds(int[] outPointerIds) { + int index = 0; + for (int idBits = mActivePointers; idBits != 0; ) { + final int id = Integer.numberOfTrailingZeros(idBits); + idBits &= ~(1 << id); + outPointerIds[index] = id; + index++; + } + } + + /** + * @return The number of non injected active pointers. + */ + public int getNotInjectedActivePointerCount() { + final int pointerState = mActivePointers & ~mInjectedPointersDown; + return Integer.bitCount(pointerState); + } + + /** + * @param pointerId The unique pointer id. + * @return Whether the pointer is active or was the last active than went up. + */ + private boolean isActiveOrWasLastActiveUpPointer(int pointerId) { + return (isActivePointer(pointerId) + || (mLastReceivedUpPointerId == pointerId + && mLastReceivedUpPointerActive)); + } + + /** + * Handles a received pointer down event. + * + * @param pointerIndex The index of the pointer that has changed. + * @param event The event to be handled. + */ + private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { + final int pointerId = event.getPointerId(pointerIndex); + final int pointerFlag = (1 << pointerId); + + mLastReceivedUpPointerId = 0; + mLastReceivedUpPointerDownX = 0; + mLastReveivedUpPointerDownY = 0; + mLastReceivedUpPointerDownTime = 0; + mLastReceivedUpPointerActive = false; + + mReceivedPointersDown |= pointerFlag; + mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); + mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); + mReceivedPointerDownTime[pointerId] = event.getEventTime(); + + if (!mHasMovingActivePointer) { + // If still no moving active pointers every + // down pointer is the only active one. + mActivePointers = pointerFlag; + mPrimaryActivePointerId = pointerId; + } else { + // If at least one moving active pointer every + // subsequent down pointer is active. + mActivePointers |= pointerFlag; + } + } + + /** + * Handles a received pointer move event. + * + * @param event The event to be handled. + */ + private void handleReceivedPointerMove(MotionEvent event) { + detectActivePointers(event); + } + + /** + * Handles a received pointer up event. + * + * @param pointerIndex The index of the pointer that has changed. + * @param event The event to be handled. + */ + private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { + final int pointerId = event.getPointerId(pointerIndex); + final int pointerFlag = (1 << pointerId); + + mLastReceivedUpPointerId = pointerId; + mLastReceivedUpPointerDownX = getReceivedPointerDownX(pointerId); + mLastReveivedUpPointerDownY = getReceivedPointerDownY(pointerId); + mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); + mLastReceivedUpPointerActive = isActivePointer(pointerId); + + mReceivedPointersDown &= ~pointerFlag; + mActivePointers &= ~pointerFlag; + mReceivedPointerDownX[pointerId] = 0; + mReceivedPointerDownY[pointerId] = 0; + mReceivedPointerDownTime[pointerId] = 0; + + if (mActivePointers == 0) { + mHasMovingActivePointer = false; + } + if (mPrimaryActivePointerId == pointerId) { + mPrimaryActivePointerId = INVALID_POINTER_ID; + } + } + + /** + * Handles a injected pointer down event. + * + * @param pointerIndex The index of the pointer that has changed. + * @param event The event to be handled. + */ + private void handleInjectedPointerDown(int pointerIndex, MotionEvent event) { + final int pointerId = event.getPointerId(pointerIndex); + final int pointerFlag = (1 << pointerId); + mInjectedPointersDown |= pointerFlag; + mLastInjectedDownEventTime = event.getEventTime(); + } + + /** + * Handles a injected pointer up event. + * + * @param pointerIndex The index of the pointer that has changed. + * @param event The event to be handled. + */ + private void handleInjectedPointerUp(int pointerIndex, MotionEvent event) { + final int pointerId = event.getPointerId(pointerIndex); + final int pointerFlag = (1 << pointerId); + mInjectedPointersDown &= ~pointerFlag; + } + + /** + * Detects the active pointers in an event. + * + * @param event The event to examine. + */ + private void detectActivePointers(MotionEvent event) { + for (int i = 0, count = event.getPointerCount(); i < count; i++) { + final int pointerId = event.getPointerId(i); + if (mHasMovingActivePointer) { + // If already active => nothing to do. + if (isActivePointer(pointerId)) { + continue; + } + } + // Active pointers are ones that moved more than a given threshold. + final float pointerDeltaMove = computePointerDeltaMove(i, event); + if (pointerDeltaMove > mThresholdActivePointer) { + final int pointerFlag = (1 << pointerId); + mActivePointers |= pointerFlag; + mHasMovingActivePointer = true; + } + } + } + + /** + * @return The primary active pointer. + */ + private int findPrimaryActivePointer() { + int primaryActivePointerId = INVALID_POINTER_ID; + long minDownTime = Long.MAX_VALUE; + // Find the active pointer that went down first. + for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) { + if (isActivePointer(i)) { + final long downPointerTime = mReceivedPointerDownTime[i]; + if (downPointerTime < minDownTime) { + minDownTime = downPointerTime; + primaryActivePointerId = i; + } + } + } + return primaryActivePointerId; + } + + /** + * Computes the move for a given action pointer index since the + * corresponding pointer went down. + * + * @param pointerIndex The action pointer index. + * @param event The event to examine. + * @return The distance the pointer has moved. + */ + private float computePointerDeltaMove(int pointerIndex, MotionEvent event) { + final int pointerId = event.getPointerId(pointerIndex); + final float deltaX = event.getX(pointerIndex) - mReceivedPointerDownX[pointerId]; + final float deltaY = event.getY(pointerIndex) - mReceivedPointerDownY[pointerId]; + return (float) Math.hypot(deltaX, deltaY); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("========================="); + builder.append("\nDown pointers #"); + builder.append(getReceivedPointerDownCount()); + builder.append(" [ "); + for (int i = 0; i < MAX_POINTER_COUNT; i++) { + if (isReceivedPointerDown(i)) { + builder.append(i); + builder.append(" "); + } + } + builder.append("]"); + builder.append("\nActive pointers #"); + builder.append(getActivePointerCount()); + builder.append(" [ "); + for (int i = 0; i < MAX_POINTER_COUNT; i++) { + if (isActivePointer(i)) { + builder.append(i); + builder.append(" "); + } + } + builder.append("]"); + builder.append("\nPrimary active pointer id [ "); + builder.append(getPrimaryActivePointerId()); + builder.append(" ]"); + builder.append("\n========================="); + return builder.toString(); + } + } + + /** + * Class for delayed sending of hover events. + */ + private final class SendHoverDelayed implements Runnable { + private static final String LOG_TAG = "SendHoverEnterOrExitDelayed"; + + private MotionEvent mEvent; + private int mAction; + private int mPointerIndex; + private int mPolicyFlags; + + public void post(MotionEvent prototype, int action, int pointerIndex, int policyFlags, + long delay) { + remove(); + mEvent = MotionEvent.obtain(prototype); + mAction = action; + mPointerIndex = pointerIndex; + mPolicyFlags = policyFlags; + mHandler.postDelayed(this, delay); + } + + public void remove() { + mHandler.removeCallbacks(this); + clear(); + } + + private boolean isPenidng() { + return (mEvent != null); + } + + private void clear() { + if (!isPenidng()) { + return; + } + mEvent.recycle(); + mEvent = null; + mAction = 0; + mPointerIndex = -1; + mPolicyFlags = 0; + } + + public void forceSendAndRemove() { + if (isPenidng()) { + run(); + remove(); + } + } + + public void run() { + if (DEBUG) { + if (mAction == MotionEvent.ACTION_HOVER_ENTER) { + Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER); + } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) { + Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE"); + } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) { + Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT"); + } + } + + sendHoverEvent(mEvent, mAction, mPointerIndex, mPolicyFlags); + clear(); + } + } +} diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java index 7e1ab07..8f0001a 100644 --- a/services/java/com/android/server/wm/InputFilter.java +++ b/services/java/com/android/server/wm/InputFilter.java @@ -105,11 +105,13 @@ public abstract class InputFilter { private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier = InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, - InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null; + InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, + "InputFilter#InboundInputEventConsistencyVerifier") : null; private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier = InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, - InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null; + InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, + "InputFilter#OutboundInputEventConsistencyVerifier") : null; /** * Creates the input filter. diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 538e38c..8b739a4 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -398,6 +398,7 @@ public class WindowManagerService extends IWindowManager.Stub int mRotation = 0; int mRequestedRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + boolean mAltOrientation = false; int mLastRotationFlags; ArrayList<IRotationWatcher> mRotationWatchers = new ArrayList<IRotationWatcher>(); @@ -585,7 +586,6 @@ public class WindowManagerService extends IWindowManager.Stub } final Configuration mTempConfiguration = new Configuration(); - int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED; // The frame use to limit the size of the app running in compatibility mode. Rect mCompatibleScreenFrame = new Rect(); @@ -4954,7 +4954,52 @@ public class WindowManagerService extends IWindowManager.Stub rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation, mDisplayEnabled); if (DEBUG_ORIENTATION) Slog.v(TAG, "new rotation is set to " + rotation); + + int desiredRotation = rotation; + int lockedRotation = mPolicy.getLockedRotationLw(); + if (lockedRotation >= 0 && rotation != lockedRotation) { + // We are locked in a rotation but something is requesting + // a different rotation... we will either keep the locked + // rotation if it results in the same orientation, or have to + // switch into an emulated orientation mode. + + // First, we know that our rotation is actually going to be + // the locked rotation. + rotation = lockedRotation; + + // Now the difference between the desired and lockedRotation + // may mean that the orientation is different... if that is + // not the case, we can just make the desired rotation be the + // same as the new locked rotation. + switch (lockedRotation) { + case Surface.ROTATION_0: + if (rotation == Surface.ROTATION_180) { + desiredRotation = lockedRotation; + } + break; + case Surface.ROTATION_90: + if (rotation == Surface.ROTATION_270) { + desiredRotation = lockedRotation; + } + break; + case Surface.ROTATION_180: + if (rotation == Surface.ROTATION_0) { + desiredRotation = lockedRotation; + } + break; + case Surface.ROTATION_270: + if (rotation == Surface.ROTATION_90) { + desiredRotation = lockedRotation; + } + break; + } + } + changed = mDisplayEnabled && mRotation != rotation; + if (mAltOrientation != (rotation != desiredRotation)) { + changed = true; + mAltOrientation = rotation != desiredRotation; + } if (changed) { if (DEBUG_ORIENTATION) Slog.v(TAG, @@ -5000,6 +5045,7 @@ public class WindowManagerService extends IWindowManager.Stub Surface.setOrientation(0, rotation, animFlags); } } + for (int i=mWindows.size()-1; i>=0; i--) { WindowState w = mWindows.get(i); if (w.mSurface != null) { @@ -5440,8 +5486,32 @@ public class WindowManagerService extends IWindowManager.Stub // Use the effective "visual" dimensions based on current rotation final boolean rotated = (mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270); - final int dw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth; - final int dh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight; + final int realdw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth; + final int realdh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight; + + if (mAltOrientation) { + mCurDisplayWidth = realdw; + mCurDisplayHeight = realdh; + if (realdw > realdh) { + // Turn landscape into portrait. + int maxw = (int)(realdh/1.3f); + if (maxw < realdw) { + mCurDisplayWidth = maxw; + } + } else { + // Turn portrait into landscape. + int maxh = (int)(realdw/1.3f); + if (maxh < realdh) { + mCurDisplayHeight = maxh; + } + } + } else { + mCurDisplayWidth = realdw; + mCurDisplayHeight = realdh; + } + + final int dw = mCurDisplayWidth; + final int dh = mCurDisplayHeight; int orientation = Configuration.ORIENTATION_SQUARE; if (dw < dh) { @@ -5452,66 +5522,69 @@ public class WindowManagerService extends IWindowManager.Stub config.orientation = orientation; DisplayMetrics dm = new DisplayMetrics(); - mDisplay.getMetrics(dm); + mDisplay.getRealMetrics(dm); + + // Override display width and height with what we are computing, + // to be sure they remain consistent. + dm.widthPixels = dw; + dm.heightPixels = dh; + CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame); config.screenWidthDp = (int)(dm.widthPixels / dm.density); config.screenHeightDp = (int)(dm.heightPixels / dm.density); - if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { - // Note we only do this once because at this point we don't - // expect the screen to change in this way at runtime, and want - // to avoid all of this computation for every config change. - int longSize = dw; - int shortSize = dh; - if (longSize < shortSize) { - int tmp = longSize; - longSize = shortSize; - shortSize = tmp; - } - longSize = (int)(longSize/dm.density); - shortSize = (int)(shortSize/dm.density); - - // These semi-magic numbers define our compatibility modes for - // applications with different screens. These are guarantees to - // app developers about the space they can expect for a particular - // configuration. DO NOT CHANGE! - if (longSize < 470) { - // This is shorter than an HVGA normal density screen (which - // is 480 pixels on its long side). - mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL - | Configuration.SCREENLAYOUT_LONG_NO; + // Compute the screen layout size class. + int screenLayout; + int longSize = dw; + int shortSize = dh; + if (longSize < shortSize) { + int tmp = longSize; + longSize = shortSize; + shortSize = tmp; + } + longSize = (int)(longSize/dm.density); + shortSize = (int)(shortSize/dm.density); + + // These semi-magic numbers define our compatibility modes for + // applications with different screens. These are guarantees to + // app developers about the space they can expect for a particular + // configuration. DO NOT CHANGE! + if (longSize < 470) { + // This is shorter than an HVGA normal density screen (which + // is 480 pixels on its long side). + screenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL + | Configuration.SCREENLAYOUT_LONG_NO; + } else { + // What size is this screen screen? + if (longSize >= 960 && shortSize >= 720) { + // 1.5xVGA or larger screens at medium density are the point + // at which we consider it to be an extra large screen. + screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE; + } else if (longSize >= 640 && shortSize >= 480) { + // VGA or larger screens at medium density are the point + // at which we consider it to be a large screen. + screenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE; } else { - // What size is this screen screen? - if (longSize >= 960 && shortSize >= 720) { - // 1.5xVGA or larger screens at medium density are the point - // at which we consider it to be an extra large screen. - mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE; - } else if (longSize >= 640 && shortSize >= 480) { - // VGA or larger screens at medium density are the point - // at which we consider it to be a large screen. - mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE; - } else { - mScreenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL; - } - - // If this screen is wider than normal HVGA, or taller - // than FWVGA, then for old apps we want to run in size - // compatibility mode. - if (shortSize > 321 || longSize > 570) { - mScreenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED; - } + screenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL; + } - // Is this a long screen? - if (((longSize*3)/5) >= (shortSize-1)) { - // Anything wider than WVGA (5:3) is considering to be long. - mScreenLayout |= Configuration.SCREENLAYOUT_LONG_YES; - } else { - mScreenLayout |= Configuration.SCREENLAYOUT_LONG_NO; - } + // If this screen is wider than normal HVGA, or taller + // than FWVGA, then for old apps we want to run in size + // compatibility mode. + if (shortSize > 321 || longSize > 570) { + screenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED; + } + + // Is this a long screen? + if (((longSize*3)/5) >= (shortSize-1)) { + // Anything wider than WVGA (5:3) is considering to be long. + screenLayout |= Configuration.SCREENLAYOUT_LONG_YES; + } else { + screenLayout |= Configuration.SCREENLAYOUT_LONG_NO; } } - config.screenLayout = mScreenLayout; + config.screenLayout = screenLayout; // Determine whether a hard keyboard is available and enabled. boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; @@ -6722,8 +6795,8 @@ public class WindowManagerService extends IWindowManager.Stub } final long currentTime = SystemClock.uptimeMillis(); - final int dw = mCurDisplayWidth = mDisplay.getRealWidth(); - final int dh = mCurDisplayHeight = mDisplay.getRealHeight(); + final int dw = mCurDisplayWidth; + final int dh = mCurDisplayHeight; int i; @@ -8709,8 +8782,9 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen); pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig); pw.print(" mRotation="); pw.print(mRotation); - pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation); - pw.print(", mRequestedRotation="); pw.println(mRequestedRotation); + pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation); + pw.print(" mRequestedRotation="); pw.print(mRequestedRotation); + pw.print(" mAltOrientation="); pw.println(mAltOrientation); pw.print(" mDeferredRotation="); pw.print(mDeferredRotation); pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags); pw.print(" mAnimationPending="); pw.print(mAnimationPending); diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java index 2f9b026..76031a8 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java @@ -153,7 +153,8 @@ public class BiDiTestView extends View { float[] advances = new float[length]; float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0); setPaintDir(paint, dir); - float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, dir, advances, 0); + float textWidthICU = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0, + 1 /* use ICU */); logAdvances(text, textWidthHB, textWidthICU, advances); drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN); diff --git a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml index 69a33bc..174cc65 100644 --- a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml +++ b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml @@ -2,10 +2,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.rs.image"> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-sdk android:minSdkVersion="11" /> - <application android:label="Image Processing"> + <application android:label="Image Processing" + android:hardwareAccelerated="true"> <activity android:name="ImageProcessingActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg Binary files differdeleted file mode 100644 index 81a87b1..0000000 --- a/tests/RenderScriptTests/ImageProcessing/res/drawable-hdpi/data.jpg +++ /dev/null diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png Binary files differnew file mode 100644 index 0000000..856eeff --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg Binary files differdeleted file mode 100644 index 81a87b1..0000000 --- a/tests/RenderScriptTests/ImageProcessing/res/drawable/data.jpg +++ /dev/null diff --git a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml index b271b43..08a010d 100644 --- a/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml +++ b/tests/RenderScriptTests/ImageProcessing/res/layout/main.xml @@ -17,31 +17,12 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" - android:layout_height="fill_parent"> + android:layout_height="fill_parent" + android:id="@+id/toplevel"> <SurfaceView android:id="@+id/surface" android:layout_width="1dip" android:layout_height="1dip" /> - <ImageView - android:id="@+id/display" - android:layout_width="320dip" - android:layout_height="266dip" /> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/benchmark" - android:onClick="benchmark"/> - <TextView - android:id="@+id/benchmarkText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="8pt" - android:text="@string/saturation"/> - </LinearLayout> <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent"> @@ -49,6 +30,26 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> + <ImageView + android:id="@+id/display" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/benchmark" + android:onClick="benchmark"/> + <TextView + android:id="@+id/benchmarkText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="8pt" + android:text="@string/saturation"/> + </LinearLayout> <TextView android:id="@+id/inSaturationText" android:layout_width="match_parent" diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index 4f2f52ab..7462701 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java @@ -40,7 +40,6 @@ public class ImageProcessingActivity extends Activity SeekBar.OnSeekBarChangeListener { private Bitmap mBitmapIn; private Bitmap mBitmapOut; - private Bitmap mBitmapScratch; private ScriptC_threshold mScript; private ScriptC_vertical_blur mScriptVBlur; private ScriptC_horizontal_blur mScriptHBlur; @@ -78,9 +77,20 @@ public class ImageProcessingActivity extends Activity private SurfaceView mSurfaceView; private ImageView mDisplayView; + private boolean mIsProcessing; + class FilterCallback extends RenderScript.RSMessageHandler { private Runnable mAction = new Runnable() { public void run() { + + synchronized (mDisplayView) { + mIsProcessing = false; + } + + // This is a hack to work around an invalidation bug + mBitmapOut = Bitmap.createBitmap(mBitmapOut); + mOutPixelsAllocation.copyTo(mBitmapOut); + mDisplayView.setImageBitmap(mBitmapOut); mDisplayView.invalidate(); } }; @@ -99,156 +109,6 @@ public class ImageProcessingActivity extends Activity // Store our coefficients here float gaussian[]; - private long javaFilter() { - final int width = mBitmapIn.getWidth(); - final int height = mBitmapIn.getHeight(); - final int count = width * height; - - if (in == null) { - in = new int[count]; - interm = new int[count]; - out = new int[count]; - gaussian = new float[MAX_RADIUS * 2 + 1]; - mBitmapIn.getPixels(in, 0, width, 0, 0, width, height); - } - - long t = java.lang.System.currentTimeMillis(); - - int w, h, r; - - float fRadius = (float)mRadius; - int radius = (int)mRadius; - - // Compute gaussian weights for the blur - // e is the euler's number - float e = 2.718281828459045f; - float pi = 3.1415926535897932f; - // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) - // x is of the form [-radius .. 0 .. radius] - // and sigma varies with radius. - // Based on some experimental radius values and sigma's - // we approximately fit sigma = f(radius) as - // sigma = radius * 0.4 + 0.6 - // The larger the radius gets, the more our gaussian blur - // will resemble a box blur since with large sigma - // the gaussian curve begins to lose its shape - float sigma = 0.4f * fRadius + 0.6f; - // Now compute the coefficints - // We will store some redundant values to save some math during - // the blur calculations - // precompute some values - float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma); - float coeff2 = - 1.0f / (2.0f * sigma * sigma); - float normalizeFactor = 0.0f; - float floatR = 0.0f; - for (r = -radius; r <= radius; r ++) { - floatR = (float)r; - gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2); - normalizeFactor += gaussian[r + radius]; - } - - //Now we need to normalize the weights because all our coefficients need to add up to one - normalizeFactor = 1.0f / normalizeFactor; - for (r = -radius; r <= radius; r ++) { - floatR = (float)r; - gaussian[r + radius] *= normalizeFactor; - } - - float blurredPixelR = 0.0f; - float blurredPixelG = 0.0f; - float blurredPixelB = 0.0f; - float blurredPixelA = 0.0f; - - for (h = 0; h < height; h ++) { - for (w = 0; w < width; w ++) { - - blurredPixelR = 0.0f; - blurredPixelG = 0.0f; - blurredPixelB = 0.0f; - blurredPixelA = 0.0f; - - for (r = -radius; r <= radius; r ++) { - // Stepping left and right away from the pixel - int validW = w + r; - // Clamp to zero and width max() isn't exposed for ints yet - if (validW < 0) { - validW = 0; - } - if (validW > width - 1) { - validW = width - 1; - } - - int input = in[h*width + validW]; - - int R = ((input >> 24) & 0xff); - int G = ((input >> 16) & 0xff); - int B = ((input >> 8) & 0xff); - int A = (input & 0xff); - - float weight = gaussian[r + radius]; - - blurredPixelR += (float)(R)*weight; - blurredPixelG += (float)(G)*weight; - blurredPixelB += (float)(B)*weight; - blurredPixelA += (float)(A)*weight; - } - - int R = (int)blurredPixelR; - int G = (int)blurredPixelG; - int B = (int)blurredPixelB; - int A = (int)blurredPixelA; - - interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A); - } - } - - for (h = 0; h < height; h ++) { - for (w = 0; w < width; w ++) { - - blurredPixelR = 0.0f; - blurredPixelG = 0.0f; - blurredPixelB = 0.0f; - blurredPixelA = 0.0f; - for (r = -radius; r <= radius; r ++) { - int validH = h + r; - // Clamp to zero and width - if (validH < 0) { - validH = 0; - } - if (validH > height - 1) { - validH = height - 1; - } - - int input = interm[validH*width + w]; - - int R = ((input >> 24) & 0xff); - int G = ((input >> 16) & 0xff); - int B = ((input >> 8) & 0xff); - int A = (input & 0xff); - - float weight = gaussian[r + radius]; - - blurredPixelR += (float)(R)*weight; - blurredPixelG += (float)(G)*weight; - blurredPixelB += (float)(B)*weight; - blurredPixelA += (float)(A)*weight; - } - - int R = (int)blurredPixelR; - int G = (int)blurredPixelG; - int B = (int)blurredPixelB; - int A = (int)blurredPixelA; - - out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A); - } - } - - t = java.lang.System.currentTimeMillis() - t; - android.util.Log.v("Img", "Java frame time ms " + t); - mBitmapOut.setPixels(out, 0, width, 0, 0, width, height); - return t; - } - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { @@ -280,17 +140,14 @@ public class ImageProcessingActivity extends Activity mScriptVBlur.invoke_setSaturation(mSaturation); } - long t = java.lang.System.currentTimeMillis(); - if (true) { - mScript.invoke_filter(); - mOutPixelsAllocation.copyTo(mBitmapOut); - } else { - javaFilter(); - mDisplayView.invalidate(); + synchronized (mDisplayView) { + if (mIsProcessing) { + return; + } + mIsProcessing = true; } - t = java.lang.System.currentTimeMillis() - t; - android.util.Log.v("Img", "Renderscript frame time core ms " + t); + mScript.invoke_filter(); } } @@ -305,9 +162,8 @@ public class ImageProcessingActivity extends Activity super.onCreate(savedInstanceState); setContentView(R.layout.main); - mBitmapIn = loadBitmap(R.drawable.data); - mBitmapOut = loadBitmap(R.drawable.data); - mBitmapScratch = loadBitmap(R.drawable.data); + mBitmapIn = loadBitmap(R.drawable.city); + mBitmapOut = loadBitmap(R.drawable.city); mSurfaceView = (SurfaceView) findViewById(R.id.surface); mSurfaceView.getHolder().addCallback(this); diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs index 652ffd7..45eea5e 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs @@ -19,7 +19,7 @@ void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32 } else { for (int r = -fs->radius; r <= fs->radius; r ++) { // Stepping left and right away from the pixel - int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1)); + int validW = rsClamp((int)x + r, (int)0, (int)(fs->width - 1)); blurredPixel += input[validW].xyz * gPtr[0]; gPtr++; } diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs index bd4ae4e..6b0cde0 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs @@ -74,7 +74,7 @@ void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32 } } else { for (int r = -fs->radius; r <= fs->radius; r ++) { - int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1)); + int validH = rsClamp((int)y + r, (int)0, (int)(fs->height - 1)); const float4 *i = input + validH * fs->width; blurredPixel += i->xyz * gPtr[0]; gPtr++; |