1 /*
2  * Copyright (C) 2011 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.android.launcher3;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ArgbEvaluator;
22 import android.animation.ValueAnimator;
23 import android.content.Context;
24 import android.graphics.Color;
25 import android.graphics.Rect;
26 import android.graphics.drawable.ColorDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.support.v4.graphics.ColorUtils;
29 import android.util.AttributeSet;
30 import android.view.LayoutInflater;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.ViewDebug;
34 import android.widget.FrameLayout;
35 import android.widget.TextView;
36 
37 import com.android.launcher3.config.FeatureFlags;
38 import com.android.launcher3.dynamicui.ExtractedColors;
39 import com.android.launcher3.logging.UserEventDispatcher;
40 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
41 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
42 import com.android.launcher3.util.Themes;
43 
44 public class Hotseat extends FrameLayout
45         implements UserEventDispatcher.LogContainerProvider {
46 
47     private CellLayout mContent;
48 
49     private Launcher mLauncher;
50 
51     @ViewDebug.ExportedProperty(category = "launcher")
52     private final boolean mHasVerticalHotseat;
53 
54     @ViewDebug.ExportedProperty(category = "launcher")
55     private int mBackgroundColor;
56     @ViewDebug.ExportedProperty(category = "launcher")
57     private ColorDrawable mBackground;
58     private ValueAnimator mBackgroundColorAnimator;
59 
Hotseat(Context context)60     public Hotseat(Context context) {
61         this(context, null);
62     }
63 
Hotseat(Context context, AttributeSet attrs)64     public Hotseat(Context context, AttributeSet attrs) {
65         this(context, attrs, 0);
66     }
67 
Hotseat(Context context, AttributeSet attrs, int defStyle)68     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
69         super(context, attrs, defStyle);
70         mLauncher = Launcher.getLauncher(context);
71         mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
72         mBackgroundColor = ColorUtils.setAlphaComponent(
73                 Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
74         mBackground = new ColorDrawable(mBackgroundColor);
75         setBackground(mBackground);
76     }
77 
getLayout()78     public CellLayout getLayout() {
79         return mContent;
80     }
81 
82     /**
83      * Returns whether there are other icons than the all apps button in the hotseat.
84      */
hasIcons()85     public boolean hasIcons() {
86         return mContent.getShortcutsAndWidgets().getChildCount() > 1;
87     }
88 
89     /**
90      * Registers the specified listener on the cell layout of the hotseat.
91      */
92     @Override
setOnLongClickListener(OnLongClickListener l)93     public void setOnLongClickListener(OnLongClickListener l) {
94         mContent.setOnLongClickListener(l);
95     }
96 
97     /* Get the orientation invariant order of the item in the hotseat for persistence. */
getOrderInHotseat(int x, int y)98     int getOrderInHotseat(int x, int y) {
99         return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
100     }
101 
102     /* Get the orientation specific coordinates given an invariant order in the hotseat. */
getCellXFromOrder(int rank)103     int getCellXFromOrder(int rank) {
104         return mHasVerticalHotseat ? 0 : rank;
105     }
106 
getCellYFromOrder(int rank)107     int getCellYFromOrder(int rank) {
108         return mHasVerticalHotseat ? (mContent.getCountY() - (rank + 1)) : 0;
109     }
110 
111     @Override
onFinishInflate()112     protected void onFinishInflate() {
113         super.onFinishInflate();
114         DeviceProfile grid = mLauncher.getDeviceProfile();
115         mContent = (CellLayout) findViewById(R.id.layout);
116         if (grid.isVerticalBarLayout()) {
117             mContent.setGridSize(1, grid.inv.numHotseatIcons);
118         } else {
119             mContent.setGridSize(grid.inv.numHotseatIcons, 1);
120         }
121 
122         resetLayout();
123     }
124 
resetLayout()125     void resetLayout() {
126         mContent.removeAllViewsInLayout();
127 
128         if (!FeatureFlags.NO_ALL_APPS_ICON) {
129             // Add the Apps button
130             Context context = getContext();
131             DeviceProfile grid = mLauncher.getDeviceProfile();
132             int allAppsButtonRank = grid.inv.getAllAppsButtonRank();
133 
134             LayoutInflater inflater = LayoutInflater.from(context);
135             TextView allAppsButton = (TextView)
136                     inflater.inflate(R.layout.all_apps_button, mContent, false);
137             Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
138             d.setBounds(0, 0, grid.iconSizePx, grid.iconSizePx);
139 
140             int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
141             Rect bounds = d.getBounds();
142             d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
143                     bounds.bottom - scaleDownPx / 2);
144             allAppsButton.setCompoundDrawables(null, d, null, null);
145 
146             allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
147             allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
148             if (mLauncher != null) {
149                 mLauncher.setAllAppsButton(allAppsButton);
150                 allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
151                 allAppsButton.setOnClickListener(mLauncher);
152                 allAppsButton.setOnLongClickListener(mLauncher);
153                 allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
154             }
155 
156             // Note: We do this to ensure that the hotseat is always laid out in the orientation of
157             // the hotseat in order regardless of which orientation they were added
158             int x = getCellXFromOrder(allAppsButtonRank);
159             int y = getCellYFromOrder(allAppsButtonRank);
160             CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x, y, 1, 1);
161             lp.canReorder = false;
162             mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
163         }
164     }
165 
166     @Override
onInterceptTouchEvent(MotionEvent ev)167     public boolean onInterceptTouchEvent(MotionEvent ev) {
168         // We don't want any clicks to go through to the hotseat unless the workspace is in
169         // the normal state or an accessible drag is in progress.
170         return !mLauncher.getWorkspace().workspaceIconsCanBeDragged() &&
171                 !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
172     }
173 
174     @Override
fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent)175     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
176         target.gridX = info.cellX;
177         target.gridY = info.cellY;
178         targetParent.containerType = ContainerType.HOTSEAT;
179     }
180 
updateColor(ExtractedColors extractedColors, boolean animate)181     public void updateColor(ExtractedColors extractedColors, boolean animate) {
182         if (!mHasVerticalHotseat) {
183             int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
184             if (mBackgroundColorAnimator != null) {
185                 mBackgroundColorAnimator.cancel();
186             }
187             if (!animate) {
188                 setBackgroundColor(color);
189             } else {
190                 mBackgroundColorAnimator = ValueAnimator.ofInt(mBackgroundColor, color);
191                 mBackgroundColorAnimator.setEvaluator(new ArgbEvaluator());
192                 mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
193                     @Override
194                     public void onAnimationUpdate(ValueAnimator animation) {
195                         mBackground.setColor((Integer) animation.getAnimatedValue());
196                     }
197                 });
198                 mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
199                     @Override
200                     public void onAnimationEnd(Animator animation) {
201                         mBackgroundColorAnimator = null;
202                     }
203                 });
204                 mBackgroundColorAnimator.start();
205             }
206             mBackgroundColor = color;
207         }
208     }
209 
setBackgroundTransparent(boolean enable)210     public void setBackgroundTransparent(boolean enable) {
211         if (enable) {
212             mBackground.setAlpha(0);
213         } else {
214             mBackground.setAlpha(255);
215         }
216     }
217 
getBackgroundDrawableColor()218     public int getBackgroundDrawableColor() {
219         return mBackgroundColor;
220     }
221 }
222