blob: 385fd509757baedfc0c0b00c286bb992ed3f1ea6 [file] [log] [blame]
/*
* Copyright (C) 2007 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 android.app;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.LoggingOnly;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.media.INearbyMediaDevicesProvider;
import android.media.INearbyMediaDevicesUpdateCallback;
import android.media.MediaRoute2Info;
import android.media.NearbyDevice;
import android.media.NearbyMediaDevicesProvider;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.View;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.statusbar.AppClipsServiceConnector;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.NotificationVisibility;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Allows an app to control the status bar.
*/
@SystemService(Context.STATUS_BAR_SERVICE)
public class StatusBarManager {
// LINT.IfChange
/** @hide */
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
/** @hide */
public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
/** @hide */
public static final int DISABLE_NOTIFICATION_ALERTS
= View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
/** @hide */
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int DISABLE_NOTIFICATION_TICKER
= View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
/** @hide */
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
/** @hide */
public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
/** @hide */
public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
/** @hide */
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
/** @hide */
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
/** @hide */
public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
/** @hide */
public static final int DISABLE_ONGOING_CALL_CHIP = View.STATUS_BAR_DISABLE_ONGOING_CALL_CHIP;
/** @hide */
@Deprecated
public static final int DISABLE_NAVIGATION =
View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
/** @hide */
public static final int DISABLE_NONE = 0x00000000;
/** @hide */
public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS
| DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER
| DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
| DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP;
/** @hide */
@IntDef(flag = true, prefix = {"DISABLE_"}, value = {
DISABLE_NONE,
DISABLE_EXPAND,
DISABLE_NOTIFICATION_ICONS,
DISABLE_NOTIFICATION_ALERTS,
DISABLE_NOTIFICATION_TICKER,
DISABLE_SYSTEM_INFO,
DISABLE_HOME,
DISABLE_RECENT,
DISABLE_BACK,
DISABLE_CLOCK,
DISABLE_SEARCH,
DISABLE_ONGOING_CALL_CHIP
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisableFlags {}
/**
* Flag to disable quick settings.
*
* Setting this flag disables quick settings completely, but does not disable expanding the
* notification shade.
*/
/** @hide */
public static final int DISABLE2_QUICK_SETTINGS = 1;
/** @hide */
public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
/** @hide */
public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
/** @hide */
public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
/** @hide */
public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
/** @hide */
public static final int DISABLE2_NONE = 0x00000000;
/** @hide */
public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
| DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS | DISABLE2_ROTATE_SUGGESTIONS;
/** @hide */
@IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
DISABLE2_NONE,
DISABLE2_MASK,
DISABLE2_QUICK_SETTINGS,
DISABLE2_SYSTEM_ICONS,
DISABLE2_NOTIFICATION_SHADE,
DISABLE2_GLOBAL_ACTIONS,
DISABLE2_ROTATE_SUGGESTIONS
})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
// LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt)
private static final String TAG = "StatusBarManager";
/**
* Default disable flags for setup
*
* @hide
*/
public static final int DEFAULT_SETUP_DISABLE_FLAGS = DISABLE_NOTIFICATION_ALERTS
| DISABLE_HOME | DISABLE_EXPAND | DISABLE_RECENT | DISABLE_CLOCK | DISABLE_SEARCH;
/**
* Default disable2 flags for setup
*
* @hide
*/
public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_NONE;
/**
* disable flags to be applied when the device is sim-locked.
*/
private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
/** @hide */
public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
/** @hide */
public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
/** @hide */
public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
/** @hide */
public static final int WINDOW_STATUS_BAR = 1;
/** @hide */
public static final int WINDOW_NAVIGATION_BAR = 2;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_" }, value = {
WINDOW_STATUS_BAR,
WINDOW_NAVIGATION_BAR
})
@Retention(RetentionPolicy.SOURCE)
public @interface WindowType {}
/** @hide */
public static final int WINDOW_STATE_SHOWING = 0;
/** @hide */
public static final int WINDOW_STATE_HIDING = 1;
/** @hide */
public static final int WINDOW_STATE_HIDDEN = 2;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_STATE_" }, value = {
WINDOW_STATE_SHOWING,
WINDOW_STATE_HIDING,
WINDOW_STATE_HIDDEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface WindowVisibleState {}
/** @hide */
public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
/** @hide */
public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
/** @hide */
public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
/** @hide */
public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3;
/**
* Session flag for {@link #registerSessionListener} indicating the listener
* is interested in sessions on the keygaurd.
* Keyguard Session Boundaries:
* START_SESSION: device starts going to sleep OR the keyguard is newly shown
* END_SESSION: device starts going to sleep OR keyguard is no longer showing
* @hide
*/
public static final int SESSION_KEYGUARD = 1 << 0;
/**
* Session flag for {@link #registerSessionListener} indicating the current session
* is interested in session on the biometric prompt.
* @hide
*/
public static final int SESSION_BIOMETRIC_PROMPT = 1 << 1;
/** @hide */
public static final Set<Integer> ALL_SESSIONS = Set.of(
SESSION_KEYGUARD,
SESSION_BIOMETRIC_PROMPT
);
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SESSION_KEYGUARD" }, value = {
SESSION_KEYGUARD,
SESSION_BIOMETRIC_PROMPT,
})
public @interface SessionFlags {}
/**
* Response indicating that the tile was not added.
*/
public static final int TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED = 0;
/**
* Response indicating that the tile was already added and the user was not prompted.
*/
public static final int TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED = 1;
/**
* Response indicating that the tile was added.
*/
public static final int TILE_ADD_REQUEST_RESULT_TILE_ADDED = 2;
/** @hide */
public static final int TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED = 3;
/**
* Values greater or equal to this value indicate an error in the request.
*/
private static final int TILE_ADD_REQUEST_FIRST_ERROR_CODE = 1000;
/**
* Indicates that this package does not match that of the
* {@link android.service.quicksettings.TileService}.
*/
public static final int TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE =
TILE_ADD_REQUEST_FIRST_ERROR_CODE;
/**
* Indicates that there's a request in progress for this package.
*/
public static final int TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS =
TILE_ADD_REQUEST_FIRST_ERROR_CODE + 1;
/**
* Indicates that the component does not match an enabled exported
* {@link android.service.quicksettings.TileService} for the current user.
*/
public static final int TILE_ADD_REQUEST_ERROR_BAD_COMPONENT =
TILE_ADD_REQUEST_FIRST_ERROR_CODE + 2;
/**
* Indicates that the user is not the current user.
*/
public static final int TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER =
TILE_ADD_REQUEST_FIRST_ERROR_CODE + 3;
/**
* Indicates that the requesting application is not in the foreground.
*/
public static final int TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND =
TILE_ADD_REQUEST_FIRST_ERROR_CODE + 4;
/**
* The request could not be processed because no fulfilling service was found. This could be
* a temporary issue (for example, SystemUI has crashed).
*/
public static final int TILE_ADD_REQUEST_ERROR_NO_STATUS_BAR_SERVICE =
TILE_ADD_REQUEST_FIRST_ERROR_CODE + 5;
/** @hide */
@IntDef(prefix = {"TILE_ADD_REQUEST"}, value = {
TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED,
TILE_ADD_REQUEST_RESULT_TILE_ADDED,
TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE,
TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS,
TILE_ADD_REQUEST_ERROR_BAD_COMPONENT,
TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER,
TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND,
TILE_ADD_REQUEST_ERROR_NO_STATUS_BAR_SERVICE
})
@Retention(RetentionPolicy.SOURCE)
public @interface RequestResult {}
/**
* Constant for {@link #setNavBarMode(int)} indicating the default navbar mode.
*
* @hide
*/
@SystemApi
public static final int NAV_BAR_MODE_DEFAULT = 0;
/**
* Constant for {@link #setNavBarMode(int)} indicating kids navbar mode.
*
* <p>When used, back and home icons will change drawables and layout, recents will be hidden,
* and enables the setting to force navbar visible, even when apps are in immersive mode.
*
* @hide
*/
@SystemApi
public static final int NAV_BAR_MODE_KIDS = 1;
/** @hide */
@IntDef(prefix = {"NAV_BAR_MODE_"}, value = {
NAV_BAR_MODE_DEFAULT,
NAV_BAR_MODE_KIDS
})
@Retention(RetentionPolicy.SOURCE)
public @interface NavBarMode {}
/**
* State indicating that this sender device is close to a receiver device, so the user can
* potentially *start* a cast to the receiver device if the user moves their device a bit
* closer.
* <p>
* Important notes:
* <ul>
* <li>This state represents that the device is close enough to inform the user that
* transferring is an option, but the device is *not* close enough to actually initiate a
* transfer yet.</li>
* <li>This state is for *starting* a cast. It should be used when this device is currently
* playing media locally and the media should be transferred to be played on the receiver
* device instead.</li>
* </ul>
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0;
/**
* State indicating that this sender device is close to a receiver device, so the user can
* potentially *end* a cast on the receiver device if the user moves this device a bit closer.
* <p>
* Important notes:
* <ul>
* <li>This state represents that the device is close enough to inform the user that
* transferring is an option, but the device is *not* close enough to actually initiate a
* transfer yet.</li>
* <li>This state is for *ending* a cast. It should be used when media is currently being
* played on the receiver device and the media should be transferred to play locally
* instead.</li>
* </ul>
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1;
/**
* State indicating that a media transfer from this sender device to a receiver device has been
* started.
* <p>
* Important note: This state is for *starting* a cast. It should be used when this device is
* currently playing media locally and the media has started being transferred to the receiver
* device instead.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2;
/**
* State indicating that a media transfer from the receiver and back to this sender device
* has been started.
* <p>
* Important note: This state is for *ending* a cast. It should be used when media is currently
* being played on the receiver device and the media has started being transferred to play
* locally instead.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3;
/**
* State indicating that a media transfer from this sender device to a receiver device has
* finished successfully.
* <p>
* Important note: This state is for *starting* a cast. It should be used when this device had
* previously been playing media locally and the media has successfully been transferred to the
* receiver device instead.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4;
/**
* State indicating that a media transfer from the receiver and back to this sender device has
* finished successfully.
* <p>
* Important note: This state is for *ending* a cast. It should be used when media was
* previously being played on the receiver device and has been successfully transferred to play
* locally on this device instead.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5;
/**
* State indicating that the attempted transfer to the receiver device has failed.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6;
/**
* State indicating that the attempted transfer back to this device has failed.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7;
/**
* State indicating that this sender device is no longer close to the receiver device.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8;
/** @hide */
@IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = {
MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface MediaTransferSenderState {}
/**
* State indicating that this receiver device is close to a sender device, so the user can
* potentially start or end a cast to the receiver device if the user moves the sender device a
* bit closer.
* <p>
* Important note: This state represents that the device is close enough to inform the user that
* transferring is an option, but the device is *not* close enough to actually initiate a
* transfer yet.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0;
/**
* State indicating that this receiver device is no longer close to the sender device.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1;
/**
* State indicating that media transfer to this receiver device is succeeded.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 2;
/**
* State indicating that media transfer to this receiver device is failed.
*
* @hide
*/
@SystemApi
public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED = 3;
/** @hide */
@IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = {
MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface MediaTransferReceiverState {}
/**
* A map from a provider registered in
* {@link #registerNearbyMediaDevicesProvider(NearbyMediaDevicesProvider)} to the wrapper
* around the provider that was created internally. We need the wrapper to make the provider
* binder-compatible, and we need to store a reference to the wrapper so that when the provider
* is un-registered, we un-register the saved wrapper instance.
*/
private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper>
nearbyMediaDevicesProviderMap = new HashMap<>();
/**
* Media controls based on {@link android.app.Notification.MediaStyle} notifications will have
* actions based on the media session's {@link android.media.session.PlaybackState}, rather than
* the notification's actions.
*
* These actions will be:
* - Play/Pause (depending on whether the current state is a playing state)
* - Previous (if declared), or a custom action if the slot is not reserved with
* {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV}
* - Next (if declared), or a custom action if the slot is not reserved with
* {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT}
* - Custom action
* - Custom action
*
* @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
* @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L;
/**
* Media controls based on {@link android.app.Notification.MediaStyle} notifications should
* include a non-empty title, either in the {@link android.media.MediaMetadata} or
* notification title.
*/
@ChangeId
@LoggingOnly
private static final long MEDIA_CONTROL_BLANK_TITLE = 274775190L;
@UnsupportedAppUsage
private Context mContext;
private IStatusBarService mService;
@UnsupportedAppUsage
private IBinder mToken = new Binder();
private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
@UnsupportedAppUsage
StatusBarManager(Context context) {
mContext = context;
}
@UnsupportedAppUsage
private synchronized IStatusBarService getService() {
if (mService == null) {
mService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (mService == null) {
Slog.w(TAG, "warning: no STATUS_BAR_SERVICE");
}
}
return mService;
}
/**
* Disable some features in the status bar. Pass the bitwise-or of the DISABLE_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
*
* @hide
*/
@UnsupportedAppUsage
public void disable(int what) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
if (svc != null) {
svc.disableForUser(what, mToken, mContext.getPackageName(), userId);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
*
* Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
*
* @hide
*/
public void disable2(@Disable2Flags int what) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
if (svc != null) {
svc.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Simulate notification click for testing
*
* @hide
*/
@TestApi
public void clickNotification(@Nullable String key, int rank, int count, boolean visible) {
clickNotificationInternal(key, rank, count, visible);
}
private void clickNotificationInternal(String key, int rank, int count, boolean visible) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.onNotificationClick(key,
NotificationVisibility.obtain(key, rank, count, visible));
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Simulate notification feedback for testing
*
* @hide
*/
@TestApi
public void sendNotificationFeedback(@Nullable String key, @Nullable Bundle feedback) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.onNotificationFeedbackReceived(key, feedback);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Expand the notifications panel.
*
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void expandNotificationsPanel() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.expandNotificationsPanel();
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Collapse the notifications and settings panels.
*
* Starting in Android {@link Build.VERSION_CODES.S}, apps targeting SDK level {@link
* Build.VERSION_CODES.S} or higher will need {@link android.Manifest.permission.STATUS_BAR}
* permission to call this API.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "This operation"
+ " is not allowed anymore, please see {@link android.content"
+ ".Intent#ACTION_CLOSE_SYSTEM_DIALOGS} for more details.")
@TestApi
public void collapsePanels() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.collapsePanels();
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Toggles the notification panel.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@TestApi
public void togglePanel() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.togglePanel();
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Sends system keys to the status bar.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@TestApi
public void handleSystemKey(@NonNull KeyEvent key) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.handleSystemKey(key);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Gets the last handled system key. A system key is a KeyEvent that the
* {@link com.android.server.policy.PhoneWindowManager} sends directly to the
* status bar, rather than forwarding to apps. If a key has never been sent to the
* status bar, will return -1.
*
* @return the keycode of the last KeyEvent that has been sent to the system.
* @hide
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@TestApi
public int getLastSystemKey() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
return svc.getLastSystemKey();
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
return -1;
}
/**
* Expand the settings panel.
*
* @hide
*/
@UnsupportedAppUsage
public void expandSettingsPanel() {
expandSettingsPanel(null);
}
/**
* Expand the settings panel and open a subPanel. If the subpanel is null or does not have a
* corresponding tile, the QS panel is simply expanded
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void expandSettingsPanel(@Nullable String subPanel) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.expandSettingsPanel(subPanel);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
contentDescription);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void removeIcon(String slot) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.removeIcon(slot);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setIconVisibility(String slot, boolean visible) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setIconVisibility(slot, visible);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Enable or disable status bar elements (notifications, clock) which are inappropriate during
* device setup.
*
* @param disabled whether to apply or remove the disabled flags
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setDisabledForSetup(boolean disabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
if (svc != null) {
svc.disableForUser(disabled ? DEFAULT_SETUP_DISABLE_FLAGS : DISABLE_NONE,
mToken, mContext.getPackageName(), userId);
svc.disable2ForUser(disabled ? DEFAULT_SETUP_DISABLE2_FLAGS : DISABLE2_NONE,
mToken, mContext.getPackageName(), userId);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Enable or disable expansion of the status bar. When the device is SIM-locked, the status
* bar should not be expandable.
*
* @param disabled If {@code true}, the status bar will be set to non-expandable. If
* {@code false}, re-enables expansion of the status bar.
* @hide
*/
@TestApi
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setExpansionDisabledForSimNetworkLock(boolean disabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
if (svc != null) {
svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE,
mToken, mContext.getPackageName(), userId);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Get this app's currently requested disabled components
*
* @return a new DisableInfo
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@NonNull
public DisableInfo getDisableInfo() {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
int[] flags = new int[] {0, 0};
if (svc != null) {
flags = svc.getDisableFlags(mToken, userId);
}
return new DisableInfo(flags[0], flags[1]);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Sets an active {@link android.service.quicksettings.TileService} to listening state
*
* The {@code componentName}'s package must match the calling package.
*
* @param componentName the tile to set into listening state
* @see android.service.quicksettings.TileService#requestListeningState
* @hide
*/
public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
Objects.requireNonNull(componentName);
try {
getService().requestTileServiceListeningState(componentName, mContext.getUserId());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Request to the user to add a {@link android.service.quicksettings.TileService}
* to the set of current QS tiles.
* <p>
* Calling this will prompt the user to decide whether they want to add the shown
* {@link android.service.quicksettings.TileService} to their current tiles. The user can
* deny the request and the system can stop processing requests for a given
* {@link ComponentName} after a number of requests.
* <p>
* The request will show to the user information about the tile:
* <ul>
* <li>Application name</li>
* <li>Label for the tile</li>
* <li>Icon for the tile</li>
* </ul>
* <p>
* The user for which this will be added is determined from the {@link Context} used to retrieve
* this service, and must match the current user. The requesting application must be in the
* foreground ({@link ActivityManager.RunningAppProcessInfo#IMPORTANCE_FOREGROUND}
* and the {@link android.service.quicksettings.TileService} must be exported.
*
* Note: the system can choose to auto-deny a request if the user has denied that specific
* request (user, ComponentName) enough times before.
*
* @param tileServiceComponentName {@link ComponentName} of the
* {@link android.service.quicksettings.TileService} for the request.
* @param tileLabel label of the tile to show to the user.
* @param icon icon to use in the tile shown to the user.
* @param resultExecutor an executor to run the callback on
* @param resultCallback callback to indicate the result of the request.
*
* @see android.service.quicksettings.TileService
*/
public void requestAddTileService(
@NonNull ComponentName tileServiceComponentName,
@NonNull CharSequence tileLabel,
@NonNull Icon icon,
@NonNull Executor resultExecutor,
@NonNull Consumer<Integer> resultCallback
) {
Objects.requireNonNull(tileServiceComponentName);
Objects.requireNonNull(tileLabel);
Objects.requireNonNull(icon);
Objects.requireNonNull(resultExecutor);
Objects.requireNonNull(resultCallback);
if (!tileServiceComponentName.getPackageName().equals(mContext.getPackageName())) {
resultExecutor.execute(
() -> resultCallback.accept(TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE));
return;
}
int userId = mContext.getUserId();
RequestResultCallback callbackProxy = new RequestResultCallback(resultExecutor,
resultCallback);
IStatusBarService svc = getService();
try {
svc.requestAddTile(
tileServiceComponentName,
tileLabel,
icon,
userId,
callbackProxy
);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
}
/**
* @hide
* @param packageName
*/
@TestApi
public void cancelRequestAddTile(@NonNull String packageName) {
Objects.requireNonNull(packageName);
IStatusBarService svc = getService();
try {
svc.cancelRequestAddTile(packageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Sets or removes the navigation bar mode.
*
* @param navBarMode the mode of the navigation bar to be set.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setNavBarMode(@NavBarMode int navBarMode) {
if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) {
throw new IllegalArgumentException("Supplied navBarMode not supported: " + navBarMode);
}
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setNavBarMode(navBarMode);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Gets the navigation bar mode. Returns default value if no mode is set.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public @NavBarMode int getNavBarMode() {
int navBarMode = NAV_BAR_MODE_DEFAULT;
try {
final IStatusBarService svc = getService();
if (svc != null) {
navBarMode = svc.getNavBarMode();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return navBarMode;
}
/**
* Notifies the system of a new media tap-to-transfer state for the <b>sender</b> device.
*
* <p>The callback should only be provided for the {@link
* MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link
* MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the
* only states where an action can be un-done.
*
* @param displayState the new state for media tap-to-transfer.
* @param routeInfo the media route information for the media being transferred.
* @param undoExecutor an executor to run the callback on and must be provided if the
* callback is non-null.
* @param undoCallback a callback that will be triggered if the user elects to undo a media
* transfer.
*
* @throws IllegalArgumentException if an undo callback is provided for states that are not a
* succeeded state.
* @throws IllegalArgumentException if an executor is not provided when a callback is.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void updateMediaTapToTransferSenderDisplay(
@MediaTransferSenderState int displayState,
@NonNull MediaRoute2Info routeInfo,
@Nullable Executor undoExecutor,
@Nullable Runnable undoCallback
) {
Objects.requireNonNull(routeInfo);
if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED
&& displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED
&& undoCallback != null) {
throw new IllegalArgumentException(
"The undoCallback should only be provided when the state is a "
+ "transfer succeeded state");
}
if (undoCallback != null && undoExecutor == null) {
throw new IllegalArgumentException(
"You must pass an executor when you pass an undo callback");
}
IStatusBarService svc = getService();
try {
UndoCallback callbackProxy = null;
if (undoExecutor != null) {
callbackProxy = new UndoCallback(undoExecutor, undoCallback);
}
svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Notifies the system of a new media tap-to-transfer state for the <b>receiver</b> device.
*
* @param displayState the new state for media tap-to-transfer.
* @param routeInfo the media route information for the media being transferred.
* @param appIcon the icon of the app playing the media.
* @param appName the name of the app playing the media.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void updateMediaTapToTransferReceiverDisplay(
@MediaTransferReceiverState int displayState,
@NonNull MediaRoute2Info routeInfo,
@Nullable Icon appIcon,
@Nullable CharSequence appName) {
Objects.requireNonNull(routeInfo);
IStatusBarService svc = getService();
try {
svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo, appIcon, appName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Registers a provider that notifies callbacks about the status of nearby devices that are able
* to play media.
* <p>
* If multiple providers are registered, all the providers will be used for nearby device
* information.
* <p>
* @param provider the nearby device information provider to register
* <p>
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void registerNearbyMediaDevicesProvider(
@NonNull NearbyMediaDevicesProvider provider
) {
Objects.requireNonNull(provider);
if (nearbyMediaDevicesProviderMap.containsKey(provider)) {
return;
}
try {
final IStatusBarService svc = getService();
NearbyMediaDevicesProviderWrapper providerWrapper =
new NearbyMediaDevicesProviderWrapper(provider);
nearbyMediaDevicesProviderMap.put(provider, providerWrapper);
svc.registerNearbyMediaDevicesProvider(providerWrapper);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Unregisters a provider that gives information about nearby devices that are able to play
* media.
* <p>
* See {@link registerNearbyMediaDevicesProvider}.
* <p>
* @param provider the nearby device information provider to unregister
* <p>
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void unregisterNearbyMediaDevicesProvider(
@NonNull NearbyMediaDevicesProvider provider
) {
Objects.requireNonNull(provider);
if (!nearbyMediaDevicesProviderMap.containsKey(provider)) {
return;
}
try {
final IStatusBarService svc = getService();
NearbyMediaDevicesProviderWrapper providerWrapper =
nearbyMediaDevicesProviderMap.get(provider);
nearbyMediaDevicesProviderMap.remove(provider);
svc.unregisterNearbyMediaDevicesProvider(providerWrapper);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Checks whether the given package should use session-based actions for its media controls.
*
* @param packageName App posting media controls
* @param user Current user handle
* @return true if the app supports session actions
*
* @hide
*/
@RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
android.Manifest.permission.LOG_COMPAT_CHANGE})
public static boolean useMediaSessionActionsForApp(String packageName, UserHandle user) {
return CompatChanges.isChangeEnabled(MEDIA_CONTROL_SESSION_ACTIONS, packageName, user);
}
/**
* Log that the given package has posted media controls with a blank title
*
* @param packageName App posting media controls
* @param userId Current user ID
* @throws RuntimeException if there is an error reporting the change
*
* @hide
*/
public void logBlankMediaTitle(String packageName, @UserIdInt int userId)
throws RuntimeException {
try {
mPlatformCompat.reportChangeByPackageName(MEDIA_CONTROL_BLANK_TITLE, packageName,
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Checks whether the supplied activity can {@link Activity#startActivityForResult(Intent, int)}
* a system activity that captures content on the screen to take a screenshot.
*
* <p>Note: The result should not be cached.
*
* <p>The system activity displays an editing tool that allows user to edit the screenshot, save
* it on device, and return the edited screenshot as {@link android.net.Uri} to the calling
* activity. User interaction is required to return the edited screenshot to the calling
* activity.
*
* <p>When {@code true}, callers can use {@link Activity#startActivityForResult(Intent, int)}
* to start start the content capture activity using
* {@link Intent#ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE}.
*
* @param activity Calling activity
* @return true if the activity supports launching the capture content activity for note.
*
* @see Intent#ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE
* @see Manifest.permission#LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE
* @see android.app.role.RoleManager#ROLE_NOTES
*/
@RequiresPermission(Manifest.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE)
public boolean canLaunchCaptureContentActivityForNote(@NonNull Activity activity) {
Objects.requireNonNull(activity);
IBinder activityToken = activity.getActivityToken();
int taskId = ActivityClient.getInstance().getTaskForActivity(activityToken, false);
return new AppClipsServiceConnector(mContext)
.canLaunchCaptureContentActivityForNote(taskId);
}
/** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
return "WINDOW_STATE_UNKNOWN";
}
/**
* DisableInfo describes this app's requested state of the StatusBar with regards to which
* components are enabled/disabled
*
* @hide
*/
@SystemApi
public static final class DisableInfo {
private boolean mStatusBarExpansion;
private boolean mNavigateHome;
private boolean mNotificationPeeking;
private boolean mRecents;
private boolean mSearch;
private boolean mSystemIcons;
private boolean mClock;
private boolean mNotificationIcons;
private boolean mRotationSuggestion;
/** @hide */
public DisableInfo(int flags1, int flags2) {
mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
mNavigateHome = (flags1 & DISABLE_HOME) != 0;
mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
mRecents = (flags1 & DISABLE_RECENT) != 0;
mSearch = (flags1 & DISABLE_SEARCH) != 0;
mSystemIcons = (flags1 & DISABLE_SYSTEM_INFO) != 0;
mClock = (flags1 & DISABLE_CLOCK) != 0;
mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
}
/** @hide */
public DisableInfo() {}
/**
* @return {@code true} if expanding the notification shade is disabled
*
* @hide
*/
@SystemApi
public boolean isStatusBarExpansionDisabled() {
return mStatusBarExpansion;
}
/** * @hide */
public void setStatusBarExpansionDisabled(boolean disabled) {
mStatusBarExpansion = disabled;
}
/**
* @return {@code true} if navigation home is disabled
*
* @hide
*/
@SystemApi
public boolean isNavigateToHomeDisabled() {
return mNavigateHome;
}
/** * @hide */
public void setNagivationHomeDisabled(boolean disabled) {
mNavigateHome = disabled;
}
/**
* @return {@code true} if notification peeking (heads-up notification) is disabled
*
* @hide
*/
@SystemApi
public boolean isNotificationPeekingDisabled() {
return mNotificationPeeking;
}
/** @hide */
public void setNotificationPeekingDisabled(boolean disabled) {
mNotificationPeeking = disabled;
}
/**
* @return {@code true} if mRecents/overview is disabled
*
* @hide
*/
@SystemApi
public boolean isRecentsDisabled() {
return mRecents;
}
/** @hide */
public void setRecentsDisabled(boolean disabled) {
mRecents = disabled;
}
/**
* @return {@code true} if mSearch is disabled
*
* @hide
*/
@SystemApi
public boolean isSearchDisabled() {
return mSearch;
}
/** @hide */
public void setSearchDisabled(boolean disabled) {
mSearch = disabled;
}
/**
* @return {@code true} if system icons are disabled
*
* @hide
*/
public boolean areSystemIconsDisabled() {
return mSystemIcons;
}
/** * @hide */
public void setSystemIconsDisabled(boolean disabled) {
mSystemIcons = disabled;
}
/**
* @return {@code true} if the clock icon is disabled
*
* @hide
*/
public boolean isClockDisabled() {
return mClock;
}
/** * @hide */
public void setClockDisabled(boolean disabled) {
mClock = disabled;
}
/**
* @return {@code true} if notification icons are disabled
*
* @hide
*/
public boolean areNotificationIconsDisabled() {
return mNotificationIcons;
}
/** * @hide */
public void setNotificationIconsDisabled(boolean disabled) {
mNotificationIcons = disabled;
}
/**
* Returns whether the rotation suggestion is disabled.
*
* @hide
*/
@TestApi
public boolean isRotationSuggestionDisabled() {
return mRotationSuggestion;
}
/**
* @return {@code true} if no components are disabled (default state)
* @hide
*/
@SystemApi
public boolean areAllComponentsEnabled() {
return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
&& !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
&& !mRotationSuggestion;
}
/** @hide */
public void setEnableAll() {
mStatusBarExpansion = false;
mNavigateHome = false;
mNotificationPeeking = false;
mRecents = false;
mSearch = false;
mSystemIcons = false;
mClock = false;
mNotificationIcons = false;
mRotationSuggestion = false;
}
/**
* @return {@code true} if all status bar components are disabled
*
* @hide
*/
public boolean areAllComponentsDisabled() {
return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
&& mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
&& mRotationSuggestion;
}
/** @hide */
public void setDisableAll() {
mStatusBarExpansion = true;
mNavigateHome = true;
mNotificationPeeking = true;
mRecents = true;
mSearch = true;
mSystemIcons = true;
mClock = true;
mNotificationIcons = true;
mRotationSuggestion = true;
}
@NonNull
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("DisableInfo: ");
sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
sb.append(" mNotificationPeeking=")
.append(mNotificationPeeking ? "disabled" : "enabled");
sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
sb.append(" mSystemIcons=").append(mSystemIcons ? "disabled" : "enabled");
sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
return sb.toString();
}
/**
* Convert a DisableInfo to equivalent flags
* @return a pair of equivalent disable flags
*
* @hide
*/
public Pair<Integer, Integer> toFlags() {
int disable1 = DISABLE_NONE;
int disable2 = DISABLE2_NONE;
if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND;
if (mNavigateHome) disable1 |= DISABLE_HOME;
if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
if (mRecents) disable1 |= DISABLE_RECENT;
if (mSearch) disable1 |= DISABLE_SEARCH;
if (mSystemIcons) disable1 |= DISABLE_SYSTEM_INFO;
if (mClock) disable1 |= DISABLE_CLOCK;
if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
return new Pair<Integer, Integer>(disable1, disable2);
}
}
/**
* @hide
*/
static final class RequestResultCallback extends IAddTileResultCallback.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
private final Consumer<Integer> mCallback;
RequestResultCallback(@NonNull Executor executor, @NonNull Consumer<Integer> callback) {
mExecutor = executor;
mCallback = callback;
}
@Override
public void onTileRequest(int userResponse) {
mExecutor.execute(() -> mCallback.accept(userResponse));
}
}
/**
* @hide
*/
static final class UndoCallback extends IUndoMediaTransferCallback.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
private final Runnable mCallback;
UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) {
mExecutor = executor;
mCallback = callback;
}
@Override
public void onUndoTriggered() {
final long callingIdentity = Binder.clearCallingIdentity();
try {
mExecutor.execute(mCallback);
} finally {
restoreCallingIdentity(callingIdentity);
}
}
}
/**
* @hide
*/
static final class NearbyMediaDevicesProviderWrapper extends INearbyMediaDevicesProvider.Stub {
@NonNull
private final NearbyMediaDevicesProvider mProvider;
// Because we're wrapping a {@link NearbyMediaDevicesProvider} in a binder-compatible
// interface, we also need to wrap the callbacks that the provider receives. We use
// this map to keep track of the original callback and the wrapper callback so that
// unregistering the callback works correctly.
@NonNull
private final Map<INearbyMediaDevicesUpdateCallback, Consumer<List<NearbyDevice>>>
mRegisteredCallbacks = new HashMap<>();
NearbyMediaDevicesProviderWrapper(@NonNull NearbyMediaDevicesProvider provider) {
mProvider = provider;
}
@Override
public void registerNearbyDevicesCallback(
@NonNull INearbyMediaDevicesUpdateCallback callback) {
Consumer<List<NearbyDevice>> callbackAsConsumer = nearbyDevices -> {
try {
callback.onDevicesUpdated(nearbyDevices);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
};
mRegisteredCallbacks.put(callback, callbackAsConsumer);
mProvider.registerNearbyDevicesCallback(callbackAsConsumer);
}
@Override
public void unregisterNearbyDevicesCallback(
@NonNull INearbyMediaDevicesUpdateCallback callback) {
if (!mRegisteredCallbacks.containsKey(callback)) {
return;
}
mProvider.unregisterNearbyDevicesCallback(mRegisteredCallbacks.get(callback));
mRegisteredCallbacks.remove(callback);
}
}
}