1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.tv.notifications;
18 
19 import android.Manifest;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.os.UserHandle;
26 import android.util.Log;
27 
28 import com.android.systemui.CoreStartable;
29 import com.android.systemui.dagger.SysUISingleton;
30 import com.android.systemui.statusbar.CommandQueue;
31 
32 import javax.inject.Inject;
33 
34 /**
35  * Offers control methods for the notification panel handler on TV devices.
36  */
37 @SysUISingleton
38 public class TvNotificationPanel implements CoreStartable, CommandQueue.Callbacks {
39     private static final String TAG = "TvNotificationPanel";
40     private final Context mContext;
41     private final CommandQueue mCommandQueue;
42     private final String mNotificationHandlerPackage;
43 
44     @Inject
TvNotificationPanel(Context context, CommandQueue commandQueue)45     public TvNotificationPanel(Context context, CommandQueue commandQueue) {
46         mContext = context;
47         mCommandQueue = commandQueue;
48         mNotificationHandlerPackage = mContext.getResources().getString(
49                 com.android.internal.R.string.config_notificationHandlerPackage);
50     }
51 
52     @Override
start()53     public void start() {
54         mCommandQueue.addCallback(this);
55     }
56 
57     @Override
toggleNotificationsPanel()58     public void toggleNotificationsPanel() {
59         if (!mNotificationHandlerPackage.isEmpty()) {
60             startNotificationHandlerActivity(
61                     new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
62         } else {
63             openInternalNotificationPanel(
64                     NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL);
65         }
66     }
67 
68     @Override
animateExpandNotificationsPanel()69     public void animateExpandNotificationsPanel() {
70         if (!mNotificationHandlerPackage.isEmpty()) {
71             startNotificationHandlerActivity(
72                     new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
73         } else {
74             openInternalNotificationPanel(
75                     NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL);
76         }
77     }
78 
79     @Override
animateCollapsePanels(int flags, boolean force)80     public void animateCollapsePanels(int flags, boolean force) {
81         if (!mNotificationHandlerPackage.isEmpty()
82                 && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
83             Intent closeNotificationIntent = new Intent(
84                     NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
85             closeNotificationIntent.setPackage(mNotificationHandlerPackage);
86             mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
87         } else {
88             openInternalNotificationPanel(
89                     NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
90         }
91     }
92 
openInternalNotificationPanel(String action)93     private void openInternalNotificationPanel(String action) {
94         Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
95         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
96         intent.setAction(action);
97         mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
98     }
99 
100     /**
101      * Starts the activity intent if all of the following are true
102      * <ul>
103      * <li> the notification handler package is a system component </li>
104      * <li> the provided intent is handled by the notification handler package </li>
105      * <li> the notification handler requests the
106      * {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission for the given intent</li>
107      * </ul>
108      *
109      * @param intent The intent for starting the desired activity
110      */
startNotificationHandlerActivity(Intent intent)111     private void startNotificationHandlerActivity(Intent intent) {
112         intent.setPackage(mNotificationHandlerPackage);
113         PackageManager pm = mContext.getPackageManager();
114         ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
115         if (ri != null && ri.activityInfo != null) {
116             if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals(
117                     Manifest.permission.STATUS_BAR_SERVICE)) {
118                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
119                 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
120             } else {
121                 Log.e(TAG,
122                         "Not launching notification handler activity: Notification handler does "
123                                 + "not require the STATUS_BAR_SERVICE permission for intent "
124                                 + intent.getAction());
125             }
126         } else {
127             Log.e(TAG,
128                     "Not launching notification handler activity: Could not resolve activityInfo "
129                             + "for intent "
130                             + intent.getAction());
131         }
132     }
133 }
134