diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java index bce9c340c76..719d2d5641d 100644 --- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java +++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java @@ -51,8 +51,8 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MediaDeviceUpdateWorker extends SliceBackgroundWorker implements LocalMediaManager.DeviceCallback { - private final Context mContext; - private final Collection mMediaDevices = new CopyOnWriteArrayList<>(); + protected final Context mContext; + protected final Collection mMediaDevices = new CopyOnWriteArrayList<>(); private final DevicesChangedBroadcastReceiver mReceiver; private final String mPackageName; diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index 773013e4d9e..4e54d7ba35f 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -375,7 +375,7 @@ public class MediaOutputSlice implements CustomSliceable { @Override public Class getBackgroundWorkerClass() { - return MediaDeviceUpdateWorker.class; + return MediaOutputSliceWorker.class; } private boolean isVisible() { diff --git a/src/com/android/settings/media/MediaOutputSliceWorker.java b/src/com/android/settings/media/MediaOutputSliceWorker.java new file mode 100644 index 00000000000..357b234acbb --- /dev/null +++ b/src/com/android/settings/media/MediaOutputSliceWorker.java @@ -0,0 +1,211 @@ +/* + * 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.settings.media; + +import static android.media.MediaRoute2ProviderService.REASON_INVALID_COMMAND; +import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR; +import static android.media.MediaRoute2ProviderService.REASON_REJECTED; +import static android.media.MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE; +import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.net.Uri; +import android.util.Log; + +import com.android.settings.core.instrumentation.SettingsStatsLog; +import com.android.settingslib.media.MediaDevice; + +/** + * SliceBackgroundWorker for the MediaOutputSlice class. + * It inherits from MediaDeviceUpdateWorker and add metrics logging. + */ +public class MediaOutputSliceWorker extends MediaDeviceUpdateWorker { + + private static final String TAG = "MediaOutputSliceWorker"; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + + private MediaDevice mSourceDevice, mTargetDevice; + private int mWiredDeviceCount; + private int mConnectedBluetoothDeviceCount; + private int mRemoteDeviceCount; + private int mAppliedDeviceCountWithinRemoteGroup; + + public MediaOutputSliceWorker(Context context, Uri uri) { + super(context, uri); + } + + @Override + public void connectDevice(MediaDevice device) { + mSourceDevice = mLocalMediaManager.getCurrentConnectedDevice(); + mTargetDevice = device; + + if (DBG) { + Log.d(TAG, "connectDevice -" + + " source:" + mSourceDevice.toString() + + " target:" + mTargetDevice.toString()); + } + + super.connectDevice(device); + } + + private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) { + switch (device.getDeviceType()) { + case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BUILTIN_SPEAKER + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BUILTIN_SPEAKER; + case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: + return isSourceDevice + ? SettingsStatsLog + .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__WIRED_3POINT5_MM_AUDIO + : SettingsStatsLog + .MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; + case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__USB_C_AUDIO + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__USB_C_AUDIO; + case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BLUETOOTH + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BLUETOOTH; + case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_SINGLE + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_SINGLE; + case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP; + default: + return isSourceDevice + ? SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE + : SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; + } + } + + private int getLoggingSwitchOpSubResult(int reason) { + switch (reason) { + case REASON_REJECTED: + return SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__REJECTED; + case REASON_NETWORK_ERROR: + return SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NETWORK_ERROR; + case REASON_ROUTE_NOT_AVAILABLE: + return SettingsStatsLog + .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__ROUTE_NOT_AVAILABLE; + case REASON_INVALID_COMMAND: + return SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__INVALID_COMMAND; + case REASON_UNKNOWN_ERROR: + default: + return SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__UNKNOWN_ERROR; + } + } + + private String getLoggingPackageName() { + final String packageName = getPackageName(); + if (packageName != null && !packageName.isEmpty()) { + try { + final ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfo(packageName, /* default flag */ 0); + if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + || (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + return packageName; + } + } catch (Exception ex) { + Log.e(TAG, packageName + "is invalid."); + } + } + + return ""; + } + + private void updateLoggingDeviceCount() { + mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; + mAppliedDeviceCountWithinRemoteGroup = 0; + + for (MediaDevice mediaDevice : mMediaDevices) { + if (mediaDevice.isConnected()) { + switch (mediaDevice.getDeviceType()) { + case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: + case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: + mWiredDeviceCount++; + break; + case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: + mConnectedBluetoothDeviceCount++; + break; + case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: + case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: + mRemoteDeviceCount++; + break; + default: + } + } + } + + if (DBG) { + Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount + + " bluetooth: " + mConnectedBluetoothDeviceCount + + " remote: " + mRemoteDeviceCount); + } + } + + @Override + public void onSelectedDeviceStateChanged(MediaDevice device, int state) { + if (DBG) { + Log.d(TAG, "onSelectedDeviceStateChanged - " + device.toString()); + } + + updateLoggingDeviceCount(); + + SettingsStatsLog.write( + SettingsStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, + getLoggingDeviceType(mSourceDevice, true), + getLoggingDeviceType(mTargetDevice, false), + SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK, + SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR, + getLoggingPackageName(), + mWiredDeviceCount, + mConnectedBluetoothDeviceCount, + mRemoteDeviceCount, + mAppliedDeviceCountWithinRemoteGroup); + + super.onSelectedDeviceStateChanged(device, state); + } + + @Override + public void onRequestFailed(int reason) { + if (DBG) { + Log.e(TAG, "onRequestFailed - " + reason); + } + + updateLoggingDeviceCount(); + + SettingsStatsLog.write( + SettingsStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, + getLoggingDeviceType(mSourceDevice, true), + getLoggingDeviceType(mTargetDevice, false), + SettingsStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR, + getLoggingSwitchOpSubResult(reason), + getLoggingPackageName(), + mWiredDeviceCount, + mConnectedBluetoothDeviceCount, + mRemoteDeviceCount, + mAppliedDeviceCountWithinRemoteGroup); + + super.onRequestFailed(reason); + } +} diff --git a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java index 0ba943605a5..364a81c4821 100644 --- a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java +++ b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java @@ -150,7 +150,7 @@ public class AppConversationListPreferenceController extends NotificationPrefere ShortcutInfo si = conversation.getShortcutInfo(); pref.setTitle(si != null - ? si.getShortLabel() + ? si.getLabel() : conversation.getNotificationChannel().getName()); pref.setSummary(conversation.getNotificationChannel().getGroup() != null ? mContext.getString(R.string.notification_conversation_summary, diff --git a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java index c74b032b4cb..50a8b23a554 100644 --- a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java +++ b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java @@ -124,7 +124,7 @@ public class ConversationHeaderPreferenceController extends NotificationPreferen @VisibleForTesting CharSequence getLabel() { return mConversationInfo != null - ? mConversationInfo.getShortLabel() + ? mConversationInfo.getLabel() : mChannel.getName(); } } diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java index b750a66efa2..57cbe6a4ad7 100644 --- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java +++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java @@ -116,7 +116,7 @@ public abstract class ConversationListPreferenceController extends AbstractPrefe CharSequence getTitle(ConversationChannelWrapper conversation) { ShortcutInfo si = conversation.getShortcutInfo(); return si != null - ? si.getShortLabel() + ? si.getLabel() : conversation.getNotificationChannel().getName(); } @@ -154,8 +154,8 @@ public abstract class ConversationListPreferenceController extends AbstractPrefe return o1.getNotificationChannel().getId().compareTo( o2.getNotificationChannel().getId()); } - return sCollator.compare(o1.getShortcutInfo().getShortLabel(), - o2.getShortcutInfo().getShortLabel()); + return sCollator.compare(o1.getShortcutInfo().getLabel(), + o2.getShortcutInfo().getLabel()); } }; } diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java index 8b4bc9d8368..ab0a8d79a7b 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -265,7 +265,7 @@ public class NotificationHistoryActivity extends Activity { LinearLayoutManager lm = new LinearLayoutManager(NotificationHistoryActivity.this); mSnoozedRv.setLayoutManager(lm); mSnoozedRv.setAdapter( - new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm)); + new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm)); mSnoozedRv.setNestedScrollingEnabled(false); if (snoozed == null || snoozed.length == 0) { @@ -280,7 +280,7 @@ public class NotificationHistoryActivity extends Activity { new LinearLayoutManager(NotificationHistoryActivity.this); mDismissedRv.setLayoutManager(dismissLm); mDismissedRv.setAdapter( - new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm)); + new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm)); mDismissedRv.setNestedScrollingEnabled(false); if (dismissed == null || dismissed.length == 0) { diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java index 5d4e7166710..27ecabdd92e 100644 --- a/src/com/android/settings/notification/history/NotificationSbnAdapter.java +++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java @@ -32,6 +32,7 @@ import android.content.res.Configuration; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.UserHandle; +import android.os.UserManager; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -47,6 +48,7 @@ import com.android.internal.util.ContrastColorUtil; import com.android.settings.R; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,8 +64,9 @@ public class NotificationSbnAdapter extends private @ColorInt int mBackgroundColor; private boolean mInNightMode; private @UserIdInt int mCurrentUser; + private List mEnabledProfiles = new ArrayList<>(); - public NotificationSbnAdapter(Context context, PackageManager pm) { + public NotificationSbnAdapter(Context context, PackageManager pm, UserManager um) { mContext = context; mPm = pm; mUserBadgeCache = new HashMap<>(); @@ -74,6 +77,12 @@ public class NotificationSbnAdapter extends mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; mCurrentUser = ActivityManager.getCurrentUser(); + int[] enabledUsers = um.getEnabledProfileIds(mCurrentUser); + for (int id : enabledUsers) { + if (!um.isQuietModeEnabled(UserHandle.of(id))) { + mEnabledProfiles.add(id); + } + } setHasStableIds(true); } @@ -115,10 +124,9 @@ public class NotificationSbnAdapter extends } public void onRebuildComplete(List notifications) { - // summaries are low content; don't bother showing them for (int i = notifications.size() - 1; i >= 0; i--) { StatusBarNotification sbn = notifications.get(i); - if (sbn.isGroup() && sbn.getNotification().isGroupSummary()) { + if (!shouldShowSbn(sbn)) { notifications.remove(i); } } @@ -127,13 +135,25 @@ public class NotificationSbnAdapter extends } public void addSbn(StatusBarNotification sbn) { - if (sbn.isGroup() && sbn.getNotification().isGroupSummary()) { + if (!shouldShowSbn(sbn)) { return; } mValues.add(0, sbn); notifyDataSetChanged(); } + private boolean shouldShowSbn(StatusBarNotification sbn) { + // summaries are low content; don't bother showing them + if (sbn.isGroup() && sbn.getNotification().isGroupSummary()) { + return false; + } + // also don't show profile notifications if the profile is currently disabled + if (!mEnabledProfiles.contains(normalizeUserId(sbn))) { + return false; + } + return true; + } + private @NonNull CharSequence loadPackageLabel(String pkg) { try { ApplicationInfo info = mPm.getApplicationInfo(pkg, diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationHeaderPreferenceControllerTest.java index 96f4e0bf497..428a1a8fc43 100644 --- a/tests/robotests/src/com/android/settings/notification/app/ConversationHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/ConversationHeaderPreferenceControllerTest.java @@ -103,10 +103,10 @@ public class ConversationHeaderPreferenceControllerTest { @Test public void testGetLabel() { ShortcutInfo si = mock(ShortcutInfo.class); - when(si.getShortLabel()).thenReturn("hello"); + when(si.getLabel()).thenReturn("hello"); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); mController.onResume(appRow, null, null, null, si, null); - assertEquals(si.getShortLabel(), mController.getLabel()); + assertEquals(si.getLabel(), mController.getLabel()); NotificationChannel channel = new NotificationChannel("cid", "cname", IMPORTANCE_NONE); mController.onResume(appRow, channel, null, null, null, null); diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java index cbd911541a0..8672afb0617 100644 --- a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java @@ -169,12 +169,12 @@ public class ConversationListPreferenceControllerTest { ccw.setPkg("pkg"); ccw.setUid(1); ShortcutInfo si = mock(ShortcutInfo.class); - when(si.getShortLabel()).thenReturn("conversation name"); + when(si.getLabel()).thenReturn("conversation name"); ccw.setShortcutInfo(si); ccw.setGroupLabel("group"); ccw.setParentChannelLabel("parent"); - assertThat(mController.getTitle(ccw).toString()).isEqualTo(si.getShortLabel()); + assertThat(mController.getTitle(ccw).toString()).isEqualTo(si.getLabel()); } @Test