Merge "TV PIP: Fix broken TV PIP" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2e0d455177
@@ -31,5 +31,6 @@
|
||||
<item>com.google.android.katniss.setting/.SpeechSettingsActivity</item>
|
||||
<item>com.google.android.katniss.setting/.SearchSettingsActivity</item>
|
||||
<item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item>
|
||||
<item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -17,17 +17,9 @@
|
||||
<resources>
|
||||
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
|
||||
when the PIP menu is shown with settings. -->
|
||||
<string translatable="false" name="pip_settings_bounds">"662 54 1142 324"</string>
|
||||
<string translatable="false" name="pip_settings_bounds">"662 756 1142 1026"</string>
|
||||
|
||||
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
|
||||
when the PIP menu is shown in center. -->
|
||||
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
|
||||
|
||||
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
|
||||
when the PIP is shown in Recents without focus. -->
|
||||
<string translatable="false" name="pip_recents_bounds">"800 54 1120 234"</string>
|
||||
|
||||
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
|
||||
when the PIP is shown in Recents with focus. -->
|
||||
<string translatable="false" name="pip_recents_focused_bounds">"775 54 1145 262"</string>
|
||||
</resources>
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<!-- Picture-in-Picture (PIP) notification -->
|
||||
<!-- Title for the notification channel for TV PIP controls. [CHAR LIMIT=NONE] -->
|
||||
<string name="notification_channel_tv_pip">Picture-in-Picture</string>
|
||||
<!-- Title of the picture-in-picture (PIP) notification title
|
||||
when the media doesn't have title [CHAR LIMIT=NONE] -->
|
||||
<string name="pip_notification_unknown_title">(No title program)</string>
|
||||
|
||||
<!-- Picture-in-Picture (PIP) menu -->
|
||||
<eat-comment />
|
||||
<!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
|
||||
|
||||
@@ -61,7 +61,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
|
||||
*/
|
||||
public class PipManager implements BasePipManager {
|
||||
private static final String TAG = "PipManager";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
|
||||
|
||||
private static PipManager sPipManager;
|
||||
@@ -122,6 +123,7 @@ public class PipManager implements BasePipManager {
|
||||
private ComponentName mPipComponentName;
|
||||
private MediaController mPipMediaController;
|
||||
private String[] mLastPackagesResourceGranted;
|
||||
private PipNotification mPipNotification;
|
||||
|
||||
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
|
||||
|
||||
@@ -246,6 +248,8 @@ public class PipManager implements BasePipManager {
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to register pinned stack listener", e);
|
||||
}
|
||||
|
||||
mPipNotification = new PipNotification(context);
|
||||
}
|
||||
|
||||
private void loadConfigurationsAndApply() {
|
||||
@@ -267,6 +271,7 @@ public class PipManager implements BasePipManager {
|
||||
*/
|
||||
public void onConfigurationChanged() {
|
||||
loadConfigurationsAndApply();
|
||||
mPipNotification.onConfigurationChanged(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,7 +350,7 @@ public class PipManager implements BasePipManager {
|
||||
* @param state In Pip state also used to determine the new size for the Pip.
|
||||
*/
|
||||
void resizePinnedStack(int state) {
|
||||
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
|
||||
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception());
|
||||
boolean wasStateNoPip = (mState == STATE_NO_PIP);
|
||||
mResumeResizePinnedStackRunnable = state;
|
||||
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
||||
@@ -511,8 +516,8 @@ public class PipManager implements BasePipManager {
|
||||
|
||||
/**
|
||||
* Returns the PIPed activity's playback state.
|
||||
* This returns one of {@link PLAYBACK_STATE_PLAYING}, {@link PLAYBACK_STATE_PAUSED},
|
||||
* or {@link PLAYBACK_STATE_UNAVAILABLE}.
|
||||
* This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
|
||||
* or {@link #PLAYBACK_STATE_UNAVAILABLE}.
|
||||
*/
|
||||
int getPlaybackState() {
|
||||
if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.util.NotificationChannels;
|
||||
import com.android.systemui.R;
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
|
||||
/**
|
||||
* A notification that informs users that PIP is running and also provides PIP controls.
|
||||
* <p>Once it's created, it will manage the PIP notification UI by itself except for handling
|
||||
* configuration changes.
|
||||
*/
|
||||
public class PipNotification {
|
||||
private static final String TAG = "PipNotification";
|
||||
private static final boolean DEBUG = PipManager.DEBUG;
|
||||
|
||||
private static final String ACTION_MENU = "PipNotification.menu";
|
||||
private static final String ACTION_CLOSE = "PipNotification.close";
|
||||
|
||||
private final PipManager mPipManager = PipManager.getInstance();
|
||||
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final Notification.Builder mNotificationBuilder;
|
||||
|
||||
private MediaController mMediaController;
|
||||
private String mDefaultTitle;
|
||||
private Icon mDefaultIcon;
|
||||
|
||||
private boolean mNotified;
|
||||
private String mTitle;
|
||||
private Bitmap mArt;
|
||||
|
||||
private PipManager.Listener mPipListener = new PipManager.Listener() {
|
||||
@Override
|
||||
public void onPipEntered() {
|
||||
updateMediaControllerMetadata();
|
||||
notifyPipNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPipActivityClosed() {
|
||||
dismissPipNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPipMenu() {
|
||||
// no-op.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveToFullscreen() {
|
||||
dismissPipNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPipResizeAboutToStart() {
|
||||
// no-op.
|
||||
}
|
||||
};
|
||||
|
||||
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
|
||||
@Override
|
||||
public void onPlaybackStateChanged(PlaybackState state) {
|
||||
if (updateMediaControllerMetadata() && mNotified) {
|
||||
// update notification
|
||||
notifyPipNotification();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
|
||||
@Override
|
||||
public void onMediaControllerChanged() {
|
||||
MediaController newController = mPipManager.getMediaController();
|
||||
if (mMediaController == newController) {
|
||||
return;
|
||||
}
|
||||
if (mMediaController != null) {
|
||||
mMediaController.unregisterCallback(mMediaControllerCallback);
|
||||
}
|
||||
mMediaController = newController;
|
||||
if (mMediaController != null) {
|
||||
mMediaController.registerCallback(mMediaControllerCallback);
|
||||
}
|
||||
if (updateMediaControllerMetadata() && mNotified) {
|
||||
// update notification
|
||||
notifyPipNotification();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Received " + intent.getAction() + " from the notification UI");
|
||||
}
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_MENU:
|
||||
mPipManager.showPictureInPictureMenu();
|
||||
break;
|
||||
case ACTION_CLOSE:
|
||||
mPipManager.closePip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public PipNotification(Context context) {
|
||||
mNotificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
|
||||
mNotificationBuilder = new Notification.Builder(context, NotificationChannels.TVPIP)
|
||||
.setLocalOnly(true)
|
||||
.setOngoing(false)
|
||||
.setCategory(Notification.CATEGORY_SYSTEM)
|
||||
.extend(new Notification.TvExtender()
|
||||
.setContentIntent(createPendingIntent(context, ACTION_MENU))
|
||||
.setDeleteIntent(createPendingIntent(context, ACTION_CLOSE)));
|
||||
|
||||
mPipManager.addListener(mPipListener);
|
||||
mPipManager.addMediaListener(mPipMediaListener);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(ACTION_MENU);
|
||||
intentFilter.addAction(ACTION_CLOSE);
|
||||
context.registerReceiver(mEventReceiver, intentFilter);
|
||||
|
||||
onConfigurationChanged(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link PipManager} when the configuration is changed.
|
||||
*/
|
||||
void onConfigurationChanged(Context context) {
|
||||
Resources res = context.getResources();
|
||||
mDefaultTitle = res.getString(R.string.pip_notification_unknown_title);
|
||||
mDefaultIcon = Icon.createWithResource(context,
|
||||
res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
|
||||
? R.drawable.pip_expand_ll : R.drawable.pip_expand_lr);
|
||||
if (mNotified) {
|
||||
// update notification
|
||||
notifyPipNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyPipNotification() {
|
||||
mNotified = true;
|
||||
mNotificationBuilder
|
||||
.setShowWhen(true)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
// TODO: Sending bitmap doesn't work in launcher side. Once launcher supports it,
|
||||
// we can set icon.
|
||||
//.setSmallIcon(mArt != null ? Icon.createWithBitmap(mArt) : mDefaultIcon)
|
||||
.setSmallIcon(mDefaultIcon.getResId())
|
||||
.setContentTitle(!TextUtils.isEmpty(mTitle) ? mTitle : mDefaultTitle);
|
||||
mNotificationManager.notify(SystemMessage.NOTE_TV_PIP, mNotificationBuilder.build());
|
||||
}
|
||||
|
||||
private void dismissPipNotification() {
|
||||
mNotified = false;
|
||||
mNotificationManager.cancel(SystemMessage.NOTE_TV_PIP);
|
||||
}
|
||||
|
||||
private boolean updateMediaControllerMetadata() {
|
||||
String title = null;
|
||||
Bitmap art = null;
|
||||
if (mPipManager.getMediaController() != null) {
|
||||
MediaMetadata metadata = mPipManager.getMediaController().getMetadata();
|
||||
if (metadata != null) {
|
||||
title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
|
||||
}
|
||||
art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
|
||||
if (art == null) {
|
||||
art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!TextUtils.equals(title, mTitle) || art != mArt) {
|
||||
mTitle = title;
|
||||
mArt = art;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PendingIntent createPendingIntent(Context context, String action) {
|
||||
return PendingIntent.getBroadcast(context, 0,
|
||||
new Intent(action), PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ public class NotificationChannels extends SystemUI {
|
||||
public static String SCREENSHOTS = "SCN";
|
||||
public static String GENERAL = "GEN";
|
||||
public static String STORAGE = "DSK";
|
||||
public static String TVPIP = "TPP";
|
||||
|
||||
@VisibleForTesting
|
||||
static void createAll(Context context) {
|
||||
@@ -55,6 +56,15 @@ public class NotificationChannels extends SystemUI {
|
||||
? NotificationManager.IMPORTANCE_DEFAULT
|
||||
: NotificationManager.IMPORTANCE_LOW)
|
||||
));
|
||||
if (isTv(context)) {
|
||||
// TV specific notification channel for TV PIP controls.
|
||||
// Importance should be {@link NotificationManager#IMPORTANCE_MAX} to have the highest
|
||||
// priority, so it can be shown in all times.
|
||||
nm.createNotificationChannel(new NotificationChannel(
|
||||
TVPIP,
|
||||
context.getString(R.string.notification_channel_tv_pip),
|
||||
NotificationManager.IMPORTANCE_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -212,6 +212,10 @@ message SystemMessage {
|
||||
// Package: com.android.systemui
|
||||
NOTE_LOGOUT_USER = 1011;
|
||||
|
||||
// Notify the user that a TV PIP is running.
|
||||
// Package: com.android.systemui
|
||||
NOTE_TV_PIP = 1100;
|
||||
|
||||
// Communicate to the user about remote bugreports.
|
||||
// Package: android
|
||||
NOTE_REMOTE_BUGREPORT = 678432343;
|
||||
|
||||
Reference in New Issue
Block a user