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.settings.media;
18 
19 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
20 
21 import android.annotation.ColorInt;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.graphics.Bitmap;
25 import android.media.session.MediaController;
26 import android.net.Uri;
27 import android.util.Log;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.core.graphics.drawable.IconCompat;
31 import androidx.slice.Slice;
32 import androidx.slice.builders.ListBuilder;
33 import androidx.slice.builders.SliceAction;
34 
35 import com.android.settings.R;
36 import com.android.settings.Utils;
37 import com.android.settings.slices.CustomSliceable;
38 import com.android.settings.slices.SliceBackgroundWorker;
39 import com.android.settingslib.media.MediaOutputConstants;
40 
41 public class MediaOutputIndicatorSlice implements CustomSliceable {
42 
43     private static final String TAG = "MediaOutputIndSlice";
44 
45     private Context mContext;
46     private MediaOutputIndicatorWorker mWorker;
47 
MediaOutputIndicatorSlice(Context context)48     public MediaOutputIndicatorSlice(Context context) {
49         mContext = context;
50     }
51 
52     @Override
getSlice()53     public Slice getSlice() {
54         if (!isVisible()) {
55             return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
56                     .setIsError(true)
57                     .build();
58         }
59         final IconCompat icon = IconCompat.createWithResource(mContext,
60                 com.android.internal.R.drawable.ic_settings_bluetooth);
61         final CharSequence title = mContext.getString(R.string.media_output_label_title,
62                 Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
63         final SliceAction primarySliceAction = SliceAction.create(
64                 getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, title);
65 
66         @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
67         // To set an empty icon to indent the row
68         final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
69                 .setAccentColor(color)
70                 .addRow(new ListBuilder.RowBuilder()
71                         .setTitle(title)
72                         .setTitleItem(createEmptyIcon(), ListBuilder.ICON_IMAGE)
73                         .setSubtitle(getWorker().getCurrentConnectedMediaDevice().getName())
74                         .setPrimaryAction(primarySliceAction));
75         return listBuilder.build();
76     }
77 
createEmptyIcon()78     private IconCompat createEmptyIcon() {
79         final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
80         return IconCompat.createWithBitmap(bitmap);
81     }
82 
83     @Override
getUri()84     public Uri getUri() {
85         return MEDIA_OUTPUT_INDICATOR_SLICE_URI;
86     }
87 
88     @Override
getIntent()89     public Intent getIntent() {
90         // This Slice reflects active media device information and launch MediaOutputDialog. It does
91         // not contain its owned Slice data
92         return null;
93     }
94 
95     @Override
getSliceHighlightMenuRes()96     public int getSliceHighlightMenuRes() {
97         return R.string.menu_key_connected_devices;
98     }
99 
100     @Override
getBackgroundWorkerClass()101     public Class getBackgroundWorkerClass() {
102         return MediaOutputIndicatorWorker.class;
103     }
104 
getWorker()105     private MediaOutputIndicatorWorker getWorker() {
106         if (mWorker == null) {
107             mWorker = SliceBackgroundWorker.getInstance(getUri());
108         }
109         return mWorker;
110     }
111 
112     @VisibleForTesting
isVisible()113     boolean isVisible() {
114         // To decide Slice's visibility.
115         // Return true if
116         // 1. AudioMode is not in on-going call
117         // 2. worker is not null
118         // 3. Available devices are more than 0
119         // 4. The local media session is active and the state is playing.
120         return getWorker() != null
121                 && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
122                 && getWorker().getMediaDevices().size() > 0
123                 && getWorker().getActiveLocalMediaController() != null;
124     }
125 
126     @Override
onNotifyChange(Intent intent)127     public void onNotifyChange(Intent intent) {
128         final MediaController mediaController = getWorker().getActiveLocalMediaController();
129 
130         if (mediaController == null) {
131             Log.d(TAG, "No active local media controller");
132             return;
133         }
134         // Launch media output dialog
135         mContext.sendBroadcast(new Intent()
136                 .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
137                 .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
138                 .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
139                         mediaController.getSessionToken())
140                 .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
141                         mediaController.getPackageName()));
142         // Dismiss volume panel
143         mContext.sendBroadcast(new Intent()
144                 .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME)
145                 .setAction(MediaOutputConstants.ACTION_CLOSE_PANEL));
146     }
147 }
148