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