1 /*
2  * Copyright (C) 2007 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 
17 package com.example.android.home;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.res.TypedArray;
22 import android.graphics.Canvas;
23 import android.graphics.Rect;
24 import android.graphics.drawable.Drawable;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.LayoutInflater;
29 import android.widget.TextView;
30 
31 import java.util.List;
32 
33 /**
34  * The ApplicationsStackLayout is a specialized layout used for the purpose of the home screen
35  * only. This layout stacks various icons in three distinct areas: the recents, the favorites
36  * (or faves) and the button.
37  *
38  * This layout supports two different orientations: vertical and horizontal. When horizontal,
39  * the areas are laid out this way:
40  *
41  * [RECENTS][FAVES][BUTTON]
42  *
43  * When vertical, the layout is the following:
44  *
45  * [RECENTS]
46  * [FAVES]
47  * [BUTTON]
48  *
49  * The layout operates from the "bottom up" (or from right to left.) This means that the button
50  * area will first be laid out, then the faves area, then the recents. When there are too many
51  * favorites, the recents area is not displayed.
52  *
53  * The following attributes can be set in XML:
54  *
55  * orientation: horizontal or vertical
56  * marginLeft: the left margin of each element in the stack
57  * marginTop: the top margin of each element in the stack
58  * marginRight: the right margin of each element in the stack
59  * marginBottom: the bottom margin of each element in the stack
60  */
61 public class ApplicationsStackLayout extends ViewGroup implements View.OnClickListener {
62     public static final int HORIZONTAL = 0;
63     public static final int VERTICAL = 1;
64 
65     private View mButton;
66     private LayoutInflater mInflater;
67 
68     private int mFavoritesEnd;
69     private int mFavoritesStart;
70 
71     private List<ApplicationInfo> mFavorites;
72     private List<ApplicationInfo> mRecents;
73 
74     private int mOrientation = VERTICAL;
75 
76     private int mMarginLeft;
77     private int mMarginTop;
78     private int mMarginRight;
79     private int mMarginBottom;
80 
81     private Rect mDrawRect = new Rect();
82 
83     private Drawable mBackground;
84     private int mIconSize;
85 
ApplicationsStackLayout(Context context)86     public ApplicationsStackLayout(Context context) {
87         super(context);
88         initLayout();
89     }
90 
ApplicationsStackLayout(Context context, AttributeSet attrs)91     public ApplicationsStackLayout(Context context, AttributeSet attrs) {
92         super(context, attrs);
93 
94         TypedArray a =
95                 context.obtainStyledAttributes(attrs, R.styleable.ApplicationsStackLayout);
96 
97         mOrientation = a.getInt(R.styleable.ApplicationsStackLayout_stackOrientation, VERTICAL);
98 
99         mMarginLeft = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginLeft, 0);
100         mMarginTop = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginTop, 0);
101         mMarginRight = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginRight, 0);
102         mMarginBottom = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginBottom, 0);
103 
104         a.recycle();
105 
106         mIconSize = 42; //(int) getResources().getDimension(android.R.dimen.app_icon_size);
107 
108         initLayout();
109     }
110 
initLayout()111     private void initLayout() {
112         mInflater = LayoutInflater.from(getContext());
113         mButton = mInflater.inflate(R.layout.all_applications_button, this, false);
114         addView(mButton);
115 
116         mBackground = getBackground();
117         setBackgroundDrawable(null);
118         setWillNotDraw(false);
119     }
120 
121     /**
122      * Return the current orientation, either VERTICAL (default) or HORIZONTAL.
123      *
124      * @return the stack orientation
125      */
getOrientation()126     public int getOrientation() {
127         return mOrientation;
128     }
129 
130     @Override
onDraw(Canvas canvas)131     protected void onDraw(Canvas canvas) {
132         final Drawable background = mBackground;
133 
134         final int right = getWidth();
135         final int bottom = getHeight();
136 
137         // Draw behind recents
138         if (mOrientation == VERTICAL) {
139             mDrawRect.set(0, 0, right, mFavoritesStart);
140         } else {
141             mDrawRect.set(0, 0, mFavoritesStart, bottom);
142         }
143         background.setBounds(mDrawRect);
144         background.draw(canvas);
145 
146         // Draw behind favorites
147         if (mFavoritesStart > -1) {
148             if (mOrientation == VERTICAL) {
149                 mDrawRect.set(0, mFavoritesStart, right, mFavoritesEnd);
150             } else {
151                 mDrawRect.set(mFavoritesStart, 0, mFavoritesEnd, bottom);
152             }
153             background.setBounds(mDrawRect);
154             background.draw(canvas);
155         }
156 
157         super.onDraw(canvas);
158     }
159 
160     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)161     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
162         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
163 
164         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
165         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
166 
167         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
168         final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
169 
170         if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
171             throw new IllegalStateException("ApplicationsStackLayout can only be used with "
172                     + "measure spec mode=EXACTLY");
173         }
174 
175         setMeasuredDimension(widthSize, heightSize);
176     }
177 
178     @Override
onLayout(boolean changed, int l, int t, int r, int b)179     protected void onLayout(boolean changed, int l, int t, int r, int b) {
180         removeAllApplications();
181 
182         LayoutParams layoutParams = mButton.getLayoutParams();
183         final int widthSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
184         final int heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
185         mButton.measure(widthSpec, heightSpec);
186 
187         if (mOrientation == VERTICAL) {
188             layoutVertical();
189         } else {
190             layoutHorizontal();
191         }
192     }
193 
layoutVertical()194     private void layoutVertical() {
195         int childLeft = 0;
196         int childTop = getHeight();
197 
198         int childWidth = mButton.getMeasuredWidth();
199         int childHeight = mButton.getMeasuredHeight();
200 
201         childTop -= childHeight + mMarginBottom;
202         mButton.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
203         childTop -= mMarginTop;
204         mFavoritesEnd = childTop - mMarginBottom;
205 
206         int oldChildTop = childTop;
207         childTop = stackApplications(mFavorites, childLeft, childTop);
208         if (childTop != oldChildTop) {
209             mFavoritesStart = childTop + mMarginTop;
210         } else {
211             mFavoritesStart = -1;
212         }
213 
214         stackApplications(mRecents, childLeft, childTop);
215     }
216 
layoutHorizontal()217     private void layoutHorizontal() {
218         int childLeft = getWidth();
219         int childTop = 0;
220 
221         int childWidth = mButton.getMeasuredWidth();
222         int childHeight = mButton.getMeasuredHeight();
223 
224         childLeft -= childWidth;
225         mButton.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
226         childLeft -= mMarginLeft;
227         mFavoritesEnd = childLeft - mMarginRight;
228 
229         int oldChildLeft = childLeft;
230         childLeft = stackApplications(mFavorites, childLeft, childTop);
231         if (childLeft != oldChildLeft) {
232             mFavoritesStart = childLeft + mMarginLeft;
233         } else {
234             mFavoritesStart = -1;
235         }
236 
237         stackApplications(mRecents, childLeft, childTop);
238     }
239 
stackApplications(List<ApplicationInfo> applications, int childLeft, int childTop)240     private int stackApplications(List<ApplicationInfo> applications, int childLeft, int childTop) {
241         LayoutParams layoutParams;
242         int widthSpec;
243         int heightSpec;
244         int childWidth;
245         int childHeight;
246 
247         final boolean isVertical = mOrientation == VERTICAL;
248 
249         final int count = applications.size();
250         for (int i = count - 1; i >= 0; i--) {
251             final ApplicationInfo info = applications.get(i);
252             final View view = createApplicationIcon(mInflater, this, info);
253 
254             layoutParams = view.getLayoutParams();
255             widthSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
256             heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
257             view.measure(widthSpec, heightSpec);
258 
259             childWidth = view.getMeasuredWidth();
260             childHeight = view.getMeasuredHeight();
261 
262             if (isVertical) {
263                 childTop -= childHeight + mMarginBottom;
264 
265                 if (childTop < 0) {
266                     childTop += childHeight + mMarginBottom;
267                     break;
268                 }
269             } else {
270                 childLeft -= childWidth + mMarginRight;
271 
272                 if (childLeft < 0) {
273                     childLeft += childWidth + mMarginRight;
274                     break;
275                 }
276             }
277 
278             addViewInLayout(view, -1, layoutParams);
279 
280             view.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
281 
282             if (isVertical) {
283                 childTop -= mMarginTop;
284             } else {
285                 childLeft -= mMarginLeft;
286             }
287         }
288 
289         return isVertical ? childTop : childLeft;
290     }
291 
removeAllApplications()292     private void removeAllApplications() {
293         final int count = getChildCount();
294         for (int i = count - 1; i >= 0; i--) {
295             final View view = getChildAt(i);
296             if (view != mButton) {
297                 removeViewAt(i);
298             }
299         }
300     }
301 
createApplicationIcon(LayoutInflater inflater, ViewGroup group, ApplicationInfo info)302     private View createApplicationIcon(LayoutInflater inflater,
303             ViewGroup group, ApplicationInfo info) {
304 
305         TextView textView = (TextView) inflater.inflate(R.layout.favorite, group, false);
306 
307         info.icon.setBounds(0, 0, mIconSize, mIconSize);
308         textView.setCompoundDrawables(null, info.icon, null, null);
309         textView.setText(info.title);
310 
311         textView.setTag(info.intent);
312         textView.setOnClickListener(this);
313 
314         return textView;
315     }
316 
317     /**
318      * Sets the list of favorites.
319      *
320      * @param applications the applications to put in the favorites area
321      */
setFavorites(List<ApplicationInfo> applications)322     public void setFavorites(List<ApplicationInfo> applications) {
323         mFavorites = applications;
324         requestLayout();
325     }
326 
327     /**
328      * Sets the list of recents.
329      *
330      * @param applications the applications to put in the recents area
331      */
setRecents(List<ApplicationInfo> applications)332     public void setRecents(List<ApplicationInfo> applications) {
333         mRecents = applications;
334         requestLayout();
335     }
336 
onClick(View v)337     public void onClick(View v) {
338         getContext().startActivity((Intent) v.getTag());
339     }
340 }
341