| /* |
| * 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.pip.tv; |
| |
| import android.content.Context; |
| import android.graphics.PixelFormat; |
| 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; |
| |
| 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(PipManager.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(PipManager.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(PipManager.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(PipManager.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); |
| } |
| } |