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