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
17package com.example.android.home;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.TypedArray;
22import android.graphics.Canvas;
23import android.graphics.Rect;
24import android.graphics.drawable.Drawable;
25import android.util.AttributeSet;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.LayoutInflater;
29import android.widget.TextView;
30
31import 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 */
61public 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
86    public ApplicationsStackLayout(Context context) {
87        super(context);
88        initLayout();
89    }
90
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
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     */
126    public int getOrientation() {
127        return mOrientation;
128    }
129
130    @Override
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
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
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
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
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
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
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
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     */
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     */
332    public void setRecents(List<ApplicationInfo> applications) {
333        mRecents = applications;
334        requestLayout();
335    }
336
337    public void onClick(View v) {
338        getContext().startActivity((Intent) v.getTag());
339    }
340}
341