blob: d6fabd63420d6af757819209255a895152ce7a0b [file] [log] [blame]
/*
* Copyright (C) 2013 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.view.Display.DEFAULT_DISPLAY;
import android.app.Presentation;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.util.InjectionInflationController;
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
private static boolean DEBUG = KeyguardConstants.DEBUG;
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
private final InjectionInflationController mInjectableInflater;
private final Context mContext;
private boolean mShowing;
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final SparseArray<Presentation> mPresentations = new SparseArray<>();
private final NavigationBarController mNavBarController =
Dependency.get(NavigationBarController.class);
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
final Display display = mDisplayService.getDisplay(displayId);
if (mShowing) {
updateNavigationBarVisibility(displayId, false /* navBarVisible */);
showPresentation(display);
}
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == DEFAULT_DISPLAY) return;
final Presentation presentation = mPresentations.get(displayId);
if (presentation != null && mShowing) {
hidePresentation(displayId);
// update DisplayInfo.
final Display display = mDisplayService.getDisplay(displayId);
if (display != null) {
showPresentation(display);
}
}
}
@Override
public void onDisplayRemoved(int displayId) {
hidePresentation(displayId);
}
};
public KeyguardDisplayManager(Context context,
InjectionInflationController injectableInflater) {
mContext = context;
mInjectableInflater = injectableInflater;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
}
private boolean isKeyguardShowable(Display display) {
if (display == null) {
if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
return false;
}
if (display.getDisplayId() == DEFAULT_DISPLAY) {
if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
return false;
}
display.getDisplayInfo(mTmpDisplayInfo);
if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
return false;
}
return true;
}
/**
* @param display The display to show the presentation on.
* @return {@code true} if a presentation was added.
* {@code false} if the presentation cannot be added on that display or the presentation
* was already there.
*/
private boolean showPresentation(Display display) {
if (!isKeyguardShowable(display)) return false;
if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
final int displayId = display.getDisplayId();
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
mInjectableInflater.injectable(LayoutInflater.from(mContext)));
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
}
});
presentation = newPresentation;
try {
presentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Log.w(TAG, "Invalid display:", ex);
presentation = null;
}
if (presentation != null) {
mPresentations.append(displayId, presentation);
return true;
}
}
return false;
}
/**
* @param displayId The id of the display to hide the presentation off.
*/
private void hidePresentation(int displayId) {
final Presentation presentation = mPresentations.get(displayId);
if (presentation != null) {
presentation.dismiss();
mPresentations.remove(displayId);
}
}
public void show() {
if (!mShowing) {
if (DEBUG) Log.v(TAG, "show");
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
updateDisplays(true /* showing */);
}
mShowing = true;
}
public void hide() {
if (mShowing) {
if (DEBUG) Log.v(TAG, "hide");
mMediaRouter.removeCallback(mMediaRouterCallback);
updateDisplays(false /* showing */);
}
mShowing = false;
}
private final MediaRouter.SimpleCallback mMediaRouterCallback =
new MediaRouter.SimpleCallback() {
@Override
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
updateDisplays(mShowing);
}
@Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
updateDisplays(mShowing);
}
@Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
updateDisplays(mShowing);
}
};
protected boolean updateDisplays(boolean showing) {
boolean changed = false;
if (showing) {
final Display[] displays = mDisplayService.getDisplays();
for (Display display : displays) {
int displayId = display.getDisplayId();
updateNavigationBarVisibility(displayId, false /* navBarVisible */);
changed |= showPresentation(display);
}
} else {
changed = mPresentations.size() > 0;
for (int i = mPresentations.size() - 1; i >= 0; i--) {
int displayId = mPresentations.keyAt(i);
updateNavigationBarVisibility(displayId, true /* navBarVisible */);
mPresentations.valueAt(i).dismiss();
}
mPresentations.clear();
}
return changed;
}
// TODO(b/127878649): this logic is from
// {@link StatusBarKeyguardViewManager#updateNavigationBarVisibility}. Try to revisit a long
// term solution in R.
private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) {
// Leave this task to {@link StatusBarKeyguardViewManager}
if (displayId == DEFAULT_DISPLAY) return;
NavigationBarView navBarView = mNavBarController.getNavigationBarView(displayId);
// We may not have nav bar on a display.
if (navBarView == null) return;
if (navBarVisible) {
navBarView.getRootView().setVisibility(View.VISIBLE);
} else {
navBarView.getRootView().setVisibility(View.GONE);
}
}
@VisibleForTesting
static final class KeyguardPresentation extends Presentation {
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final LayoutInflater mInjectableLayoutInflater;
private View mClock;
private int mUsableWidth;
private int mUsableHeight;
private int mMarginTop;
private int mMarginLeft;
Runnable mMoveTextRunnable = new Runnable() {
@Override
public void run() {
int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
mClock.setTranslationX(x);
mClock.setTranslationY(y);
mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
}
};
KeyguardPresentation(Context context, Display display,
LayoutInflater injectionLayoutInflater) {
super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
mInjectableLayoutInflater = injectionLayoutInflater;
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@Override
public void cancel() {
// Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager.
}
@Override
public void onDetachedFromWindow() {
mClock.removeCallbacks(mMoveTextRunnable);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Point p = new Point();
getDisplay().getSize(p);
mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
getWindow().setNavigationBarContrastEnforced(false);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
mClock = findViewById(R.id.clock);
// Avoid screen burn in
mClock.post(mMoveTextRunnable);
}
}
}