• 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.systemui.car.userpicker;
18 
19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
20 import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
21 
22 import static com.android.systemui.car.userpicker.HeaderState.HEADER_STATE_CHANGE_USER;
23 import static com.android.systemui.car.userpicker.HeaderState.HEADER_STATE_LOGOUT;
24 import static com.android.systemui.car.users.CarSystemUIUserUtil.isMUPANDSystemUI;
25 
26 import android.app.Activity;
27 import android.content.Context;
28 import android.content.res.Configuration;
29 import android.os.Bundle;
30 import android.util.Log;
31 import android.util.Slog;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.Window;
36 import android.view.WindowInsets;
37 import android.view.WindowInsetsController;
38 import android.view.WindowManager;
39 import android.window.OnBackInvokedCallback;
40 
41 import androidx.annotation.NonNull;
42 import androidx.annotation.VisibleForTesting;
43 import androidx.recyclerview.widget.GridLayoutManager;
44 
45 import com.android.car.ui.recyclerview.CarUiRecyclerView;
46 import com.android.systemui.Dumpable;
47 import com.android.systemui.R;
48 import com.android.systemui.car.CarServiceProvider;
49 import com.android.systemui.car.statusicon.ui.UserPickerReadOnlyIconsController;
50 import com.android.systemui.car.userpicker.UserPickerController.Callbacks;
51 import com.android.systemui.dump.DumpManager;
52 import com.android.systemui.settings.DisplayTracker;
53 
54 import java.io.PrintWriter;
55 import java.util.List;
56 
57 import javax.inject.Inject;
58 
59 /**
60  * Main activity for user picker.
61  *
62  * <p>This class uses the Trampoline pattern to ensure the activity is executed as user 0.
63  * It has user picker controller object for the executed display, and cleans it up
64  * when the activity is destroyed.
65  */
66 public class UserPickerActivity extends Activity implements Dumpable {
67     private static final String TAG = UserPickerActivity.class.getSimpleName();
68     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
69 
70     private UserPickerActivityComponent mUserPickerActivityComponent;
71     private boolean mIsDriver;
72 
73     @Inject
74     UserPickerReadOnlyIconsController mUserPickerReadOnlyIconsController;
75     @Inject
76     DisplayTracker mDisplayTracker;
77     @Inject
78     DumpManager mDumpManager;
79 
80     @VisibleForTesting
81     UserPickerController mController;
82     @VisibleForTesting
83     SnackbarManager mSnackbarManager;
84     @VisibleForTesting
85     DialogManager mDialogManager;
86     @VisibleForTesting
87     UserPickerAdapter mAdapter;
88     @VisibleForTesting
89     CarUiRecyclerView mUserPickerView;
90     @VisibleForTesting
91     View mRootView;
92     @VisibleForTesting
93     View mHeaderBarTextForLogout;
94     @VisibleForTesting
95     View mLogoutButton;
96     @VisibleForTesting
97     View mBackButton;
98 
99     private final OnBackInvokedCallback mIgnoreBackCallback = () -> {
100         // Ignore back press.
101         if (DEBUG) Log.d(TAG, "Skip Back");
102     };
103 
104     @Inject
UserPickerActivity( Context context, DisplayTracker displayTracker, CarServiceProvider carServiceProvider, UserPickerSharedState userPickerSharedState )105     UserPickerActivity(
106             Context context, //application context
107             DisplayTracker displayTracker,
108             CarServiceProvider carServiceProvider,
109             UserPickerSharedState userPickerSharedState
110     ) {
111         this();
112         mUserPickerActivityComponent = DaggerUserPickerActivityComponent.builder()
113                 .context(context)
114                 .carServiceProvider(carServiceProvider)
115                 .displayTracker(displayTracker)
116                 .userPickerSharedState(userPickerSharedState)
117                 .build();
118         //Component.inject(this) is not working because constructor and activity itself is
119         //scoped to SystemUiScope but the deps below are scoped to UserPickerScope
120         mDialogManager = mUserPickerActivityComponent.dialogManager();
121         mSnackbarManager = mUserPickerActivityComponent.snackbarManager();
122         mController = mUserPickerActivityComponent.userPickerController();
123     }
124 
125     @VisibleForTesting
UserPickerActivity()126     UserPickerActivity() {
127         super();
128     }
129 
130     private final Callbacks mCallbacks = new Callbacks() {
131         @Override
132         public void onUpdateUsers(List<UserRecord> users) {
133             mAdapter.updateUsers(users);
134             mAdapter.notifyDataSetChanged();
135         }
136 
137         @Override
138         public void onHeaderStateChanged(HeaderState headerState) {
139             setupHeaderBar(headerState);
140         }
141 
142         @Override
143         public void onFinishRequested() {
144             finishAndRemoveTask();
145         }
146     };
147 
148     @Override
onCreate(Bundle savedInstanceState)149     protected void onCreate(Bundle savedInstanceState) {
150         if (shouldStartAsSystemUser()
151                 && !ActivityHelper.startUserPickerAsUserSystem(this)) {
152             super.onCreate(savedInstanceState);
153             return;
154         }
155 
156         if (DEBUG) {
157             Slog.d(TAG, "onCreate: userId=" + getUserId() + " displayId=" + getDisplayId());
158         }
159 
160         super.onCreate(savedInstanceState);
161         setShowWhenLocked(true);
162         requestWindowFeature(Window.FEATURE_NO_TITLE);
163         getOnBackInvokedDispatcher()
164                 .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mIgnoreBackCallback);
165         init();
166     }
167 
168     @VisibleForTesting
init()169     void init() {
170         mIsDriver = getIsDriver();
171         LayoutInflater inflater = LayoutInflater.from(this);
172         mRootView = inflater.inflate(R.layout.user_picker, null);
173         if (getWindow() != null) {
174             setContentView(mRootView);
175             initWindow();
176         }
177 
178         initManagers(mRootView);
179         initViews();
180         initController();
181 
182         mController.onConfigurationChanged();
183         String dumpableName = TAG + "#" + getDisplayId();
184         mDumpManager.unregisterDumpable(dumpableName);
185         mDumpManager.registerNormalDumpable(dumpableName, /* module= */ this);
186     }
187 
188     @Override
onStart()189     protected void onStart() {
190         super.onStart();
191         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
192     }
193 
initViews()194     private void initViews() {
195         View powerBtn = mRootView.findViewById(R.id.power_button_icon_view);
196         powerBtn.setOnClickListener(v -> mController.screenOffDisplay());
197         if (mIsDriver) {
198             powerBtn.setVisibility(View.GONE);
199         }
200         mHeaderBarTextForLogout = mRootView.findViewById(R.id.message);
201 
202         mLogoutButton = mRootView.findViewById(R.id.logout_button_icon_view);
203         mLogoutButton.setOnClickListener(v -> mController.logoutUser());
204 
205         mBackButton = mRootView.findViewById(R.id.back_button);
206         mBackButton.setOnClickListener(v -> {
207             finishAndRemoveTask();
208         });
209 
210         initRecyclerView();
211 
212         ViewGroup statusIconContainer = mRootView
213                 .findViewById(R.id.user_picker_status_icon_container);
214         if (statusIconContainer != null && mUserPickerReadOnlyIconsController != null) {
215             mUserPickerReadOnlyIconsController.addIconViews(statusIconContainer,
216                     /* shouldAttachPanel= */ false);
217         }
218     }
219 
initRecyclerView()220     private void initRecyclerView() {
221         int numCols = getResources().getInteger(R.integer.user_fullscreen_switcher_num_col);
222         mUserPickerView = mRootView.findViewById(R.id.user_picker);
223         mUserPickerView.setLayoutManager(new GridLayoutManager(this, numCols));
224         mAdapter = createUserPickerAdapter();
225         mUserPickerView.setAdapter(mAdapter);
226     }
227 
initWindow()228     private void initWindow() {
229         Window window = getWindow();
230         WindowInsetsController insetsController = window.getInsetsController();
231         if (insetsController != null) {
232             insetsController.setAnimationsDisabled(true);
233             insetsController.hide(WindowInsets.Type.statusBars()
234                     | WindowInsets.Type.navigationBars());
235         }
236     }
237 
initManagers(View rootView)238     private void initManagers(View rootView) {
239         mDialogManager.initContextFromView(rootView);
240         mSnackbarManager.setRootView(rootView, R.id.user_picker_bottom_bar);
241     }
242 
initController()243     private void initController() {
244         mController.init(mCallbacks, getDisplayId());
245     }
246 
247     @VisibleForTesting
createUserPickerAdapter()248     UserPickerAdapter createUserPickerAdapter() {
249         return new UserPickerAdapter(this);
250     }
251 
252     @VisibleForTesting
shouldStartAsSystemUser()253     boolean shouldStartAsSystemUser() {
254         return true;
255     }
256 
257     @VisibleForTesting
getIsDriver()258     boolean getIsDriver() {
259         return !isMUPANDSystemUI() && getDisplayId() == mDisplayTracker.getDefaultDisplayId();
260     }
261 
262     @Override
onStop()263     protected void onStop() {
264         Window window = getWindow();
265         WindowManager.LayoutParams attrs = window.getAttributes();
266         attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
267         window.setAttributes(attrs);
268 
269         super.onStop();
270     }
271 
272     @Override
onDestroy()273     protected void onDestroy() {
274         if (DEBUG) {
275             Slog.d(TAG, "onDestroy: displayId=" + getDisplayId());
276         }
277         getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mIgnoreBackCallback);
278         if (mController != null) {
279             mController.onDestroy();
280         }
281         if (mDialogManager != null) {
282             mDialogManager.clearAllDialogs();
283         }
284         mDumpManager.unregisterDumpable(TAG + "#" + getDisplayId());
285 
286         super.onDestroy();
287     }
288 
289     @Override
onConfigurationChanged(@onNull Configuration newConfig)290     public void onConfigurationChanged(@NonNull Configuration newConfig) {
291         super.onConfigurationChanged(newConfig);
292         mAdapter.onConfigurationChanged();
293         mController.onConfigurationChanged();
294     }
295 
296     @VisibleForTesting
setupHeaderBar(HeaderState headerState)297     void setupHeaderBar(HeaderState headerState) {
298         int state = headerState.getState();
299         switch (state) {
300             case HEADER_STATE_LOGOUT:
301                 mHeaderBarTextForLogout.setVisibility(View.VISIBLE);
302                 mBackButton.setVisibility(View.GONE);
303                 mLogoutButton.setVisibility(View.GONE);
304                 break;
305             case HEADER_STATE_CHANGE_USER:
306                 mHeaderBarTextForLogout.setVisibility(View.GONE);
307                 mBackButton.setVisibility(View.VISIBLE);
308                 if (!mIsDriver) {
309                     mLogoutButton.setVisibility(View.VISIBLE);
310                 }
311                 break;
312         }
313     }
314 
315     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)316     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
317         if (mController != null) {
318             mController.dump(pw);
319         }
320         if (mUserPickerView != null && mUserPickerView.getAdapter() != null) {
321             ((UserPickerAdapter) mUserPickerView.getAdapter()).dump(pw);
322         }
323     }
324 }
325