• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.car.carlauncher.recents;
18 
19 import static android.widget.Toast.LENGTH_SHORT;
20 
21 import android.animation.Animator;
22 import android.animation.AnimatorInflater;
23 import android.animation.AnimatorListenerAdapter;
24 import android.content.Intent;
25 import android.graphics.Rect;
26 import android.os.Bundle;
27 import android.view.View;
28 import android.view.WindowInsets;
29 import android.view.WindowMetrics;
30 import android.widget.Toast;
31 
32 import androidx.annotation.NonNull;
33 import androidx.appcompat.app.AppCompatActivity;
34 import androidx.constraintlayout.widget.Group;
35 import androidx.recyclerview.widget.GridLayoutManager;
36 import androidx.recyclerview.widget.ItemTouchHelper;
37 import androidx.recyclerview.widget.RecyclerView;
38 
39 import com.android.car.carlauncher.R;
40 import com.android.car.carlauncher.recents.view.RecentTasksAdapter;
41 import com.android.car.carlauncher.recents.view.RecentsRecyclerView;
42 import com.android.car.carlauncher.recents.view.TaskTouchHelperCallback;
43 
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Set;
47 
48 /**
49  * Recents activity to display list of recent tasks in Car.
50  */
51 public class CarRecentsActivity extends AppCompatActivity implements
52         RecentTasksViewModel.RecentTasksChangeListener {
53     public static final String OPEN_RECENT_TASK_ACTION =
54             "com.android.car.carlauncher.recents.OPEN_RECENT_TASK_ACTION";
55     private RecentsRecyclerView mRecentsRecyclerView;
56     private GridLayoutManager mGridLayoutManager;
57     private RecentTasksViewModel mRecentTasksViewModel;
58     private Group mRecentTasksGroup;
59     private View mEmptyStateView;
60     private Animator mClearAllAnimator;
61     private NonDODisabledTaskProvider mNonDODisabledTaskProvider;
62     private Set<String> mPackagesToHideFromRecents;
63 
64     @Override
onCreate(Bundle savedInstanceState)65     protected void onCreate(Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         setContentView(R.layout.recents_activity);
68         mRecentsRecyclerView = findViewById(R.id.recent_tasks_list);
69         mRecentTasksGroup = findViewById(R.id.recent_tasks_group);
70         mEmptyStateView = findViewById(R.id.empty_state);
71         mPackagesToHideFromRecents = new HashSet<>(List.of(getResources().getStringArray(
72                 R.array.packages_hidden_from_recents)));
73         mRecentTasksViewModel = RecentTasksViewModel.getInstance();
74         mRecentTasksViewModel.addRecentTasksChangeListener(this);
75         mRecentTasksViewModel.addHiddenTaskProvider(
76                 (packageName, className, baseIntent) ->
77                         mPackagesToHideFromRecents.contains(packageName));
78         mNonDODisabledTaskProvider = new NonDODisabledTaskProvider(this);
79         mRecentTasksViewModel.setDisabledTaskProvider(mNonDODisabledTaskProvider);
80         WindowMetrics windowMetrics = this.getWindowManager().getCurrentWindowMetrics();
81         mRecentTasksViewModel.init(
82                 /* displayId= */ getDisplay().getDisplayId(),
83                 /* windowWidth= */ windowMetrics.getBounds().width(),
84                 /* windowHeight= */ windowMetrics.getBounds().height(),
85                 /* windowInsets= */ windowMetrics.getWindowInsets()
86                         .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).toRect(),
87                 /* defaultThumbnailColor= */
88                 getResources().getColor(R.color.default_recents_thumbnail_color, /* theme= */null));
89 
90         if (!(mRecentsRecyclerView.getLayoutManager() instanceof GridLayoutManager)) {
91             throw new UnsupportedOperationException(
92                     "Only classes that inherit GridLayoutManager are supported");
93         }
94         mGridLayoutManager = (GridLayoutManager) mRecentsRecyclerView.getLayoutManager();
95         int gridSpanCount = mGridLayoutManager.getSpanCount();
96         mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
97             @Override
98             public int getSpanSize(int position) {
99                 boolean isLastPosition = mRecentsRecyclerView != null
100                         && mRecentsRecyclerView.getAdapter() != null
101                         && position == mRecentsRecyclerView.getAdapter().getItemCount() - 1;
102                 if (position == 0 || isLastPosition) {
103                     return gridSpanCount;
104                 }
105                 return 1;
106             }
107         });
108 
109         int colSpacing = getResources().getDimensionPixelSize(R.dimen.recent_task_col_space);
110         mRecentsRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
111             @Override
112             public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
113                     @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
114                 outRect.right = colSpacing / 2;
115                 outRect.left = colSpacing / 2;
116             }
117         });
118 
119         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new TaskTouchHelperCallback(
120                 /* dragDirs= */ 0, ItemTouchHelper.UP,
121                 getResources().getFloat(R.dimen.recent_task_swiped_threshold)));
122         itemTouchHelper.attachToRecyclerView(mRecentsRecyclerView);
123 
124         mClearAllAnimator = AnimatorInflater.loadAnimator(this,
125                 R.animator.recents_clear_all);
126         mClearAllAnimator.addListener(new AnimatorListenerAdapter() {
127             @Override
128             public void onAnimationEnd(Animator animation) {
129                 super.onAnimationEnd(animation);
130                 mRecentTasksViewModel.removeAllRecentTasks();
131                 launchHomeIntent();
132             }
133         });
134         mClearAllAnimator.setTarget(mRecentsRecyclerView);
135         View.OnClickListener clearAllOnClickListener = v -> mClearAllAnimator.start();
136 
137         mRecentsRecyclerView.setAdapter(new RecentTasksAdapter(this, getLayoutInflater(),
138                 itemTouchHelper, clearAllOnClickListener));
139     }
140 
141     @Override
onResume()142     protected void onResume() {
143         super.onResume();
144         if (OPEN_RECENT_TASK_ACTION.equals(getIntent().getAction())) {
145             mRecentTasksViewModel.openMostRecentTask();
146             return;
147         }
148         mRecentTasksViewModel.fetchRecentTaskList();
149         resetViewState();
150         mRecentsRecyclerView.resetPadding();
151     }
152 
153     @Override
onNewIntent(Intent intent)154     protected void onNewIntent(Intent intent) {
155         super.onNewIntent(intent);
156         setIntent(intent);
157     }
158 
159     @Override
onStop()160     protected void onStop() {
161         super.onStop();
162         mRecentTasksViewModel.clearCache();
163     }
164 
165     @Override
onDestroy()166     protected void onDestroy() {
167         super.onDestroy();
168         mNonDODisabledTaskProvider.terminate();
169         mRecentTasksViewModel.terminate();
170         mClearAllAnimator.end();
171         mClearAllAnimator.removeAllListeners();
172     }
173 
174     @Override
onRecentTasksFetched()175     public void onRecentTasksFetched() {
176         resetViewState();
177         mRecentsRecyclerView.resetPadding();
178     }
179 
180     @Override
onOpenRecentTaskFail()181     public void onOpenRecentTaskFail() {
182         // notify the user when there is a failure to open a task from recents
183         Toast.makeText(this, R.string.failure_opening_recent_task_message, LENGTH_SHORT).show();
184     }
185 
186     @Override
onOpenTopRunningTaskFail()187     public void onOpenTopRunningTaskFail() {
188         launchHomeIntent();
189     }
190 
191     @Override
onRecentTaskRemoved(int position)192     public void onRecentTaskRemoved(int position) {
193         mRecentsRecyclerView.resetPadding();
194         if (mRecentTasksViewModel.getRecentTasksSize() == 0) {
195             launchHomeIntent();
196         }
197     }
198 
199     /**
200      * Launches the Home Activity.
201      */
launchHomeIntent()202     protected void launchHomeIntent() {
203         Intent homeActivityIntent = new Intent(Intent.ACTION_MAIN);
204         homeActivityIntent.addCategory(Intent.CATEGORY_HOME);
205         homeActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
206         startActivity(homeActivityIntent);
207     }
208 
resetViewState()209     private void resetViewState() {
210         boolean isRecentTaskListEmpty = mRecentTasksViewModel.getRecentTasksSize() == 0;
211         if (!isRecentTaskListEmpty) {
212             mRecentsRecyclerView.setAlpha(1f);
213             mGridLayoutManager.scrollToPositionWithOffset(/* position= */ 0, /* offset= */ 0);
214         }
215         mRecentTasksGroup.setVisibility(isRecentTaskListEmpty ? View.GONE : View.VISIBLE);
216         mEmptyStateView.setVisibility(isRecentTaskListEmpty ? View.VISIBLE : View.GONE);
217     }
218 }
219