Merge "Grant permissions to apps on sd when enabling/disabling packages."
diff --git a/api/current.xml b/api/current.xml
index 64f0c23..cd11b1d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -199698,6 +199698,8 @@
>
<implements name="android.widget.ExpandableListAdapter">
</implements>
+<implements name="android.widget.HeterogeneousExpandableList">
+</implements>
<constructor name="BaseExpandableListAdapter"
type="android.widget.BaseExpandableListAdapter"
static="false"
@@ -199717,6 +199719,32 @@
visibility="public"
>
</method>
+<method name="getChildType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getCombinedChildId"
return="long"
abstract="false"
@@ -199745,6 +199773,30 @@
<parameter name="groupId" type="long">
</parameter>
</method>
+<method name="getGroupType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isEmpty"
return="boolean"
abstract="false"
@@ -203487,6 +203539,64 @@
</parameter>
</method>
</class>
+<interface name="HeterogeneousExpandableList"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getChildType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGroupType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<class name="HorizontalScrollView"
extends="android.widget.FrameLayout"
abstract="false"
diff --git a/core/java/android/bluetooth/ScoSocket.java b/core/java/android/bluetooth/ScoSocket.java
index 116310a..b65a99a 100644
--- a/core/java/android/bluetooth/ScoSocket.java
+++ b/core/java/android/bluetooth/ScoSocket.java
@@ -86,14 +86,14 @@
/** Connect this SCO socket to the given BT address.
* Does not block.
*/
- public synchronized boolean connect(String address) {
+ public synchronized boolean connect(String address, String name) {
if (DBG) log("connect() " + this);
if (mState != STATE_READY) {
if (DBG) log("connect(): Bad state");
return false;
}
acquireWakeLock();
- if (connectNative(address)) {
+ if (connectNative(address, name)) {
mState = STATE_CONNECTING;
return true;
} else {
@@ -102,7 +102,7 @@
return false;
}
}
- private native boolean connectNative(String address);
+ private native boolean connectNative(String address, String name);
/** Accept incoming SCO connections.
* Does not block.
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 816f8a8..72ceb9b 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -139,7 +139,10 @@
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
}
- Log.w(TAG, "finalizer should never be called on sql: " + mSqlStmt, mStackTrace);
+ int len = mSqlStmt.length();
+ Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
+ "that you explicitly call close() on your cursor: " +
+ mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
releaseSqlStatement();
} finally {
super.finalize();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 984e48b..7e748c0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5728,7 +5728,7 @@
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
- * units used by {@link #computeHorizontalScrollRange()} and
+ * units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollOffset()}.</p>
*
* <p>The default extent is the drawing height of this view.</p>
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 176169e..2709cff 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -28,11 +28,11 @@
public class DecelerateInterpolator implements Interpolator {
public DecelerateInterpolator() {
}
-
+
/**
* Constructor
*
- * @param factor Degree to which the animation should be eased. Seting factor to 1.0f produces
+ * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
* an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
* ease-out effect (i.e., it starts even faster and ends evens slower)
*/
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 622d22d..1becf9e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1903,15 +1903,8 @@
// Expects y in view coordinates
private int pinLocY(int y) {
if (mInOverScrollMode) return y;
- int titleH = getTitleHeight();
- // if the titlebar is still visible, just pin against 0
- if (y <= titleH) {
- return Math.max(y, 0);
- }
- // convert to 0-based coordinate (subtract the title height)
- // pin(), and then add the title height back in
- return pinLoc(y - titleH, getViewHeight(),
- computeVerticalScrollRange()) + titleH;
+ return pinLoc(y, getViewHeightWithTitle(),
+ computeVerticalScrollRange() + getTitleHeight());
}
/**
@@ -3133,7 +3126,7 @@
mOverScrollBackground = new Paint();
Bitmap bm = BitmapFactory.decodeResource(
mContext.getResources(),
- com.android.internal.R.drawable.pattern_underwear);
+ com.android.internal.R.drawable.status_bar_background);
mOverScrollBackground.setShader(new BitmapShader(bm,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
}
@@ -4203,12 +4196,6 @@
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
sendOurVisibleRect();
- // update WebKit if visible title bar height changed. The logic is same
- // as getVisibleTitleHeight.
- int titleHeight = getTitleHeight();
- if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
- sendViewSizeZoom();
- }
}
@Override
@@ -5371,7 +5358,7 @@
private int computeMaxScrollY() {
return Math.max(computeVerticalScrollRange() + getTitleHeight()
- - getViewHeightWithTitle(), getTitleHeight());
+ - getViewHeightWithTitle(), 0);
}
public void flingScroll(int vx, int vy) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a78429e..86011d7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -32,6 +32,7 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -2388,6 +2389,7 @@
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
mScrollY = scrollY;
+
if (clampedY) {
// Velocity is broken by hitting the limit; don't start a fling off of this.
if (mVelocityTracker != null) {
@@ -2561,7 +2563,7 @@
/**
* Tracks the decay of a fling scroll
*/
- private OverScroller mScroller;
+ private final OverScroller mScroller;
/**
* Y value reported by mScroller on the previous fling
@@ -2598,6 +2600,21 @@
void startOverfling(int initialVelocity) {
mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight());
+ edgeReached();
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ invalidate();
+ post(this);
+ }
+
+ void edgeReached() {
+ mScroller.notifyVerticalEdgeReached(mScrollY, 0, Integer.MAX_VALUE);
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ invalidate();
+ post(this);
+ }
+
+ void marginReached() {
+ mScroller.notifyVerticalBoundaryReached(mScrollY, 0);
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
@@ -2677,11 +2694,7 @@
overscrollBy(0, overshoot, 0, mScrollY, 0, 0,
0, getOverscrollMax(), false);
}
- float vel = scroller.getCurrVelocity();
- if (delta > 0) {
- vel = -vel;
- }
- startOverfling(Math.round(vel));
+ edgeReached();
break;
}
@@ -2738,7 +2751,7 @@
private int mBoundPos;
private int mLastSeenPos;
private int mScrollDuration;
- private int mExtraScroll;
+ private final int mExtraScroll;
PositionScroller() {
mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
@@ -3977,7 +3990,7 @@
for (int i = 0; i < count; i++) {
if (activeViews[i] != null) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
"AbsListView " + this + " has a view in its active recycler: " +
activeViews[i]);
}
@@ -4005,12 +4018,12 @@
final View view = scrap.get(i);
if (view.getParent() != null) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
" has a view in its scrap heap still attached to a parent: " + view);
}
if (indexOfChild(view) >= 0) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
" has a view in its scrap heap that is also a direct child: " + view);
}
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 5b52107..eb2da71 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1629,7 +1629,10 @@
// of iterating throught he list of observers.
post(new Runnable() {
public void run() {
- updateDropDownForFilter(mAdapter.getCount());
+ final ListAdapter adapter = mAdapter;
+ if (adapter != null) {
+ updateDropDownForFilter(adapter.getCount());
+ }
}
});
}
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 1bba7f0..396b7ae 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -18,7 +18,6 @@
import android.database.DataSetObservable;
import android.database.DataSetObserver;
-import android.view.KeyEvent;
/**
* Base class for a {@link ExpandableListAdapter} used to provide data and Views
@@ -31,7 +30,8 @@
* @see SimpleExpandableListAdapter
* @see SimpleCursorTreeAdapter
*/
-public abstract class BaseExpandableListAdapter implements ExpandableListAdapter {
+public abstract class BaseExpandableListAdapter implements ExpandableListAdapter,
+ HeterogeneousExpandableList {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
@@ -102,5 +102,37 @@
public boolean isEmpty() {
return getGroupCount() == 0;
}
-
+
+
+ /**
+ * {@inheritDoc}
+ * @return 0 for any group or child position, since only one child type count is declared.
+ */
+ public int getChildType(int groupPosition, int childPosition) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1 as a default value in BaseExpandableListAdapter.
+ */
+ public int getChildTypeCount() {
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 0 for any groupPosition, since only one group type count is declared.
+ */
+ public int getGroupType(int groupPosition) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1 as a default value in BaseExpandableListAdapter.
+ */
+ public int getGroupTypeCount() {
+ return 1;
+ }
}
diff --git a/core/java/android/widget/ExpandableListAdapter.java b/core/java/android/widget/ExpandableListAdapter.java
index b75983c..7f6781b 100644
--- a/core/java/android/widget/ExpandableListAdapter.java
+++ b/core/java/android/widget/ExpandableListAdapter.java
@@ -17,7 +17,6 @@
package android.widget;
import android.database.DataSetObserver;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -108,7 +107,7 @@
/**
* Gets a View that displays the given group. This View is only for the
* group--the Views for the group's children will be fetched using
- * getChildrenView.
+ * {@link #getChildView(int, int, boolean, View, ViewGroup)}.
*
* @param groupPosition the position of the group for which the View is
* returned
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 01d3a4a..2ff6b70 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -442,8 +442,8 @@
View retValue;
if (posMetadata.position.type == ExpandableListPosition.GROUP) {
- retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos, posMetadata
- .isExpanded(), convertView, parent);
+ retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos,
+ posMetadata.isExpanded(), convertView, parent);
} else if (posMetadata.position.type == ExpandableListPosition.CHILD) {
final boolean isLastChild = posMetadata.groupMetadata.lastChildFlPos == flatListPos;
@@ -464,10 +464,21 @@
final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position;
int retValue;
- if (pos.type == ExpandableListPosition.GROUP) {
- retValue = 0;
+ if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+ HeterogeneousExpandableList adapter =
+ (HeterogeneousExpandableList) mExpandableListAdapter;
+ if (pos.type == ExpandableListPosition.GROUP) {
+ retValue = adapter.getGroupType(pos.groupPos);
+ } else {
+ final int childType = adapter.getChildType(pos.groupPos, pos.childPos);
+ retValue = adapter.getGroupTypeCount() + childType;
+ }
} else {
- retValue = 1;
+ if (pos.type == ExpandableListPosition.GROUP) {
+ retValue = 0;
+ } else {
+ retValue = 1;
+ }
}
pos.recycle();
@@ -477,7 +488,13 @@
@Override
public int getViewTypeCount() {
- return 2;
+ if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+ HeterogeneousExpandableList adapter =
+ (HeterogeneousExpandableList) mExpandableListAdapter;
+ return adapter.getGroupTypeCount() + adapter.getChildTypeCount();
+ } else {
+ return 2;
+ }
}
@Override
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
new file mode 100644
index 0000000..1292733
--- /dev/null
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Additional methods that when implemented make an
+ * {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
+ * mechanism.
+ *
+ * An {@link ExpandableListAdapter} declares one view type for its group items
+ * and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
+ * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
+ * different types of group and/or child item views, should use an adapter that implements this
+ * interface. This way, the recycled views that will be provided to
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * and
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * will be of the appropriate group or child type, resulting in a more efficient reuse of the
+ * previously created views.
+ */
+public interface HeterogeneousExpandableList {
+ /**
+ * Get the type of group View that will be created by
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . for the specified group item.
+ *
+ * @param groupPosition the position of the group for which the type should be returned.
+ * @return An integer representing the type of group View. Two group views should share the same
+ * type if one can be converted to the other in
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
+ * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+ * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+ * @see getGroupTypeCount()
+ */
+ int getGroupType(int groupPosition);
+
+ /**
+ * Get the type of child View that will be created by
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * for the specified child item.
+ *
+ * @param groupPosition the position of the group that the child resides in
+ * @param childPosition the position of the child with respect to other children in the group
+ * @return An integer representing the type of child View. Two child views should share the same
+ * type if one can be converted to the other in
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
+ * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+ * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+ * @see getChildTypeCount()
+ */
+ int getChildType(int groupPosition, int childPosition);
+
+ /**
+ * <p>
+ * Returns the number of types of group Views that will be created by
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . Each type represents a set of views that can be converted in
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . If the adapter always returns the same type of View for all group items, this method should
+ * return 1.
+ * </p>
+ * <p>
+ * This method will only be called when the adapter is set on the {@link AdapterView}.
+ * </p>
+ *
+ * @return The number of types of group Views that will be created by this adapter.
+ * @see getChildTypeCount()
+ * @see getGroupType()
+ */
+ int getGroupTypeCount();
+
+ /**
+ * <p>
+ * Returns the number of types of child Views that will be created by
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * . Each type represents a set of views that can be converted in
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * , for any group. If the adapter always returns the same type of View for
+ * all child items, this method should return 1.
+ * </p>
+ * <p>
+ * This method will only be called when the adapter is set on the {@link AdapterView}.
+ * </p>
+ *
+ * @return The total number of types of child Views that will be created by this adapter.
+ * @see getGroupTypeCount()
+ * @see getChildType()
+ */
+ int getChildTypeCount();
+}
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 8469c8b..6258024 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -17,8 +17,8 @@
package android.widget;
import android.content.Context;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
/**
* This class encapsulates scrolling with the ability to overshoot the bounds
@@ -27,110 +27,34 @@
*
* @hide Pending API approval
*/
-public class OverScroller {
- private static final int SPRINGBACK_DURATION = 150;
- private static final int OVERFLING_DURATION = 150;
-
- private static final int MODE_DEFAULT = 0;
- private static final int MODE_OVERFLING = 1;
- private static final int MODE_SPRINGBACK = 2;
-
- private Scroller mDefaultScroller;
- private Scroller mDecelScroller;
- private Scroller mAccelDecelScroller;
- private Scroller mCurrScroller;
-
- private int mScrollMode = MODE_DEFAULT;
-
- private int mMinimumX;
- private int mMinimumY;
- private int mMaximumX;
- private int mMaximumY;
-
- public OverScroller(Context context) {
- mDefaultScroller = new Scroller(context);
- mDecelScroller = new Scroller(context, new DecelerateInterpolator());
- mAccelDecelScroller = new Scroller(context, new AccelerateDecelerateInterpolator());
- mCurrScroller = mDefaultScroller;
- }
-
+public class OverScroller extends Scroller {
+
+ // Identical to mScrollers, but casted to MagneticOverScroller.
+ private MagneticOverScroller mOverScrollerX;
+ private MagneticOverScroller mOverScrollerY;
+
/**
- * Call this when you want to know the new location. If it returns true,
- * the animation is not yet finished. loc will be altered to provide the
- * new location.
- */
- public boolean computeScrollOffset() {
- boolean inProgress = mCurrScroller.computeScrollOffset();
-
- switch (mScrollMode) {
- case MODE_OVERFLING:
- if (!inProgress) {
- // Overfling ended
- if (springback(mCurrScroller.getCurrX(), mCurrScroller.getCurrY(),
- mMinimumX, mMaximumX, mMinimumY, mMaximumY, mAccelDecelScroller)) {
- return mCurrScroller.computeScrollOffset();
- } else {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- }
- }
- break;
-
- case MODE_SPRINGBACK:
- if (!inProgress) {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- }
- break;
-
- case MODE_DEFAULT:
- // Fling/autoscroll - did we go off the edge?
- if (inProgress) {
- Scroller scroller = mCurrScroller;
- final int x = scroller.getCurrX();
- final int y = scroller.getCurrY();
- final int minX = mMinimumX;
- final int maxX = mMaximumX;
- final int minY = mMinimumY;
- final int maxY = mMaximumY;
- if (x < minX || x > maxX || y < minY || y > maxY) {
- final int startx = scroller.getStartX();
- final int starty = scroller.getStartY();
- final int time = scroller.timePassed();
- final float timeSecs = time / 1000.f;
- final float xvel = ((x - startx) / timeSecs);
- final float yvel = ((y - starty) / timeSecs);
-
- if ((x < minX && xvel > 0) || (y < minY && yvel > 0) ||
- (x > maxX && xvel < 0) || (y > maxY && yvel < 0)) {
- // If our velocity would take us back into valid areas,
- // try to springback rather than overfling.
- if (springback(x, y, minX, maxX, minY, maxY)) {
- return mCurrScroller.computeScrollOffset();
- }
- } else {
- overfling(x, y, xvel, yvel);
- return mCurrScroller.computeScrollOffset();
- }
- }
- }
- break;
- }
-
- return inProgress;
+ * Creates an OverScroller with a viscous fluid scroll interpolator.
+ * @param context
+ */
+ public OverScroller(Context context) {
+ this(context, null);
}
-
- private void overfling(int startx, int starty, float xvel, float yvel) {
- Scroller scroller = mDecelScroller;
- final float durationSecs = (OVERFLING_DURATION / 1000.f);
- int dx = (int)(xvel * durationSecs) / 8;
- int dy = (int)(yvel * durationSecs) / 8;
- mCurrScroller.abortAnimation();
- scroller.startScroll(startx, starty, dx, dy, OVERFLING_DURATION);
- mCurrScroller = scroller;
- mScrollMode = MODE_OVERFLING;
+
+ /**
+ * Creates a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used.
+ */
+ public OverScroller(Context context, Interpolator interpolator) {
+ super(context, interpolator);
}
-
+
+ @Override
+ void instantiateScrollers() {
+ mScrollerX = mOverScrollerX = new MagneticOverScroller();
+ mScrollerY = mOverScrollerY = new MagneticOverScroller();
+ }
+
/**
* Call this when you want to 'spring back' into a valid coordinate range.
*
@@ -140,310 +64,262 @@
* @param maxX Maximum valid X value
* @param minY Minimum valid Y value
* @param maxY Minimum valid Y value
- * @return true if a springback was initiated, false if startX/startY was
+ * @return true if a springback was initiated, false if startX and startY were
* already within the valid range.
*/
- public boolean springback(int startX, int startY, int minX, int maxX,
- int minY, int maxY) {
- return springback(startX, startY, minX, maxX, minY, maxY, mDecelScroller);
- }
-
- private boolean springback(int startX, int startY, int minX, int maxX,
- int minY, int maxY, Scroller scroller) {
- int xoff = 0;
- int yoff = 0;
- if (startX < minX) {
- xoff = minX - startX;
- } else if (startX > maxX) {
- xoff = maxX - startX;
- }
- if (startY < minY) {
- yoff = minY - startY;
- } else if (startY > maxY) {
- yoff = maxY - startY;
- }
-
- if (xoff != 0 || yoff != 0) {
- mCurrScroller.abortAnimation();
- scroller.startScroll(startX, startY, xoff, yoff, SPRINGBACK_DURATION);
- mCurrScroller = scroller;
- mScrollMode = MODE_SPRINGBACK;
- return true;
- }
-
- return false;
+ public boolean springback(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+ mMode = FLING_MODE;
+ return mOverScrollerX.springback(startX, minX, maxX)
+ || mOverScrollerY.springback(startY, minY, maxY);
}
- /**
- *
- * Returns whether the scroller has finished scrolling.
- *
- * @return True if the scroller has finished scrolling, false otherwise.
- */
- public final boolean isFinished() {
- return mCurrScroller.isFinished();
- }
-
- /**
- * Returns the current X offset in the scroll.
- *
- * @return The new X offset as an absolute distance from the origin.
- */
- public final int getCurrX() {
- return mCurrScroller.getCurrX();
- }
-
- /**
- * Returns the current Y offset in the scroll.
- *
- * @return The new Y offset as an absolute distance from the origin.
- */
- public final int getCurrY() {
- return mCurrScroller.getCurrY();
- }
-
- /**
- * Stops the animation, resets any springback/overfling and completes
- * any standard flings/scrolls in progress.
- */
- public void abortAnimation() {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mCurrScroller.abortAnimation();
- }
-
- /**
- * Start scrolling by providing a starting point and the distance to travel.
- * The scroll will use the default value of 250 milliseconds for the
- * duration. This version does not spring back to boundaries.
- *
- * @param startX Starting horizontal scroll offset in pixels. Positive
- * numbers will scroll the content to the left.
- * @param startY Starting vertical scroll offset in pixels. Positive numbers
- * will scroll the content up.
- * @param dx Horizontal distance to travel. Positive numbers will scroll the
- * content to the left.
- * @param dy Vertical distance to travel. Positive numbers will scroll the
- * content up.
- */
- public void startScroll(int startX, int startY, int dx, int dy) {
- final int minX = Math.min(startX, startX + dx);
- final int maxX = Math.max(startX, startX + dx);
- final int minY = Math.min(startY, startY + dy);
- final int maxY = Math.max(startY, startY + dy);
- startScroll(startX, startY, dx, dy, minX, maxX, minY, maxY);
- }
-
- /**
- * Start scrolling by providing a starting point and the distance to travel.
- * The scroll will use the default value of 250 milliseconds for the
- * duration. This version will spring back to the provided boundaries if
- * the scroll value would take it too far.
- *
- * @param startX Starting horizontal scroll offset in pixels. Positive
- * numbers will scroll the content to the left.
- * @param startY Starting vertical scroll offset in pixels. Positive numbers
- * will scroll the content up.
- * @param dx Horizontal distance to travel. Positive numbers will scroll the
- * content to the left.
- * @param dy Vertical distance to travel. Positive numbers will scroll the
- * content up.
- * @param minX Minimum X value. The scroller will not scroll past this
- * point.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
- */
- public void startScroll(int startX, int startY, int dx, int dy,
- int minX, int maxX, int minY, int maxY) {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = minX;
- mMaximumX = maxX;
- mMinimumY = minY;
- mMaximumY = maxY;
- mCurrScroller.startScroll(startX, startY, dx, dy);
- }
-
- /**
- * Start scrolling by providing a starting point and the distance to travel.
- *
- * @param startX Starting horizontal scroll offset in pixels. Positive
- * numbers will scroll the content to the left.
- * @param startY Starting vertical scroll offset in pixels. Positive numbers
- * will scroll the content up.
- * @param dx Horizontal distance to travel. Positive numbers will scroll the
- * content to the left.
- * @param dy Vertical distance to travel. Positive numbers will scroll the
- * content up.
- * @param duration Duration of the scroll in milliseconds.
- */
- public void startScroll(int startX, int startY, int dx, int dy, int duration) {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = Math.min(startX, startX + dx);
- mMinimumY = Math.min(startY, startY + dy);
- mMaximumX = Math.max(startX, startX + dx);
- mMaximumY = Math.max(startY, startY + dy);
- mCurrScroller.startScroll(startX, startY, dx, dy, duration);
- }
-
- /**
- * Returns the duration of the active scroll in progress; standard, fling,
- * springback, or overfling. Does not account for any overflings or springback
- * that may result.
- */
- public int getDuration() {
- return mCurrScroller.getDuration();
- }
-
- /**
- * Start scrolling based on a fling gesture. The distance travelled will
- * depend on the initial velocity of the fling.
- *
- * @param startX Starting point of the scroll (X)
- * @param startY Starting point of the scroll (Y)
- * @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
- * @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
- * @param minX Minimum X value. The scroller will not scroll past this
- * point.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
- */
+ @Override
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
- this.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+ fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
/**
- * Start scrolling based on a fling gesture. The distance travelled will
+ * Start scrolling based on a fling gesture. The distance traveled will
* depend on the initial velocity of the fling.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
+ * second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
- * @param minX Minimum X value. The scroller will not scroll past this
- * point unless overX > 0. If overfling is allowed, it will use minX
- * as a springback boundary.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point unless overX > 0. If overfling is allowed, it will use maxX
- * as a springback boundary.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point unless overY > 0. If overfling is allowed, it will use minY
- * as a springback boundary.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point unless overY > 0. If overfling is allowed, it will use maxY
- * as a springback boundary.
+ * second
+ * @param minX Minimum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use minX as
+ * a springback boundary.
+ * @param maxX Maximum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use maxX as
+ * a springback boundary.
+ * @param minY Minimum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use minY as
+ * a springback boundary.
+ * @param maxY Maximum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use maxY as
+ * a springback boundary.
* @param overX Overfling range. If > 0, horizontal overfling in either
- * direction will be possible.
+ * direction will be possible.
* @param overY Overfling range. If > 0, vertical overfling in either
- * direction will be possible.
+ * direction will be possible.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY, int overX, int overY) {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = minX;
- mMaximumX = maxX;
- mMinimumY = minY;
- mMaximumY = maxY;
- mCurrScroller.fling(startX, startY, velocityX, velocityY,
- minX - overX, maxX + overX, minY - overY, maxY + overY);
+ mMode = FLING_MODE;
+ mOverScrollerX.fling(startX, velocityX, minX, maxX, overX);
+ mOverScrollerY.fling(startY, velocityY, minY, maxY, overY);
+ }
+
+ void notifyHorizontalBoundaryReached(int startX, int finalX) {
+ mOverScrollerX.springback(startX, finalX, finalX);
+ }
+
+ void notifyVerticalBoundaryReached(int startY, int finalY) {
+ mOverScrollerY.springback(startY, finalY, finalY);
+ }
+
+ void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
+ mOverScrollerX.notifyEdgeReached(startX, finalX, overX);
+ }
+
+ void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
+ mOverScrollerY.notifyEdgeReached(startY, finalY, overY);
}
/**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
+ * Returns whether the current Scroller position is overscrolled or still within the minimum and
+ * maximum bounds provided in the
+ * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
*
- * @return The final X offset as an absolute distance from the origin.
- */
- public int getFinalX() {
- return mCurrScroller.getFinalX();
- }
-
- /**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
+ * One should check this value before calling
+ * {@link startScroll(int, int, int, int)} as the interpolation currently in progress to restore
+ * a valid position will then be stopped. The caller has to take into account the fact that the
+ * started scroll will start from an overscrolled position.
*
- * @return The final Y offset as an absolute distance from the origin.
+ * @return true when the current position is overscrolled.
*/
- public int getFinalY() {
- return mCurrScroller.getFinalY();
+ public boolean isOverscrolled() {
+ return ((!mOverScrollerX.mFinished && mOverScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+ (!mOverScrollerY.mFinished && mOverScrollerY.mState != MagneticOverScroller.TO_EDGE));
}
-
- /**
- * @hide
- * Returns the current velocity.
- *
- * @return The original velocity less the deceleration. Result may be
- * negative.
- */
- public float getCurrVelocity() {
- return mCurrScroller.getCurrVelocity();
- }
-
- /**
- * Extend the scroll animation. This allows a running animation to scroll
- * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
- *
- * @param extend Additional time to scroll in milliseconds.
- * @see #setFinalX(int)
- * @see #setFinalY(int)
- */
- public void extendDuration(int extend) {
- if (mScrollMode == MODE_DEFAULT) {
- mDefaultScroller.extendDuration(extend);
+
+ static class MagneticOverScroller extends Scroller.MagneticScroller {
+ private static final int TO_EDGE = 0;
+ private static final int TO_BOUNDARY = 1;
+ private static final int TO_BOUNCE = 2;
+
+ private int mState = TO_EDGE;
+
+ // The allowed overshot distance before boundary is reached.
+ private int mOver;
+
+ // When the scroll goes beyond the edges limits, the deceleration is
+ // multiplied by this coefficient, so that the return to a valid
+ // position is faster.
+ private static final float OVERSCROLL_DECELERATION_COEF = 16.0f;
+
+ // If the velocity is smaller than this value, no bounce is triggered
+ // when the edge limits are reached (would result in a zero pixels
+ // displacement anyway).
+ private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 200.0f;
+
+ // Could be made public for tuning, but applications would no longer
+ // have the same look and feel.
+ private static final float BOUNCE_COEFFICIENT = 0.4f;
+
+ /*
+ * Get a signed deceleration that will reduce the velocity.
+ */
+ @Override
+ float getDeceleration(int velocity) {
+ float decelerationY = super.getDeceleration(velocity);
+ if (mState != TO_EDGE) {
+ decelerationY *= OVERSCROLL_DECELERATION_COEF;
+ }
+ return decelerationY;
}
- }
-
- /**
- * Sets the final position (X) for this scroller.
- *
- * @param newX The new X offset as an absolute distance from the origin.
- * @see #extendDuration(int)
- * @see #setFinalY(int)
- */
- public void setFinalX(int newX) {
- if (mScrollMode == MODE_DEFAULT) {
- if (newX < mMinimumX) {
- mMinimumX = newX;
+
+ boolean springback(int start, int min, int max) {
+ mFinished = true;
+
+ mStart = start;
+ mVelocity = 0;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = 0;
+
+ if (start < min) {
+ startSpringback(start, min, -1);
+ } else if (start > max) {
+ startSpringback(start, max, 1);
}
- if (newX > mMaximumX) {
- mMaximumX = newX;
- }
- mDefaultScroller.setFinalX(newX);
+
+ return !mFinished;
}
- }
-
- /**
- * Sets the final position (Y) for this scroller.
- *
- * @param newY The new Y offset as an absolute distance from the origin.
- * @see #extendDuration(int)
- * @see #setFinalX(int)
- */
- public void setFinalY(int newY) {
- if (mScrollMode == MODE_DEFAULT) {
- if (newY < mMinimumY) {
- mMinimumY = newY;
+
+ private void startSpringback(int start, int end, int sign) {
+ mFinished = false;
+ mState = TO_BOUNCE;
+ mDeceleration = getDeceleration(sign);
+ mFinal = end;
+ mDuration = (int) (1000.0f * Math.sqrt(2.0f * (end - start) / mDeceleration));
+ }
+
+ void fling(int start, int velocity, int min, int max, int over) {
+ mState = TO_EDGE;
+ mOver = over;
+
+ super.fling(start, velocity, min, max);
+
+ if (mStart > max) {
+ if (mStart >= max + over) {
+ springback(max + over, min, max);
+ } else {
+ // Make sure the deceleration brings us back to edge
+ mVelocity = velocity > 0 ? velocity : -velocity;
+ mCurrVelocity = velocity;
+ notifyEdgeReached(start, max, over);
+ }
+ } else {
+ if (mStart < min) {
+ if (mStart <= min - over) {
+ springback(min - over, min, max);
+ } else {
+ // Make sure the deceleration brings us back to edge
+ mVelocity = velocity < 0 ? velocity : -velocity;
+ mCurrVelocity = velocity;
+ notifyEdgeReached(start, min, over);
+ }
+ }
}
- if (newY > mMaximumY) {
- mMaximumY = newY;
+ }
+
+ void notifyEdgeReached(int start, int end, int over) {
+ // Compute post-edge deceleration
+ mState = TO_BOUNDARY;
+ mDeceleration = getDeceleration(mVelocity);
+
+ // Local time, used to compute edge crossing time.
+ float timeCurrent = mCurrVelocity / mDeceleration;
+ final int distance = end - start;
+ float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration)
+ + (timeCurrent * timeCurrent));
+
+ mVelocity = (int) (mDeceleration * timeEdge);
+
+ // Simulate a symmetric bounce that started from edge
+ mStart = end;
+
+ mOver = over;
+
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge));
+
+ onEdgeReached();
+ }
+
+ void onEdgeReached() {
+ // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
+ mState = TO_BOUNDARY;
+ mDeceleration = getDeceleration(mVelocity);
+
+ int distance = Math.round((mVelocity * mVelocity) / (2.0f * mDeceleration));
+
+ if (Math.abs(distance) < mOver) {
+ // Deceleration will bring us back to final position
+ mState = TO_BOUNCE;
+ mFinal = mStart;
+ mDuration = (int) (-2000.0f * mVelocity / mDeceleration);
+ } else {
+ // Velocity is too high, we will hit the boundary limit
+ mFinal = mStart + (mVelocity > 0 ? mOver : -mOver);
+ mDuration = computeDuration(mStart, mFinal, mVelocity, mDeceleration);
}
- mDefaultScroller.setFinalY(newY);
+ }
+
+ @Override
+ boolean continueWhenFinished() {
+ switch (mState) {
+ case TO_EDGE:
+ // Duration from start to null velocity
+ int duration = (int) (-1000.0f * mVelocity / mDeceleration);
+ if (mDuration < duration) {
+ // If the animation was clamped, we reached the edge
+ mStart = mFinal;
+ // Speed when edge was reached
+ mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f);
+ mStartTime += mDuration;
+ onEdgeReached();
+ } else {
+ // Normal stop, no need to continue
+ return false;
+ }
+ break;
+ case TO_BOUNDARY:
+ mStartTime += mDuration;
+ mStart = mFinal;
+ mFinal = mStart - (mVelocity > 0 ? mOver : -mOver);
+ mVelocity = 0;
+ mDuration = (int) (1000.0f * Math.sqrt(Math.abs(2.0f * mOver / mDeceleration)));
+ mState = TO_BOUNCE;
+ break;
+ case TO_BOUNCE:
+ float edgeVelocity = mVelocity + mDeceleration * mDuration / 1000.0f;
+ mVelocity = (int) (-edgeVelocity * BOUNCE_COEFFICIENT);
+ if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
+ return false;
+ }
+ mStart = mFinal;
+ mStartTime += mDuration;
+ mDuration = (int) (-2000.0f * mVelocity / mDeceleration);
+ break;
+ }
+
+ update();
+ return true;
}
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 68c0ff0..239c5f4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1272,7 +1272,7 @@
* Fling the scroll view
*
* @param velocityY The initial velocity in the Y direction. Positive
- * numbers mean that the finger/curor is moving down the screen,
+ * numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
public void fling(int velocityY) {
@@ -1307,6 +1307,7 @@
*
* <p>This version also clamps the scrolling to the bounds of our child.
*/
+ @Override
public void scrollTo(int x, int y) {
// we rely on the fact the View.scrollBy calls scrollTo.
if (getChildCount() > 0) {
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 11dab02..542866a 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -16,8 +16,10 @@
package android.widget;
+
import android.content.Context;
import android.hardware.SensorManager;
+import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -25,48 +27,34 @@
/**
* This class encapsulates scrolling. The duration of the scroll
- * can be passed in the constructor and specifies the maximum time that
- * the scrolling animation should take. Past this time, the scrolling is
- * automatically moved to its final stage and computeScrollOffset()
- * will always return false to indicate that scrolling is over.
+ * is either specified along with the distance or depends on the initial fling velocity.
+ * Past this time, the scrolling is automatically moved to its final stage and
+ * computeScrollOffset() will always return false to indicate that scrolling is over.
*/
public class Scroller {
- private int mMode;
+ int mMode;
- private int mStartX;
- private int mStartY;
- private int mFinalX;
- private int mFinalY;
+ MagneticScroller mScrollerX;
+ MagneticScroller mScrollerY;
- private int mMinX;
- private int mMaxX;
- private int mMinY;
- private int mMaxY;
+ private final Interpolator mInterpolator;
- private int mCurrX;
- private int mCurrY;
- private long mStartTime;
- private int mDuration;
- private float mDurationReciprocal;
- private float mDeltaX;
- private float mDeltaY;
- private float mViscousFluidScale;
- private float mViscousFluidNormalize;
- private boolean mFinished;
- private Interpolator mInterpolator;
+ static final int DEFAULT_DURATION = 250;
+ static final int SCROLL_MODE = 0;
+ static final int FLING_MODE = 1;
- private float mCoeffX = 0.0f;
- private float mCoeffY = 1.0f;
- private float mVelocity;
+ // This controls the viscous fluid effect (how much of it)
+ private final static float VISCOUS_FLUID_SCALE = 8.0f;
+ private static float VISCOUS_FLUID_NORMALIZE;
- private static final int DEFAULT_DURATION = 250;
- private static final int SCROLL_MODE = 0;
- private static final int FLING_MODE = 1;
-
- private final float mDeceleration;
+ static {
+ // Set a neutral value that will be used in the next call to viscousFluid().
+ VISCOUS_FLUID_NORMALIZE = 1.0f;
+ VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
+ }
/**
- * Create a Scroller with the default duration and interpolator.
+ * Create a Scroller with a viscous fluid scroll interpolator.
*/
public Scroller(Context context) {
this(context, null);
@@ -77,15 +65,17 @@
* null, the default (viscous) interpolator will be used.
*/
public Scroller(Context context, Interpolator interpolator) {
- mFinished = true;
+ instantiateScrollers();
+ MagneticScroller.initializeFromContext(context);
+
mInterpolator = interpolator;
- float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
- mDeceleration = SensorManager.GRAVITY_EARTH // g (m/s^2)
- * 39.37f // inch/meter
- * ppi // pixels per inch
- * ViewConfiguration.getScrollFriction();
}
+ void instantiateScrollers() {
+ mScrollerX = new MagneticScroller();
+ mScrollerY = new MagneticScroller();
+ }
+
/**
*
* Returns whether the scroller has finished scrolling.
@@ -93,150 +83,148 @@
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
- return mFinished;
+ return mScrollerX.mFinished && mScrollerY.mFinished;
}
-
+
/**
* Force the finished field to a particular value.
- *
+ *
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
- mFinished = finished;
+ mScrollerX.mFinished = mScrollerY.mFinished = finished;
}
-
+
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
- return mDuration;
+ return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
}
-
+
/**
- * Returns the current X offset in the scroll.
+ * Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
- return mCurrX;
+ return mScrollerX.mCurrentPosition;
}
-
+
/**
- * Returns the current Y offset in the scroll.
+ * Returns the current Y offset in the scroll.
*
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
- return mCurrY;
- }
-
- /**
- * @hide
- * Returns the current velocity.
- *
- * @return The original velocity less the deceleration. Result may be
- * negative.
- */
- public float getCurrVelocity() {
- return mVelocity - mDeceleration * timePassed() / 2000.0f;
+ return mScrollerY.mCurrentPosition;
}
/**
- * Returns the start X offset in the scroll.
+ * @hide
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
+ */
+ public float getCurrVelocity() {
+ float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+ squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+ return FloatMath.sqrt(squaredNorm);
+ }
+
+ /**
+ * Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final int getStartX() {
- return mStartX;
+ return mScrollerX.mStart;
}
-
+
/**
- * Returns the start Y offset in the scroll.
+ * Returns the start Y offset in the scroll.
*
* @return The start Y offset as an absolute distance from the origin.
*/
public final int getStartY() {
- return mStartY;
+ return mScrollerY.mStart;
}
-
+
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final X offset as an absolute distance from the origin.
*/
public final int getFinalX() {
- return mFinalX;
+ return mScrollerX.mFinal;
}
-
+
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final Y offset as an absolute distance from the origin.
*/
public final int getFinalY() {
- return mFinalY;
+ return mScrollerY.mFinal;
}
/**
- * Call this when you want to know the new location. If it returns true,
- * the animation is not yet finished. loc will be altered to provide the
- * new location.
- */
+ * Call this when you want to know the new location. If it returns true, the
+ * animation is not yet finished.
+ */
public boolean computeScrollOffset() {
- if (mFinished) {
+ if (isFinished()) {
return false;
}
- int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
-
- if (timePassed < mDuration) {
- switch (mMode) {
+ switch (mMode) {
case SCROLL_MODE:
- float x = (float)timePassed * mDurationReciprocal;
-
- if (mInterpolator == null)
- x = viscousFluid(x);
- else
- x = mInterpolator.getInterpolation(x);
-
- mCurrX = mStartX + Math.round(x * mDeltaX);
- mCurrY = mStartY + Math.round(x * mDeltaY);
- if ((mCurrX == mFinalX) && (mCurrY == mFinalY)) {
- mFinished = true;
- }
- break;
- case FLING_MODE:
- float timePassedSeconds = timePassed / 1000.0f;
- float distance = (mVelocity * timePassedSeconds)
- - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
-
- mCurrX = mStartX + Math.round(distance * mCoeffX);
- // Pin to mMinX <= mCurrX <= mMaxX
- mCurrX = Math.min(mCurrX, mMaxX);
- mCurrX = Math.max(mCurrX, mMinX);
-
- mCurrY = mStartY + Math.round(distance * mCoeffY);
- // Pin to mMinY <= mCurrY <= mMaxY
- mCurrY = Math.min(mCurrY, mMaxY);
- mCurrY = Math.max(mCurrY, mMinY);
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ // Any scroller can be used for time, since they were started
+ // together in scroll mode. We use X here.
+ final long elapsedTime = time - mScrollerX.mStartTime;
- if (mCurrX == mFinalX && mCurrY == mFinalY) {
- mFinished = true;
+ final int duration = mScrollerX.mDuration;
+ if (elapsedTime < duration) {
+ float q = (float) (elapsedTime) / duration;
+
+ if (mInterpolator == null)
+ q = viscousFluid(q);
+ else
+ q = mInterpolator.getInterpolation(q);
+
+ mScrollerX.updateScroll(q);
+ mScrollerY.updateScroll(q);
+ } else {
+ abortAnimation();
}
-
break;
- }
+
+ case FLING_MODE:
+ if (!mScrollerX.mFinished) {
+ if (!mScrollerX.update()) {
+ if (!mScrollerX.continueWhenFinished()) {
+ mScrollerX.finish();
+ }
+ }
+ }
+
+ if (!mScrollerY.mFinished) {
+ if (!mScrollerY.update()) {
+ if (!mScrollerY.continueWhenFinished()) {
+ mScrollerY.finish();
+ }
+ }
+ }
+
+ break;
}
- else {
- mCurrX = mFinalX;
- mCurrY = mFinalY;
- mFinished = true;
- }
+
return true;
}
-
+
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
@@ -270,83 +258,39 @@
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
- mFinished = false;
- mDuration = duration;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStartX = startX;
- mStartY = startY;
- mFinalX = startX + dx;
- mFinalY = startY + dy;
- mDeltaX = dx;
- mDeltaY = dy;
- mDurationReciprocal = 1.0f / (float) mDuration;
- // This controls the viscous fluid effect (how much of it)
- mViscousFluidScale = 8.0f;
- // must be set to 1.0 (used in viscousFluid())
- mViscousFluidNormalize = 1.0f;
- mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+ mScrollerX.startScroll(startX, dx, duration);
+ mScrollerY.startScroll(startY, dy, duration);
}
/**
- * Start scrolling based on a fling gesture. The distance travelled will
- * depend on the initial velocity of the fling.
+ * Start scrolling based on a fling gesture. The distance traveled will
+ * depend on the initial velocity of the fling. Velocity is slowed down by a
+ * constant deceleration until it reaches 0 or the limits are reached.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
+ * second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
+ * second.
* @param minX Minimum X value. The scroller will not scroll past this
- * point.
+ * point.
* @param maxX Maximum X value. The scroller will not scroll past this
- * point.
+ * point.
* @param minY Minimum Y value. The scroller will not scroll past this
- * point.
+ * point.
* @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
+ * point.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
mMode = FLING_MODE;
- mFinished = false;
-
- float velocity = (float)Math.hypot(velocityX, velocityY);
-
- mVelocity = velocity;
- mDuration = (int) (1000 * velocity / mDeceleration); // Duration is in
- // milliseconds
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStartX = startX;
- mStartY = startY;
-
- mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity;
- mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
-
- int totalDistance = (int) ((velocity * velocity) / (2 * mDeceleration));
-
- mMinX = minX;
- mMaxX = maxX;
- mMinY = minY;
- mMaxY = maxY;
-
-
- mFinalX = startX + Math.round(totalDistance * mCoeffX);
- // Pin to mMinX <= mFinalX <= mMaxX
- mFinalX = Math.min(mFinalX, mMaxX);
- mFinalX = Math.max(mFinalX, mMinX);
-
- mFinalY = startY + Math.round(totalDistance * mCoeffY);
- // Pin to mMinY <= mFinalY <= mMaxY
- mFinalY = Math.min(mFinalY, mMaxY);
- mFinalY = Math.max(mFinalY, mMinY);
+ mScrollerX.fling(startX, velocityX, minX, maxX);
+ mScrollerY.fling(startY, velocityY, minY, maxY);
}
-
-
-
- private float viscousFluid(float x)
- {
- x *= mViscousFluidScale;
+
+ private static float viscousFluid(float x) {
+ x *= VISCOUS_FLUID_SCALE;
if (x < 1.0f) {
x -= (1.0f - (float)Math.exp(-x));
} else {
@@ -354,70 +298,237 @@
x = 1.0f - (float)Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
- x *= mViscousFluidNormalize;
+ x *= VISCOUS_FLUID_NORMALIZE;
return x;
}
-
+
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
- *
+ *
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
- mCurrX = mFinalX;
- mCurrY = mFinalY;
- mFinished = true;
+ mScrollerX.finish();
+ mScrollerY.finish();
}
-
+
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
- *
+ *
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*/
public void extendDuration(int extend) {
- int passed = timePassed();
- mDuration = passed + extend;
- mDurationReciprocal = 1.0f / (float)mDuration;
- mFinished = false;
+ mScrollerX.extendDuration(extend);
+ mScrollerY.extendDuration(extend);
}
/**
* Returns the time elapsed since the beginning of the scrolling.
- *
+ *
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
- return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+ return (int) (time - startTime);
}
/**
* Sets the final position (X) for this scroller.
- *
+ *
* @param newX The new X offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalY(int)
*/
public void setFinalX(int newX) {
- mFinalX = newX;
- mDeltaX = mFinalX - mStartX;
- mFinished = false;
+ mScrollerX.setFinalPosition(newX);
}
/**
* Sets the final position (Y) for this scroller.
- *
+ *
* @param newY The new Y offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalX(int)
*/
public void setFinalY(int newY) {
- mFinalY = newY;
- mDeltaY = mFinalY - mStartY;
- mFinished = false;
+ mScrollerY.setFinalPosition(newY);
+ }
+
+ static class MagneticScroller {
+ // Initial position
+ int mStart;
+
+ // Current position
+ int mCurrentPosition;
+
+ // Final position
+ int mFinal;
+
+ // Initial velocity
+ int mVelocity;
+
+ // Current velocity
+ float mCurrVelocity;
+
+ // Constant current deceleration
+ float mDeceleration;
+
+ // Animation starting time, in system milliseconds
+ long mStartTime;
+
+ // Animation duration, in milliseconds
+ int mDuration;
+
+ // Whether the animation is currently in progress
+ boolean mFinished;
+
+ // Constant gravity value, used to scale deceleration
+ static float GRAVITY;
+
+ static void initializeFromContext(Context context) {
+ final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
+ GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * ppi // pixels per inch
+ * ViewConfiguration.getScrollFriction();
+ }
+
+ MagneticScroller() {
+ mFinished = true;
+ }
+
+ void updateScroll(float q) {
+ mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
+ }
+
+ /*
+ * Update the current position and velocity for current time. Returns
+ * true if update has been done and false if animation duration has been
+ * reached.
+ */
+ boolean update() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long duration = time - mStartTime;
+
+ if (duration > mDuration) {
+ return false;
+ }
+
+ final float t = duration / 1000.0f;
+ mCurrVelocity = mVelocity + mDeceleration * t;
+ final float distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+ mCurrentPosition = mStart + (int) distance;
+
+ return true;
+ }
+
+ /*
+ * Get a signed deceleration that will reduce the velocity.
+ */
+ float getDeceleration(int velocity) {
+ return velocity > 0 ? -GRAVITY : GRAVITY;
+ }
+
+ /*
+ * Returns the time (in milliseconds) it will take to go from start to end.
+ */
+ static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
+ final int distance = start - end;
+ final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
+ * distance;
+ if (discriminant >= 0.0f) {
+ float delta = (float) Math.sqrt(discriminant);
+ if (deceleration < 0.0f) {
+ delta = -delta;
+ }
+ return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
+ }
+
+ // End position can not be reached
+ return 0;
+ }
+
+ void startScroll(int start, int distance, int duration) {
+ mFinished = false;
+
+ mStart = start;
+ mFinal = start + distance;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = duration;
+
+ // Unused
+ mDeceleration = 0.0f;
+ mVelocity = 0;
+ }
+
+ void fling(int start, int velocity, int min, int max) {
+ mFinished = false;
+
+ mStart = start;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+
+ mVelocity = velocity;
+
+ mDeceleration = getDeceleration(velocity);
+
+ // A start from an invalid position immediately brings back to a valid position
+ if (mStart < min) {
+ mDuration = 0;
+ mFinal = min;
+ return;
+ }
+
+ if (mStart > max) {
+ mDuration = 0;
+ mFinal = max;
+ return;
+ }
+
+ // Duration are expressed in milliseconds
+ mDuration = (int) (-1000.0f * velocity / mDeceleration);
+
+ mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
+
+ // Clamp to a valid final position
+ if (mFinal < min) {
+ mFinal = min;
+ mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
+ }
+
+ if (mFinal > max) {
+ mFinal = max;
+ mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
+ }
+ }
+
+ void finish() {
+ mCurrentPosition = mFinal;
+ // Not reset since WebView relies on this value for fast fling.
+ // mCurrVelocity = 0.0f;
+ mFinished = true;
+ }
+
+ boolean continueWhenFinished() {
+ return false;
+ }
+
+ void setFinalPosition(int position) {
+ mFinal = position;
+ mFinished = false;
+ }
+
+ void extendDuration(int extend) {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final int elapsedTime = (int) (time - mStartTime);
+ mDuration = elapsedTime + extend;
+ mFinished = false;
+ }
}
}
diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp
index 3afe5f5..8588bc2 100644
--- a/core/jni/android_bluetooth_ScoSocket.cpp
+++ b/core/jni/android_bluetooth_ScoSocket.cpp
@@ -37,6 +37,23 @@
#ifdef HAVE_BLUETOOTH
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+
+#define MAX_LINE 255
+
+/*
+ * Defines the module strings used in the blacklist file.
+ * These are used by consumers of the blacklist file to see if the line is
+ * used by that module.
+ */
+#define SCO_BLACKLIST_MODULE_NAME "scoSocket"
+
+
+/* Define the type strings used in the blacklist file. */
+#define BLACKLIST_BY_NAME "name"
+#define BLACKLIST_BY_PARTIAL_NAME "partial_name"
+#define BLACKLIST_BY_OUI "vendor_oui"
+
#endif
/* Ideally, blocking I/O on a SCO socket would return when another thread
@@ -67,11 +84,28 @@
struct thread_data_t;
static void *work_thread(void *arg);
-static int connect_work(const char *address);
+static int connect_work(const char *address, uint16_t sco_pkt_type);
static int accept_work(int signal_sk);
static void wait_for_close(int sk, int signal_sk);
static void closeNative(JNIEnv *env, jobject object);
+static void parseBlacklist(void);
+static uint16_t getScoType(char *address, const char *name);
+
+#define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
+
+/* Blacklist data */
+typedef struct scoBlacklist {
+ int fieldType;
+ char *value;
+ uint16_t scoType;
+ struct scoBlacklist *next;
+} scoBlacklist_t;
+
+#define BL_TYPE_NAME 1 // Field type is name string
+
+static scoBlacklist_t *blacklist = NULL;
+
/* shared native data - protected by mutex */
typedef struct {
pthread_mutex_t mutex;
@@ -87,11 +121,144 @@
bool is_accept; // accept (listening) or connect (outgoing) thread
int signal_sk; // socket for thread to listen for unblock signal
char address[BTADDR_SIZE]; // BT addres as string
+ uint16_t sco_pkt_type; // SCO packet types supported
};
static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
return (native_data_t *)(env->GetIntField(object, field_mNativeData));
}
+
+static uint16_t str2scoType (char *key) {
+ LOGV("%s: key = %s", __FUNCTION__, key);
+ if (COMPARE_STRING(key, "ESCO_HV1"))
+ return ESCO_HV1;
+ if (COMPARE_STRING(key, "ESCO_HV2"))
+ return ESCO_HV2;
+ if (COMPARE_STRING(key, "ESCO_HV3"))
+ return ESCO_HV3;
+ if (COMPARE_STRING(key, "ESCO_EV3"))
+ return ESCO_EV3;
+ if (COMPARE_STRING(key, "ESCO_EV4"))
+ return ESCO_EV4;
+ if (COMPARE_STRING(key, "ESCO_EV5"))
+ return ESCO_EV5;
+ if (COMPARE_STRING(key, "ESCO_2EV3"))
+ return ESCO_2EV3;
+ if (COMPARE_STRING(key, "ESCO_3EV3"))
+ return ESCO_3EV3;
+ if (COMPARE_STRING(key, "ESCO_2EV5"))
+ return ESCO_2EV5;
+ if (COMPARE_STRING(key, "ESCO_3EV5"))
+ return ESCO_3EV5;
+ if (COMPARE_STRING(key, "SCO_ESCO_MASK"))
+ return SCO_ESCO_MASK;
+ if (COMPARE_STRING(key, "EDR_ESCO_MASK"))
+ return EDR_ESCO_MASK;
+ if (COMPARE_STRING(key, "ALL_ESCO_MASK"))
+ return ALL_ESCO_MASK;
+ LOGE("Unknown SCO Type (%s) skipping",key);
+ return 0;
+}
+
+static void parseBlacklist(void) {
+ const char *filename = "/etc/bluetooth/blacklist.conf";
+ char line[MAX_LINE];
+ scoBlacklist_t *list = NULL;
+ scoBlacklist_t *newelem;
+
+ LOGV(__FUNCTION__);
+
+ /* Open file */
+ FILE *fp = fopen(filename, "r");
+ if(!fp) {
+ LOGE("Error(%s)opening blacklist file", strerror(errno));
+ return;
+ }
+
+ while (fgets(line, MAX_LINE, fp) != NULL) {
+ if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
+ continue;
+ char *module = strtok(line,":");
+ if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) {
+ newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t));
+ if (newelem == NULL) {
+ LOGE("%s: out of memory!", __FUNCTION__);
+ return;
+ }
+ // parse line
+ char *type = strtok(NULL, ",");
+ char *valueList = strtok(NULL, ",");
+ char *paramList = strtok(NULL, ",");
+ if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) {
+ // Extract Name from Value list
+ newelem->fieldType = BL_TYPE_NAME;
+ newelem->value = (char *)calloc(1, strlen(valueList));
+ if (newelem->value == NULL) {
+ LOGE("%s: out of memory!", __FUNCTION__);
+ continue;
+ }
+ valueList++; // Skip open quote
+ strncpy(newelem->value, valueList, strlen(valueList) - 1);
+
+ // Get Sco Settings from Parameters
+ char *param = strtok(paramList, ";");
+ uint16_t scoTypes = 0;
+ while (param != NULL) {
+ uint16_t sco;
+ if (param[0] == '-') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes &= ~sco;
+ } else if (param[0] == '+') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes |= sco;
+ } else if (param[0] == '=') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes = sco;
+ } else {
+ LOGE("Invalid SCO type must be =, + or -");
+ }
+ param = strtok(NULL, ";");
+ }
+ newelem->scoType = scoTypes;
+ } else {
+ LOGE("Unknown SCO type entry in Blacklist file");
+ continue;
+ }
+ if (list) {
+ list->next = newelem;
+ list = newelem;
+ } else {
+ blacklist = list = newelem;
+ }
+ LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
+ newelem->scoType);
+ }
+ }
+ fclose(fp);
+ return;
+}
+static uint16_t getScoType(char *address, const char *name) {
+ uint16_t ret = 0;
+ scoBlacklist_t *list = blacklist;
+
+ while (list != NULL) {
+ if (list->fieldType == BL_TYPE_NAME) {
+ if (COMPARE_STRING(name, list->value)) {
+ ret = list->scoType;
+ break;
+ }
+ }
+ list = list->next;
+ }
+ LOGI("%s %s - 0x%x", __FUNCTION__, name, ret);
+ return ret;
+}
#endif
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -104,6 +271,9 @@
method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
+
+ /* Read the blacklist file in here */
+ parseBlacklist();
#endif
}
@@ -192,7 +362,9 @@
return JNI_FALSE;
}
-static jboolean connectNative(JNIEnv *env, jobject object, jstring address) {
+static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
+ jstring name) {
+
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
native_data_t *nat = get_native_data(env, object);
@@ -200,6 +372,7 @@
pthread_t thread;
struct thread_data_t *data;
const char *c_address;
+ const char *c_name;
pthread_mutex_lock(&nat->mutex);
if (nat->signal_sk != -1) {
@@ -231,6 +404,11 @@
env->ReleaseStringUTFChars(address, c_address);
data->is_accept = false;
+ c_name = env->GetStringUTFChars(name, NULL);
+ /* See if this device is in the black list */
+ data->sco_pkt_type = getScoType(data->address, c_name);
+ env->ReleaseStringUTFChars(name, c_name);
+
if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
return JNI_FALSE;
@@ -282,7 +460,7 @@
sk = accept_work(data->signal_sk);
LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
} else {
- sk = connect_work(data->address);
+ sk = connect_work(data->address, data->sco_pkt_type);
}
/* callback with connection result */
@@ -426,7 +604,7 @@
return -1;
}
-static int connect_work(const char *address) {
+static int connect_work(const char *address, uint16_t sco_pkt_type) {
LOGV(__FUNCTION__);
struct sockaddr_sco addr;
int sk = -1;
@@ -449,6 +627,7 @@
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
get_bdaddr(address, &addr.sco_bdaddr);
+ addr.sco_pkt_type = sco_pkt_type;
LOGI("Connecting to socket");
while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errno != EINTR) {
@@ -493,7 +672,7 @@
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void *)initNative},
{"destroyNative", "()V", (void *)destroyNative},
- {"connectNative", "(Ljava/lang/String;)Z", (void *)connectNative},
+ {"connectNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectNative},
{"acceptNative", "()Z", (void *)acceptNative},
{"closeNative", "()V", (void *)closeNative},
};
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
deleted file mode 100644
index 651212f..0000000
--- a/core/res/res/drawable/pattern_underwear.png
+++ /dev/null
Binary files differ
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 473f580..0016503 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -935,17 +935,27 @@
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
- bool doBackground = !gDisableBackgroundScheduling &&
- getpriority(PRIO_PROCESS, mMyThreadId)
- >= ANDROID_PRIORITY_BACKGROUND;
- if (doBackground) {
- // We have inherited a background priority from the caller.
- // Ensure this thread is in the background scheduling class,
- // since the driver won't modify scheduling classes for us.
- androidSetThreadSchedulingGroup(mMyThreadId,
- ANDROID_TGROUP_BG_NONINTERACT);
+ int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
+ if (gDisableBackgroundScheduling) {
+ if (curPrio > ANDROID_PRIORITY_NORMAL) {
+ // We have inherited a reduced priority from the caller, but do not
+ // want to run in that state in this process. The driver set our
+ // priority already (though not our scheduling class), so bounce
+ // it back to the default before invoking the transaction.
+ setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
+ }
+ } else {
+ if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
+ // We want to use the inherited priority from the caller.
+ // Ensure this thread is in the background scheduling class,
+ // since the driver won't modify scheduling classes for us.
+ // The scheduling group is reset to default by the caller
+ // once this method returns after the transaction is complete.
+ androidSetThreadSchedulingGroup(mMyThreadId,
+ ANDROID_TGROUP_BG_NONINTERACT);
+ }
}
-
+
//LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
Parcel reply;
@@ -982,14 +992,7 @@
mCallingPid = origPid;
mCallingUid = origUid;
-
- if (doBackground) {
- // We moved to the background scheduling group to execute
- // this transaction, so now that we are done go back in the
- // foreground.
- androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
- }
-
+
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index e9bcafe..f845fec1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -634,12 +634,12 @@
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
- String artist = mArtist != null && mArtist.length() > 0 ?
- mArtist : MediaStore.UNKNOWN_STRING;
- map.put(Audio.Media.ARTIST, artist);
- map.put(Audio.Media.ALBUM_ARTIST, mAlbumArtist != null &&
- mAlbumArtist.length() > 0 ? mAlbumArtist : artist);
- map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING));
+ 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);
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index ee6b89c..63d50c7 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -121,6 +121,10 @@
* -e class com.android.foo.FooTest,com.android.foo.TooTest
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
+ * <b>Running all tests in a java package:</b> adb shell am instrument -w
+ * -e package com.android.foo.subpkg
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
* <b>Including performance tests:</b> adb shell am instrument -w
* -e perf true
* com.android.foo/android.test.InstrumentationTestRunner