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.app.slice.Slice.HINT_LIST_ITEM;
20 
21 import android.app.PendingIntent;
22 import android.net.Uri;
23 import android.os.Handler;
24 import android.os.Trace;
25 import android.provider.Settings;
26 import android.util.Log;
27 import android.view.Display;
28 import android.view.View;
29 import android.view.ViewGroup;
30 
31 import androidx.annotation.NonNull;
32 import androidx.lifecycle.LiveData;
33 import androidx.lifecycle.Observer;
34 import androidx.slice.Slice;
35 import androidx.slice.SliceViewManager;
36 import androidx.slice.widget.ListContent;
37 import androidx.slice.widget.RowContent;
38 import androidx.slice.widget.SliceContent;
39 import androidx.slice.widget.SliceLiveData;
40 
41 import com.android.keyguard.dagger.KeyguardStatusViewScope;
42 import com.android.systemui.Dumpable;
43 import com.android.systemui.Flags;
44 import com.android.systemui.dagger.qualifiers.Background;
45 import com.android.systemui.dagger.qualifiers.Main;
46 import com.android.systemui.dump.DumpManager;
47 import com.android.systemui.keyguard.KeyguardSliceProvider;
48 import com.android.systemui.plugins.ActivityStarter;
49 import com.android.systemui.settings.DisplayTracker;
50 import com.android.systemui.statusbar.policy.ConfigurationController;
51 import com.android.systemui.tuner.TunerService;
52 import com.android.systemui.util.ViewController;
53 
54 import java.io.PrintWriter;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.stream.Collectors;
58 
59 import javax.inject.Inject;
60 
61 /** Controller for a {@link KeyguardSliceView}. */
62 @KeyguardStatusViewScope
63 public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements
64         Dumpable {
65     private static final String TAG = "KeyguardSliceViewCtrl";
66 
67     private final Handler mHandler;
68     private final Handler mBgHandler;
69     private final ActivityStarter mActivityStarter;
70     private final ConfigurationController mConfigurationController;
71     private final TunerService mTunerService;
72     private final DumpManager mDumpManager;
73     private final DisplayTracker mDisplayTracker;
74     private int mDisplayId;
75     private LiveData<Slice> mLiveData;
76     private Uri mKeyguardSliceUri;
77     private Slice mSlice;
78     private Map<View, PendingIntent> mClickActions;
79 
80     TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
81 
82     ConfigurationController.ConfigurationListener mConfigurationListener =
83             new ConfigurationController.ConfigurationListener() {
84         @Override
85         public void onDensityOrFontScaleChanged() {
86             mView.onDensityOrFontScaleChanged();
87         }
88         @Override
89         public void onThemeChanged() {
90             mView.onOverlayChanged();
91         }
92     };
93 
94     Observer<Slice> mObserver = new Observer<Slice>() {
95         @Override
96         public void onChanged(Slice slice) {
97             mSlice = slice;
98             showSlice(slice);
99         }
100     };
101 
102     private View.OnClickListener mOnClickListener = new View.OnClickListener() {
103         @Override
104         public void onClick(View v) {
105             final PendingIntent action = mClickActions.get(v);
106             if (action != null && mActivityStarter != null) {
107                 mActivityStarter.startPendingIntentDismissingKeyguard(action);
108             }
109         }
110     };
111 
112     @Inject
KeyguardSliceViewController( @ain Handler handler, @Background Handler bgHandler, KeyguardSliceView keyguardSliceView, ActivityStarter activityStarter, ConfigurationController configurationController, TunerService tunerService, DumpManager dumpManager, DisplayTracker displayTracker)113     public KeyguardSliceViewController(
114             @Main Handler handler,
115             @Background Handler bgHandler,
116             KeyguardSliceView keyguardSliceView,
117             ActivityStarter activityStarter,
118             ConfigurationController configurationController,
119             TunerService tunerService,
120             DumpManager dumpManager,
121             DisplayTracker displayTracker) {
122         super(keyguardSliceView);
123         mHandler = handler;
124         mBgHandler = bgHandler;
125         mActivityStarter = activityStarter;
126         mConfigurationController = configurationController;
127         mTunerService = tunerService;
128         mDumpManager = dumpManager;
129         mDisplayTracker = displayTracker;
130     }
131 
132     @Override
onViewAttached()133     protected void onViewAttached() {
134         Display display = mView.getDisplay();
135         if (display != null) {
136             mDisplayId = display.getDisplayId();
137         }
138         mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
139         // Make sure we always have the most current slice
140         if (mDisplayId == mDisplayTracker.getDefaultDisplayId() && mLiveData != null) {
141             mLiveData.observeForever(mObserver);
142         }
143         mConfigurationController.addCallback(mConfigurationListener);
144         mDumpManager.registerNormalDumpable(
145                 TAG + "@" + Integer.toHexString(
146                         KeyguardSliceViewController.this.hashCode()),
147                 KeyguardSliceViewController.this);
148     }
149 
150     @Override
onViewDetached()151     protected void onViewDetached() {
152         // TODO(b/117344873) Remove below work around after this issue be fixed.
153         if (mDisplayId == mDisplayTracker.getDefaultDisplayId()) {
154             mLiveData.removeObserver(mObserver);
155         }
156         mTunerService.removeTunable(mTunable);
157         mConfigurationController.removeCallback(mConfigurationListener);
158         mDumpManager.unregisterDumpable(
159                 TAG + "@" + Integer.toHexString(
160                         KeyguardSliceViewController.this.hashCode()));
161     }
162 
updateTopMargin(float clockTopTextPadding)163     void updateTopMargin(float clockTopTextPadding) {
164         ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mView.getLayoutParams();
165         lp.topMargin = (int) clockTopTextPadding;
166         mView.setLayoutParams(lp);
167     }
168 
169     /**
170      * Sets the slice provider Uri.
171      */
setupUri(String uriString)172     public void setupUri(String uriString) {
173         if (uriString == null) {
174             uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
175         }
176 
177         boolean wasObserving = false;
178         if (mLiveData != null && mLiveData.hasActiveObservers()) {
179             wasObserving = true;
180             mLiveData.removeObserver(mObserver);
181         }
182 
183         mKeyguardSliceUri = Uri.parse(uriString);
184         mLiveData = SliceLiveData.fromUri(mView.getContext(), mKeyguardSliceUri);
185 
186         if (wasObserving) {
187             mLiveData.observeForever(mObserver);
188         }
189     }
190 
191     /**
192      * Update contents of the view.
193      */
refresh()194     public void refresh() {
195 
196         Trace.beginSection("KeyguardSliceViewController#refresh");
197         try {
198             Slice slice;
199             if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
200                 KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
201                 if (instance != null) {
202                     if (Flags.sliceManagerBinderCallBackground()) {
203                         mBgHandler.post(() -> {
204                             Slice _slice = instance.onBindSlice(mKeyguardSliceUri);
205                             mHandler.post(() -> mObserver.onChanged(_slice));
206                         });
207                         return;
208                     }
209                     slice = instance.onBindSlice(mKeyguardSliceUri);
210                 } else {
211                     Log.w(TAG, "Keyguard slice not bound yet?");
212                     slice = null;
213                 }
214             } else {
215                 // TODO: Make SliceViewManager injectable
216                 slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(
217                         mKeyguardSliceUri);
218             }
219             mObserver.onChanged(slice);
220         } finally {
221             Trace.endSection();
222         }
223     }
224 
showSlice(Slice slice)225     void showSlice(Slice slice) {
226         Trace.beginSection("KeyguardSliceViewController#showSlice");
227         if (slice == null) {
228             mView.hideSlice();
229             Trace.endSection();
230             return;
231         }
232 
233         ListContent lc = new ListContent(slice);
234         RowContent headerContent = lc.getHeader();
235         boolean hasHeader =
236                 headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
237 
238         List<SliceContent> subItems = lc.getRowItems().stream().filter(sliceContent -> {
239             String itemUri = sliceContent.getSliceItem().getSlice().getUri().toString();
240             // Filter out the action row
241             return !KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri);
242         }).collect(Collectors.toList());
243 
244 
245         mClickActions = mView.showSlice(hasHeader ? headerContent : null, subItems);
246 
247         Trace.endSection();
248     }
249 
250     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)251     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
252         pw.println("  mSlice: " + mSlice);
253         pw.println("  mClickActions: " + mClickActions);
254     }
255 }
256