Add QS media player options
This adds a mini player in the QQS, and media carousel in QS Incorporates the WIP layout changes from ag/9415169 To enable: adb shell settings put system qs_media_player 1 then toggle dark mode, or adb shell stop && adb shell start Known issues with color overlays not updating until you press a button, and old sessions do not get automatically removed from the carousel. Test: manual Change-Id: Iaeda470a920cb115c28ec98f04d74f255e1d5a12
This commit is contained in:
@@ -41,6 +41,8 @@
|
||||
<dimen name="quick_qs_offset_height">48dp</dimen>
|
||||
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
|
||||
<dimen name="quick_qs_total_height">176dp</dimen>
|
||||
<!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
|
||||
<dimen name="quick_qs_total_height_with_media">224dp</dimen>
|
||||
<!-- Height of the bottom navigation / system bar. -->
|
||||
<dimen name="navigation_bar_height">48dp</dimen>
|
||||
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
|
||||
|
||||
@@ -1774,6 +1774,7 @@
|
||||
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
|
||||
<java-symbol type="dimen" name="quick_qs_offset_height" />
|
||||
<java-symbol type="dimen" name="quick_qs_total_height" />
|
||||
<java-symbol type="dimen" name="quick_qs_total_height_with_media" />
|
||||
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
|
||||
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
|
||||
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
|
||||
|
||||
100
packages/SystemUI/res/layout/qqs_media_panel.xml
Normal file
100
packages/SystemUI/res/layout/qqs_media_panel.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<!-- Layout for QQS media controls -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/qqs_media_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
>
|
||||
<!-- Top line: icon + artist name -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:gravity="center"
|
||||
>
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/header_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||
android:singleLine="true"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Second line: song name -->
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:gravity="center"/>
|
||||
|
||||
<!-- Bottom section: controls -->
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action0"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action1"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action2"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
124
packages/SystemUI/res/layout/qs_media_panel.xml
Normal file
124
packages/SystemUI/res/layout/qs_media_panel.xml
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<!-- Layout for media controls inside QSPanel carousel -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/qs_media_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal|fill_vertical"
|
||||
android:padding="10dp"
|
||||
>
|
||||
|
||||
<!-- placeholder for notification header -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/header"
|
||||
android:padding="3dp"
|
||||
android:layout_marginEnd="-12dp"
|
||||
/>
|
||||
|
||||
<!-- Top line: artist name -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/header_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||
android:singleLine="true"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Second line: song name -->
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:gravity="center"/>
|
||||
|
||||
<!-- Bottom section: controls -->
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action0"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action1"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action2"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action3"
|
||||
/>
|
||||
<ImageButton
|
||||
style="@android:style/Widget.Material.Button.Borderless.Small"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/action4"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -43,7 +43,7 @@
|
||||
<com.android.systemui.qs.QuickQSPanel
|
||||
android:id="@+id/quick_qs_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/quick_qs_status_icons"
|
||||
android:layout_marginStart="@dimen/qs_header_tile_margin_horizontal"
|
||||
android:layout_marginEnd="@dimen/qs_header_tile_margin_horizontal"
|
||||
|
||||
@@ -1164,4 +1164,11 @@
|
||||
|
||||
<!-- Size of the RAT type for CellularTile -->
|
||||
<dimen name="celltile_rat_type_size">10sp</dimen>
|
||||
|
||||
<dimen name="new_qs_vertical_margin">8dp</dimen>
|
||||
|
||||
<!-- Size of media cards in the QSPanel carousel -->
|
||||
<dimen name="qs_media_height">150dp</dimen>
|
||||
<dimen name="qs_media_width">350dp</dimen>
|
||||
<dimen name="qs_media_padding">8dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.qs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.qs.TileLayout.exactly
|
||||
|
||||
class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout {
|
||||
|
||||
protected val mRecords = ArrayList<QSPanel.TileRecord>()
|
||||
private var _listening = false
|
||||
private var smallTileSize = 0
|
||||
private val twoLineHeight
|
||||
get() = smallTileSize * 2 + cellMarginVertical
|
||||
private var cellMarginHorizontal = 0
|
||||
private var cellMarginVertical = 0
|
||||
|
||||
init {
|
||||
isFocusableInTouchMode = true
|
||||
clipChildren = false
|
||||
clipToPadding = false
|
||||
|
||||
updateResources()
|
||||
}
|
||||
|
||||
override fun addTile(tile: QSPanel.TileRecord) {
|
||||
mRecords.add(tile)
|
||||
tile.tile.setListening(this, _listening)
|
||||
addTileView(tile)
|
||||
}
|
||||
|
||||
protected fun addTileView(tile: QSPanel.TileRecord) {
|
||||
addView(tile.tileView)
|
||||
}
|
||||
|
||||
override fun removeTile(tile: QSPanel.TileRecord) {
|
||||
mRecords.remove(tile)
|
||||
tile.tile.setListening(this, false)
|
||||
removeView(tile.tileView)
|
||||
}
|
||||
|
||||
override fun removeAllViews() {
|
||||
mRecords.forEach { it.tile.setListening(this, false) }
|
||||
mRecords.clear()
|
||||
super.removeAllViews()
|
||||
}
|
||||
|
||||
override fun getOffsetTop(tile: QSPanel.TileRecord?) = top
|
||||
|
||||
override fun updateResources(): Boolean {
|
||||
with(mContext.resources) {
|
||||
smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
|
||||
cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
|
||||
cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
|
||||
}
|
||||
requestLayout()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setListening(listening: Boolean) {
|
||||
if (_listening == listening) return
|
||||
_listening = listening
|
||||
for (record in mRecords) {
|
||||
record.tile.setListening(this, listening)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNumVisibleTiles() = mRecords.size
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
updateResources()
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
updateResources()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
var previousView: View = this
|
||||
var tiles = 0
|
||||
|
||||
mRecords.forEach {
|
||||
val tileView = it.tileView
|
||||
if (tileView.visibility != View.GONE) {
|
||||
tileView.updateAccessibilityOrder(previousView)
|
||||
previousView = tileView
|
||||
tiles++
|
||||
tileView.measure(exactly(smallTileSize), exactly(smallTileSize))
|
||||
}
|
||||
}
|
||||
|
||||
val height = twoLineHeight
|
||||
val columns = tiles / 2
|
||||
val width = paddingStart + paddingEnd +
|
||||
columns * smallTileSize +
|
||||
(columns - 1) * cellMarginHorizontal
|
||||
setMeasuredDimension(width, height)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
val tiles = mRecords.filter { it.tileView.visibility != View.GONE }
|
||||
tiles.forEachIndexed {
|
||||
index, tile ->
|
||||
val column = index % (tiles.size / 2)
|
||||
val left = getLeftForColumn(column)
|
||||
val top = if (index < tiles.size / 2) 0 else getTopBottomRow()
|
||||
tile.tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLeftForColumn(column: Int) = column * (smallTileSize + cellMarginHorizontal)
|
||||
|
||||
private fun getTopBottomRow() = smallTileSize + cellMarginVertical
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
@@ -267,6 +268,17 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
|
||||
mAllViews.add(tileView);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(),
|
||||
"qs_media_player", 0);
|
||||
if (flag == 1) {
|
||||
View qsMediaView = mQsPanel.getMediaPanel();
|
||||
View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView();
|
||||
translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1);
|
||||
translationXBuilder.addFloat(qqsMediaView, "alpha", 1, 0);
|
||||
}
|
||||
|
||||
if (mAllowFancy) {
|
||||
// Make brightness appear static position and alpha in through second half.
|
||||
View brightness = mQsPanel.getBrightnessView();
|
||||
|
||||
301
packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
Normal file
301
packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.qs;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
import com.android.settingslib.media.MediaOutputSliceConstants;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.statusbar.MediaTransferManager;
|
||||
|
||||
/**
|
||||
* Single media player for carousel in QSPanel
|
||||
*/
|
||||
public class QSMediaPlayer {
|
||||
|
||||
private static final String TAG = "QSMediaPlayer";
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mMediaNotifView;
|
||||
private MediaSession.Token mToken;
|
||||
private MediaController mController;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context
|
||||
* @param parent
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) {
|
||||
mContext = context;
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false);
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mMediaNotifView;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param token token for this media session
|
||||
* @param icon app notification icon
|
||||
* @param iconColor foreground color (for text, icons)
|
||||
* @param bgColor background color
|
||||
* @param actionsContainer a LinearLayout containing the media action buttons
|
||||
* @param notif
|
||||
*/
|
||||
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
|
||||
View actionsContainer, Notification notif) {
|
||||
Log.d(TAG, "got media session: " + token);
|
||||
mToken = token;
|
||||
mController = new MediaController(mContext, token);
|
||||
MediaMetadata mMediaMetadata = mController.getMetadata();
|
||||
Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
|
||||
|
||||
// Album art
|
||||
addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight);
|
||||
|
||||
// Reuse notification header instead of reimplementing everything
|
||||
RemoteViews headerRemoteView = builder.makeNotificationHeader();
|
||||
LinearLayout headerView = mMediaNotifView.findViewById(R.id.header);
|
||||
View result = headerRemoteView.apply(mContext, headerView);
|
||||
result.setPadding(0, 0, 0, 0);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, 75);
|
||||
result.setLayoutParams(lp);
|
||||
headerView.removeAllViews();
|
||||
headerView.addView(result);
|
||||
|
||||
View seamless = headerView.findViewById(com.android.internal.R.id.media_seamless);
|
||||
seamless.setVisibility(View.VISIBLE);
|
||||
|
||||
// App icon
|
||||
ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon);
|
||||
Drawable iconDrawable = icon.loadDrawable(mContext);
|
||||
iconDrawable.setTint(iconColor);
|
||||
appIcon.setImageDrawable(iconDrawable);
|
||||
|
||||
// App title
|
||||
TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text);
|
||||
String appNameString = builder.loadHeaderAppName();
|
||||
appName.setText(appNameString);
|
||||
appName.setTextColor(iconColor);
|
||||
|
||||
// Action
|
||||
mMediaNotifView.setOnClickListener(v -> {
|
||||
try {
|
||||
notif.contentIntent.send();
|
||||
// Also close shade
|
||||
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.e(TAG, "Pending intent was canceled");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
// Separator
|
||||
TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider);
|
||||
separator.setTextColor(iconColor);
|
||||
|
||||
// Album name
|
||||
TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
|
||||
String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
|
||||
if (!albumString.isEmpty()) {
|
||||
albumName.setText(albumString);
|
||||
albumName.setTextColor(iconColor);
|
||||
albumName.setVisibility(View.VISIBLE);
|
||||
separator.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
albumName.setVisibility(View.GONE);
|
||||
separator.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Transfer chip
|
||||
MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext);
|
||||
View transferBackgroundView = headerView.findViewById(
|
||||
com.android.internal.R.id.media_seamless);
|
||||
LinearLayout viewLayout = (LinearLayout) transferBackgroundView;
|
||||
RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
|
||||
GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
|
||||
rect.setStroke(2, iconColor);
|
||||
rect.setColor(bgColor);
|
||||
ImageView transferIcon = headerView.findViewById(
|
||||
com.android.internal.R.id.media_seamless_image);
|
||||
transferIcon.setBackgroundColor(bgColor);
|
||||
transferIcon.setImageTintList(ColorStateList.valueOf(iconColor));
|
||||
TextView transferText = headerView.findViewById(
|
||||
com.android.internal.R.id.media_seamless_text);
|
||||
transferText.setTextColor(iconColor);
|
||||
|
||||
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
|
||||
transferBackgroundView.setOnClickListener(v -> {
|
||||
final Intent intent = new Intent()
|
||||
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
|
||||
mActivityStarter.startActivity(intent, false, true /* dismissShade */,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
});
|
||||
|
||||
// Artist name
|
||||
TextView artistText = mMediaNotifView.findViewById(R.id.header_title);
|
||||
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
|
||||
artistText.setText(artistName);
|
||||
artistText.setTextColor(iconColor);
|
||||
|
||||
// Song name
|
||||
TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
|
||||
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
|
||||
titleText.setText(songName);
|
||||
titleText.setTextColor(iconColor);
|
||||
|
||||
// Media controls
|
||||
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
||||
final int[] actionIds = {
|
||||
R.id.action0,
|
||||
R.id.action1,
|
||||
R.id.action2,
|
||||
R.id.action3,
|
||||
R.id.action4
|
||||
};
|
||||
final int[] notifActionIds = {
|
||||
com.android.internal.R.id.action0,
|
||||
com.android.internal.R.id.action1,
|
||||
com.android.internal.R.id.action2,
|
||||
com.android.internal.R.id.action3,
|
||||
com.android.internal.R.id.action4
|
||||
};
|
||||
for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) {
|
||||
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
|
||||
ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]);
|
||||
if (thatBtn == null || thatBtn.getDrawable() == null) {
|
||||
thisBtn.setVisibility(View.GONE);
|
||||
continue;
|
||||
}
|
||||
|
||||
Drawable thatIcon = thatBtn.getDrawable();
|
||||
thisBtn.setImageDrawable(thatIcon.mutate());
|
||||
thisBtn.setVisibility(View.VISIBLE);
|
||||
thisBtn.setOnClickListener(v -> {
|
||||
Log.d(TAG, "clicking on other button");
|
||||
thatBtn.performClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public MediaSession.Token getMediaSessionToken() {
|
||||
return mToken;
|
||||
}
|
||||
|
||||
public String getMediaPlayerPackage() {
|
||||
return mController.getPackageName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the media controlled by this player is currently playing
|
||||
* @return whether it is playing, or false if no controller information
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
if (mController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlaybackState state = mController.getPlaybackState();
|
||||
if (state == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (state.getState() == PlaybackState.STATE_PLAYING);
|
||||
}
|
||||
|
||||
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
|
||||
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
|
||||
if (albumArt != null) {
|
||||
|
||||
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
|
||||
Bitmap scaled = scaleBitmap(original, width, height);
|
||||
Canvas canvas = new Canvas(scaled);
|
||||
|
||||
// Add translucent layer over album art to improve contrast
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
p.setColor(bgColor);
|
||||
p.setAlpha(200);
|
||||
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
|
||||
|
||||
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
|
||||
mContext.getResources(), scaled);
|
||||
roundedDrawable.setCornerRadius(20);
|
||||
|
||||
mMediaNotifView.setBackground(roundedDrawable);
|
||||
} else {
|
||||
Log.e(TAG, "No album art available");
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap scaleBitmap(Bitmap original, int width, int height) {
|
||||
Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(cropped);
|
||||
|
||||
float scale = (float) cropped.getWidth() / (float) original.getWidth();
|
||||
float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
|
||||
Matrix transformation = new Matrix();
|
||||
transformation.postTranslate(0, dy);
|
||||
transformation.preScale(scale, scale);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setFilterBitmap(true);
|
||||
canvas.drawBitmap(original, transformation, paint);
|
||||
|
||||
return cropped;
|
||||
}
|
||||
}
|
||||
@@ -24,16 +24,22 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.session.MediaSession;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
@@ -82,6 +88,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
|
||||
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
|
||||
private final QSTileRevealController mQsTileRevealController;
|
||||
|
||||
private final LinearLayout mMediaCarousel;
|
||||
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
|
||||
|
||||
protected boolean mExpanded;
|
||||
protected boolean mListening;
|
||||
|
||||
@@ -140,6 +149,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
|
||||
|
||||
addDivider();
|
||||
|
||||
// Add media carousel
|
||||
int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
|
||||
if (flag == 1) {
|
||||
HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext);
|
||||
mediaScrollView.setHorizontalScrollBarEnabled(false);
|
||||
int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
|
||||
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
|
||||
LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight);
|
||||
lpView.setMarginStart(padding);
|
||||
lpView.setMarginEnd(padding);
|
||||
addView(mediaScrollView, lpView);
|
||||
|
||||
LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
mMediaCarousel = new LinearLayout(mContext);
|
||||
mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL);
|
||||
mediaScrollView.addView(mMediaCarousel, lpCarousel);
|
||||
} else {
|
||||
mMediaCarousel = null;
|
||||
}
|
||||
|
||||
mFooter = new QSSecurityFooter(this, context);
|
||||
addView(mFooter.getView());
|
||||
|
||||
@@ -159,6 +189,72 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update a player for the associated media session
|
||||
* @param token
|
||||
* @param icon
|
||||
* @param iconColor
|
||||
* @param bgColor
|
||||
* @param actionsContainer
|
||||
* @param notif
|
||||
*/
|
||||
public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
|
||||
View actionsContainer, StatusBarNotification notif) {
|
||||
int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
|
||||
if (flag != 1) {
|
||||
// Shouldn't happen, but just in case
|
||||
Log.e(TAG, "Tried to add media session without player!");
|
||||
return;
|
||||
}
|
||||
QSMediaPlayer player = null;
|
||||
String packageName = notif.getPackageName();
|
||||
for (QSMediaPlayer p : mMediaPlayers) {
|
||||
if (p.getMediaSessionToken().equals(token)) {
|
||||
Log.d(TAG, "a player for this session already exists");
|
||||
player = p;
|
||||
break;
|
||||
}
|
||||
|
||||
if (packageName.equals(p.getMediaPlayerPackage())) {
|
||||
Log.d(TAG, "found an old session for this app");
|
||||
player = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
|
||||
int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
|
||||
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
|
||||
LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
lp.setMarginStart(padding);
|
||||
lp.setMarginEnd(padding);
|
||||
|
||||
if (player == null) {
|
||||
Log.d(TAG, "creating new player");
|
||||
|
||||
player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight);
|
||||
|
||||
if (player.isPlaying()) {
|
||||
mMediaCarousel.addView(player.getView(), 0, lp); // add in front
|
||||
} else {
|
||||
mMediaCarousel.addView(player.getView(), lp); // add at end
|
||||
}
|
||||
} else if (player.isPlaying()) {
|
||||
// move it to the front
|
||||
mMediaCarousel.removeView(player.getView());
|
||||
mMediaCarousel.addView(player.getView(), 0, lp);
|
||||
}
|
||||
|
||||
Log.d(TAG, "setting player session");
|
||||
player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
|
||||
notif.getNotification());
|
||||
mMediaPlayers.add(player);
|
||||
}
|
||||
|
||||
protected View getMediaPanel() {
|
||||
return mMediaCarousel;
|
||||
}
|
||||
|
||||
protected void addDivider() {
|
||||
mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
|
||||
mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.qs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* QQS mini media player
|
||||
*/
|
||||
public class QuickQSMediaPlayer {
|
||||
|
||||
private static final String TAG = "QQSMediaPlayer";
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mMediaNotifView;
|
||||
private MediaSession.Token mToken;
|
||||
private MediaController mController;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context
|
||||
* @param parent
|
||||
*/
|
||||
public QuickQSMediaPlayer(Context context, ViewGroup parent) {
|
||||
mContext = context;
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qqs_media_panel, parent, false);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mMediaNotifView;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param token token for this media session
|
||||
* @param icon app notification icon
|
||||
* @param iconColor foreground color (for text, icons)
|
||||
* @param bgColor background color
|
||||
* @param actionsContainer a LinearLayout containing the media action buttons
|
||||
*/
|
||||
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
|
||||
View actionsContainer) {
|
||||
Log.d(TAG, "Setting media session: " + token);
|
||||
mToken = token;
|
||||
mController = new MediaController(mContext, token);
|
||||
MediaMetadata mMediaMetadata = mController.getMetadata();
|
||||
|
||||
// Album art
|
||||
addAlbumArtBackground(mMediaMetadata, bgColor);
|
||||
|
||||
// App icon
|
||||
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
|
||||
Drawable iconDrawable = icon.loadDrawable(mContext);
|
||||
iconDrawable.setTint(iconColor);
|
||||
appIcon.setImageDrawable(iconDrawable);
|
||||
|
||||
// Artist name
|
||||
TextView appText = mMediaNotifView.findViewById(R.id.header_title);
|
||||
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
|
||||
appText.setText(artistName);
|
||||
appText.setTextColor(iconColor);
|
||||
|
||||
// Song name
|
||||
TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
|
||||
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
|
||||
titleText.setText(songName);
|
||||
titleText.setTextColor(iconColor);
|
||||
|
||||
// Action buttons
|
||||
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
||||
final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
|
||||
|
||||
// TODO some apps choose different buttons to show in compact mode
|
||||
final int[] notifActionIds = {
|
||||
com.android.internal.R.id.action1,
|
||||
com.android.internal.R.id.action2,
|
||||
com.android.internal.R.id.action3
|
||||
};
|
||||
for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) {
|
||||
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
|
||||
ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]);
|
||||
if (thatBtn == null || thatBtn.getDrawable() == null) {
|
||||
thisBtn.setVisibility(View.GONE);
|
||||
continue;
|
||||
}
|
||||
|
||||
Drawable thatIcon = thatBtn.getDrawable();
|
||||
thisBtn.setImageDrawable(thatIcon.mutate());
|
||||
thisBtn.setVisibility(View.VISIBLE);
|
||||
|
||||
thisBtn.setOnClickListener(v -> {
|
||||
Log.d(TAG, "clicking on other button");
|
||||
thatBtn.performClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public MediaSession.Token getMediaSessionToken() {
|
||||
return mToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the media controlled by this player is currently playing
|
||||
* @return whether it is playing, or false if no controller information
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
if (mController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlaybackState state = mController.getPlaybackState();
|
||||
if (state == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (state.getState() == PlaybackState.STATE_PLAYING);
|
||||
}
|
||||
|
||||
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
|
||||
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
|
||||
if (albumArt != null) {
|
||||
Rect bounds = new Rect();
|
||||
mMediaNotifView.getBoundsOnScreen(bounds);
|
||||
int width = bounds.width();
|
||||
int height = bounds.height();
|
||||
|
||||
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
|
||||
Bitmap scaled = scaleBitmap(original, width, height);
|
||||
Canvas canvas = new Canvas(scaled);
|
||||
|
||||
// Add translucent layer over album art to improve contrast
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
p.setColor(bgColor);
|
||||
p.setAlpha(200);
|
||||
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
|
||||
|
||||
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
|
||||
mContext.getResources(), scaled);
|
||||
roundedDrawable.setCornerRadius(20);
|
||||
|
||||
mMediaNotifView.setBackground(roundedDrawable);
|
||||
} else {
|
||||
Log.e(TAG, "No album art available");
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap scaleBitmap(Bitmap original, int width, int height) {
|
||||
Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(cropped);
|
||||
|
||||
float scale = (float) cropped.getWidth() / (float) original.getWidth();
|
||||
float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
|
||||
Matrix transformation = new Matrix();
|
||||
transformation.postTranslate(0, dy);
|
||||
transformation.preScale(scale, scale);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setFilterBitmap(true);
|
||||
canvas.drawBitmap(original, transformation, paint);
|
||||
|
||||
return cropped;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.provider.Settings;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
@@ -55,6 +56,7 @@ public class QuickQSPanel extends QSPanel {
|
||||
private boolean mDisabledByPolicy;
|
||||
private int mMaxTiles;
|
||||
protected QSPanel mFullPanel;
|
||||
private QuickQSMediaPlayer mMediaPlayer;
|
||||
|
||||
@Inject
|
||||
public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
|
||||
@@ -69,11 +71,43 @@ public class QuickQSPanel extends QSPanel {
|
||||
}
|
||||
removeView((View) mTileLayout);
|
||||
}
|
||||
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
|
||||
mTileLayout = new HeaderTileLayout(context);
|
||||
mTileLayout.setListening(mListening);
|
||||
addView((View) mTileLayout, 0 /* Between brightness and footer */);
|
||||
super.setPadding(0, 0, 0, 0);
|
||||
|
||||
int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
|
||||
if (flag == 1) {
|
||||
LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext);
|
||||
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
mHorizontalLinearLayout.setClipChildren(false);
|
||||
mHorizontalLinearLayout.setClipToPadding(false);
|
||||
|
||||
LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
|
||||
|
||||
mTileLayout = new DoubleLineTileLayout(context);
|
||||
lp.setMarginEnd(10);
|
||||
lp.setMarginStart(0);
|
||||
mHorizontalLinearLayout.addView((View) mTileLayout, lp);
|
||||
|
||||
mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
|
||||
|
||||
lp.setMarginEnd(0);
|
||||
lp.setMarginStart(10);
|
||||
mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp);
|
||||
|
||||
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
|
||||
|
||||
mTileLayout.setListening(mListening);
|
||||
addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
|
||||
super.setPadding(0, 0, 0, 0);
|
||||
} else {
|
||||
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
|
||||
mTileLayout = new HeaderTileLayout(context);
|
||||
mTileLayout.setListening(mListening);
|
||||
addView((View) mTileLayout, 0 /* Between brightness and footer */);
|
||||
super.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public QuickQSMediaPlayer getMediaPlayer() {
|
||||
return mMediaPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -392,9 +392,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
|
||||
mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
|
||||
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
|
||||
|
||||
int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
|
||||
if (mQsDisabled) {
|
||||
lp.height = resources.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.quick_qs_offset_height);
|
||||
} else if (flag == 1) {
|
||||
lp.height = Math.max(getMinimumHeight(),
|
||||
resources.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.quick_qs_total_height_with_media));
|
||||
} else {
|
||||
lp.height = Math.max(getMinimumHeight(),
|
||||
resources.getDimensionPixelSize(
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -86,6 +87,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
private final BubbleController mBubbleController;
|
||||
private final DynamicPrivacyController mDynamicPrivacyController;
|
||||
private final KeyguardBypassController mBypassController;
|
||||
private final Context mContext;
|
||||
|
||||
private NotificationPresenter mPresenter;
|
||||
private NotificationListContainer mListContainer;
|
||||
@@ -107,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
KeyguardBypassController bypassController,
|
||||
BubbleController bubbleController,
|
||||
DynamicPrivacyController privacyController) {
|
||||
mContext = context;
|
||||
mHandler = mainHandler;
|
||||
mLockscreenUserManager = notificationLockscreenUserManager;
|
||||
mBypassController = bypassController;
|
||||
@@ -143,7 +146,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
final int N = activeNotifications.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
NotificationEntry ent = activeNotifications.get(i);
|
||||
int flag = Settings.System.getInt(mContext.getContentResolver(),
|
||||
"qs_media_player", 0);
|
||||
boolean hideMedia = (flag == 1);
|
||||
if (ent.isRowDismissed() || ent.isRowRemoved()
|
||||
|| (ent.isMediaNotification() && hideMedia)
|
||||
|| mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) {
|
||||
// we don't want to update removed notifications because they could
|
||||
// temporarily become children if they were isolated before.
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -41,9 +42,12 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.MediaNotificationView;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.qs.QSPanel;
|
||||
import com.android.systemui.qs.QuickQSPanel;
|
||||
import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
import com.android.systemui.statusbar.TransformableView;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.phone.StatusBarWindowController;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -178,6 +182,26 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
|
||||
final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
|
||||
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
|
||||
|
||||
int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
|
||||
if (flag == 1) {
|
||||
StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
|
||||
QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
|
||||
com.android.systemui.R.id.quick_qs_panel);
|
||||
panel.getMediaPlayer().setMediaSession(token,
|
||||
mRow.getStatusBarNotification().getNotification().getSmallIcon(),
|
||||
getNotificationHeader().getOriginalIconColor(),
|
||||
mRow.getCurrentBackgroundTint(),
|
||||
mActions);
|
||||
QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
|
||||
com.android.systemui.R.id.quick_settings_panel);
|
||||
bigPanel.addMediaSession(token,
|
||||
mRow.getStatusBarNotification().getNotification().getSmallIcon(),
|
||||
getNotificationHeader().getOriginalIconColor(),
|
||||
mRow.getCurrentBackgroundTint(),
|
||||
mActions,
|
||||
mRow.getStatusBarNotification());
|
||||
}
|
||||
|
||||
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
|
||||
if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) {
|
||||
if (mSeekBarView != null) {
|
||||
|
||||
@@ -770,6 +770,11 @@ public class NotificationPanelView extends PanelView implements
|
||||
int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
|
||||
int topMargin =
|
||||
res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
|
||||
int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
|
||||
if (flag == 1) {
|
||||
topMargin = res.getDimensionPixelOffset(
|
||||
com.android.internal.R.dimen.quick_qs_total_height_with_media);
|
||||
}
|
||||
lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
|
||||
if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
|
||||
|| lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
|
||||
|
||||
Reference in New Issue
Block a user