OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / core / java / com / android / internal / widget / WatchListDecorLayout.java
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.widget;
17
18 import android.content.Context;
19 import android.graphics.drawable.Drawable;
20 import android.graphics.Rect;
21 import android.util.AttributeSet;
22 import android.view.Gravity;
23 import android.view.View;
24 import android.view.ViewTreeObserver;
25 import android.widget.ListView;
26 import android.widget.FrameLayout;
27
28 import java.util.ArrayList;
29
30
31 /**
32  * Layout for the decor for ListViews on watch-type devices with small screens.
33  * <p>
34  * Supports one panel with the gravity set to top, and one panel with gravity set to bottom.
35  * <p>
36  * Use with one ListView child. The top and bottom panels will track the ListView's scrolling.
37  * If there is no ListView child, it will act like a normal FrameLayout.
38  */
39 public class WatchListDecorLayout extends FrameLayout
40         implements ViewTreeObserver.OnScrollChangedListener {
41
42     private int mForegroundPaddingLeft = 0;
43     private int mForegroundPaddingTop = 0;
44     private int mForegroundPaddingRight = 0;
45     private int mForegroundPaddingBottom = 0;
46
47     private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
48
49     /** Track the amount the ListView has to scroll up to account for padding change difference. */
50     private int mPendingScroll;
51     private View mBottomPanel;
52     private View mTopPanel;
53     private ListView mListView;
54     private ViewTreeObserver mObserver;
55
56
57     public WatchListDecorLayout(Context context, AttributeSet attrs) {
58         super(context, attrs);
59     }
60
61     public WatchListDecorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
62         super(context, attrs, defStyleAttr);
63     }
64
65     public WatchListDecorLayout(
66             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
67         super(context, attrs, defStyleAttr, defStyleRes);
68     }
69
70     @Override
71     protected void onAttachedToWindow() {
72         super.onAttachedToWindow();
73
74         mPendingScroll = 0;
75
76         for (int i = 0; i < getChildCount(); ++i) {
77             View child = getChildAt(i);
78             if (child instanceof ListView) {
79                 if (mListView != null) {
80                     throw new IllegalArgumentException("only one ListView child allowed");
81                 }
82                 mListView = (ListView) child;
83
84                 mListView.setNestedScrollingEnabled(true);
85                 mObserver = mListView.getViewTreeObserver();
86                 mObserver.addOnScrollChangedListener(this);
87             } else {
88                 int gravity = (((LayoutParams) child.getLayoutParams()).gravity
89                         & Gravity.VERTICAL_GRAVITY_MASK);
90                 if (gravity == Gravity.TOP && mTopPanel == null) {
91                     mTopPanel = child;
92                 } else if (gravity == Gravity.BOTTOM && mBottomPanel == null) {
93                     mBottomPanel = child;
94                 }
95             }
96         }
97     }
98
99     @Override
100     public void onDetachedFromWindow() {
101         mListView = null;
102         mBottomPanel = null;
103         mTopPanel = null;
104         if (mObserver != null) {
105             if (mObserver.isAlive()) {
106                 mObserver.removeOnScrollChangedListener(this);
107             }
108             mObserver = null;
109         }
110     }
111
112     private void applyMeasureToChild(View child, int widthMeasureSpec, int heightMeasureSpec) {
113         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
114
115         final int childWidthMeasureSpec;
116         if (lp.width == LayoutParams.MATCH_PARENT) {
117             final int width = Math.max(0, getMeasuredWidth()
118                     - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
119                     - lp.leftMargin - lp.rightMargin);
120             childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
121                     width, MeasureSpec.EXACTLY);
122         } else {
123             childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
124                     getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
125                     lp.leftMargin + lp.rightMargin,
126                     lp.width);
127         }
128
129         final int childHeightMeasureSpec;
130         if (lp.height == LayoutParams.MATCH_PARENT) {
131             final int height = Math.max(0, getMeasuredHeight()
132                     - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
133                     - lp.topMargin - lp.bottomMargin);
134             childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
135                     height, MeasureSpec.EXACTLY);
136         } else {
137             childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
138                     getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
139                     lp.topMargin + lp.bottomMargin,
140                     lp.height);
141         }
142
143         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
144     }
145
146     private int measureAndGetHeight(View child, int widthMeasureSpec, int heightMeasureSpec) {
147         if (child != null) {
148             if (child.getVisibility() != GONE) {
149                 applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
150                 return child.getMeasuredHeight();
151             } else if (getMeasureAllChildren()) {
152                 applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec);
153             }
154         }
155         return 0;
156     }
157
158     @Override
159     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
160         int count = getChildCount();
161
162         final boolean measureMatchParentChildren =
163                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
164                 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
165         mMatchParentChildren.clear();
166
167         int maxHeight = 0;
168         int maxWidth = 0;
169         int childState = 0;
170
171         for (int i = 0; i < count; i++) {
172             final View child = getChildAt(i);
173             if (getMeasureAllChildren() || child.getVisibility() != GONE) {
174                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
175                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
176                 maxWidth = Math.max(maxWidth,
177                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
178                 maxHeight = Math.max(maxHeight,
179                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
180                 childState = combineMeasuredStates(childState, child.getMeasuredState());
181                 if (measureMatchParentChildren) {
182                     if (lp.width == LayoutParams.MATCH_PARENT ||
183                             lp.height == LayoutParams.MATCH_PARENT) {
184                         mMatchParentChildren.add(child);
185                     }
186                 }
187             }
188         }
189
190         // Account for padding too
191         maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
192         maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
193
194         // Check against our minimum height and width
195         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
196         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
197
198         // Check against our foreground's minimum height and width
199         final Drawable drawable = getForeground();
200         if (drawable != null) {
201             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
202             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
203         }
204
205         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
206                 resolveSizeAndState(maxHeight, heightMeasureSpec,
207                         childState << MEASURED_HEIGHT_STATE_SHIFT));
208
209         if (mListView != null) {
210             if (mPendingScroll != 0) {
211                 mListView.scrollListBy(mPendingScroll);
212                 mPendingScroll = 0;
213             }
214
215             int paddingTop = Math.max(mListView.getPaddingTop(),
216                     measureAndGetHeight(mTopPanel, widthMeasureSpec, heightMeasureSpec));
217             int paddingBottom = Math.max(mListView.getPaddingBottom(),
218                     measureAndGetHeight(mBottomPanel, widthMeasureSpec, heightMeasureSpec));
219
220             if (paddingTop != mListView.getPaddingTop()
221                     || paddingBottom != mListView.getPaddingBottom()) {
222                 mPendingScroll += mListView.getPaddingTop() - paddingTop;
223                 mListView.setPadding(
224                         mListView.getPaddingLeft(), paddingTop,
225                         mListView.getPaddingRight(), paddingBottom);
226             }
227         }
228
229         count = mMatchParentChildren.size();
230         if (count > 1) {
231             for (int i = 0; i < count; i++) {
232                 final View child = mMatchParentChildren.get(i);
233                 if (mListView == null || (child != mTopPanel && child != mBottomPanel)) {
234                     applyMeasureToChild(child, widthMeasureSpec, heightMeasureSpec);
235                 }
236             }
237         }
238     }
239
240     @Override
241     public void setForegroundGravity(int foregroundGravity) {
242         if (getForegroundGravity() != foregroundGravity) {
243             super.setForegroundGravity(foregroundGravity);
244
245             // calling get* again here because the set above may apply default constraints
246             final Drawable foreground = getForeground();
247             if (getForegroundGravity() == Gravity.FILL && foreground != null) {
248                 Rect padding = new Rect();
249                 if (foreground.getPadding(padding)) {
250                     mForegroundPaddingLeft = padding.left;
251                     mForegroundPaddingTop = padding.top;
252                     mForegroundPaddingRight = padding.right;
253                     mForegroundPaddingBottom = padding.bottom;
254                 }
255             } else {
256                 mForegroundPaddingLeft = 0;
257                 mForegroundPaddingTop = 0;
258                 mForegroundPaddingRight = 0;
259                 mForegroundPaddingBottom = 0;
260             }
261         }
262     }
263
264     private int getPaddingLeftWithForeground() {
265         return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
266             mPaddingLeft + mForegroundPaddingLeft;
267     }
268
269     private int getPaddingRightWithForeground() {
270         return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
271             mPaddingRight + mForegroundPaddingRight;
272     }
273
274     private int getPaddingTopWithForeground() {
275         return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
276             mPaddingTop + mForegroundPaddingTop;
277     }
278
279     private int getPaddingBottomWithForeground() {
280         return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
281             mPaddingBottom + mForegroundPaddingBottom;
282     }
283
284     @Override
285     public void onScrollChanged() {
286         if (mListView == null) {
287             return;
288         }
289
290         if (mTopPanel != null) {
291             if (mListView.getChildCount() > 0) {
292                 if (mListView.getFirstVisiblePosition() == 0) {
293                     View firstChild = mListView.getChildAt(0);
294                     setScrolling(mTopPanel,
295                             firstChild.getY() - mTopPanel.getHeight() - mTopPanel.getTop());
296                 } else {
297                     // shift to hide the frame, last child is not the last position
298                     setScrolling(mTopPanel, -mTopPanel.getHeight());
299                 }
300             } else {
301                 setScrolling(mTopPanel, 0); // no visible child, fallback to default behaviour
302             }
303         }
304
305         if (mBottomPanel != null) {
306             if (mListView.getChildCount() > 0) {
307                 if (mListView.getLastVisiblePosition() >= mListView.getCount() - 1) {
308                     View lastChild = mListView.getChildAt(mListView.getChildCount() - 1);
309                     setScrolling(mBottomPanel,
310                             lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop());
311                 } else {
312                     // shift to hide the frame, last child is not the last position
313                     setScrolling(mBottomPanel, mBottomPanel.getHeight());
314                 }
315             } else {
316                 setScrolling(mBottomPanel, 0); // no visible child, fallback to default behaviour
317             }
318         }
319     }
320
321     /** Only set scrolling for the panel if there is a change in its translationY. */
322     private void setScrolling(View panel, float translationY) {
323         if (panel.getTranslationY() != translationY) {
324             panel.setTranslationY(translationY);
325         }
326     }
327 }