All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.wm.shell.pip.tv.TvPipNotificationController Maven / Gradle / Ivy

/*
 * 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.wm.shell.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.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipMediaController;

import java.util.Objects;

/**
 * A notification that informs users that PIP is running and also provides PIP controls.
 * 

Once it's created, it will manage the PIP notification UI by itself except for handling * configuration changes. */ public class TvPipNotificationController { private static final String TAG = "TvPipNotification"; private static final boolean DEBUG = TvPipController.DEBUG; // Referenced in com.android.systemui.util.NotificationChannels. public static final String NOTIFICATION_CHANNEL = "TVPIP"; private static final String NOTIFICATION_TAG = "TvPip"; private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"; private static final String ACTION_SHOW_PIP_MENU = "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU"; private static final String ACTION_CLOSE_PIP = "com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP"; private final Context mContext; private final PackageManager mPackageManager; private final NotificationManager mNotificationManager; private final Notification.Builder mNotificationBuilder; private final ActionBroadcastReceiver mActionBroadcastReceiver; private final Handler mMainHandler; private Delegate mDelegate; private String mDefaultTitle; /** Package name for the application that owns PiP window. */ private String mPackageName; private boolean mNotified; private String mMediaTitle; private Bitmap mArt; public TvPipNotificationController(Context context, PipMediaController pipMediaController, Handler mainHandler) { mContext = context; mPackageManager = context.getPackageManager(); mNotificationManager = context.getSystemService(NotificationManager.class); mMainHandler = mainHandler; mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL) .setLocalOnly(true) .setOngoing(false) .setCategory(Notification.CATEGORY_SYSTEM) .setShowWhen(true) .setSmallIcon(R.drawable.pip_icon) .extend(new Notification.TvExtender() .setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU)) .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP))); mActionBroadcastReceiver = new ActionBroadcastReceiver(); pipMediaController.addMetadataListener(this::onMediaMetadataChanged); onConfigurationChanged(context); } void setDelegate(Delegate delegate) { if (DEBUG) Log.d(TAG, "setDelegate(), delegate=" + delegate); if (mDelegate != null) { throw new IllegalStateException( "The delegate has already been set and should not change."); } if (delegate == null) { throw new IllegalArgumentException("The delegate must not be null."); } mDelegate = delegate; } void show(String packageName) { if (mDelegate == null) { throw new IllegalStateException("Delegate is not set."); } mPackageName = packageName; update(); mActionBroadcastReceiver.register(); } void dismiss() { mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); mNotified = false; mPackageName = null; mActionBroadcastReceiver.unregister(); } private void onMediaMetadataChanged(MediaMetadata metadata) { if (updateMediaControllerMetadata(metadata) && mNotified) { // update notification update(); } } /** * Called by {@link PipController} when the configuration is changed. */ void onConfigurationChanged(Context context) { mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title); if (mNotified) { // Update the notification. update(); } } private void update() { mNotified = true; mNotificationBuilder .setWhen(System.currentTimeMillis()) .setContentTitle(getNotificationTitle()); if (mArt != null) { mNotificationBuilder.setStyle(new Notification.BigPictureStyle() .bigPicture(mArt)); } else { mNotificationBuilder.setStyle(null); } mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP, mNotificationBuilder.build()); } private boolean updateMediaControllerMetadata(MediaMetadata metadata) { String title = null; Bitmap art = null; 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, mMediaTitle) && Objects.equals(art, mArt)) { return false; } mMediaTitle = title; mArt = art; return true; } private String getNotificationTitle() { if (!TextUtils.isEmpty(mMediaTitle)) { return mMediaTitle; } final String applicationTitle = getApplicationLabel(mPackageName); if (!TextUtils.isEmpty(applicationTitle)) { return applicationTitle; } return mDefaultTitle; } private String getApplicationLabel(String packageName) { try { final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0); return mPackageManager.getApplicationLabel(appInfo).toString(); } catch (PackageManager.NameNotFoundException e) { return null; } } private static PendingIntent createPendingIntent(Context context, String action) { return PendingIntent.getBroadcast(context, 0, new Intent(action).setPackage(context.getPackageName()), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); } private class ActionBroadcastReceiver extends BroadcastReceiver { final IntentFilter mIntentFilter; { mIntentFilter = new IntentFilter(); mIntentFilter.addAction(ACTION_CLOSE_PIP); mIntentFilter.addAction(ACTION_SHOW_PIP_MENU); } boolean mRegistered = false; void register() { if (mRegistered) return; mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION, mMainHandler); mRegistered = true; } void unregister() { if (!mRegistered) return; mContext.unregisterReceiver(this); mRegistered = false; } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DEBUG) Log.d(TAG, "on(Broadcast)Receive(), action=" + action); if (ACTION_SHOW_PIP_MENU.equals(action)) { mDelegate.showPictureInPictureMenu(); } else if (ACTION_CLOSE_PIP.equals(action)) { mDelegate.closePip(); } } } interface Delegate { void showPictureInPictureMenu(); void closePip(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy