Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad749416 authored by Michael Kwan's avatar Michael Kwan Committed by Android (Google) Code Review
Browse files

Merge "Add top and bottom panel overlay to lists in AlertDialog for watch." into nyc-mr1-dev

parents 0217d4e6 55e4030f
Loading
Loading
Loading
Loading
+6 −38
Original line number Diff line number Diff line
@@ -178,11 +178,6 @@ public class AlertController {
        return outValue.data != 0;
    }

    private static boolean isWatch(Context context) {
        return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_WATCH)
                == Configuration.UI_MODE_TYPE_WATCH;
    }

    public static final AlertController create(Context context, DialogInterface di, Window window) {
        final TypedArray a = context.obtainStyledAttributes(
                null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
@@ -892,17 +887,11 @@ public class AlertController {
            listView.setAdapter(mAdapter);
            final int checkedItem = mCheckedItem;
            if (checkedItem > -1) {
                // TODO: Remove temp watch specific code
                if (isWatch(mContext)) {
                    listView.setItemChecked(checkedItem + listView.getHeaderViewsCount(), true);
                    listView.setSelection(checkedItem + listView.getHeaderViewsCount());
                } else {
                listView.setItemChecked(checkedItem, true);
                listView.setSelection(checkedItem);
            }
        }
    }
    }

    public static class RecycleListView extends ListView {
        private final int mPaddingTopNoTitle;
@@ -1078,15 +1067,9 @@ public class AlertController {
                            if (mCheckedItems != null) {
                                boolean isItemChecked = mCheckedItems[position];
                                if (isItemChecked) {
                                    // TODO: Remove temp watch specific code
                                    if (isWatch(mContext)) {
                                        listView.setItemChecked(
                                                position + listView.getHeaderViewsCount(), true);
                                    } else {
                                    listView.setItemChecked(position, true);
                                }
                            }
                            }
                            return view;
                        }
                    };
@@ -1105,17 +1088,10 @@ public class AlertController {
                        public void bindView(View view, Context context, Cursor cursor) {
                            CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
                            text.setText(cursor.getString(mLabelIndex));
                            // TODO: Remove temp watch specific code
                            if (isWatch(mContext)) {
                                listView.setItemChecked(
                                        cursor.getPosition() + listView.getHeaderViewsCount(),
                                        cursor.getInt(mIsCheckedIndex) == 1);
                            } else {
                            listView.setItemChecked(
                                    cursor.getPosition(),
                                    cursor.getInt(mIsCheckedIndex) == 1);
                        }
                        }

                        @Override
                        public View newView(Context context, Cursor cursor, ViewGroup parent) {
@@ -1157,10 +1133,6 @@ public class AlertController {
                listView.setOnItemClickListener(new OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
                        // TODO: Remove temp watch specific code
                        if (isWatch(mContext)) {
                            position -= listView.getHeaderViewsCount();
                        }
                        mOnClickListener.onClick(dialog.mDialogInterface, position);
                        if (!mIsSingleChoice) {
                            dialog.mDialogInterface.dismiss();
@@ -1171,10 +1143,6 @@ public class AlertController {
                listView.setOnItemClickListener(new OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
                        // TODO: Remove temp watch specific code
                        if (isWatch(mContext)) {
                            position -= listView.getHeaderViewsCount();
                        }
                        if (mCheckedItems != null) {
                            mCheckedItems[position] = listView.isItemChecked(position);
                        }
+23 −12
Original line number Diff line number Diff line
@@ -18,12 +18,15 @@ package com.android.internal.app;

import android.content.Context;
import android.content.DialogInterface;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.AbsListView;

import com.android.internal.app.AlertController;
import com.android.internal.R;
@@ -52,30 +55,38 @@ public class MicroAlertController extends AlertController {
            contentPanel.removeView(mMessageView);

            if (mListView != null) {
                // has ListView, swap ScrollView with ListView
                // has ListView, swap scrollView with ListView

                // move topPanel into header of ListView
                // move topPanel into top of scrollParent
                View topPanel = mScrollView.findViewById(R.id.topPanel);
                ((ViewGroup) topPanel.getParent()).removeView(topPanel);
                topPanel.setLayoutParams(
                        new AbsListView.LayoutParams(topPanel.getLayoutParams()));
                mListView.addHeaderView(topPanel, null, false);
                FrameLayout.LayoutParams topParams =
                        new FrameLayout.LayoutParams(topPanel.getLayoutParams());
                topParams.gravity = Gravity.TOP;
                topPanel.setLayoutParams(topParams);

                // move buttonPanel into footer of ListView
                // move buttonPanel into bottom of scrollParent
                View buttonPanel = mScrollView.findViewById(R.id.buttonPanel);
                ((ViewGroup) buttonPanel.getParent()).removeView(buttonPanel);
                buttonPanel.setLayoutParams(
                        new AbsListView.LayoutParams(buttonPanel.getLayoutParams()));
                mListView.addFooterView(buttonPanel, null, false);
                FrameLayout.LayoutParams buttonParams =
                        new FrameLayout.LayoutParams(buttonPanel.getLayoutParams());
                buttonParams.gravity = Gravity.BOTTOM;
                buttonPanel.setLayoutParams(buttonParams);

                // swap ScrollView w/ ListView
                // remove scrollview
                final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
                final int childIndex = scrollParent.indexOfChild(mScrollView);
                scrollParent.removeViewAt(childIndex);
                scrollParent.addView(mListView, childIndex,

                // add list view
                scrollParent.addView(mListView,
                        new ViewGroup.LayoutParams(
                                ViewGroup.LayoutParams.MATCH_PARENT,
                                ViewGroup.LayoutParams.MATCH_PARENT));

                // add top and button panel
                scrollParent.addView(topPanel);
                scrollParent.addView(buttonPanel);
            } else {
                // no content, just hide everything
                contentPanel.setVisibility(View.GONE);
+327 −0
Original line number Diff line number Diff line
/*
 * 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 android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ListView;
import android.widget.FrameLayout;

import java.util.ArrayList;


/**
 * Layout for the decor for ListViews on watch-type devices with small screens.
 * <p>
 * Supports one panel with the gravity set to top, and one panel with gravity set to bottom.
 * <p>
 * Use with one ListView child. The top and bottom panels will track the ListView's scrolling.
 * If there is no ListView child, it will act like a normal FrameLayout.
 */
public class WatchListDecorLayout extends FrameLayout
        implements ViewTreeObserver.OnScrollChangedListener {

    private int mForegroundPaddingLeft = 0;
    private int mForegroundPaddingTop = 0;
    private int mForegroundPaddingRight = 0;
    private int mForegroundPaddingBottom = 0;

    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);

    /** Track the amount the ListView has to scroll up to account for padding change difference. */
    private int mPendingScroll;
    private View mBottomPanel;
    private View mTopPanel;
    private ListView mListView;
    private ViewTreeObserver mObserver;


    public WatchListDecorLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WatchListDecorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public WatchListDecorLayout(
            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        mPendingScroll = 0;

        for (int i = 0; i < getChildCount(); ++i) {
            View child = getChildAt(i);
            if (child instanceof ListView) {
                if (mListView != null) {
                    throw new IllegalArgumentException("only one ListView child allowed");
                }
                mListView = (ListView) child;

                mListView.setNestedScrollingEnabled(true);
                mObserver = mListView.getViewTreeObserver();
                mObserver.addOnScrollChangedListener(this);
            } else {
                int gravity = (((LayoutParams) child.getLayoutParams()).gravity
                        & Gravity.VERTICAL_GRAVITY_MASK);
                if (gravity == Gravity.TOP && mTopPanel == null) {
                    mTopPanel = child;
                } else if (gravity == Gravity.BOTTOM && mBottomPanel == null) {
                    mBottomPanel = child;
                }
            }
        }
    }

    @Override
    public void onDetachedFromWindow() {
        mListView = null;
        mBottomPanel = null;
        mTopPanel = null;
        if (mObserver != null) {
            if (mObserver.isAlive()) {
                mObserver.removeOnScrollChangedListener(this);
            }
            mObserver = null;
        }
    }

    private void applyMeasureToChild(View child, int widthMeasureSpec, int heightMeasureSpec) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec;
        if (lp.width == LayoutParams.MATCH_PARENT) {
            final int width = Math.max(0, getMeasuredWidth()
                    - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                    - lp.leftMargin - lp.rightMargin);
            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                    width, MeasureSpec.EXACTLY);
        } else {
            childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                    getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                    lp.leftMargin + lp.rightMargin,
                    lp.width);
        }

        final int childHeightMeasureSpec;
        if (lp.height == LayoutParams.MATCH_PARENT) {
            final int height = Math.max(0, getMeasuredHeight()
                    - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                    - lp.topMargin - lp.bottomMargin);
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                    height, MeasureSpec.EXACTLY);
        } else {
            childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                    getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                    lp.topMargin + lp.bottomMargin,
                    lp.height);
        }

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    private int measureAndGetHeight(View child, int widthMeasureSpec, int heightMeasureSpec) {
        if (child != null) {
            if (child.getVisibility() != GONE) {
                applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
                return child.getMeasuredHeight();
            } else if (getMeasureAllChildren()) {
                applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
            }
        }
        return 0;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (getMeasureAllChildren() || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

        // Account for padding too
        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Check against our foreground's minimum height and width
        final Drawable drawable = getForeground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        if (mListView != null) {
            if (mPendingScroll != 0) {
                mListView.scrollListBy(mPendingScroll);
                mPendingScroll = 0;
            }

            int paddingTop = Math.max(mListView.getPaddingTop(),
                    measureAndGetHeight(mTopPanel, widthMeasureSpec, heightMeasureSpec));
            int paddingBottom = Math.max(mListView.getPaddingBottom(),
                    measureAndGetHeight(mBottomPanel, widthMeasureSpec, heightMeasureSpec));

            if (paddingTop != mListView.getPaddingTop()
                    || paddingBottom != mListView.getPaddingBottom()) {
                mPendingScroll += mListView.getPaddingTop() - paddingTop;
                mListView.setPadding(
                        mListView.getPaddingLeft(), paddingTop,
                        mListView.getPaddingRight(), paddingBottom);
            }
        }

        count = mMatchParentChildren.size();
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                if (mListView == null || (child != mTopPanel && child != mBottomPanel)) {
                    applyMeasureToChild(child, widthMeasureSpec, heightMeasureSpec);
                }
            }
        }
    }

    @Override
    public void setForegroundGravity(int foregroundGravity) {
        if (getForegroundGravity() != foregroundGravity) {
            super.setForegroundGravity(foregroundGravity);

            // calling get* again here because the set above may apply default constraints
            final Drawable foreground = getForeground();
            if (getForegroundGravity() == Gravity.FILL && foreground != null) {
                Rect padding = new Rect();
                if (foreground.getPadding(padding)) {
                    mForegroundPaddingLeft = padding.left;
                    mForegroundPaddingTop = padding.top;
                    mForegroundPaddingRight = padding.right;
                    mForegroundPaddingBottom = padding.bottom;
                }
            } else {
                mForegroundPaddingLeft = 0;
                mForegroundPaddingTop = 0;
                mForegroundPaddingRight = 0;
                mForegroundPaddingBottom = 0;
            }
        }
    }

    private int getPaddingLeftWithForeground() {
        return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
            mPaddingLeft + mForegroundPaddingLeft;
    }

    private int getPaddingRightWithForeground() {
        return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
            mPaddingRight + mForegroundPaddingRight;
    }

    private int getPaddingTopWithForeground() {
        return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
            mPaddingTop + mForegroundPaddingTop;
    }

    private int getPaddingBottomWithForeground() {
        return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
            mPaddingBottom + mForegroundPaddingBottom;
    }

    @Override
    public void onScrollChanged() {
        if (mListView == null) {
            return;
        }

        if (mTopPanel != null) {
            if (mListView.getChildCount() > 0) {
                if (mListView.getFirstVisiblePosition() == 0) {
                    View firstChild = mListView.getChildAt(0);
                    setScrolling(mTopPanel,
                            firstChild.getY() - mTopPanel.getHeight() - mTopPanel.getTop());
                } else {
                    // shift to hide the frame, last child is not the last position
                    setScrolling(mTopPanel, -mTopPanel.getHeight());
                }
            } else {
                setScrolling(mTopPanel, 0); // no visible child, fallback to default behaviour
            }
        }

        if (mBottomPanel != null) {
            if (mListView.getChildCount() > 0) {
                if (mListView.getLastVisiblePosition() >= mListView.getCount() - 1) {
                    View lastChild = mListView.getChildAt(mListView.getChildCount() - 1);
                    setScrolling(mBottomPanel,
                            lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop());
                } else {
                    // shift to hide the frame, last child is not the last position
                    setScrolling(mBottomPanel, mBottomPanel.getHeight());
                }
            } else {
                setScrolling(mBottomPanel, 0); // no visible child, fallback to default behaviour
            }
        }
    }

    /** Only set scrolling for the panel if there is a change in its translationY. */
    private void setScrolling(View panel, float translationY) {
        if (panel.getTranslationY() != translationY) {
            panel.setTranslationY(translationY);
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
<FrameLayout
<com.android.internal.widget.WatchListDecorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/parentPanel"
        android:layout_width="match_parent"
@@ -104,4 +104,4 @@
            </FrameLayout>
        </LinearLayout>
    </ScrollView>
</FrameLayout>
</com.android.internal.widget.WatchListDecorLayout>