blob: 8038ce4c7b691184fe6c88900f2192449eaed486 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.PendingIntent;
import android.net.Uri;
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.slice.Slice;
import androidx.slice.SliceViewManager;
import androidx.slice.widget.ListContent;
import androidx.slice.widget.RowContent;
import androidx.slice.widget.SliceContent;
import androidx.slice.widget.SliceLiveData;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
/** Controller for a {@link KeyguardSliceView}. */
@KeyguardStatusViewScope
public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements
Dumpable {
private static final String TAG = "KeyguardSliceViewCtrl";
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
private final TunerService mTunerService;
private final DumpManager mDumpManager;
private int mDisplayId;
private LiveData<Slice> mLiveData;
private Uri mKeyguardSliceUri;
private Slice mSlice;
private Map<View, PendingIntent> mClickActions;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
public void onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged();
}
@Override
public void onOverlayChanged() {
mView.onOverlayChanged();
}
};
Observer<Slice> mObserver = new Observer<Slice>() {
@Override
public void onChanged(Slice slice) {
mSlice = slice;
showSlice(slice);
}
};
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final PendingIntent action = mClickActions.get(v);
if (action != null && mActivityStarter != null) {
mActivityStarter.startPendingIntentDismissingKeyguard(action);
}
}
};
@Inject
public KeyguardSliceViewController(
KeyguardSliceView keyguardSliceView,
ActivityStarter activityStarter,
ConfigurationController configurationController,
TunerService tunerService,
DumpManager dumpManager) {
super(keyguardSliceView);
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
mTunerService = tunerService;
mDumpManager = dumpManager;
}
@Override
protected void onViewAttached() {
Display display = mView.getDisplay();
if (display != null) {
mDisplayId = display.getDisplayId();
}
mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
// Make sure we always have the most current slice
if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
mLiveData.observeForever(mObserver);
}
mConfigurationController.addCallback(mConfigurationListener);
mDumpManager.registerDumpable(
TAG + "@" + Integer.toHexString(
KeyguardSliceViewController.this.hashCode()),
KeyguardSliceViewController.this);
mView.updateLockScreenMode(mLockScreenMode);
}
@Override
protected void onViewDetached() {
// TODO(b/117344873) Remove below work around after this issue be fixed.
if (mDisplayId == DEFAULT_DISPLAY) {
mLiveData.removeObserver(mObserver);
}
mTunerService.removeTunable(mTunable);
mConfigurationController.removeCallback(mConfigurationListener);
mDumpManager.unregisterDumpable(
TAG + "@" + Integer.toHexString(
KeyguardSliceViewController.this.hashCode()));
}
void updateTopMargin(float clockTopTextPadding) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mView.getLayoutParams();
lp.topMargin = (int) clockTopTextPadding;
mView.setLayoutParams(lp);
}
/**
* Updates the lockscreen mode which may change the layout of the keyguard slice view.
*/
public void updateLockScreenMode(int mode) {
mLockScreenMode = mode;
mView.updateLockScreenMode(mLockScreenMode);
}
/**
* Sets the slice provider Uri.
*/
public void setupUri(String uriString) {
if (uriString == null) {
uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
}
boolean wasObserving = false;
if (mLiveData != null && mLiveData.hasActiveObservers()) {
wasObserving = true;
mLiveData.removeObserver(mObserver);
}
mKeyguardSliceUri = Uri.parse(uriString);
mLiveData = SliceLiveData.fromUri(mView.getContext(), mKeyguardSliceUri);
if (wasObserving) {
mLiveData.observeForever(mObserver);
}
}
/**
* Update contents of the view.
*/
public void refresh() {
Slice slice;
Trace.beginSection("KeyguardSliceViewController#refresh");
// We can optimize performance and avoid binder calls when we know that we're bound
// to a Slice on the same process.
if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
if (instance != null) {
slice = instance.onBindSlice(mKeyguardSliceUri);
} else {
Log.w(TAG, "Keyguard slice not bound yet?");
slice = null;
}
} else {
// TODO: Make SliceViewManager injectable
slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri);
}
mObserver.onChanged(slice);
Trace.endSection();
}
/**
* Update position of the view, with optional animation
*/
void updatePosition(int x, AnimationProperties props, boolean animate) {
PropertyAnimator.setProperty(mView, AnimatableProperty.TRANSLATION_X, x, props, animate);
}
void showSlice(Slice slice) {
Trace.beginSection("KeyguardSliceViewController#showSlice");
if (slice == null) {
mView.hideSlice();
Trace.endSection();
return;
}
ListContent lc = new ListContent(slice);
RowContent headerContent = lc.getHeader();
boolean hasHeader =
headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
List<SliceContent> subItems = lc.getRowItems().stream().filter(sliceContent -> {
String itemUri = sliceContent.getSliceItem().getSlice().getUri().toString();
// Filter out the action row
return !KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri);
}).collect(Collectors.toList());
mClickActions = mView.showSlice(hasHeader ? headerContent : null, subItems);
Trace.endSection();
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(" mSlice: " + mSlice);
pw.println(" mClickActions: " + mClickActions);
pw.println(" mLockScreenMode: " + mLockScreenMode);
}
}