Notification MessagingStyle: Add handset views
Bug: 27250207
Change-Id: I499cf2beaeeb34f0f189815fc0911f3b8954bd50
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
new file mode 100644
index 0000000..dc7b7f5
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 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.internal.widget;
+
+import com.android.internal.R;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+
+/**
+ * A custom-built layout for the Notification.MessagingStyle.
+ *
+ * Evicts children until they all fit.
+ */
+@RemoteViews.RemoteView
+public class MessagingLinearLayout extends ViewGroup {
+
+ /**
+ * Spacing to be applied between views.
+ */
+ private int mSpacing;
+
+ /**
+ * The maximum height allowed.
+ */
+ private int mMaxHeight;
+
+ private int mIndentLines;
+
+ public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.MessagingLinearLayout, 0,
+ 0);
+
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.MessagingLinearLayout_maxHeight:
+ mMaxHeight = a.getDimensionPixelSize(i, 0);
+ break;
+ case R.styleable.MessagingLinearLayout_spacing:
+ mSpacing = a.getDimensionPixelSize(i, 0);
+ break;
+ }
+ }
+
+ if (mMaxHeight <= 0) {
+ throw new IllegalStateException(
+ "MessagingLinearLayout: Must specify positive maxHeight");
+ }
+
+ a.recycle();
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // This is essentially a bottom-up linear layout that only adds children that fit entirely
+ // up to a maximum height.
+
+ switch (MeasureSpec.getMode(heightMeasureSpec)) {
+ case MeasureSpec.AT_MOST:
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(mMaxHeight, MeasureSpec.getSize(heightMeasureSpec)),
+ MeasureSpec.AT_MOST);
+ break;
+ case MeasureSpec.UNSPECIFIED:
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ mMaxHeight,
+ MeasureSpec.AT_MOST);
+ break;
+ case MeasureSpec.EXACTLY:
+ break;
+ }
+ final int targetHeight = MeasureSpec.getSize(heightMeasureSpec);
+ final int count = getChildCount();
+
+ for (int i = 0; i < count; ++i) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.hide = true;
+ }
+
+ int totalHeight = mPaddingTop + mPaddingBottom;
+ boolean first = true;
+
+ // Starting from the bottom: we measure every view as if it were the only one. If it still
+ // fits, we take it, otherwise we stop there.
+ for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
+ if (getChildAt(i).getVisibility() == GONE) {
+ continue;
+ }
+ final View child = getChildAt(i);
+ LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
+
+ if (child instanceof ImageFloatingTextView) {
+ // Pretend we need the image padding for all views, we don't know which
+ // one will end up needing to do this (might end up not using all the space,
+ // but calculating this exactly would be more expensive).
+ ((ImageFloatingTextView) child).setNumIndentLines(mIndentLines);
+ }
+
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+
+ final int childHeight = child.getMeasuredHeight();
+ int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
+ lp.bottomMargin + (first ? 0 : mSpacing));
+ first = false;
+
+ if (newHeight <= targetHeight) {
+ totalHeight = newHeight;
+ lp.hide = false;
+ } else {
+ break;
+ }
+ }
+
+ // Now that we know which views to take, fix up the indents and see what width we get.
+ int measuredWidth = mPaddingLeft + mPaddingRight;
+ int imageLines = mIndentLines;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child.getVisibility() == GONE || lp.hide) {
+ continue;
+ }
+
+ if (child instanceof ImageFloatingTextView) {
+ ImageFloatingTextView textChild = (ImageFloatingTextView) child;
+ if (imageLines == 2 && textChild.getLineCount() > 2) {
+ // HACK: If we need indent for two lines, and they're coming from the same
+ // view, we need extra spacing to compensate for the lack of margins,
+ // so add an extra line of indent.
+ imageLines = 3;
+ }
+ boolean changed = textChild.setNumIndentLines(Math.max(0, imageLines));
+ if (changed) {
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ }
+ imageLines -= textChild.getLineCount();
+ }
+
+ measuredWidth = Math.max(measuredWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
+ + mPaddingLeft + mPaddingRight);
+ }
+
+
+ setMeasuredDimension(
+ resolveSize(Math.max(getSuggestedMinimumWidth(), measuredWidth),
+ widthMeasureSpec),
+ resolveSize(Math.max(getSuggestedMinimumHeight(), totalHeight),
+ heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int paddingLeft = mPaddingLeft;
+
+ int childTop;
+
+ // Where right end of child should go
+ final int width = right - left;
+ final int childRight = width - mPaddingRight;
+
+ final int layoutDirection = getLayoutDirection();
+ final int count = getChildCount();
+
+ childTop = mPaddingTop;
+
+ boolean first = true;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child.getVisibility() == GONE || lp.hide) {
+ continue;
+ }
+
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ int childLeft;
+ if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+ childLeft = childRight - childWidth - lp.rightMargin;
+ } else {
+ childLeft = paddingLeft + lp.leftMargin;
+ }
+
+ if (!first) {
+ childTop += mSpacing;
+ }
+
+ childTop += lp.topMargin;
+ child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
+
+ childTop += childHeight + lp.bottomMargin;
+
+ first = false;
+ }
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.hide) {
+ return true;
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(mContext, attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+ LayoutParams copy = new LayoutParams(lp.width, lp.height);
+ if (lp instanceof MarginLayoutParams) {
+ copy.copyMarginsFrom((MarginLayoutParams) lp);
+ }
+ return copy;
+ }
+
+ @RemotableViewMethod
+ /**
+ * Sets how many lines should be indented to avoid a floating image.
+ */
+ public void setNumIndentLines(int numberLines) {
+ mIndentLines = numberLines;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+
+ boolean hide = false;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+ }
+}