diff --git a/api/system-current.txt b/api/system-current.txt index 84a8a1d5c7a61..f4029f0fe25db 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -671,6 +671,9 @@ package android.app { method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; } public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0627bc8559346..fe8936654aa48 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -130,6 +131,73 @@ public class NotificationManager { public static final String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED"; + /** + * Activity action: Toggle notification panel of the specified handler. + * + *

Important:You must protect the activity that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can launch this activity. Activities that are not properly protected will not + * be launched. + * + *

This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. + * + * Input: nothing + * Output: nothing + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = + "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; + + /** + * Activity action: Open notification panel of the specified handler. + * + *

Important:You must protect the activity that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can launch this activity. Activities that are not properly protected will + * not be launched. + * + *

This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. + * + * Input: nothing + * Output: nothing + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = + "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + + /** + * Intent that is broadcast when the notification panel of the specified handler is to be + * closed. + * + *

Important:You should protect the receiver that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can send this broadcast to the notification handler. + * + *

This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. This is a protected intent that can + * only be sent by the system. + * + * Input: nothing. + * Output: nothing. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = + "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + /** * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} containing the id of the * {@link NotificationChannel} which has a new blocked state. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 560e3c11d2f45..714f8cde55177 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -107,6 +107,8 @@ + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 550162a242dc8..bfa2628d31851 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1166,6 +1166,7 @@ 0 - Nothing 1 - Launch all apps intent 2 - Launch assist intent + 3 - Launch notification panel This needs to match the constants in policy/src/com/android/internal/policy/impl/PhoneWindowManager.java --> @@ -3390,6 +3391,9 @@ service. --> + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 35ce780d34080..0fbcaf4053edb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3047,6 +3047,9 @@ + + + diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index bd2d4af54c831..6ac73b1972d49 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -223,7 +223,7 @@ key 200 MEDIA_PLAY key 201 MEDIA_PAUSE # key 202 "KEY_PROG3" # key 203 "KEY_PROG4" -# key 204 (undefined) +key 204 NOTIFICATION # key 205 "KEY_SUSPEND" # key 206 "KEY_CLOSE" key 207 MEDIA_PLAY diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index a403f4521aa0f..a8e8cd92176d3 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -671,6 +671,9 @@ package android.app { method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; } public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 66304013da460..981a953127369 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -29,6 +29,7 @@ com.android.systemui.util.NotificationChannels com.android.systemui.volume.VolumeUI com.android.systemui.statusbar.tv.TvStatusBar + com.android.systemui.statusbar.tv.TvNotificationPanel com.android.systemui.usb.StorageNotification com.android.systemui.power.PowerUI com.android.systemui.media.RingtonePlayer diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 3a5ce4d82540e..1f6288a94ad46 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -35,6 +35,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.InstantAppNotifier; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.tv.TvNotificationPanel; import com.android.systemui.statusbar.tv.TvStatusBar; import com.android.systemui.theme.ThemeOverlayController; import com.android.systemui.toast.ToastUI; @@ -156,6 +157,12 @@ public abstract class SystemUIBinder { @ClassKey(TvStatusBar.class) public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui); + /** Inject into TvNotificationPanel. */ + @Binds + @IntoMap + @ClassKey(TvNotificationPanel.class) + public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui); + /** Inject into VolumeUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java new file mode 100644 index 0000000000000..7a78c157e5b4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java @@ -0,0 +1,115 @@ +/* + * 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.systemui.statusbar.tv; + +import android.Manifest; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; +import android.util.Log; + +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.CommandQueue; + +import javax.inject.Inject; + +/** + * Offers control methods for the notification panel handler on TV devices. + */ +@SysUISingleton +public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks { + private static final String TAG = "TvNotificationPanel"; + private final CommandQueue mCommandQueue; + private final String mNotificationHandlerPackage; + + @Inject + public TvNotificationPanel(Context context, CommandQueue commandQueue) { + super(context); + mCommandQueue = commandQueue; + mNotificationHandlerPackage = mContext.getResources().getString( + com.android.internal.R.string.config_notificationHandlerPackage); + } + + @Override + public void start() { + mCommandQueue.addCallback(this); + } + + @Override + public void togglePanel() { + if (!mNotificationHandlerPackage.isEmpty()) { + startNotificationHandlerActivity( + new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL)); + } + } + + @Override + public void animateExpandNotificationsPanel() { + if (!mNotificationHandlerPackage.isEmpty()) { + startNotificationHandlerActivity( + new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL)); + } + } + + @Override + public void animateCollapsePanels(int flags, boolean force) { + if (!mNotificationHandlerPackage.isEmpty() + && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { + Intent closeNotificationIntent = new Intent( + NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL); + closeNotificationIntent.setPackage(mNotificationHandlerPackage); + mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT); + } + } + + /** + * Starts the activity intent if all of the following are true + *

+ * + * @param intent The intent for starting the desired activity + */ + private void startNotificationHandlerActivity(Intent intent) { + intent.setPackage(mNotificationHandlerPackage); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY); + if (ri != null && ri.activityInfo != null) { + if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals( + Manifest.permission.STATUS_BAR_SERVICE)) { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } else { + Log.e(TAG, + "Not launching notification handler activity: Notification handler does " + + "not require the STATUS_BAR_SERVICE permission for intent " + + intent.getAction()); + } + } else { + Log.e(TAG, + "Not launching notification handler activity: Could not resolve activityInfo " + + "for intent " + + intent.getAction()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index bcfff60dadf32..2795857383e8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -17,13 +17,9 @@ package com.android.systemui.statusbar.tv; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; @@ -49,9 +45,6 @@ import dagger.Lazy; @SysUISingleton public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { - private static final String ACTION_OPEN_TV_NOTIFICATIONS_PANEL = - "com.android.tv.action.OPEN_NOTIFICATIONS_PANEL"; - private final CommandQueue mCommandQueue; private final Lazy mAssistManagerLazy; @@ -74,24 +67,11 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { // If the system process isn't there we're doomed anyway. } - if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { + if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { // Creating AudioRecordingDisclosureBar and just letting it run new AudioRecordingDisclosureBar(mContext); } - } - @Override - public void animateExpandNotificationsPanel() { - startSystemActivity(new Intent(ACTION_OPEN_TV_NOTIFICATIONS_PANEL)); - } - - private void startSystemActivity(Intent intent) { - PackageManager pm = mContext.getPackageManager(); - ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY); - if (ri != null && ri.activityInfo != null) { - intent.setPackage(ri.activityInfo.packageName); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } } @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d01a30fbd818a..35b14490eba29 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -281,7 +281,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int LONG_PRESS_HOME_NOTHING = 0; static final int LONG_PRESS_HOME_ALL_APPS = 1; static final int LONG_PRESS_HOME_ASSIST = 2; - static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_ASSIST; + static final int LONG_PRESS_HOME_NOTIFICATION_PANEL = 3; + static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_NOTIFICATION_PANEL; // must match: config_doubleTapOnHomeBehavior in config.xml static final int DOUBLE_TAP_HOME_NOTHING = 0; @@ -1694,8 +1695,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { case LONG_PRESS_HOME_ASSIST: launchAssistAction(null, deviceId); break; + case LONG_PRESS_HOME_NOTIFICATION_PANEL: + IStatusBarService statusBarService = getStatusBarService(); + if (statusBarService != null) { + try { + statusBarService.togglePanel(); + } catch (RemoteException e) { + // do nothing. + } + } + break; default: - Log.w(TAG, "Undefined home long press behavior: " + Log.w(TAG, "Undefined long press on home behavior: " + mLongPressOnHomeBehavior); break; }