1 /*
2  * Copyright (C) 2019 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.quickstep.views;
17 
18 import android.content.Context;
19 import android.content.res.Configuration;
20 import android.content.res.Resources;
21 import android.graphics.drawable.Drawable;
22 import android.util.AttributeSet;
23 import android.util.FloatProperty;
24 import android.view.View;
25 import android.widget.ImageView;
26 import android.widget.LinearLayout;
27 import android.widget.TextView;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 
32 import com.android.launcher3.R;
33 import com.android.quickstep.ThumbnailDrawable;
34 import com.android.systemui.shared.recents.model.ThumbnailData;
35 
36 /**
37  * View representing an individual task item with the icon + thumbnail adjacent to the task label.
38  */
39 public final class TaskItemView extends LinearLayout {
40 
41     private static final String EMPTY_LABEL = "";
42     private static final String DEFAULT_LABEL = "...";
43     private final Drawable mDefaultIcon;
44     private final Drawable mDefaultThumbnail;
45     private final TaskLayerDrawable mIconDrawable;
46     private final TaskLayerDrawable mThumbnailDrawable;
47     private View mTaskIconThumbnailView;
48     private TextView mLabelView;
49     private ImageView mIconView;
50     private ImageView mThumbnailView;
51     private float mContentTransitionProgress;
52     private int mDisplayedOrientation;
53 
54     /**
55      * Property representing the content transition progress of the view. 1.0f represents that the
56      * currently bound icon, thumbnail, and label are fully animated in and visible.
57      */
58     public static FloatProperty CONTENT_TRANSITION_PROGRESS =
59             new FloatProperty<TaskItemView>("taskContentTransitionProgress") {
60                 @Override
61                 public void setValue(TaskItemView view, float progress) {
62                     view.setContentTransitionProgress(progress);
63                 }
64 
65                 @Override
66                 public Float get(TaskItemView view) {
67                     return view.mContentTransitionProgress;
68                 }
69             };
70 
TaskItemView(Context context, AttributeSet attrs)71     public TaskItemView(Context context, AttributeSet attrs) {
72         super(context, attrs);
73         Resources res = context.getResources();
74         mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme());
75         mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme());
76         mIconDrawable = new TaskLayerDrawable(context);
77         mThumbnailDrawable = new TaskLayerDrawable(context);
78     }
79 
80     @Override
onFinishInflate()81     protected void onFinishInflate() {
82         super.onFinishInflate();
83         mLabelView = findViewById(R.id.task_label);
84         mTaskIconThumbnailView = findViewById(R.id.task_icon_and_thumbnail);
85         mThumbnailView = findViewById(R.id.task_thumbnail);
86         mIconView = findViewById(R.id.task_icon);
87 
88         mThumbnailView.setImageDrawable(mThumbnailDrawable);
89         mIconView.setImageDrawable(mIconDrawable);
90 
91         resetToEmptyUi();
92         CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
93     }
94 
95     /**
96      * Resets task item view to empty, loading UI.
97      */
resetToEmptyUi()98     public void resetToEmptyUi() {
99         mIconDrawable.resetDrawable();
100         mThumbnailDrawable.resetDrawable();
101         setLabel(EMPTY_LABEL);
102     }
103 
104     /**
105      * Set the label for the task item. Sets to a default label if null.
106      *
107      * @param label task label
108      */
setLabel(@ullable String label)109     public void setLabel(@Nullable String label) {
110         mLabelView.setText(getSafeLabel(label));
111         // TODO: Animation for label
112     }
113 
114     /**
115      * Set the icon for the task item. Sets to a default icon if null.
116      *
117      * @param icon task icon
118      */
setIcon(@ullable Drawable icon)119     public void setIcon(@Nullable Drawable icon) {
120         // TODO: Scale the icon up based off the padding on the side
121         // The icon proper is actually smaller than the drawable and has "padding" on the side for
122         // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
123         // view if we want the icon to be flush with the bottom of the thumbnail.
124         mIconDrawable.setCurrentDrawable(getSafeIcon(icon));
125     }
126 
127     /**
128      * Set the task thumbnail for the task. Sets to a default thumbnail if null.
129      *
130      * @param thumbnailData task thumbnail data for the task
131      */
setThumbnail(@ullable ThumbnailData thumbnailData)132     public void setThumbnail(@Nullable ThumbnailData thumbnailData) {
133         mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnailData));
134     }
135 
getThumbnailView()136     public View getThumbnailView() {
137         return mThumbnailView;
138     }
139 
getIconView()140     public View getIconView() {
141         return mIconView;
142     }
143 
getLabelView()144     public View getLabelView() {
145         return mLabelView;
146     }
147 
148     /**
149      * Start a new animation from the current task content to the specified new content. The caller
150      * is responsible for the actual animation control via the property
151      * {@link #CONTENT_TRANSITION_PROGRESS}.
152      *
153      * @param endIcon the icon to animate to
154      * @param endThumbnail the thumbnail to animate to
155      * @param endLabel the label to animate to
156      */
startContentAnimation(@ullable Drawable endIcon, @Nullable ThumbnailData endThumbnail, @Nullable String endLabel)157     public void startContentAnimation(@Nullable Drawable endIcon,
158             @Nullable ThumbnailData endThumbnail, @Nullable String endLabel) {
159         mIconDrawable.startNewTransition(getSafeIcon(endIcon));
160         mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
161         // TODO: Animation for label
162 
163         setContentTransitionProgress(0.0f);
164     }
165 
setContentTransitionProgress(float progress)166     private void setContentTransitionProgress(float progress) {
167         mContentTransitionProgress = progress;
168         mIconDrawable.setTransitionProgress(progress);
169         mThumbnailDrawable.setTransitionProgress(progress);
170         // TODO: Animation for label
171     }
172 
getSafeIcon(@ullable Drawable icon)173     private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) {
174         return (icon != null) ? icon : mDefaultIcon;
175     }
176 
getSafeThumbnail(@ullable ThumbnailData thumbnailData)177     private @NonNull Drawable getSafeThumbnail(@Nullable ThumbnailData thumbnailData) {
178         if (thumbnailData == null || thumbnailData.thumbnail == null) {
179             return mDefaultThumbnail;
180         }
181         int orientation = getResources().getConfiguration().orientation;
182         return new ThumbnailDrawable(getResources(), thumbnailData,
183                 orientation /* requestedOrientation */);
184     }
185 
getSafeLabel(@ullable String label)186     private @NonNull String getSafeLabel(@Nullable String label) {
187         return (label != null) ? label : DEFAULT_LABEL;
188     }
189 
190     @Override
onAttachedToWindow()191     protected void onAttachedToWindow() {
192         super.onAttachedToWindow();
193         onOrientationChanged(getResources().getConfiguration().orientation);
194     }
195 
196     @Override
onConfigurationChanged(Configuration newConfig)197     protected void onConfigurationChanged(Configuration newConfig) {
198         super.onConfigurationChanged(newConfig);
199         onOrientationChanged(newConfig.orientation);
200     }
201 
onOrientationChanged(int newOrientation)202     private void onOrientationChanged(int newOrientation) {
203         if (mDisplayedOrientation == newOrientation) {
204             return;
205         }
206         mDisplayedOrientation = newOrientation;
207         int layerCount = mThumbnailDrawable.getNumberOfLayers();
208         for (int i = 0; i < layerCount; i++) {
209             Drawable drawable = mThumbnailDrawable.getDrawable(i);
210             if (drawable instanceof ThumbnailDrawable) {
211                 ((ThumbnailDrawable) drawable).setRequestedOrientation(newOrientation);
212             }
213         }
214         mTaskIconThumbnailView.forceLayout();
215     }
216 }
217