1 /*
2  * Copyright (C) 2020 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.keyguard;
18 
19 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
20 
21 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
22 
23 import android.util.Log;
24 import android.view.LayoutInflater;
25 
26 import androidx.annotation.Nullable;
27 import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.keyguard.KeyguardInputViewController.Factory;
31 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
32 import com.android.keyguard.dagger.KeyguardBouncerScope;
33 import com.android.systemui.flags.FeatureFlags;
34 import com.android.systemui.res.R;
35 import com.android.systemui.util.ViewController;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 import javax.inject.Inject;
41 
42 /**
43  * Controller for a {@link KeyguardSecurityViewFlipper}.
44  */
45 @KeyguardBouncerScope
46 public class KeyguardSecurityViewFlipperController
47         extends ViewController<KeyguardSecurityViewFlipper> {
48 
49     private static final boolean DEBUG = KeyguardConstants.DEBUG;
50     private static final String TAG = "KeyguardSecurityView";
51 
52     private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
53             new ArrayList<>();
54     private final LayoutInflater mLayoutInflater;
55     private final AsyncLayoutInflater mAsyncLayoutInflater;
56     private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
57     private final Factory mKeyguardSecurityViewControllerFactory;
58     private final FeatureFlags mFeatureFlags;
59 
60     @Inject
KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view, LayoutInflater layoutInflater, AsyncLayoutInflater asyncLayoutInflater, KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory, EmergencyButtonController.Factory emergencyButtonControllerFactory, FeatureFlags featureFlags)61     protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
62             LayoutInflater layoutInflater,
63             AsyncLayoutInflater asyncLayoutInflater,
64             KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory,
65             EmergencyButtonController.Factory emergencyButtonControllerFactory,
66             FeatureFlags featureFlags) {
67         super(view);
68         mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
69         mLayoutInflater = layoutInflater;
70         mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
71         mAsyncLayoutInflater = asyncLayoutInflater;
72         mFeatureFlags = featureFlags;
73     }
74 
75     @Override
onViewAttached()76     protected void onViewAttached() {
77 
78     }
79 
80     @Override
onViewDetached()81     protected void onViewDetached() {
82 
83     }
84 
reset()85     public void reset() {
86         for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
87             child.reset();
88         }
89     }
90 
91     /** Handles density or font scale changes. */
clearViews()92     public void clearViews() {
93         mView.removeAllViews();
94         mChildren.clear();
95     }
96 
97 
98     @VisibleForTesting
getSecurityView(SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback, OnViewInflatedCallback onViewInflatedCallback)99     void getSecurityView(SecurityMode securityMode,
100             KeyguardSecurityCallback keyguardSecurityCallback,
101             OnViewInflatedCallback onViewInflatedCallback) {
102         for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
103             if (child.getSecurityMode() == securityMode) {
104                 onViewInflatedCallback.onViewInflated(child);
105                 return;
106             }
107         }
108 
109         asynchronouslyInflateView(securityMode, keyguardSecurityCallback, onViewInflatedCallback);
110     }
111 
112     /**
113      * Asynchronously inflate view and then add it to view flipper on the main thread when complete.
114      *
115      * OnInflateFinishedListener will be called on the main thread.
116      *
117      * @param securityMode
118      * @param keyguardSecurityCallback
119      */
asynchronouslyInflateView(SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback, @Nullable OnViewInflatedCallback onViewInflatedListener)120     public void asynchronouslyInflateView(SecurityMode securityMode,
121             KeyguardSecurityCallback keyguardSecurityCallback,
122             @Nullable OnViewInflatedCallback onViewInflatedListener) {
123         int layoutId = mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)
124                 ? getLayoutIdFor(securityMode) : getLegacyLayoutIdFor(securityMode);
125         if (layoutId != 0) {
126             if (DEBUG) {
127                 Log.v(TAG, "inflating on bg thread id = " + layoutId + " .");
128             }
129             mAsyncLayoutInflater.inflate(layoutId, mView,
130                     (view, resId, parent) -> {
131                         mView.addView(view);
132                         KeyguardInputViewController<KeyguardInputView> childController =
133                                 mKeyguardSecurityViewControllerFactory.create(
134                                         (KeyguardInputView) view,
135                                         securityMode, keyguardSecurityCallback);
136                         childController.init();
137                         mChildren.add(childController);
138                         if (onViewInflatedListener != null) {
139                             onViewInflatedListener.onViewInflated(childController);
140 
141                             // Single bouncer constrains are default
142                             if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
143                                 boolean useSplitBouncer =
144                                         getResources().getBoolean(R.bool.update_bouncer_constraints)
145                                         && getResources().getConfiguration().orientation
146                                                 == ORIENTATION_LANDSCAPE;
147 
148                                 updateConstraints(useSplitBouncer);
149                             }
150                         }
151                     });
152         }
153     }
154 
getLayoutIdFor(SecurityMode securityMode)155     private int getLayoutIdFor(SecurityMode securityMode) {
156         // TODO (b/297863911, b/297864907) - implement motion layout for other bouncers
157         switch (securityMode) {
158             case Pattern: return R.layout.keyguard_pattern_motion_layout;
159             case PIN: return R.layout.keyguard_pin_motion_layout;
160             case Password: return R.layout.keyguard_password_motion_layout;
161             case SimPin: return R.layout.keyguard_sim_pin_view;
162             case SimPuk: return R.layout.keyguard_sim_puk_view;
163             default:
164                 return 0;
165         }
166     }
167 
getLegacyLayoutIdFor(SecurityMode securityMode)168     private int getLegacyLayoutIdFor(SecurityMode securityMode) {
169         switch (securityMode) {
170             case Pattern: return R.layout.keyguard_pattern_view;
171             case PIN: return R.layout.keyguard_pin_view;
172             case Password: return R.layout.keyguard_password_view;
173             case SimPin: return R.layout.keyguard_sim_pin_view;
174             case SimPuk: return R.layout.keyguard_sim_puk_view;
175             default:
176                 return 0;
177         }
178     }
179 
180     /** Updates the keyguard view's constraints (single or split constraints).
181      *  Split constraints are only used for small landscape screens.
182      *  Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled. */
updateConstraints(boolean useSplitBouncer)183     public void updateConstraints(boolean useSplitBouncer) {
184         mView.updateConstraints(useSplitBouncer);
185     }
186 
187     /** Makes the supplied child visible if it is contained win this view, */
show(KeyguardInputViewController<KeyguardInputView> childController)188     public void show(KeyguardInputViewController<KeyguardInputView> childController) {
189         int index = childController.getIndexIn(mView);
190         if (index != -1) {
191             mView.setDisplayedChild(index);
192         }
193     }
194 
195     /** Listener to when view has finished inflation. */
196     public interface OnViewInflatedCallback {
197         /** Notifies that view has been inflated */
onViewInflated(KeyguardInputViewController<KeyguardInputView> controller)198         void onViewInflated(KeyguardInputViewController<KeyguardInputView> controller);
199     }
200 }
201