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 import static android.view.Display.DEFAULT_DISPLAY; 21 22 import android.app.PendingIntent; 23 import android.net.Uri; 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.dump.DumpManager; 44 import com.android.systemui.keyguard.KeyguardSliceProvider; 45 import com.android.systemui.plugins.ActivityStarter; 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.FileDescriptor; 51 import java.io.PrintWriter; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.stream.Collectors; 55 56 import javax.inject.Inject; 57 58 /** Controller for a {@link KeyguardSliceView}. */ 59 @KeyguardStatusViewScope 60 public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements 61 Dumpable { 62 private static final String TAG = "KeyguardSliceViewCtrl"; 63 64 private final ActivityStarter mActivityStarter; 65 private final ConfigurationController mConfigurationController; 66 private final TunerService mTunerService; 67 private final DumpManager mDumpManager; 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)107 public KeyguardSliceViewController( 108 KeyguardSliceView keyguardSliceView, 109 ActivityStarter activityStarter, 110 ConfigurationController configurationController, 111 TunerService tunerService, 112 DumpManager dumpManager) { 113 super(keyguardSliceView); 114 mActivityStarter = activityStarter; 115 mConfigurationController = configurationController; 116 mTunerService = tunerService; 117 mDumpManager = dumpManager; 118 } 119 120 @Override onViewAttached()121 protected void onViewAttached() { 122 Display display = mView.getDisplay(); 123 if (display != null) { 124 mDisplayId = display.getDisplayId(); 125 } 126 mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); 127 // Make sure we always have the most current slice 128 if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { 129 mLiveData.observeForever(mObserver); 130 } 131 mConfigurationController.addCallback(mConfigurationListener); 132 mDumpManager.registerDumpable( 133 TAG + "@" + Integer.toHexString( 134 KeyguardSliceViewController.this.hashCode()), 135 KeyguardSliceViewController.this); 136 } 137 138 @Override onViewDetached()139 protected void onViewDetached() { 140 // TODO(b/117344873) Remove below work around after this issue be fixed. 141 if (mDisplayId == DEFAULT_DISPLAY) { 142 mLiveData.removeObserver(mObserver); 143 } 144 mTunerService.removeTunable(mTunable); 145 mConfigurationController.removeCallback(mConfigurationListener); 146 mDumpManager.unregisterDumpable( 147 TAG + "@" + Integer.toHexString( 148 KeyguardSliceViewController.this.hashCode())); 149 } 150 updateTopMargin(float clockTopTextPadding)151 void updateTopMargin(float clockTopTextPadding) { 152 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mView.getLayoutParams(); 153 lp.topMargin = (int) clockTopTextPadding; 154 mView.setLayoutParams(lp); 155 } 156 157 /** 158 * Sets the slice provider Uri. 159 */ setupUri(String uriString)160 public void setupUri(String uriString) { 161 if (uriString == null) { 162 uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; 163 } 164 165 boolean wasObserving = false; 166 if (mLiveData != null && mLiveData.hasActiveObservers()) { 167 wasObserving = true; 168 mLiveData.removeObserver(mObserver); 169 } 170 171 mKeyguardSliceUri = Uri.parse(uriString); 172 mLiveData = SliceLiveData.fromUri(mView.getContext(), mKeyguardSliceUri); 173 174 if (wasObserving) { 175 mLiveData.observeForever(mObserver); 176 } 177 } 178 179 /** 180 * Update contents of the view. 181 */ refresh()182 public void refresh() { 183 Slice slice; 184 Trace.beginSection("KeyguardSliceViewController#refresh"); 185 // We can optimize performance and avoid binder calls when we know that we're bound 186 // to a Slice on the same process. 187 if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { 188 KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); 189 if (instance != null) { 190 slice = instance.onBindSlice(mKeyguardSliceUri); 191 } else { 192 Log.w(TAG, "Keyguard slice not bound yet?"); 193 slice = null; 194 } 195 } else { 196 // TODO: Make SliceViewManager injectable 197 slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri); 198 } 199 mObserver.onChanged(slice); 200 Trace.endSection(); 201 } 202 showSlice(Slice slice)203 void showSlice(Slice slice) { 204 Trace.beginSection("KeyguardSliceViewController#showSlice"); 205 if (slice == null) { 206 mView.hideSlice(); 207 Trace.endSection(); 208 return; 209 } 210 211 ListContent lc = new ListContent(slice); 212 RowContent headerContent = lc.getHeader(); 213 boolean hasHeader = 214 headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); 215 216 List<SliceContent> subItems = lc.getRowItems().stream().filter(sliceContent -> { 217 String itemUri = sliceContent.getSliceItem().getSlice().getUri().toString(); 218 // Filter out the action row 219 return !KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri); 220 }).collect(Collectors.toList()); 221 222 223 mClickActions = mView.showSlice(hasHeader ? headerContent : null, subItems); 224 225 Trace.endSection(); 226 } 227 228 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args)229 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { 230 pw.println(" mSlice: " + mSlice); 231 pw.println(" mClickActions: " + mClickActions); 232 } 233 } 234