1 /* 2 * Copyright (C) 2019 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.statusbar; 18 19 import android.content.Context; 20 import android.content.res.ColorStateList; 21 import android.graphics.drawable.Drawable; 22 import android.graphics.drawable.GradientDrawable; 23 import android.graphics.drawable.RippleDrawable; 24 import android.service.notification.StatusBarNotification; 25 import android.util.FeatureFlagUtils; 26 import android.util.Log; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.ViewParent; 30 import android.widget.ImageView; 31 import android.widget.LinearLayout; 32 import android.widget.TextView; 33 34 import com.android.settingslib.bluetooth.LocalBluetoothManager; 35 import com.android.settingslib.media.InfoMediaManager; 36 import com.android.settingslib.media.LocalMediaManager; 37 import com.android.settingslib.media.MediaDevice; 38 import com.android.settingslib.widget.AdaptiveIcon; 39 import com.android.systemui.Dependency; 40 import com.android.systemui.media.dialog.MediaOutputDialogFactory; 41 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 42 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Class for handling MediaTransfer state over a set of notifications. 49 */ 50 public class MediaTransferManager { 51 private final Context mContext; 52 private final MediaOutputDialogFactory mMediaOutputDialogFactory; 53 private MediaDevice mDevice; 54 private List<View> mViews = new ArrayList<>(); 55 private LocalMediaManager mLocalMediaManager; 56 57 private static final String TAG = "MediaTransferManager"; 58 59 private final View.OnClickListener mOnClickHandler = new View.OnClickListener() { 60 @Override 61 public void onClick(View view) { 62 if (handleMediaTransfer(view)) { 63 return; 64 } 65 } 66 67 private boolean handleMediaTransfer(View view) { 68 if (view.findViewById(com.android.internal.R.id.media_seamless) == null) { 69 return false; 70 } 71 72 ViewParent parent = view.getParent(); 73 StatusBarNotification statusBarNotification = 74 getRowForParent(parent).getEntry().getSbn(); 75 mMediaOutputDialogFactory.create(statusBarNotification.getPackageName(), true); 76 return true; 77 } 78 }; 79 80 private final LocalMediaManager.DeviceCallback mMediaDeviceCallback = 81 new LocalMediaManager.DeviceCallback() { 82 @Override 83 public void onDeviceListUpdate(List<MediaDevice> devices) { 84 MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); 85 // Check because this can be called several times while changing devices 86 if (mDevice == null || !mDevice.equals(currentDevice)) { 87 mDevice = currentDevice; 88 updateAllChips(); 89 } 90 } 91 92 @Override 93 public void onSelectedDeviceStateChanged(MediaDevice device, int state) { 94 if (mDevice == null || !mDevice.equals(device)) { 95 mDevice = device; 96 updateAllChips(); 97 } 98 } 99 }; 100 MediaTransferManager(Context context)101 public MediaTransferManager(Context context) { 102 mContext = context; 103 mMediaOutputDialogFactory = Dependency.get(MediaOutputDialogFactory.class); 104 LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class); 105 InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm); 106 mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null); 107 } 108 109 /** 110 * Mark a view as removed. If no views remain the media device listener will be unregistered. 111 * @param root 112 */ setRemoved(View root)113 public void setRemoved(View root) { 114 if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER) 115 || mLocalMediaManager == null || root == null) { 116 return; 117 } 118 View view = root.findViewById(com.android.internal.R.id.media_seamless); 119 if (mViews.remove(view)) { 120 if (mViews.size() == 0) { 121 mLocalMediaManager.unregisterCallback(mMediaDeviceCallback); 122 } 123 } else { 124 Log.e(TAG, "Tried to remove unknown view " + view); 125 } 126 } 127 getRowForParent(ViewParent parent)128 private ExpandableNotificationRow getRowForParent(ViewParent parent) { 129 while (parent != null) { 130 if (parent instanceof ExpandableNotificationRow) { 131 return ((ExpandableNotificationRow) parent); 132 } 133 parent = parent.getParent(); 134 } 135 return null; 136 } 137 138 /** 139 * apply the action button for MediaTransfer 140 * 141 * @param root The parent container of the view. 142 * @param entry The entry of MediaTransfer action button. 143 */ applyMediaTransferView(ViewGroup root, NotificationEntry entry)144 public void applyMediaTransferView(ViewGroup root, NotificationEntry entry) { 145 if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER) 146 || mLocalMediaManager == null || root == null) { 147 return; 148 } 149 150 View view = root.findViewById(com.android.internal.R.id.media_seamless); 151 if (view == null) { 152 return; 153 } 154 155 view.setVisibility(View.VISIBLE); 156 view.setOnClickListener(mOnClickHandler); 157 if (!mViews.contains(view)) { 158 mViews.add(view); 159 if (mViews.size() == 1) { 160 mLocalMediaManager.registerCallback(mMediaDeviceCallback); 161 } 162 } 163 164 // Initial update 165 mLocalMediaManager.startScan(); 166 mDevice = mLocalMediaManager.getCurrentConnectedDevice(); 167 updateChip(view); 168 } 169 updateAllChips()170 private void updateAllChips() { 171 for (View view : mViews) { 172 updateChip(view); 173 } 174 } 175 updateChip(View view)176 private void updateChip(View view) { 177 ExpandableNotificationRow enr = getRowForParent(view.getParent()); 178 int fgColor = enr.getNotificationHeader().getOriginalIconColor(); 179 ColorStateList fgTintList = ColorStateList.valueOf(fgColor); 180 int bgColor = enr.getCurrentBackgroundTint(); 181 182 // Update outline color 183 LinearLayout viewLayout = (LinearLayout) view; 184 RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); 185 GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); 186 rect.setStroke(2, fgColor); 187 rect.setColor(bgColor); 188 189 ImageView iconView = view.findViewById(com.android.internal.R.id.media_seamless_image); 190 TextView deviceName = view.findViewById(com.android.internal.R.id.media_seamless_text); 191 deviceName.setTextColor(fgTintList); 192 193 if (mDevice != null) { 194 Drawable icon = mDevice.getIcon(); 195 iconView.setVisibility(View.VISIBLE); 196 iconView.setImageTintList(fgTintList); 197 198 if (icon instanceof AdaptiveIcon) { 199 AdaptiveIcon aIcon = (AdaptiveIcon) icon; 200 aIcon.setBackgroundColor(bgColor); 201 iconView.setImageDrawable(aIcon); 202 } else { 203 iconView.setImageDrawable(icon); 204 } 205 deviceName.setText(mDevice.getName()); 206 } else { 207 // Reset to default 208 iconView.setVisibility(View.GONE); 209 deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); 210 } 211 } 212 } 213