blob: 895b8a228a00dc072f00b21d02e06e92b16f489d [file] [log] [blame]
/*
* Copyright (C) 2016 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.systemui.tv.pip;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.TOP;
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED;
public class PipRecentsOverlayManager {
private static final String TAG = "PipRecentsOverlayManager";
public interface Callback {
void onClosed();
void onBackPressed();
void onRecentsFocused();
}
private final PipManager mPipManager = PipManager.getInstance();
private final WindowManager mWindowManager;
private final SystemServicesProxy mSystemServicesProxy;
private View mOverlayView;
private PipRecentsControlsView mPipControlsView;
private View mRecentsView;
private boolean mTalkBackEnabled;
private LayoutParams mPipRecentsControlsViewLayoutParams;
private LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
private boolean mHasFocusableInRecents;
private boolean mIsPipRecentsOverlayShown;
private boolean mIsRecentsShown;
private boolean mIsPipFocusedInRecent;
private Callback mCallback;
private PipRecentsControlsView.Listener mPipControlsViewListener =
new PipRecentsControlsView.Listener() {
@Override
public void onClosed() {
if (mCallback != null) {
mCallback.onClosed();
}
}
@Override
public void onBackPressed() {
if (mCallback != null) {
mCallback.onBackPressed();
}
}
};
PipRecentsOverlayManager(Context context) {
mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
mSystemServicesProxy = SystemServicesProxy.getInstance(context);
initViews(context);
}
private void initViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls);
mRecentsView = mOverlayView.findViewById(R.id.recents);
mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
clearFocus();
}
}
});
mOverlayView.measure(UNSPECIFIED, UNSPECIFIED);
mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
LayoutParams.TYPE_SYSTEM_DIALOG,
LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
mPipRecentsControlsViewLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
LayoutParams.TYPE_SYSTEM_DIALOG,
0,
PixelFormat.TRANSLUCENT);
mPipRecentsControlsViewFocusedLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
}
/**
* Add Recents overlay view.
* This is expected to be called after the PIP animation is over.
*/
void addPipRecentsOverlayView() {
if (mIsPipRecentsOverlayShown) {
return;
}
mTalkBackEnabled = mSystemServicesProxy.isTouchExplorationEnabled();
mRecentsView.setVisibility(mTalkBackEnabled ? View.VISIBLE : View.GONE);
mIsPipRecentsOverlayShown = true;
mIsPipFocusedInRecent = true;
mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
}
/**
* Remove Recents overlay view.
* This should be called when Recents or PIP is closed.
*/
public void removePipRecentsOverlayView() {
if (!mIsPipRecentsOverlayShown) {
return;
}
mWindowManager.removeView(mOverlayView);
// Resets the controls view when its removed.
// If not, changing focus in reset will be show animation when Recents is resumed.
mPipControlsView.reset();
mIsPipRecentsOverlayShown = false;
}
/**
* Request focus to the PIP Recents overlay.
* This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
* @param hasFocusableInRecents {@code true} if Recents can have focus. (i.e. Has a recent task)
*/
public void requestFocus(boolean hasFocusableInRecents) {
mHasFocusableInRecents = hasFocusableInRecents;
if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent
|| !mPipManager.isPipShown()) {
return;
}
mIsPipFocusedInRecent = true;
mPipControlsView.startFocusGainAnimation();
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
if (mTalkBackEnabled) {
mPipControlsView.requestFocus();
mPipControlsView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}
/**
* Request focus to the PIP Recents overlay.
*/
public void clearFocus() {
if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent
|| !mPipManager.isPipShown() || !mHasFocusableInRecents) {
return;
}
mIsPipFocusedInRecent = false;
mPipControlsView.startFocusLossAnimation();
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
if (mCallback != null) {
mCallback.onRecentsFocused();
}
}
public void setCallback(Callback listener) {
mCallback = listener;
mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null);
}
/**
* Called when Recents is resumed.
* PIPed activity will be resized accordingly and overlay will show available buttons.
*/
public void onRecentsResumed() {
if (!mPipManager.isPipShown()) {
return;
}
mIsRecentsShown = true;
mIsPipFocusedInRecent = true;
mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
// Overlay view will be added after the resize animation ends, if any.
}
/**
* Called when Recents is paused.
* PIPed activity will be resized accordingly and overlay will hide available buttons.
*/
public void onRecentsPaused() {
mIsRecentsShown = false;
mIsPipFocusedInRecent = false;
removePipRecentsOverlayView();
if (mPipManager.isPipShown()) {
mPipManager.resizePinnedStack(STATE_PIP_OVERLAY);
}
}
/**
* Returns {@code true} if recents is shown.
*/
boolean isRecentsShown() {
return mIsRecentsShown;
}
/**
* Updates the PIP per configuration changed.
*/
void onConfigurationChanged(Context context) {
if (mIsRecentsShown) {
Log.w(TAG, "Configuration is changed while Recents is shown");
}
initViews(context);
}
}