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

Commit 253bc8d0 authored by Michael Kwan's avatar Michael Kwan Committed by android-build-merger
Browse files

Add top and bottom panel overlay to lists in AlertDialog for watch.

am: 55e4030f

Change-Id: I383c2ad50871265c1f521ecfd7f46d15027dc29c
parents c5767e2e 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>