summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java21
-rw-r--r--core/java/android/provider/TvContract.java151
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml3
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java73
18 files changed, 709 insertions, 244 deletions
diff --git a/api/current.txt b/api/current.txt
index b81c83347b7e..150af9a32bb7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23673,7 +23673,11 @@ package android.provider {
public final class TvContract {
method public static final android.net.Uri buildChannelUri(long);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
method public static final android.net.Uri buildProgramUri(long);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
field public static final java.lang.String AUTHORITY = "com.android.tv";
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 528e1194c18c..722d9568f4e4 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -455,22 +455,25 @@ public final class CameraCharacteristics extends CameraMetadata {
* <p>The maximum numbers of different types of output streams
* that can be configured and used simultaneously by a camera device.</p>
* <p>This is a 3 element tuple that contains the max number of output simultaneous
- * streams for raw sensor, processed (and uncompressed), and JPEG formats respectively.
- * For example, if max raw sensor format output stream number is 1, max YUV streams
+ * streams for raw sensor, processed (but not stalling), and processed (and stalling)
+ * formats respectively. For example, assuming that JPEG is typically a processed and
+ * stalling stream, if max raw sensor format output stream number is 1, max YUV streams
* number is 3, and max JPEG stream number is 2, then this tuple should be <code>(1, 3, 2)</code>.</p>
* <p>This lists the upper bound of the number of output streams supported by
* the camera device. Using more streams simultaneously may require more hardware and
* CPU resources that will consume more power. The image format for a output stream can
- * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats}. The formats
- * defined in {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats} can be catergorized into the 3 stream types
- * as below:</p>
+ * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}.
+ * The formats defined in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations} can be catergorized
+ * into the 3 stream types as below:</p>
* <ul>
- * <li>JPEG-compressed format: BLOB.</li>
- * <li>Raw formats: RAW_SENSOR and RAW_OPAQUE.</li>
- * <li>processed, uncompressed formats: YCbCr_420_888, YCrCb_420_SP, YV12.</li>
+ * <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
+ * Typically JPEG format (ImageFormat#JPEG).</li>
+ * <li>Raw formats: ImageFormat#RAW_SENSOR and ImageFormat#RAW_OPAQUE.</li>
+ * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
+ * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
* </ul>
*
- * @see CameraCharacteristics#SCALER_AVAILABLE_FORMATS
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
*/
public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 233e0caaec4f..62252be15fef 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -16,9 +16,13 @@
package android.provider;
+import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.net.Uri;
+import java.util.List;
+
/**
* <p>
* The contract between the TV provider and applications. Contains definitions for the supported
@@ -42,6 +46,35 @@ public final class TvContract {
/** The authority for the TV provider. */
public static final String AUTHORITY = "com.android.tv";
+ private static final String PATH_CHANNEL = "channel";
+ private static final String PATH_PROGRAM = "program";
+ private static final String PATH_INPUT = "input";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify start
+ * time (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_START_TIME = "start_time";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify end time
+ * (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_END_TIME = "end_time";
+
+ /**
+ * A query, update or delete URI parameter that allows the caller to operate on all or
+ * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
+ * not affected.
+ *
+ * @hide
+ */
+ public static final String PARAM_BROWSABLE_ONLY = "browable_only";
+
/**
* Builds a URI that points to a specific channel.
*
@@ -52,6 +85,32 @@ public final class TvContract {
}
/**
+ * Builds a URI that points to all browsable channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name) {
+ return buildChannelsUriForInput(name, true);
+ }
+
+ /**
+ * Builds a URI that points to all or browsable-only channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
+ * to {@code false} the URI points to all channels regardless of whether they are
+ * browsable or not.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_INPUT).appendPath(name.getPackageName())
+ .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
+ .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program.
*
* @param programId The ID of the program to point to.
@@ -61,6 +120,37 @@ public final class TvContract {
}
/**
+ * Builds a URI that points to all programs on a given channel.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri) {
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ String channelId = String.valueOf(ContentUris.parseId(channelUri));
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelUri);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program the user watched.
*
* @param watchedProgramId The ID of the watched program to point to.
@@ -70,6 +160,61 @@ public final class TvContract {
return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
}
+ /**
+ * Extracts the {@link Channels#PACKAGE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getPackageName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(1);
+ }
+
+ /**
+ * Extracts the {@link Channels#SERVICE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getServiceName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(2);
+ }
+
+ /**
+ * Extracts the {@link Channels#_ID} from a given URI.
+ *
+ * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
+ * {@link #buildProgramsUriForChannel(Uri, long, long)}.
+ * @hide
+ */
+ public static final String getChannelId(Uri programsUri) {
+ final List<String> paths = programsUri.getPathSegments();
+ if (paths.size() < 3) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ return paths.get(1);
+ }
+
+
private TvContract() {}
/**
@@ -93,7 +238,8 @@ public final class TvContract {
public static final class Channels implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/channel");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_CHANNEL);
/** The MIME type of a directory of TV channels. */
public static final String CONTENT_TYPE =
@@ -276,7 +422,8 @@ public final class TvContract {
public static final class Programs implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/program");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_PROGRAM);
/** The MIME type of a directory of TV programs. */
public static final String CONTENT_TYPE =
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index d81e525bf3de..2e08bff92a1e 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -18,10 +18,9 @@
<com.android.systemui.statusbar.NotificationOverflowContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="32dp"
android:focusable="true"
android:clickable="true"
- android:background="@*android:drawable/notification_quantum_bg_dim"
>
<TextView
android:id="@+id/more_text"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 41e7dac84edd..8959a5b56fe8 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -4,14 +4,13 @@
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
- android:background="@*android:drawable/notification_quantum_bg"
>
- <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expanded"
+ <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expanded"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expandedPublic"
+ <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expandedPublic"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index c585a5b928c0..61c268e65417 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -31,8 +31,9 @@ import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
+import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ScrollAdapter;
public class ExpandHelper implements Gefingerpoken, OnClickListener {
@@ -115,9 +116,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
float focusY = detector.getFocusY();
final View underFocus = findView(focusX, focusY);
- if (underFocus != null) {
- startExpanding(underFocus, STRETCH);
- }
+ startExpanding(underFocus, STRETCH);
return mExpanding;
}
@@ -133,41 +132,21 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
};
private class ViewScaler {
- View mView;
+ ExpandableView mView;
public ViewScaler() {}
- public void setView(View v) {
+ public void setView(ExpandableView v) {
mView = v;
}
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
- ViewGroup.LayoutParams lp = mView.getLayoutParams();
- lp.height = (int)h;
- mView.setLayoutParams(lp);
- mView.requestLayout();
+ mView.setActualHeight((int) h);
}
public float getHeight() {
- int height = mView.getLayoutParams().height;
- if (height < 0) {
- height = mView.getMeasuredHeight();
- }
- return height;
+ return mView.getActualHeight();
}
public int getNaturalHeight(int maximum) {
- ViewGroup.LayoutParams lp = mView.getLayoutParams();
- if (DEBUG_SCALE) Log.v(TAG, "Inspecting a child of type: " +
- mView.getClass().getName());
- int oldHeight = lp.height;
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mView.setLayoutParams(lp);
- mView.measure(
- View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(),
- View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(maximum,
- View.MeasureSpec.AT_MOST));
- lp.height = oldHeight;
- mView.setLayoutParams(lp);
- return mView.getMeasuredHeight();
+ return Math.min(maximum, mView.getMaxHeight());
}
}
@@ -189,12 +168,6 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
- mScaleAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCallback.setUserLockedChild(mCurrView, false);
- }
- });
mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
@@ -341,9 +314,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
final View underFocus = findView(x, y);
- if (underFocus != null) {
- startExpanding(underFocus, PULL);
- }
+ startExpanding(underFocus, PULL);
return true;
}
if (mScrollAdapter != null && !mScrollAdapter.isScrolledToTop()) {
@@ -358,8 +329,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mLastMotionY = y;
final View underFocus = findView(x, y);
- if (underFocus != null) {
- startExpanding(underFocus, BLINDS);
+ if (startExpanding(underFocus, BLINDS)) {
mInitialTouchY = mLastMotionY;
mHasPopped = false;
}
@@ -459,16 +429,22 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
return true;
}
- private void startExpanding(View v, int expandType) {
+ /**
+ * @return True if the view is expandable, false otherwise.
+ */
+ private boolean startExpanding(View v, int expandType) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ return false;
+ }
mExpansionStyle = expandType;
- if (mExpanding && v == mCurrView) {
- return;
+ if (mExpanding && v == mCurrView) {
+ return true;
}
mExpanding = true;
if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
mCallback.setUserLockedChild(v, true);
setView(v);
- mScaler.setView(v);
+ mScaler.setView((ExpandableView) v);
mOldHeight = mScaler.getHeight();
if (mCallback.canChildBeExpanded(v)) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
@@ -480,6 +456,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
v.getParent().requestDisallowInterceptTouchEvent(true);
+ return true;
}
private void finishExpanding(boolean force) {
@@ -499,10 +476,18 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight);
+ mCallback.setUserExpandedChild(mCurrView, targetHeight == mNaturalHeight);
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
+ final View scaledView = mCurrView;
+ mScaleAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCallback.setUserLockedChild(scaledView, false);
+ mScaleAnimation.removeListener(this);
+ }
+ });
mScaleAnimation.start();
} else {
mCallback.setUserLockedChild(mCurrView, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index d647dfa82045..0f32dc015a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -29,7 +29,7 @@ import com.android.internal.R;
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
* to implement dimming/activating on Keyguard for the double-tap gesture
*/
-public class ActivatableNotificationView extends FrameLayout {
+public abstract class ActivatableNotificationView extends ExpandableOutlineView {
private static final long DOUBLETAP_TIMEOUT_MS = 1000;
@@ -54,6 +54,7 @@ public class ActivatableNotificationView extends FrameLayout {
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ updateBackgroundResource();
}
@@ -84,6 +85,9 @@ public class ActivatableNotificationView extends FrameLayout {
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
+ if (mDownY > getActualHeight()) {
+ return false;
+ }
// Call the listener tentatively directly, even if we don't know whether the user
// will stay within the touch slop, as the listener is implemented as a scale
@@ -122,7 +126,7 @@ public class ActivatableNotificationView extends FrameLayout {
}
private void makeActive(float x, float y) {
- getBackground().setHotspot(0, x, y);
+ mCustomBackground.setHotspot(0, x, y);
mActivated = true;
}
@@ -132,8 +136,8 @@ public class ActivatableNotificationView extends FrameLayout {
private void makeInactive() {
if (mActivated) {
// Make sure that we clear the hotspot from the center.
- getBackground().setHotspot(0, getWidth() / 2, getHeight() / 2);
- getBackground().removeHotspot(0);
+ mCustomBackground.setHotspot(0, getWidth() / 2, getActualHeight() / 2);
+ mCustomBackground.removeHotspot(0);
mActivated = false;
}
if (mOnActivatedListener != null) {
@@ -178,7 +182,19 @@ public class ActivatableNotificationView extends FrameLayout {
}
private void updateBackgroundResource() {
- setBackgroundResource(mDimmed ? mDimmedBgResId : mBgResId);
+ setCustomBackgroundResource(mDimmed ? mDimmedBgResId : mBgResId);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ setPivotX(getWidth()/2);
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ setPivotY(actualHeight/2);
}
public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 3e2164077820..90b63f48f7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -760,9 +760,10 @@ public abstract class BaseStatusBar extends SystemUI implements
// NB: the large icon is now handled entirely by the template
// bind the click event to the content area
- SizeAdaptiveLayout expanded = (SizeAdaptiveLayout)row.findViewById(R.id.expanded);
- SizeAdaptiveLayout expandedPublic
- = (SizeAdaptiveLayout)row.findViewById(R.id.expandedPublic);
+ NotificationContentView expanded =
+ (NotificationContentView) row.findViewById(R.id.expanded);
+ NotificationContentView expandedPublic =
+ (NotificationContentView) row.findViewById(R.id.expandedPublic);
row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
@@ -794,19 +795,11 @@ public abstract class BaseStatusBar extends SystemUI implements
if (contentViewLocal != null) {
contentViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
- params.minHeight = minHeight;
- params.maxHeight = minHeight;
- expanded.addView(contentViewLocal, params);
+ expanded.setContractedChild(contentViewLocal);
}
if (bigContentViewLocal != null) {
bigContentViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
- params.minHeight = minHeight+1;
- params.maxHeight = maxHeight;
- expanded.addView(bigContentViewLocal, params);
+ expanded.setExpandedChild(bigContentViewLocal);
}
PackageManager pm = mContext.getPackageManager();
@@ -820,11 +813,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (publicViewLocal != null) {
publicViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
- params.minHeight = minHeight;
- params.maxHeight = minHeight;
- expandedPublic.addView(publicViewLocal, params);
+ expandedPublic.setContractedChild(publicViewLocal);
}
}
catch (RuntimeException e) {
@@ -1352,6 +1341,7 @@ public abstract class BaseStatusBar extends SystemUI implements
} else {
entry.row.setOnClickListener(null);
}
+ entry.row.notifyContentUpdated();
}
protected void notifyHeadsUpScreenOn(boolean screenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 35c02eb67a6c..b813e6573639 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -17,12 +17,12 @@
package com.android.systemui.statusbar;
import android.content.Context;
+import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
public class ExpandableNotificationRow extends ActivatableNotificationView {
@@ -45,12 +45,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
* user expansion.
*/
private boolean mIsSystemExpanded;
- private SizeAdaptiveLayout mPublicLayout;
- private SizeAdaptiveLayout mPrivateLayout;
+ private NotificationContentView mPublicLayout;
+ private NotificationContentView mPrivateLayout;
private int mMaxExpandHeight;
- private boolean mMaxHeightNeedsUpdate;
private NotificationActivator mActivator;
- private boolean mSelfInitiatedLayout;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -59,8 +57,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPublicLayout = (SizeAdaptiveLayout) findViewById(R.id.expandedPublic);
- mPrivateLayout = (SizeAdaptiveLayout) findViewById(R.id.expanded);
+ mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
+ mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mActivator = new NotificationActivator(this);
}
@@ -82,7 +80,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
mRowMinHeight = rowMinHeight;
mRowMaxHeight = rowMaxHeight;
- mMaxHeightNeedsUpdate = true;
}
public boolean isExpandable() {
@@ -145,13 +142,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
* @param expand should the layout be in the expanded state
*/
public void applyExpansionToLayout(boolean expand) {
- ViewGroup.LayoutParams lp = getLayoutParams();
if (expand && mExpandable) {
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ setActualHeight(mMaxExpandHeight);
} else {
- lp.height = mRowMinHeight;
+ setActualHeight(mRowMinHeight);
}
- setLayoutParams(lp);
}
/**
@@ -161,6 +156,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
* @return the maximum allowed expansion height of this view.
*/
public int getMaximumAllowedExpandHeight() {
+ if (isUserLocked()) {
+ return getActualHeight();
+ }
boolean inExpansionState = isExpanded();
if (!inExpansionState) {
// not expanded, so we return the collapsed size
@@ -170,31 +168,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return mShowingPublic ? mRowMinHeight : getMaxExpandHeight();
}
- private void updateMaxExpandHeight() {
-
- // We don't want this method to trigger a layout of the whole view hierarchy,
- // as the layout parameters in the end are the same which they were in the beginning.
- // Otherwise a loop may occur if this method is called on the layout of a parent.
- mSelfInitiatedLayout = true;
- ViewGroup.LayoutParams lp = getLayoutParams();
- int oldHeight = lp.height;
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- setLayoutParams(lp);
- measure(View.MeasureSpec.makeMeasureSpec(getWidth(), View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(mRowMaxHeight, View.MeasureSpec.AT_MOST));
- lp.height = oldHeight;
- setLayoutParams(lp);
- mMaxExpandHeight = getMeasuredHeight();
- mSelfInitiatedLayout = false;
- }
-
- @Override
- public void requestLayout() {
- if (!mSelfInitiatedLayout) {
- super.requestLayout();
- }
- }
-
/**
* Check whether the view state is currently expanded. This is given by the system in {@link
* #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -210,7 +183,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mMaxHeightNeedsUpdate = true;
+ boolean updateExpandHeight = mMaxExpandHeight == 0;
+ mMaxExpandHeight = mPrivateLayout.getMaxHeight();
+ if (updateExpandHeight) {
+ applyExpansionToLayout(isExpanded());
+ }
}
public void setShowingPublic(boolean show) {
@@ -233,10 +210,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
public int getMaxExpandHeight() {
- if (mMaxHeightNeedsUpdate) {
- updateMaxExpandHeight();
- mMaxHeightNeedsUpdate = false;
- }
return mMaxExpandHeight;
}
@@ -248,6 +221,28 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
* @return the potential height this view could expand in addition.
*/
public int getExpandPotential() {
- return getMaximumAllowedExpandHeight() - getHeight();
+ return getMaximumAllowedExpandHeight() - getActualHeight();
+ }
+
+ @Override
+ public void setActualHeight(int height) {
+ mPrivateLayout.setActualHeight(height);
+ invalidate();
+ super.setActualHeight(height);
+ }
+
+ @Override
+ public int getMaxHeight() {
+ return mPrivateLayout.getMaxHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ mPrivateLayout.setClipTopAmount(clipTopAmount);
+ }
+
+ public void notifyContentUpdated() {
+ mPrivateLayout.notifyContentUpdated();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
new file mode 100644
index 000000000000..43eb5b5f952e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Outline;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Like {@link ExpandableView}, but setting an outline for the height and clipping.
+ */
+public abstract class ExpandableOutlineView extends ExpandableView {
+
+ private final Outline mOutline = new Outline();
+
+ public ExpandableOutlineView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ updateOutline();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ updateOutline();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateOutline();
+ }
+
+ private void updateOutline() {
+ mOutline.setRect(0,
+ mClipTopAmount,
+ getWidth(),
+ Math.max(mActualHeight, mClipTopAmount));
+ setOutline(mOutline);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
new file mode 100644
index 000000000000..35913fa9a1bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * An abstract view for expandable views.
+ */
+public abstract class ExpandableView extends FrameLayout {
+
+ private OnHeightChangedListener mOnHeightChangedListener;
+ protected int mActualHeight;
+ protected int mClipTopAmount;
+ protected Drawable mCustomBackground;
+ private boolean mActualHeightInitialized;
+
+ public ExpandableView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mCustomBackground != null) {
+ mCustomBackground.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
+ mCustomBackground.draw(canvas);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mCustomBackground;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ final Drawable d = mCustomBackground;
+ if (d != null && d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (!mActualHeightInitialized && mActualHeight == 0) {
+ mActualHeight = getHeight();
+ }
+ mActualHeightInitialized = true;
+ }
+
+ /**
+ * Sets the actual height of this notification. This is different than the laid out
+ * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
+ */
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
+ invalidate();
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(this);
+ }
+ }
+
+ /**
+ * See {@link #setActualHeight}.
+ *
+ * @return The actual height of this notification.
+ */
+ public int getActualHeight() {
+ return mActualHeight;
+ }
+
+ /**
+ * @return The maximum height of this notification.
+ */
+ public abstract int getMaxHeight();
+
+ /**
+ * Sets the amount this view should be clipped from the top. This is used when an expanded
+ * notification is scrolling in the top or bottom stack.
+ *
+ * @param clipTopAmount The amount of pixels this view should be clipped from top.
+ */
+ public void setClipTopAmount(int clipTopAmount) {
+ mClipTopAmount = clipTopAmount;
+ invalidate();
+ }
+
+ public void setOnHeightChangedListener(OnHeightChangedListener listener) {
+ mOnHeightChangedListener = listener;
+ }
+
+ /**
+ * Sets a custom background drawable. As we need to change our bounds independently of layout,
+ * we need the notition of a custom background.
+ */
+ public void setCustomBackground(Drawable customBackground) {
+ if (mCustomBackground != null) {
+ mCustomBackground.setCallback(null);
+ unscheduleDrawable(mCustomBackground);
+ }
+ mCustomBackground = customBackground;
+ mCustomBackground.setCallback(this);
+ setWillNotDraw(customBackground == null);
+ invalidate();
+ }
+
+ public void setCustomBackgroundResource(int drawableResId) {
+ setCustomBackground(getResources().getDrawable(drawableResId));
+ }
+
+ /**
+ * A listener notifying when {@link #getActualHeight} changes.
+ */
+ public interface OnHeightChangedListener {
+ void onHeightChanged(ExpandableView view);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
new file mode 100644
index 000000000000..fd0cb08c3d2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * A frame layout containing the actual payload of the notification, including the contracted and
+ * expanded layout. This class is responsible for clipping the content and and switching between the
+ * expanded and contracted view depending on its clipped size.
+ */
+public class NotificationContentView extends ExpandableView {
+
+ private final Rect mClipBounds = new Rect();
+
+ private View mContractedChild;
+ private View mExpandedChild;
+
+ private int mSmallHeight;
+
+ public NotificationContentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ mActualHeight = mSmallHeight;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateClipping();
+ }
+
+ public void setContractedChild(View child) {
+ if (mContractedChild != null) {
+ removeView(mContractedChild);
+ }
+ sanitizeContractedLayoutParams(child);
+ addView(child);
+ mContractedChild = child;
+ selectLayout();
+ }
+
+ public void setExpandedChild(View child) {
+ if (mExpandedChild != null) {
+ removeView(mExpandedChild);
+ }
+ addView(child);
+ mExpandedChild = child;
+ selectLayout();
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ selectLayout();
+ updateClipping();
+ }
+
+ @Override
+ public int getMaxHeight() {
+
+ // The maximum height is just the laid out height.
+ return getHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ updateClipping();
+ }
+
+ public int getClipTopAmount() {
+ return mClipTopAmount;
+ }
+
+ private void updateClipping() {
+ mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
+ setClipBounds(mClipBounds);
+ }
+
+ private void sanitizeContractedLayoutParams(View contractedChild) {
+ LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();
+ lp.height = mSmallHeight;
+ contractedChild.setLayoutParams(lp);
+ }
+
+ private void selectLayout() {
+ if (mActualHeight <= mSmallHeight || mExpandedChild == null) {
+ if (mContractedChild.getVisibility() != View.VISIBLE) {
+ mContractedChild.setVisibility(View.VISIBLE);
+ }
+ if (mExpandedChild != null && mExpandedChild.getVisibility() != View.INVISIBLE) {
+ mExpandedChild.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (mExpandedChild.getVisibility() != View.VISIBLE) {
+ mExpandedChild.setVisibility(View.VISIBLE);
+ }
+ if (mContractedChild.getVisibility() != View.INVISIBLE) {
+ mContractedChild.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ public void notifyContentUpdated() {
+ selectLayout();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index af913147d823..8ebd50d3947c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -35,6 +35,26 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
}
@Override
+ public void setActualHeight(int currentHeight) {
+ // noop
+ }
+
+ @Override
+ public int getActualHeight() {
+ return getHeight();
+ }
+
+ @Override
+ public int getMaxHeight() {
+ return getHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ // noop
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
@@ -43,6 +63,7 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
mActivator = new NotificationActivator(this);
mActivator.setDimmed(true);
setLocked(true);
+ setDimmed(true);
}
public NotificationOverflowIconsView getIconsView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5d758018cf21..82fbb1648b86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -23,10 +23,12 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-public class NotificationPanelView extends PanelView {
+public class NotificationPanelView extends PanelView implements
+ ExpandableView.OnHeightChangedListener {
public static final boolean DEBUG_GESTURES = true;
PhoneStatusBar mStatusBar;
@@ -67,6 +69,7 @@ public class NotificationPanelView extends PanelView {
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mNotificationStackScroller = (NotificationStackScrollLayout)
findViewById(R.id.notification_stack_scroller);
+ mNotificationStackScroller.setOnHeightChangedListener(this);
mNotificationParent = findViewById(R.id.notification_container_parent);
}
@@ -218,4 +221,9 @@ public class NotificationPanelView extends PanelView {
super.onExpandingFinished();
mNotificationStackScroller.onExpansionStopped();
}
+
+ @Override
+ public void onHeightChanged(ExpandableView view) {
+ requestPanelHeightUpdate();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
index a5e016aca97a..1bc97a075ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
@@ -92,6 +92,7 @@ public abstract class ZenModeViewAdapter implements ZenModeView.Adapter {
mExitIndex = 0;
dispatchChanged();
}
+ setZenModeCondition();
}
@Override
@@ -143,7 +144,15 @@ public abstract class ZenModeViewAdapter implements ZenModeView.Adapter {
}
mExitIndex = i;
dispatchChanged();
- final Uri conditionUri = (Uri) ec.tag;
+ setZenModeCondition();
+ }
+
+ private void setZenModeCondition() {
+ if (mExitIndex < 0 || mExitIndex >= mExits.size()) {
+ Log.w(TAG, "setZenModeCondition to bad index " + mExitIndex + " of " + mExits.size());
+ return;
+ }
+ final Uri conditionUri = (Uri) mExits.get(mExitIndex).tag;
try {
mNoMan.setZenModeCondition(conditionUri);
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 948ef907dbb5..d6d90a63df26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -37,6 +37,7 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -44,7 +45,8 @@ import com.android.systemui.statusbar.policy.ScrollAdapter;
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
public class NotificationStackScrollLayout extends ViewGroup
- implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter {
+ implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
+ ExpandableView.OnHeightChangedListener {
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
@@ -78,6 +80,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private int mBottomStackPeekSize;
private int mEmptyMarginBottom;
private int mPaddingBetweenElements;
+ private boolean mListenForHeightChanges = true;
/**
* The algorithm which calculates the properties for our children
@@ -90,6 +93,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private final StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private OnChildLocationsChangedListener mListener;
+ private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -227,7 +231,9 @@ public class NotificationStackScrollLayout extends ViewGroup
if (!isCurrentlyAnimating()) {
mCurrentStackScrollState.setScrollY(mOwnScrollY);
mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
+ mListenForHeightChanges = false;
mCurrentStackScrollState.apply();
+ mListenForHeightChanges = true;
if (mListener != null) {
mListener.onChildLocationsChanged(this);
}
@@ -306,12 +312,12 @@ public class NotificationStackScrollLayout extends ViewGroup
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
- View slidingChild = getChildAt(childIdx);
+ ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() == GONE) {
continue;
}
float top = slidingChild.getTranslationY();
- float bottom = top + slidingChild.getHeight();
+ float bottom = top + slidingChild.getActualHeight();
int left = slidingChild.getLeft();
int right = slidingChild.getRight();
@@ -615,16 +621,11 @@ public class NotificationStackScrollLayout extends ViewGroup
private int getScrollRange() {
int scrollRange = 0;
- View firstChild = getFirstChildNotGone();
+ ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
if (firstChild != null) {
int contentHeight = getContentHeight();
int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
- int firstChildExpandPotential = firstChildMaxExpandHeight - firstChild.getHeight();
- // If we already scrolled in, the first child is layouted smaller than it actually
- // could be when expanded. We have to compensate for this loss of the contentHeight
- // by adding the expand potential again.
- contentHeight += firstChildExpandPotential;
scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
if (scrollRange > 0 && getChildCount() > 0) {
// We want to at least be able collapse the first item and not ending in a weird
@@ -666,10 +667,17 @@ public class NotificationStackScrollLayout extends ViewGroup
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
- height += child.getHeight();
- if (i < getChildCount()-1) {
+ if (height != 0) {
+ // add the padding before this element
height += mPaddingBetweenElements;
}
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ height += row.getMaximumAllowedExpandHeight();
+ } else if (child instanceof ExpandableView) {
+ ExpandableView expandableView = (ExpandableView) child;
+ height += expandableView.getActualHeight();
+ }
}
}
mContentHeight = height;
@@ -723,6 +731,7 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
protected void onViewRemoved(View child) {
super.onViewRemoved(child);
+ ((ExpandableView) child).setOnHeightChangedListener(null);
mCurrentStackScrollState.removeViewStateForView(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
}
@@ -731,6 +740,7 @@ public class NotificationStackScrollLayout extends ViewGroup
protected void onViewAdded(View child) {
super.onViewAdded(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
+ ((ExpandableView) child).setOnHeightChangedListener(this);
}
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
@@ -891,6 +901,23 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ @Override
+ public void onHeightChanged(ExpandableView view) {
+ if (mListenForHeightChanges) {
+ updateContentHeight();
+ updateScrollPositionIfNecessary();
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(view);
+ }
+ updateChildren();
+ }
+ }
+
+ public void setOnHeightChangedListener(
+ ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
+ this.mOnHeightChangedListener = mOnHeightChangedListener;
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 2a6e4aef86c7..acd1c6ac212e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -23,6 +23,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
import java.util.ArrayList;
@@ -53,7 +54,7 @@ public class StackScrollAlgorithm {
private boolean mIsExpansionChanging;
private int mFirstChildMaxHeight;
private boolean mIsExpanded;
- private View mFirstChildWhileExpanding;
+ private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
private int mTopStackTotalSize;
@@ -82,7 +83,7 @@ public class StackScrollAlgorithm {
mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_BOTTOM_STACK,
mBottomStackPeekSize,
- mCollapsedSize + mPaddingBetweenElements + mBottomStackPeekSize,
+ mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements,
0.5f);
}
@@ -101,11 +102,10 @@ public class StackScrollAlgorithm {
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
+ algorithmState.scrollY = resultState.getScrollY() + mCollapsedSize;
updateVisibleChildren(resultState, algorithmState);
- algorithmState.scrollY = getAlgorithmScrollPosition(resultState, algorithmState);
-
// Phase 1:
findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
@@ -117,42 +117,6 @@ public class StackScrollAlgorithm {
}
/**
- * Calculates the scroll offset of the algorithm, based on the resultState.
- *
- * @param resultState the state to base the calculation on
- * @param algorithmState The state in which the current pass of the algorithm is currently in
- * @return the scroll offset used for the algorithm
- */
- private int getAlgorithmScrollPosition(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState) {
-
- int resultScroll = resultState.getScrollY() + mCollapsedSize;
-
- // If the first child was collapsed in an earlier pass, we have to decrease the scroll
- // position to get into the same state again.
- if (algorithmState.visibleChildren.size() > 0) {
- View firstView = algorithmState.visibleChildren.get(0);
- if (firstView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow firstRow = (ExpandableNotificationRow) firstView;
- if (firstRow.isUserLocked()) {
- // User is currently modifying this height.
- return resultScroll;
- }
- int scrolledInAmount = 0;
- // If the child size was not decreased due to scrolling, we don't substract it,
- if (!mIsExpansionChanging) {
- scrolledInAmount = firstRow.getExpandPotential();
- } else if (mExpandedOnStart && mFirstChildWhileExpanding == firstView) {
- scrolledInAmount = firstRow.getMaximumAllowedExpandHeight() -
- mFirstChildMaxHeight;
- }
- resultScroll -= scrolledInAmount;
- }
- }
- return resultScroll;
- }
-
- /**
* Update the visible children on the state.
*/
private void updateVisibleChildren(StackScrollState resultState,
@@ -162,7 +126,7 @@ public class StackScrollAlgorithm {
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
for (int i = 0; i < childCount; i++) {
- View v = hostView.getChildAt(i);
+ ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
state.visibleChildren.add(v);
}
@@ -194,10 +158,10 @@ public class StackScrollAlgorithm {
int childCount = algorithmState.visibleChildren.size();
int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
- int childHeight = child.getHeight();
+ int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
+ mPaddingBetweenElements;
@@ -291,7 +255,7 @@ public class StackScrollAlgorithm {
/**
* Clamp the yTranslation of the child up such that its end is at lest on the end of the top
- * stack.
+ * stack.get
*
* @param childViewState the view state of the child
* @param childHeight the height of this child
@@ -306,8 +270,11 @@ public class StackScrollAlgorithm {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
return row.getMaximumAllowedExpandHeight();
+ } else if (child instanceof ExpandableView) {
+ ExpandableView expandableView = (ExpandableView) child;
+ return expandableView.getActualHeight();
}
- return child.getHeight();
+ return child == null? mCollapsedSize : child.getHeight();
}
private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
@@ -419,9 +386,9 @@ public class StackScrollAlgorithm {
// find the number of elements in the top stack.
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
- int childHeight = child.getHeight();
+ int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
+ mPaddingBetweenElements;
@@ -524,13 +491,13 @@ public class StackScrollAlgorithm {
}
private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) {
- mFirstChildWhileExpanding = findFirstVisibleChild(hostView);
+ mFirstChildWhileExpanding = (ExpandableView) findFirstVisibleChild(hostView);
if (mFirstChildWhileExpanding != null) {
if (mExpandedOnStart) {
// We are collapsing the shade, so the first child can get as most as high as the
// current height.
- mFirstChildMaxHeight = mFirstChildWhileExpanding.getHeight();
+ mFirstChildMaxHeight = mFirstChildWhileExpanding.getActualHeight();
} else {
// We are expanding the shade, expand it to its full height.
@@ -627,7 +594,7 @@ public class StackScrollAlgorithm {
/**
* The children from the host view which are not gone.
*/
- public final ArrayList<View> visibleChildren = new ArrayList<View>();
+ public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 6e2e87e4b115..92151109cc7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -22,7 +22,7 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableView;
import java.util.HashMap;
import java.util.Map;
@@ -36,12 +36,11 @@ public class StackScrollState {
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
- private Map<View, ViewState> mStateMap;
+ private Map<ExpandableView, ViewState> mStateMap;
private int mScrollY;
private final Rect mClipRect = new Rect();
private int mBackgroundRoundedRectCornerRadius;
private final Outline mChildOutline = new Outline();
- private final int mChildDividerHeight;
public int getScrollY() {
return mScrollY;
@@ -53,11 +52,9 @@ public class StackScrollState {
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
- mStateMap = new HashMap<View, ViewState>();
+ mStateMap = new HashMap<ExpandableView, ViewState>();
mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
- mChildDividerHeight = hostView.getResources().getDimensionPixelSize(R.dimen
- .notification_divider_height);
}
public ViewGroup getHostView() {
@@ -67,14 +64,14 @@ public class StackScrollState {
public void resetViewStates() {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
- View child = mHostView.getChildAt(i);
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState viewState = mStateMap.get(child);
if (viewState == null) {
viewState = new ViewState();
mStateMap.put(child, viewState);
}
// initialize with the default values of the view
- viewState.height = child.getHeight();
+ viewState.height = child.getActualHeight();
viewState.alpha = 1;
viewState.gone = child.getVisibility() == View.GONE;
}
@@ -98,7 +95,7 @@ public class StackScrollState {
float previousNotificationEnd = 0;
float previousNotificationStart = 0;
for (int i = 0; i < numChildren; i++) {
- View child = mHostView.getChildAt(i);
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState state = mStateMap.get(child);
if (state == null) {
Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
@@ -109,7 +106,7 @@ public class StackScrollState {
float alpha = child.getAlpha();
float yTranslation = child.getTranslationY();
float zTranslation = child.getTranslationZ();
- int height = child.getHeight();
+ int height = child.getActualHeight();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
@@ -152,14 +149,14 @@ public class StackScrollState {
// apply height
if (height != newHeight) {
- applyNewHeight(child, newHeight);
+ child.setActualHeight(newHeight);
}
// apply clipping and shadow
float newNotificationEnd = newYTranslation + newHeight;
- updateChildClippingAndShadow(child, newHeight,
- newNotificationEnd - (previousNotificationEnd - mChildDividerHeight),
- newHeight - (previousNotificationStart - newYTranslation));
+ updateChildClippingAndBackground(child, newHeight,
+ newNotificationEnd - (previousNotificationEnd),
+ (int) (newHeight - (previousNotificationStart - newYTranslation)));
previousNotificationStart = newYTranslation;
previousNotificationEnd = newNotificationEnd;
@@ -173,20 +170,21 @@ public class StackScrollState {
* @param child the view to update
* @param realHeight the currently applied height of the view
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
- * @param shadowHeight the desired height of the shadow, the shadow ends on the bottom
+ * @param backgroundHeight the desired background height. The shadows of the view will be
+ * based on this height and the content will be clipped from the top
*/
- private void updateChildClippingAndShadow(View child, int realHeight, float clipHeight,
- float shadowHeight) {
- if (realHeight > shadowHeight) {
- updateChildOutline(child, realHeight, shadowHeight);
- } else {
- updateChildOutline(child, realHeight, realHeight);
- }
+ private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
+ float clipHeight, int backgroundHeight) {
if (realHeight > clipHeight) {
updateChildClip(child, realHeight, clipHeight);
} else {
child.setClipBounds(null);
}
+ if (realHeight > backgroundHeight) {
+ child.setClipTopAmount(realHeight - backgroundHeight);
+ } else {
+ child.setClipTopAmount(0);
+ }
}
/**
@@ -205,37 +203,6 @@ public class StackScrollState {
child.setClipBounds(mClipRect);
}
- /**
- * Updates the outline of a view
- *
- * @param child the view to update
- * @param height the currently applied height of the view
- * @param outlineHeight the desired height of the outline, the outline ends on the bottom
- */
- private void updateChildOutline(View child, int height,
- float outlineHeight) {
- int shadowInset = (int) (height - outlineHeight);
- getOutlineForSize(child.getLeft(),
- child.getTop() + shadowInset,
- child.getWidth(),
- child.getHeight() - shadowInset,
- mChildOutline);
- child.setOutline(mChildOutline);
- }
-
- private void getOutlineForSize(int leftInset, int topInset, int width, int height,
- Outline result) {
- result.setRoundRect(leftInset, topInset, leftInset + width, topInset + height,
- mBackgroundRoundedRectCornerRadius);
- }
-
- private void applyNewHeight(View child, int newHeight) {
- ViewGroup.LayoutParams lp = child.getLayoutParams();
- lp.height = newHeight;
- child.setLayoutParams(lp);
- }
-
-
public static class ViewState {
// These are flags such that we can create masks for filtering.